Files
ozon-seller-kit/docs/old/ai-copy-backend-plan.md
2026-08-11 17:09:23 +08:00

16 KiB
Raw Permalink Blame History

Ozon Seller Kit:俄文文案生成方案

状态:方案稿
日期:2026-08-07
后端语言:PythonFastAPI
范围:先落地「中文采买信息 → 俄文标题 / 描述 / 标签 + 中文对照」;图片白底、Ozon 上传作为后续阶段预留扩展位。


1. 背景与目标

当前项目原为根目录散落的纯静态页面工具(ozonSeller.html + js/app.js),可直接 file:// 打开,已具备:

  • 计价 / 录入 / 上品登记表 / 组合码导出
  • 浏览器 Canvas 水印

后续需要接入大模型 API。浏览器直连 DeepSeek 会遇到 CORS,且 API Key 不能放前端,因此引入 本地 Python 服务 做代理与业务编排;仓库采用一体扁平结构,静态页放在 web/

本阶段目标:

  1. 在现有页面增加「俄文文案」独立区块。
  2. 用户在单个输入框粘贴采买站商品信息(如有本次特殊要求可直接写在后面),点击生成。
  3. 后端调用 DeepSeek,返回俄文标题 / 描述 / 标签,并同步中文对照。
  4. 前端可编辑、一键复制;可选回填左侧「商品名」。

2. 总体架构

┌─────────────────────────────────────────────────────────────┐
│  浏览器                                                      │
│  web/ozonSeller.html + js/ + css/                            │
│  http://127.0.0.1:8000/ozonSeller.html(由 FastAPI 托管)     │
└───────────────────────────┬─────────────────────────────────┘
                            │ fetch JSON
                            ▼
┌─────────────────────────────────────────────────────────────┐
│  同仓 FastAPImain.py                                     │
│  - 挂载 web/ 静态资源                                         │
│  - /api/ai/copy 文案生成                                      │
│  - (预留)/api/image/*  /api/ozon/*                           │
└───────────────────────────┬─────────────────────────────────┘
                            │ HTTPS + Bearer Key
                            ▼
┌─────────────────────────────────────────────────────────────┐
│  DeepSeek APIOpenAI 兼容)                                  │
│  https://api.deepseek.com                                    │
└─────────────────────────────────────────────────────────────┘

原则:

  • 密钥只放 .env(不进 git);模型清单放 server/config/models.yaml(可入库)。
  • 前端只请求本机 API,不直连大模型、不接触密钥。
  • 一体扁平结构:不拆 frontend/ / backend/;Python 入口在仓库根,静态资源独占 web/
  • 现有静态能力尽量保留;服务端先做薄代理 + Prompt 编排。
  • 本地开发统一用 http://127.0.0.1,不再依赖 file://file:// 无法稳定调后端)。

3. 页面落位(前端交互)

3.1 插入位置

计价双栏区域之后上品登记表之前,新增全宽区块「俄文文案」。

页面顺序变为:

  1. Header(外链 + 汇率)
  2. 计价输入 + 计价结果
  3. 俄文文案(新)
  4. 上品登记表
  5. 上品组合码
  6. 图片水印

3.2 区块结构

区域 内容
左侧输入 商品资料(单个大文本,事实与本次要求写在一起)、带入当前商品名/型号(只读提示)
右侧结果 标题 / 描述 / 标签:俄文主展示 + 中文对照;可编辑;各字段复制按钮
操作 「生成文案」「重新生成」;可选「中文标题填入商品名」

3.3 与现有字段关系

  • 左侧 productName / SKU / 型号:仍服务计价与登记,不改成俄文主编辑区。
  • 文案结果独立保存于本区块 DOM / 内存;后续接 Ozon 上传时再读取俄文字段。
  • 「填入商品名」仅把生成的 中文标题 写入 #productName

4. 仓库目录与代码放置

采用 一体扁平结构(方案 B):不拆 frontend/ / backend/。Python 入口与配置在仓库根;静态页独占 web/,避免挂载时误暴露源码。

ozon-seller-kit/
├── docs/
│   └── ai-copy-backend-plan.md      # 本方案
├── main.py                          # FastAPI 入口、CORS、挂载 web/
├── config/
│   ├── settings.py                  # .env 密钥与运行参数
│   └── models.yaml                  # 模型目录(可入库,不含密钥)
├── api/
│   ├── ai.py                        # /api/ai/*
│   ├── image.py                     # (预留)/api/image/*
│   └── ozon.py                      # (预留)/api/ozon/*
├── services/
│   ├── models_catalog.py            # 读取 models.yaml、解析密钥
│   ├── deepseek.py                  # OpenAI 兼容 chat 调用
│   └── prompts/
│       └── copy_ru.py
├── schemas/
│   └── copy.py
├── web/
│   ├── ozonSeller.html
│   ├── css/
│   ├── imgs/
│   └── js/
│       ├── app.js
│       ├── ai-copy.js
│       ├── watermark-data.js
│       └── tailwind.config.js
├── requirements.txt
├── .env.example
├── start.command
├── .gitignore
└── README.md

4.1 放置约定

内容 放哪里 说明
页面结构 web/ozonSeller.html 文案区 DOM、模型下拉
文案交互 web/js/ai-copy.js 拉模型列表、带 model 调生成
模型目录 server/config/models.yaml id/label/base_url/api_key_env;可入库
密钥 根目录 .env 仅密钥与 HOST/PORT;不入库
API 路由 api/ 按业务拆文件
LLM 调用 services/deepseek.py 按 ModelSpec 调 OpenAI 兼容接口
Prompt services/prompts/ 方便单独迭代文案质量

4.2 静态资源如何被访问

由 FastAPI 挂载 web/,一个进程同时提供页面和 API

# main.py 示意
WEB_DIR = Path(__file__).resolve().parent / "web"
app.mount("/", StaticFiles(directory=WEB_DIR, html=True), name="web")

注意:静态挂载应放在 /api 路由注册之后,避免吞掉 API 路径。

访问地址:

  • 页面:http://127.0.0.1:8000/ozonSeller.html
  • APIhttp://127.0.0.1:8000/api/ai/copy

前端请求基址:

const API_BASE = window.location.origin; // 同域,无 CORS 烦恼

若暂时用 VS Code Live Server 单独打开 web/、Python 另开端口,则需开 CORS,前端写死 API_BASE = 'http://127.0.0.1:8000'首选同域挂载方案。


5. 后端设计

5.1 技术选型

选择 原因
Web 框架 FastAPI 轻量、类型清晰、异步友好
HTTP 客户端 httpx 调 OpenAI 兼容接口
密钥/运行参数 pydantic-settings + .env 只放秘密与端口
模型目录 server/config/models.yaml 可扩展多模型/多厂商
运行 uvicorn 标准 ASGI

5.2 核心依赖

fastapi
uvicorn[standard]
httpx
pydantic-settings
python-dotenv
PyYAML

5.3 配置分层(模型目录 + 密钥)

原则:

  • server/config/models.yaml:模型清单(id、显示名、api_model、base_url、api_key_env),可入库,不含密钥
  • .env:只放密钥与 HOST/PORT 等运行参数,不入库
  • 前端通过 GET /api/ai/models 获取可选项,永不接触密钥

server/config/models.yaml 示例:

default: deepseek-v4-flash
models:
  - id: deepseek-v4-flash
    label: Flash(快/省)
    provider: deepseek
    api_model: deepseek-v4-flash
    base_url: https://api.deepseek.com
    api_key_env: DEEPSEEK_API_KEY
  - id: deepseek-v4-pro
    label: Pro(质量更好)
    provider: deepseek
    api_model: deepseek-v4-pro
    base_url: https://api.deepseek.com
    api_key_env: DEEPSEEK_API_KEY

.env.example

DEEPSEEK_API_KEY=sk-xxxx
# OPENAI_API_KEY=sk-xxxx   # 若 models.yaml 引用了该变量再配置
HOST=127.0.0.1
PORT=8000
CORS_ORIGINS=

同一厂商可共用一把 Key;按「账号/厂商」分 Key,不必每个模型名硬拆一把。

5.4 API 约定

GET /api/ai/models

{
  "default": "deepseek-v4-flash",
  "models": [
    { "id": "deepseek-v4-flash", "label": "Flash(快/省)" },
    { "id": "deepseek-v4-pro", "label": "Pro(质量更好)" }
  ]
}

POST /api/ai/copy

请求:

{
  "source_text": "从采买站粘贴的中文商品信息,可在末尾附带本次生成要求……",
  "product_name": "可选,当前表单商品名",
  "model_code": "可选,型号",
  "model": "deepseek-v4-pro"
}
  • model 可选;空则用 models.yamldefault
  • 不在白名单 → 400

成功响应:

{
  "titles_ru": ["……", "……"],
  "titles_zh": ["……", "……"],
  "description_ru": "……",
  "description_zh": "……",
  "tags_ru": ["……", "……"],
  "tags_zh": ["……", "……"],
  "model": "deepseek-v4-pro",
  "usage": {
    "prompt_tokens": 0,
    "completion_tokens": 0
  }
}

校验规则(后端):

  • source_text 必填,去空白后长度 ≥ 10。
  • model 必须属于 models.yaml(或空=默认)。
  • 要求模型输出 严格 JSON,解析失败则重试 1 次或返回 502。

5.5 Prompt 策略(概要)

System 角色职责:

  • 面向俄罗斯消费者和 Ozon 搜索生成完整商品卡,而非把采买资料压缩成摘要。
  • 优先级固定为:事实准确 > 俄语自然 > 信息完整与转化力 > 关键词覆盖。
  • 标题一次输出 2 个互补方案(titles_ru / titles_zh),以核心品类词开头,前 30 字符覆盖关键属性,长度 60~90 字符,去除年份、新款、爆款等噪声。
  • 描述采用 Описание товара / Характеристики / Преимущества 三段结构,原文明确提到配件时追加 Комплектация;资料足够时约 900~1500 个俄文字符。
  • 允许将已有事实改写成温和的使用利益点,但禁止补充原文没有的结构、认证、安全结论、适用年龄、开口位置、礼物节日等硬信息。
  • 对常见行业词做翻译约束,例如“搪胶”优先译为 винил (ПВХ),不擅自译为天然橡胶 каучук
  • 标签输出 10~15 个;每个俄文标签只允许一个单词,不带 #,中俄数组按索引一一对应。
  • 中文标题和描述保持与最终俄文相同的事实与段落结构,便于逐项核对。
  • 调用参数使用较稳定的 temperature=0.45,并给描述预留充足输出长度。

User 内容拼接:

<当前商品名>…</当前商品名>
<型号>…</型号>
<商品资料>
…
</商品资料>

输入用边界标签隔离,避免把采买文本中的内容误当作系统指令。实现放在 services/prompts/copy_ru.py,便于单独调优,不必改路由。

5.6 服务分层

api/ai.py
  GET  /models → models_catalog.list_model_options()
  POST /copy   → deepseek.generate_copy(req)

services/models_catalog.py
  → 读 models.yaml
  → 白名单校验
  → 按 api_key_env 从环境变量取密钥

services/deepseek.py
  → 组装 messages
  → 用 ModelSpec.base_url / api_model 调 /chat/completions
  → 解析 JSON → CopyResponse

6. 前端设计

6.1 DOM 落点

web/ozonSeller.html 中,计价 grid 结束后、上品登记表 mt-8 卡片前插入文案区。

模型下拉位于「生成文案」按钮上方;区块标题右侧提供折叠按钮。

元素 id
折叠按钮 / 内容区 aiToggleBtn / aiCopyBody
模型下拉 aiModelSelect
商品资料 aiSourceText
生成按钮 aiGenerateBtn
推荐标题容器 aiTitlesContainer
描述俄/中 aiDescRu / aiDescZh
标签容器 aiTagsContainer
状态提示 aiCopyStatus

结果区为只读展示:标题渲染成卡片(含复制俄文、填入商品名),描述用等高只读文本块保留换行,标签渲染成「俄文|中文」芯片,点击即复制俄文。需要调整文案时通过「生成要求」重新生成。

6.2 JS 职责拆分

web/js/ai-copy.js

  • 启动时 GET /api/ai/models 填充下拉;localStorage 记住上次选择
  • 收集输入(含 #productName#modelCode、选中的 model
  • POST /api/ai/copy
  • 渲染标题卡片、描述文本块、标签芯片,并处理 loading / 错误态
  • 复制(标题、描述、单个标签、全部标签)与「填入商品名」
  • 折叠 / 展开整个文案区

web/js/app.js:保持计价、历史、水印。

6.3 交互细节

  • 生成中:按钮 disabled +「生成中…」。
  • 失败:在 aiCopyStatus 显示可读错误。
  • 不在前端存 API Key;模型列表不硬编码(以后加模型只改 models.yaml)。

7. 本地运行方式

7.1 一次性准备

在仓库根目录:

python3 -m venv .venv
source .venv/bin/activate
pip install -r server/requirements.txt
cp .env.example .env
# 编辑 .env,填入 DEEPSEEK_API_KEY

7.2 启动

source .venv/bin/activate
uvicorn main:app --app-dir server --reload --host 127.0.0.1 --port 8000

浏览器打开:

http://127.0.0.1:8000/ozonSeller.html

7.3 .gitignore 建议追加

.venv/
.env
**/__pycache__/
*.pyc
.DS_Store
web/ozonSeller.html.bak

8. 分阶段落地

Phase 1(本方案当前实施范围)

  • 静态资源收拢到 web/
  • 一体扁平:main.py / api/ / services/ / schemas/
  • 实现 POST /api/ai/copy
  • 页面插入「俄文文案」区块
  • web/js/ai-copy.js 对接 API
  • FastAPI 挂载 web/,同域静态托管可本地跑通
  • 根目录 README.md / start.command
  • server/config/models.yaml 模型目录 + GET /api/ai/models + 前端下拉切换

Phase 2(图片)

  • POST /api/image/process:白底、服务端水印(可选保留前端水印)
  • 预留图生图代理接口

Phase 3Ozon

  • POST /api/ozon/products:读取文案区俄文字段 + 计价结果上传
  • Client-Id / Api-Key 仅存根目录 .env

9. 风险与约束

说明
不能用 file:// 调后端 需通过 http://127.0.0.1:8000 打开页面
Prompt 质量 俄文 listing 需多轮样本调优;Prompt 独立文件便于改
模型偶发非 JSON 后端要做解析容错与明确报错
Key 泄露 .env 不入库;勿把 Key 打进前端或日志
费用 前端防连点;可后续加简易速率限制

10. 结论

  • 目录:一体扁平;Python 在仓库根,静态页在 web/
  • 模型配置:清单在 server/config/models.yaml,密钥在 .env;前端只消费 /api/ai/models
  • 服务FastAPImain.py)挂载 web/ 并提供 /api/*
  • 前端:俄文文案区支持模型下拉;逻辑在 web/js/ai-copy.js
  • 扩展api/image.pyapi/ozon.py 预留;加模型只需改 yaml + 对应密钥环境变量。