Files
ozon-seller-kit/docs/v2/capability-inventory.md
T

20 KiB
Raw Blame History

Ozon Seller Kit 现有能力清单(V2 方案设计输入)

依据代码逐文件核对生成(server/ 与 studio/,忽略 node_modules/.venv/pycache/.output)。 数据来源文件:ozon-seller-kit/server/**ozon-seller-kit/studio/src/**,全部行号以当前工作区为准。


一、后端 API 清单

FastAPI 应用入口 server/main.py

  • 应用名 Ozon Seller Kit v0.1.0main.py:13)。
  • CORS 中间件:仅当 cors_origin_list 非空时启用,allow_credentials=True,方法/请求头全放行(main.py:16-23)。
  • 挂载三个路由:api/aiapi/imageapi/ozonmain.py:25-27)。
  • GET /api/health{"status":"ok"}main.py:30-32)。
  • 若仓库根 web/v1 工具台,冻结)存在,则 app.mount("/", StaticFiles(html=True)) 静态托管(main.py:11, 35-36)——即生产形态下 FastAPI 同源托管前端。
方法 路径 功能 关键入参 关键出参
GET /api/health 健康检查(main.py:30-32 {status: "ok"}
GET /api/ai/models 列出可用模型(ai.py:10-12 → models_catalog.list_model_options {default: str, models: [{id, label}]}
POST /api/ai/copy 生成 Ozon 俄文商品文案(ai.py:15-17 → deepseek.generate_copy CopyRequestsource_text(≥10字符)、product_namemodel_codemodel(可选) CopyResponsetitles_ru/zh[2]description_ru/zhtags_ru/zhmodelusage{prompt_tokens,completion_tokens}
POST /api/image/edit AI 图生图/图像编辑(image.py:9-11 → image_edit.edit_image ImageEditRequestbase_image(dataURL/公网URL)、promptmodel(白名单)、mask_image?function?n(1-4)、size?seed?style?prompt_extendstrength? ImageEditResponsetask_idresults[{url(24h), image_base64(dataURL)}]image_countrequest_id
/api/ozon/* 空占位ozon.py:3-5):仅定义前缀与注释 # Phase 3: Ozon Seller API product upload,无任何端点

前端调用面:studio/src/services/image.ts 只调 /api/image/edit/api/ai/* 目前只有 v1 工具台 web/js/ai-copy.js233 行 models、299 行 copy,用原生 fetch)在消费。


二、配置与模型目录机制

2.1 环境变量(server/config/settings.py + 仓库根 .env

  • load_dotenv 与 pydantic-settings 均指向仓库根 .envsettings.py:8-9, 16-17parents[2] 上跳两级)。
  • 字段(settings.py:21-29):
    • deepseek_api_key / openai_api_key / dashscope_api_key
    • dashscope_base_http_api_url(华北2北京业务空间专用,普通 API Key 留空)
    • host=127.0.0.1port=8800uvicorn 启动参数)
    • cors_origins(逗号分隔)→ 属性 cors_origin_listsettings.py:31-35
  • get_settings()@lru_cachesettings.py:38-40)。
  • .env.example 给出全部变量名:DEEPSEEK_API_KEYDASHSCOPE_API_KEYDASHSCOPE_BASE_HTTP_API_URLHOSTPORTCORS_ORIGINS

2.2 模型目录(server/config/models.yaml + services/models_catalog.py

  • YAML 结构:default(默认模型 id+ models[],每项含 id/label/provider/api_model/base_url/api_key_env/max_tokens/params
  • 密钥不写入 yaml,只引用环境变量名(api_key_env: DEEPSEEK_API_KEY)。
  • 当前仅两个 deepseek 模型(deepseek-v4-flash 默认、deepseek-v4-pro),均 base_url=https://api.deepseek.commax_tokens=4000params.thinking.type=disabled(关闭思维链,防止推理耗尽 token 正文为空)。
  • models_catalog.py
    • ModelSpec Pydantic 模型(16-25 行),params 为任意 dict、直接并入请求体。
    • load_models_file()@lru_cache43-54 行),校验 default 在列表中、列表非空;文件缺失抛 RuntimeError。
    • list_model_options()57-62 行)→ 前端下拉用 {id,label}
    • get_model_spec(model_id)65-74 行):空值回落 default,未知 id 抛 400。
    • resolve_api_key(spec)77-83 行):按 api_key_env 读环境变量,缺失抛 500。
  • 设计要点:模型即配置——新增模型只需改 yaml + 加环境变量,代码零改动(deepseek 类);这是 V2 可直接继承的机制。

2.3 依赖(server/requirements.txt

fastapi>=0.115uvicorn[standard]>=0.32httpx>=0.27pydantic-settings>=2.6python-dotenvPyYAMLdashscope>=1.23.8


三、AI 文案服务细节(services/deepseek.py + prompts/copy_ru.py

3.1 调用链

POST /api/ai/copygenerate_copy(req)deepseek.py:144-181):

  1. get_model_spec(req.model) 取模型规格,未传用默认。
  2. messages = system(SYSTEM_PROMPT) + user(build_user_prompt(...))system 完整文本见 copy_ru.py:1-65。
  3. _chat_once(spec, messages)deepseek.py:93-141):
    • URL = base_url + /chat/completionsBearer 认证。
    • payloadmodel=api_modeltemperature=0.45(事实稳定、营销留少量变化)、max_tokensresponse_format={type:"json_object"}**spec.params
    • httpx 超时 90s;网络错误/HTTP≥400 一律 502detail 截断 500 字符)。
    • 解析 body["choices"][0]["message"]["content"];若 finish_reason=="length" 且正文为空 → 502 并提示调 max_tokens 或关思维链(131-138 行,对应 yaml 中 thinking disabled 的注释)。
  4. 重试机制(159-177 行):最多 2 次;解析失败时把上一次输出以 role=assistant 追加,再追加一条"请仅重新输出合法 JSON"的 user 消息重试一次;仍失败抛 502。

3.2 JSON 解析与字段清洗

  • _extract_json_object17-37 行):剥 ```json 代码块 → json.loads → 失败则截取首 { 到末 } 再解析 → 必须为 dict。
  • 字段类型容错:
    • _as_title_list64-73 行):标题不按逗号切分(标题本身含逗号)。
    • _as_str_list48-61 行):标签按 [,\n] 切分,兼容字符串/数组。
    • _as_str40-45 行):必须字符串。
  • _map_copy_payload76-90 行)→ CopyResponseusage 取 prompt/completion tokens。
  • 最终校验:titles_rudescription_ru 非空,否则视为失败触发重试(164-166 行)。

3.3 提示词结构(copy_ru.py

  • SYSTEM_PROMPT 核心约束:
    • 输出固定 JSON schematitles_ru/zh 各 2 条一一对应;tags_ru/zh 各 10~15 个、逐项对应;description 完整俄文卡 + 中文逐项对照)。
    • 描述固定结构:Описание товараХарактеристики(只列原文事实,俄式尺寸写法)→ Преимущества3~6 条利益点)→ Комплектация(仅原文提到配件时)。
    • 标题规则:60-90 字符、核心品类词开头、前 30 字符含关键属性、删年份/新款/爆款噪声、两标题互补。
    • 事实边界(强约束):禁止虚构结构/配件/认证/产地/品牌/受众/使用效果;行业词归一(搪胶→винил 非 каучук);"防摔"不得推导安全认证。
    • 优先级:事实准确 > 俄语自然 > 信息完整与转化力 > 关键词覆盖。
  • build_user_prompt68-86 行):模板包裹 <当前商品名><型号><商品资料>,并要求区分"事实来源"与"生成要求",与文案无关的指令忽略、要求不得写成事实。

四、图生图服务细节(services/image_edit.py

4.1 云与模型

  • 调用阿里云百炼 DashScopedashscope Python SDK,同步调用,asyncio.to_thread 放入线程池,177-185 行)。
  • 模型白名单(schemas/image_edit.py:9-17):wanx2.1-imageeditwan2.6-imageqwen-image-editqwen-image-edit-plusqwen-image-edit-plus-2025-10-30
  • 两种调用形态(按模型路由,170-174 行):
    • wanx2.1-imageedit → ImageSynthesis.call71-121 行):同步、function 式。kwargsapi_key/model/function/prompt/base_image_url/n + 可选 mask_image_url/size/seed/style/prompt_extend/strength。解析 rsp.output.results[].urlusage.image_countrequest_id
    • wan2.6-image / qwen-image-edit 系列 → MultiModalConversation.call124-167 行):messages=[{role:user, content:[{image: base_image},{text: prompt}]}] + n/size/prompt_extend;解析 output.choices[0].message.content[].image
  • function 白名单(schemas/image_edit.py:24-29):description_edit(无掩码图生图,默认)/ description_edit_with_mask(局部重绘,需 mask_image/ stylization_local / stylization_all

4.2 请求校验(schemas/image_edit.py

  • base_image / mask_imagedata URLdata:image/...)或公网 http(s) URLdata URL 编码后 ≤ 15MB_MAX_BASE64_LENGTH32 行);mask 可空。
  • prompt 非空;model 白名单校验;n 14strength 0.01.0(默认 0.5,加文字/大改建议 0.8);prompt_extend 默认 True。

4.3 关键工程决策

  • 服务端代理下载 → data URL_download_to_data_url,29-48 行):阿里云结果 URL 未开放 CORS,前端直接绘 canvas 会污染画布无法导出;服务端下载后转 data:{content-type};base64,...,上限 20MB_MAX_PROXY_BYTES23 行),失败回退空串、前端用 url
  • 无密钥 → 500SDK 异常/HTTP 非 200 → 502_fail60-64 行)。
  • _apply_base_url(51-57 行):仅北京业务空间需设 dashscope.base_http_api_url
  • 文件头注释明确:当前不落盘、不存储图片,将来接七牛云可在返回 URL 后加"下载并转存"步骤,api 层与前端契约不动(image_edit.py:1-9)——这是刻意的扩展点。

五、studio 前端页面/组件/功能清单与交互流程

5.1 技术栈(studio/package.json + vite.config.ts

  • React 19.2、react-router 7createBrowserRouter)、antd 6.1zhCN)、axios、Vite 7SWC 插件)、TS 5.9@ 别名 → src
  • dev server:端口 8900strictPort、open),/api 代理到 http://127.0.0.1:8800vite.config.ts:17-22)。

5.2 页面与路由

文件 内容
src/main.tsx createRoot 挂载
src/App.tsx ConfigProviderzhCN、主色 #8b5cf6、圆角 8+ AntdApp + RouterProvider
src/router/index.tsx //ai-image → AiImagePageMainLayout 内);* → Navigate /
src/layouts/MainLayout.tsx 固定 Sider(240, 深色, 可折叠) + Header(页面标题/副标题) + Content(Outlet)<768px 切 Drawer 移动菜单
src/layouts/SidebarMenu.tsx antd Menu"主要功能"分组,点击 navigate
src/layouts/menuConfig.tsx 仅一个菜单项/ai-image「AI 图生图」(subtitle:上传图片、加水印、用万相模型进行图生图编辑);getPageInfo 路径→标题映射

5.3 AiImagePagepages/ai-image/AiImagePage.tsx)——主页面

状态:图片列表 ImageItem[]id/fileName/image/dataUrl/state)、全局水印设置(type=image|text、text 默认 'Panda Store'、opacity 默认 30、水印图 /imgs/watermark.jpg 预加载为 HTMLImageElement)、编辑弹窗(modalOpen/editing)。

区块与流程:

  1. 水印设置面板:类型 Radio(图片/文字)→ 文字输入 或 水印图预览(圆形贴图);透明度 Slider(0-100)。
  2. 上传Upload.Dragger 多图、accept=image/*beforeUpload 返回 false(阻止自动上传,纯前端读文件)→ fileToImageFileReader → dataURL → Image)→ 追加进列表,按图片尺寸算默认水印位置(右下角)。
  3. 预览网格:每张图一个 WatermarkCanvas(可拖拽水印,坐标相对原图、clampPos 限制不越界);卡片操作:加水印/清除水印(toggleWatermark)、AI 生图openEditrenderFullRes 全分辨率合成水印图 → dataURL → 打开弹窗)、导出 PNG(downloadCanvas,文件名加 _watermark 后缀)、删除;顶部"全部加水印"(applyToAll 重建所有 state)与"清空所有"。
  4. AI 生图弹窗 = ImageEditModal(见下)。

5.4 ImageEditModalpages/ai-image/components/ImageEditModal.tsx)——编辑工作台 Drawer

右抽屉(min(1240px, 96vw)),每次打开重置状态;三段式布局 + 底部 AI 指令条:

  • 左:底图切换 Thumb 列表——「原图」+ 每次 AI 生成的结果图(AiResult{id,image,dataUrl,url}),点击切换当前底图(标注可叠加在 AI 结果上继续编辑)。
  • 中:AnnotationCanvas(预览最大宽 900)——标注画布,Pointer 交互:拖拽移动、旋转手柄旋转、四角手柄缩放(文字改 fontSize、标尺等比改 length/tSize/lineWidth/labelFontSize);命中检测 hitTest(旋转手柄 → 四角 → body);onBeginInteraction 每次交互开始时压撤销快照。
  • 右:属性编辑——Tabs(文字/标尺);添加文字/标尺按钮;选中元素属性面板 ElementPropsPanel样式预设:选中元素可"保存预设"localStorage key ozon_annotation_presets_v1,只存样式不含内容/位置),新建元素/选中元素可套用预设;撤销/清空/导出 PNG。
  • 底部 AI 条TextArea 指令(Enter 快捷生成)+ 模型 Select(5 个模型带中文说明)+「AI 生成」按钮。
  • runAi195-228 行):editImage({base_image: currentDataUrl, prompt, model, n:1, strength:0.8, prompt_extend:true}) → 取 results[0],优先 image_base64 否则 url → 解码为 Image 追加为结果底图并自动切换。出错用 apiErrorMessage 提示。
  • exportImage230-240 行):新 canvas 合成底图 + 全部标注元素 → PNG 下载。

5.5 组件与工具细节

文件 能力
components/WatermarkCanvas.tsx 单图水印预览(PREVIEW_MAX_WIDTH=360),命中水印区域拖拽,坐标映射回原图,clampPos 防越界
components/AnnotationCanvas.tsx 标注画布(预览最大宽 900):绘制底图+元素+选中框;Pointer 交互(move/rotate/resize);交互开始回调存撤销
components/ElementPropsPanel.tsx 文字:内容/字体(9 种)/字号/加粗/文字色/描边色/描边宽;标尺:长度/线色/线宽/T字大小/标签文字/标签色/标签字号;公共:透明度/旋转
utils/watermark.ts WATERMARK_SCALE=0.15(图片水印直径比)、WATERMARK_MARGIN=10WATERMARK_TEXT_SCALE=0.051getWatermarkSize/clampPos/defaultPosdrawWatermarked:图片水印圆形裁剪+全局透明度,文字水印先绘离屏层再合成(避免描边透出);renderFullRes 全分辨率合成
utils/annotation.ts FONT_FAMILIESHANDLE_RADIUS=7ROTATE_GAP=26measureText/elementBox/toLocal/hitTestdrawElement/drawText/drawRuler/drawSelectionHandlescreateTextElement/createRulerElement 工厂(默认值随图宽缩放)
utils/image.ts fileToImagecanvasToDataUrldownloadCanvas
types/image.ts WatermarkType/WatermarkState/ImageItem/ImageEditRequest/ImageEditResponse(与后端 schema 字段一致)
types/annotation.ts TextElement/RulerElement(中心点/旋转/透明度 + 各自样式字段)
services/api.ts axios 实例:baseURL=envConfig.apiBaseUrl(默认 /api)、timeout 120sapi.get/post 解包 res.dataapiErrorMessage 提取 FastAPI detail
services/image.ts editImage(payload)POST /image/edit
config/env.ts VITE_API_BASE_URL(默认 /api)、VITE_APP_NAMEdebug=import.meta.env.DEV

5.6 前后端衔接方式

  • 前端只调 /api/image/editservices/image.ts);/api/ai/* 暂无 studio 页面(v1 web/js/ai-copy.js 在消费,v1 已冻结)。
  • 图片以 data URL 在请求体内传输(15MB 上限),响应以服务端代理的 image_base64 为主、url 兜底——专为规避阿里云结果 URL 无 CORS 的画布污染问题。

六、可直接复用到 V2 vs 需要重写的部分

6.1 可直接复用(成熟、结构清晰、低耦合)

资产 理由
模型目录机制(models.yaml + models_catalog.py "模型即配置":换/加 LLM 只改 yaml+env,代码零改动;resolve_api_key 按 env 名取密钥,安全;V2 加多厂商直接扩展
DeepSeek 调用骨架(deepseek.py 的 _chat_once + _extract_json_object + 重试纠错循环) 通用性强:JSON 输出强制、代码块剥离、容错截取、失败追加修正消息重试一次;可抽象为通用 "JSON 任务 LLM 调用器" 供 V2 任意生成任务复用
文案 schema 与提示词(schemas/copy.py + prompts/copy_ru.py 领域逻辑已打磨(事实边界、俄语标题/描述结构、中俄对照);V2 若保留文案生成,整套平移
图生图服务(services/image_edit.py + schemas/image_edit.py 多模型路由(ImageSynthesis vs MultiModalConversation)、服务端代理下载规避 CORS、线程池隔离同步 SDK、响应契约(task_id/request_id 已预留异步字段);注释已规划"返回后接七牛转存"扩展点,与 V2 存储需求天然衔接
前端 API 层(services/api.ts + services/image.ts + config/env.ts axios 封装(120s 超时、detail 提取)与端点封装可原样复用;新增端点照 image.ts 模式加即可
水印/图片/标注工具集(utils/watermark.ts、utils/image.ts、utils/annotation.ts 纯 canvas 数学、框架无关、按原图坐标系建模(预览缩放/全分辨率导出分离),可直接搬入 V2
标注数据模型(types/annotation.tsTextElement/RulerElement 字段设计合理(中心点+旋转+透明度+样式),可直接作为 V2 标注/叠加元素的数据契约种子,甚至与后端共享 schema
布局壳(MainLayout + SidebarMenu + menuConfig 菜单配置驱动、移动端适配完整;V2 加页面只改 menuConfig + router
水印批处理主流程(AiImagePage 上传→全局水印→批量套用→导出) 完整闭环,可整体作为 V2 的一个功能模块迁入

6.2 需要重写/新建(当前缺失或形态不适配 V2)

资产 现状与重写理由
Ozon 对接(api/ozon.py 纯占位,V2 若做"发布到 Ozon"需从零实现 Seller API(token 管理、类目树、商品上传、图片上传等)
前端 AI 文案页 studio 完全没有文案 UI;后端 /api/ai/copy 就绪,V2 需新建页面(可参考 v1 web/js/ai-copy.js 的表单交互)
服务端文件/任务持久化 现状:图不落盘、无上传端点、无 DB、无任务队列;图生图同步等待(前端 120s 超时)。V2 若引入"商品文件夹"契约(README 所述四部分衔接点)与异步任务,需新增上传/资产/任务/持久化体系
ImageEditModal 状态管理 编辑+AI 生成+撤销+预设全在组件本地 useState;V2 若需跨步骤工作流/历史/多页共享,需抽出 store(如 zustand/redux)与后端任务状态同步
AiImagePage 批量状态 纯内存 useState,刷新即失;V2 按商品文件夹组织时需要持久化/恢复会话
鉴权与错误治理 无任何鉴权、无统一错误码规范(全部 HTTPException detail 字符串);V2 多用户/上线需补齐
水印可配置性 水印图硬编码 /imgs/watermark.jpg、比例/字号为常量;V2 应支持自定义水印资源与配置
路由/菜单 仅 1 页;V2 多页面需按功能域重组(当前结构太薄,扩展时建议直接重构而非继续堆叠)
测试与文档链 无自动化测试;契约靠 README/docs 维护。V2 建议引入 schema 共享(README 中 packages/schema 待建项)避免前后端字段漂移

6.3 一句话结论

后端资产(模型目录、LLM 调用骨架、图生图多模型服务、schema)质量高且刻意留了扩展点(异步字段、七牛转存注释),可大幅平移;前端可平移的是纯 canvas 工具层与布局壳,而所有"产品化"能力——Ozon 对接、文案 UI、持久化/资产、异步任务、鉴权、状态管理——目前为空或雏形,是 V2 的主要建设量。