Files
atlas-seller-kit/docs/product/ai-autofill-plan.md
T
2026-09-23 17:18:09 +08:00

141 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 商品 AI 填报:设计与开发方案
> 状态:2026-09-23 已实现。目标平台首期为 Ozon;采集来源覆盖 Ozon、1688、淘宝、天猫。全局 AI 填报和 SKU 文案通过异步任务回填未保存表单;动态类目属性不在当前范围。
## 1. 目标与边界
用户采集商品、进入编辑页并点击底部唯一的「AI 填报」按钮后,系统依据已经保存的采集资料、当前草稿和 Atlas 平台参考目录,生成类目、型号、每个未导入 SKU 的货号、证据充分的包装规格,以及 **SPU 的** Ozon 俄文标题、主题标签和产品描述。任务完成后,页面**直接将合格结果写入当前表单**,标记为未保存修改;用户可以继续改动,并通过现有「保存」操作统一持久化。生成任务本身不修改商品草稿、店铺或 Ozon 商品。SKU 三项文案默认继承 SPU;用户展开 SKU 字段后可针对当前 SKU 单独触发 AI 生成。
AI 不生成售价、采购价、品牌、条形码、图片、富内容、动态类目属性或已导入 SKU 的货号。类目改变后仍由现有机制加载属性 schema,并由用户按既有控件填写。输入资料不足时允许部分填报,不能为了填满表单编造重量、尺寸或商品事实。
该能力的内部入口是「已规范化商品资料 + 目标平台 + 可售 SKU + 草稿版本」,而不是 React 页面。未来从商品详情链接直接采集后,先进入现有采集规范化链路,再调用同一 AI 填报服务。
## 2. 现状与可行性
- Atlas 已有 `source_material`、`source_material_state.latest`、`ai_input`、平台级类目树、商品/SKU 草稿、版本号及 `editor-draft` 原子保存接口。底部现有类目和文案两个占位按钮,可合并为一个入口。
- Atlas 现有 AI API 是尚未实施的任务契约:先保存草稿,再创建异步任务,结果由用户应用。此次保持「先保存」和「任务不写草稿」,但把结果应用动作改为任务完成后直接写入浏览器表单;页面现有保存按钮仍是数据库写入边界。
- 旧版 `ozon-seller-kit` 的 `server/services/prompts/copy_ru.py` 已定义中俄成对的标题、描述和标签及事实边界;`server/services/deepseek.py` 实现 JSON 请求、解析和一次纠错重试。可复用规则和测试样本,不直接复制旧版 FastAPI、无工作空间隔离的调用路径或试算页「生成后立即保存简介」行为。
- DeepSeek 官方 JSON 输出文档支持 `response_format: {"type":"json_object"}`,同时提示空内容和输出截断的可能性。因此 JSON 模式只保证可解析性的目标,**不替代 Atlas 的字段 schema、事实引用、类目 ID、长度和唯一性校验**。具体模型名和参数应作为服务端配置,经上线时再次确认。
- 当前迁移尚无 `product_ai_tasks` 实表;`docs/architecture/sql/002_product-domain.sql` 仅有设计草案。落地任务状态、结果和中文对照需要新迁移。
## 3. 一次点击的页面流程
1. 用户点击「AI 填报」。若表单有未保存修改,页面先调用现有保存流程;保存失败时不创建任务。当前 `source_snapshot_revision`、商品/profile revision、SKU ID、目标店铺和平台由服务端固化为任务输入快照。
2. 页面将底部按钮改为「填报中…」,显示步骤:分析采集资料 → 推荐类目 → 生成文案与编号 → 校验结果。用户可继续浏览页面。重复点击沿用同一输入版本的进行中任务,避免重复计费。任务以字段组为独立产出单元:类目、编号、包装和文案可以分别成功或失败;只有完全没有合格结果才记为整体失败。
3. Worker 完成后,服务端返回已校验结果、警告及中俄文案。页面核对任务快照与当前编辑版本;若复采或草稿有新版本,提示结果已过期,不自动回填。
4. 在同一版本下,页面直接写入符合规则的表单字段并显示「已填报 N 项,M 项待处理/证据不足」。任务部分成功时,合格字段照常回填,失败字段保留原值并按原因列出。类目写入表单后触发已有动态 schema 加载;旧类目属性草稿按现有切换规则处理。本期不让 AI 接续填写新 schema。
5. 已导入 SKU 的货号不变;全局结果只填空字段,原本由用户填写的非空字段保留。异步等待期间若草稿版本变化,本次结果不回填。
6. 回填只改变浏览器表单并触发页面 `dirty` 状态。关闭页面前继续使用现有未保存提示;保存后与其他草稿字段一同持久化。
不增加逐字段确认弹窗。字段建议、被跳过项目和原因可在一个简短的填报结果面板查看;用户最终通过编辑页原有的保存动作提交。生成失败或部分字段失败时,已校验合格的其他字段仍可应用。
## 4. 字段规则
### 4.1 类目
- Ozon 来源先使用现有「一级面包屑 + `Type_0` 商品类型」匹配;已经有用户选择的类目不因 AI 填报改变。确定性匹配唯一时沿用它,不再为该字段消耗模型调用。
- 1688、淘宝、天猫来源无 Ozon 类目 ID。服务端向模型分层提供 Atlas **活动、可发布目录**:先给顶层选项,再按选定分支给子节点,直到叶子;每一步只允许返回给定 UUID,并记录候选路径和理由。不得让模型自造类目名或把来源平台类目 ID 当成 Ozon ID。
- 最终叶子由服务端复核所属平台、活动状态、可发布 `description_category_id + type_id` 以及祖先链。证据明确时把 Atlas 叶子 UUID 填入未保存表单,`category_source=ai_suggestion`;歧义或证据不足时不填,并显示至多三个候选供用户通过现有类目控件选择。
- 不用模型自报的置信度单独决定是否回填;自动回填阈值应基于四来源样本评测校准。已保存的用户类目优先级高于新建议。
- 类目目录若缺失、未同步完整、已失效或目标叶子缺少有效 `description_category_id + type_id`,先复用现有目录同步机制;在有界等待后仍不可用时,类目结果标为 `catalog_unavailable`,不向 AI 提供不完整候选,也不自造类目 ID。已有已确认类目可继续作为其他字段的上下文;否则依赖类目码的型号与货号暂不生成。文案和有独立证据的包装建议照常处理。
- 这里的类目目录与 Ozon **动态属性字典**不同。类目确认并保存后,页面只加载 schema,动态属性由用户通过既有下拉搜索和输入控件填写;当前 AI 填报不处理属性及字典值。
### 4.2 型号与 SKU 货号
- **同一 SPU 只有一个 `model`**,不随 SKU 的作品、角色、颜色变化。AI 先归纳所有 SKU 真实共有的商品类型与形态;只有全部 SKU 确实属于同一系列时,系列语义才允许进入型号。死亡笔记、海贼王、火影忍者等作品混在一个手办 SPU 时,公共型号使用中性的类别/形态码,如 `FIG-MIX-8K4F`,绝不取首个 SKU 的作品名当整组型号。若 SKU 连基本商品形态都不同,提示先拆分 SPU,不强行生成共同型号。
- 格式由服务端固定:`model = 类目短码-共通形态或 MIX-商品稳定码`,例如 `FIG-MIX-8K4F`;只用大写英文字母、数字和 `-`,最多 **32 个 ASCII 字符、2 个连接符**。短码来自 Atlas 维护的跨平台商品形态码表,由目标类目映射而来,不直接使用 Ozon 专有 ID;AI 只建议共有语义,不能直接输出最终编号。模型不可用但类目明确时,可保守回退 `MIX`;无可用类目时不生成编号。类目名称或 SKU 排序变化后,不自动重编已经保存的型号。
- 每个未导入 SKU 的 `offer_id = model-变体识别码`,识别码可由作品、角色和必要规格组成。例如 `FIG-MIX-8K4F-DN-L`、`FIG-MIX-8K4F-OP-LUFFY`、`FIG-MIX-8K4F-NAR-SASUKE`。整串最多 **48 个 ASCII 字符、4 个连接符**,通常为 3~4 个连接符;额外的颜色/尺寸须压入已有变体段,不能不断追加 `-`。只有来源明确提供作品/角色时才使用其缩写,不能凭图片猜测 IP;语义不可靠时改用基于稳定 SKU 身份的 `V` 码。变体识别码在本 SPU 内必须唯一,不依据显示顺序生成 `V01/V02`。
- 服务端维护短码、缩写、稳定商品码及 SKU 身份的映射;AI 不负责计数或唯一性。生成后对本次所有 SKU、目标店铺已有草稿和在线镜像检查冲突;冲突时仅调整尚未保存的新码并保持上述长度/连接符上限,仍冲突则跳过该货号并报告。保存时仍由数据库唯一索引和现有服务端校验兜底。
- 当前通用保存契约仅有较宽的 `model ≤ 300`、`offer_id ≤ 500` 字符上限。上述 32/48 及连接符规则是**AI 生成值的强校验**,不应悄悄拒绝已有或手填的较长编号;如要统一收紧所有编号,需要先盘点历史值并单独迁移、通知用户。
- `model` 是跨平台商品字段,也会映射到 Ozon「型号名称」属性。本期按用户希望使用可识别编码;后续若需要单独的内部编码与对外型号,应新增独立字段,不能在生成逻辑里偷偷分叉。
- 已导入 SKU 的货号是历史事实;AI 结果绝不填入或改动。手填非空型号/货号按页面覆盖规则保留。
### 4.3 包装重量与尺寸
- 只从明确的「包装/外箱/包裹」事实提出建议,区分商品本体的尺寸、净重和物流包装规格。模型为每个建议返回原文证据片段、来源字段、原单位和换算后的 `g/mm` 数值。
- 服务端检查证据存在、单位可识别、值为正数且数量级合理。若只有商品尺寸或重量、只看图片推测、三维缺项或存在多种包装版本,相关字段保持空值或现有值,不生成“确切数值”。
- 商品级包装建议填 SPU 默认值;仅当某 SKU 有专属包装证据时填该 SKU 覆盖值。有明确证据的结果字段状态为 `suggested`,允许直接回填;回填后的包装**确认状态**先标为 `ambiguous`,用户核对后改为 `confirmed`。证据本身有歧义的结果字段状态为 `ambiguous`,不自动回填。因此 AI 可减少录入,但不能绕过定价和发布所需的包装确认。
### 4.4 SPU 默认文案、SKU 按需文案与中文对照
- 三项指**发布标题、主题标签、产品描述**。底部「AI 填报」只生成 SPU 三项及逐项中文对照,不生成、不写入 SKU 三项覆盖值。SPU 文案只能陈述所有 SKU 都成立的共有事实;不同作品混在一组手办 SKU 时,不把其中一个作品写成 SPU 通用事实。
- SPU 各字段旁的 `+` 只开启对应 SKU 字段编辑入口,**不批量复制 SPU 文案、不隐藏 SPU 字段**。各 SKU 的标题、标签和描述逐字段默认继承 SPU;表单显示有效值和「继承 SPU」状态,只有用户手动输入或主动应用该 SKU 的生成结果才保存覆盖。
- SKU 三项字段旁的 AI 图标打开同一个当前 SKU 生成弹窗。左侧展示中文来源描述、SKU 名称、规格和可证的作品/角色资料;点击「AI 生成」仅为该 SKU 生成俄文标题、标签、描述及中文配对,右侧预览,点击「应用到当前 SKU」才把合格结果写入该 SKU 的未保存表单。已有手工覆盖不得被静默替换。某项证据不足时可单独保持继承,其他合格项仍可应用。
- 参考旧版提示词,每次文案生成可返回两组标题候选及逐项中文,默认采用第一组;标签逐项中俄配对,描述按段落对应。俄文是发布表单值,中文只是对照资料,不进入 Ozon 发布 payload。不得编造材料、认证、配件、安全性、产地、适用年龄、包装或功效;也不得把 A SKU 的角色、颜色或规格写到 B SKU。
- SPU 和每个 SKU 的三个字段旁另有「中文对照」图标。统一弹窗左列展示当前有效俄文,右列展示对应中文,标题标明 SPU 或 SKU;继承项显示 SPU 对照与「继承 SPU」。中文对照按 `profile_id + scope(SPU/SKU) + variant_id + field` 保存配对版本;继承项只引用 SPU 对照,不复制记录。手工改俄文后若不再与中文配对,标记「中文对照待更新」,不得把 SPU 中文错配给 SKU 俄文覆盖。
## 5. 可复用服务与当前接口
全局入口使用 `POST /api/product-platform-profiles/{profile_id}/ai/autofills`;当前 SKU 的独立文案入口使用 `POST /api/product-platform-profiles/{profile_id}/variants/{variant_id}/ai/copy`。两者均要求 `Idempotency-Key` 和平台头,body 可带 `hint`;返回 `202 { ai_task_id, status }`。`GET /api/product-ai-tasks/{ai_task_id}` 返回任务状态、输入版本与已校验结果,不返回提示词、密钥或原始模型响应。Worker 从服务端 `.env` 读取 `DEEPSEEK_API_KEY` 与 `DEEPSEEK_MODEL_NAME`。
全局结果包含 `category` 或 `category_candidates`、`model`、`offers`、可选 `package_spec`、`spu_copy`、`warnings`。单 SKU 结果只包含 `variant_id`、`copy`、`warnings`。任务输入固化商品、profile、来源快照版本和 SKU ID;前端在回填前再次比对版本。
接口结构示意(实际契约见 `@atlas/contracts`):
```json
{
"id": "task-uuid",
"kind": "autofill",
"status": "partial",
"input_revision": { "product": 12, "profile": 7, "source_snapshot": 4 },
"result": {
"category": { "category_id": "atlas-category-uuid", "path": "目录路径" },
"model": "FIG-MIX-8K4F",
"offers": [{ "variant_id": "variant-uuid", "offer_id": "FIG-MIX-8K4F-V123456" }],
"spu_copy": {
"title": { "ru": "…", "zh": "…" },
"tags": [{ "ru": "…", "zh": "…" }],
"description": { "ru": "…", "zh": "…" }
},
"warnings": ["未找到可核对的包装重量和尺寸"]
}
}
```
任务状态区分 `succeeded`、`partial`、`failed`;部分失败通过 `warnings` 表达。候选类目不自动回填,由用户点击候选确认。包装缺少可核对证据时不返回包装建议。
内部拆为四个与页面无关的组件:
1. `SourceFactAssembler`:把采集快照和已保存的用户整理稿转为带证据的、长度受限的规范化商品事实;未来 URL 入口复用此层。
2. `CategoryResolver`:已有类目优先沿用;否则分层查询平台目录并向模型提供封闭候选,返回经数据库验证的叶子 UUID 或需用户确认的多个候选。
3. `AutofillGenerator`:Worker 调用 DeepSeek 生成中俄文案与有证据的包装建议;稳定型号/货号由服务端规则生成。API Key 只在 Worker 环境读取,不进入浏览器、任务输入、outbox 或日志。
4. `AutofillValidator`:校验中俄配对、包装证据与正数、类目 UUID、编号长度和货号冲突,丢弃不合格字段。
NestJS 负责鉴权、工作空间事务、快照和任务创建;任务与 outbox 在同一事务入库;Worker 调用模型和写回结果。调用超时、限流、空响应、截断和非法 JSON 使用有界重试,重复任务以任务 ID 幂等。失败只更新任务状态,不触碰草稿。记录模型、提示词版本、耗时和 token 用量以便成本分析;日志及对外错误只输出脱敏摘要。
迁移 `029_product_ai_autofill.sql` 建立任务和中俄对照表,并给 SKU listing 增加可空 `title`;`null` 表示继承 SPU 发布标题。SKU `tags=[]`、`description=null` 继续表示继承。中文对照随草稿事务保存、随编辑快照读取;表单俄文若与对照时的俄文不一致,页面显示「待更新」。
页面只回填空白的全局字段和 SPU 文案,不覆盖已有手填值;SKU 文案必须由当前 SKU 弹窗单独生成,并经用户点「应用到当前 SKU」才覆盖其标题、标签、描述。中文只进入对应作用域的对照记录。回填只改变未保存的表单状态,不独立写入草稿。
## 6. 版本、冲突与失败处理
| 情况 | 页面行为 |
|---|---|
| 任务期间用户改了部分字段 | 页面保存前先固化草稿;任务完成时若版本已变化,拒绝整次自动回填并提示重新生成。 |
| 任务期间商品被复采、平台或 SKU 集合改变 | 结果标为过期,不自动回填;用户可重新点击生成。 |
| AI 找不到可确定的目标类目 | 类目留空,展示候选;文案和有独立证据的包装仍可回填。若已有已确认类目,可继续生成型号与货号;否则编号留空。 |
| 类目目录不完整或同步失败 | 无已确认类目时,型号与货号跳过;其他独立字段仍可回填。 |
| Ozon 动态属性字典缺失 | 本期不由 AI 填报该属性,不影响其他字段;页面显示字典加载状态,保存草稿允许缺失,发布预检继续阻止无效字典值。 |
| 包装数据没有明确证据 | 包装项留空,说明缺失,不将商品尺寸当包装尺寸。 |
| 目标店铺货号冲突 | 服务端重算稳定后缀;仍冲突则仅跳过该 SKU 货号并提示。 |
| DeepSeek 超时、限流、空响应或截断 | 按错误类别执行有界退避重试;仍失败则模型依赖组标为失败,确定性类目/编号组若已合格可形成 `partial`,否则任务为 `failed`;已填值不回滚,未填值保留原样。 |
| 结构不合法或事实/字段规则不合格 | JSON 请求有限次重试;仍失败则拒绝模型依赖组。可解析时逐字段校验,中俄不成对或数值无证据的字段单独丢弃。 |
| 当前 SKU 文案失败 | 弹窗显示错误;原 SKU 文案不变,不影响已生成的 SPU 文案或其他 SKU。 |
| 用户修改俄文后查看中文对照 | 标记「对照待更新」,不把旧中文当作当前俄文的翻译。 |
## 7. 实施顺序与验收
1. **契约与迁移**:修订 `docs/api/`、`@atlas/contracts`;落地 AI 任务、SKU 发布标题和分作用域中文对照表、工作空间 RLS、outbox 事件与幂等键。测试跨工作空间不可读取任务/对照及 SKU 标题继承。
2. **服务端核心**:事实组装、平台目录分层候选、DeepSeek JSON 输出校验、稳定型号/货号规则、包装证据校验。全局只生成 SPU 文案;当前 SKU 文案由独立任务按需生成。不使用真实密钥跑自动化测试。
3. **编辑页**:合并底部按钮、任务轮询、表单直接回填和保存前版本保护;类目回填触发现有 schema 请求,AI 不碰动态属性。SKU 展开区补独立标题编辑,标签/描述改为逐字段继承 SPU,留空即继承。
4. **中文对照**:SPU 和每个 SKU 的三个字段旁入口、统一双栏弹窗、继承来源标记、随草稿保存与重新加载、各作用域俄文编辑后的过期提示。
5. **端到端验收**:维护者启动 API/Worker 并完成迁移后,用 Ozon、1688、淘宝、天猫样本回归;覆盖跨作品 SKU、单 SKU 按需生成、中文对照刷新后读取、已导入 SKU 货号保护、无包装证据、歧义类目、模型异常及复采并发。
完成标准:点击一次「AI 填报」后,页面出现合格的类目、型号、未导入 SKU 货号及 SPU 俄文文案;证据不足的包装项保持空白。用户可以再对指定 SKU 单独生成并确认文案;中文对照保存后仍可查看。动态 Ozon 类目属性由后续单独设计。
## 8. 已知取舍
- 一键入口可以在内部多次调用模型,特别是非 Ozon 来源的分层类目选择;页面应显示异步进度,不能假设一个同步 HTTP 请求必定快速完成。
- 直接回填提高速度,也要求可撤销、保留手填值、版本检查和完整任务记录。当前页面不增加逐项确认步骤。
- 目前 `model` 是跨平台商品字段并会成为 Ozon 型号名称。若将来需要「内部编码」与「消费者可见型号」分离,应再增字段和迁移;本期不为此增加隐藏的第二套型号。