docs(tasks): add T-658 multi-image reference submission rules

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
chengma
2026-07-17 16:34:00 +08:00
co-authored by Claude Fable 5
parent f0be2a291f
commit 3c68dd1ef2
+136
View File
@@ -0,0 +1,136 @@
---
id: T-658
title: 商品套图多图参考提交与主图生成规则
status: TODO
phase: 7
deps: [T-622, T-637]
created: 2026-07-17
---
# T-658 商品套图多图参考提交与主图生成规则
## 问题 / 背景
⑥「商品套图」当前每个生成请求只向 cmhub 提交**单张**源图(`app/image_studio_generation.py::_submit_or_resume_job()` 的顶层 `image_base64` 字段)。不勾选「每张上传图分别作为主图生成」时,除第一张外其余上传图**完全不参与生成**——用户上传多角度图没有任何价值。调研笔记《虾皮圈电商图生成器提示词组装分析》第 6.1 节指出源项目「提示词声称有参考图、实际未提交」的缺陷,cmshopee 目前靠 `{参考图规则}` 的「唯一主参考图」表述回避了这个不一致,但多图能力本身缺失。
cmhub 图生图接口已更新(cmhub T-620,契约见 obsidian《cmhub-接口对接文档-桌面端生文生图》2026-07-17 版):
- 新增 `images` 有序数组,1–8 张,每项 `image_base64` 或 `image_url` 二选一;与顶层旧单图字段**互斥**;
- **第 1 张固定为主商品图**,cmhub 服务端固定要求上游优先保留其主体、外观和关键细节;
- **第 2 张及之后为参考图**,仅用于风格、构图、场景或排版,不得替换主商品——该规则由服务端固定追加,客户端 prompt 不能关闭或覆盖;
- 混用/空数组/空图片项/超过 8 张返回 `400 bad_request`;
- 单次请求**无论传几张图只扣一次点**,不按图片数量重复扣费;
- 同步 `/generate/image` 与异步 `/generate/image/tasks` 请求字段一致,桌面端继续走异步 + `Idempotency-Key`(T-564)。
用户已定稿生成规则(2026-07-17 讨论):不勾选时所有分类「第 1 张主图 + 其余全部作为参考图」一起提交;勾选时维持现行为(逐图主图、单图提交);白底图按配置数量生成、默认 1 张(现状即如此,不改)。
## 目标
1. **不勾选**「每张上传图分别作为主图生成」时:所有分类的每个请求提交「第 1 张上传图为主图 + 其余上传图按序作为参考图」,受 cmhub 上限约束最多 1+7 张,超出部分忽略并如实告知。
2. **勾选**时:维持现行为——白底图只用第 1 张、按配置数量生成;其他分类每张上传图分别作为主图、各生成配置数量;每个请求仍**单图提交**(多 SKU 场景防止参考图混款)。
3. `{参考图规则}` 渲染文本与实际提交严格一致:单图与多图两种文本,参考图数量取实际提交数。
4. 提交层统一迁移到 `images` 数组结构(单图也用单元素数组),不再使用顶层旧字段,消除两套代码路径。
5. 参考图列表在 `build_job_specs()` 时冻结进 job 快照,重试/恢复使用快照,不受生成后增删图片影响。
6. 勾选项旁增加 helper 文案,说明勾选依据(多 SKU 勾选 / 同商品多角度不勾选)。
## 实现方案
### 1. 生成规则矩阵(定稿)
| 场景 | 白底图 | 场景图 / 卖点图 / 自定义分类 | 每请求提交内容 |
| --- | --- | --- | --- |
| 不勾选 | 第 1 张为主图,按配置数量生成 | 同左 | 主图 + 其余上传图为参考图(最多 7 张参考) |
| 勾选 | 第 1 张为主图,按配置数量生成 | 每张上传图分别为主图,各生成配置数量 | 仅当前主图单图提交,无参考图 |
- 张数计算与点数估算不变:`suite_total_count()` 逻辑不动;白底图默认数量保持 1(`DEFAULT_CATEGORY_COUNTS` 现状)。
- 多图参考不增加点数消耗(cmhub 单次请求扣一次点),确认弹窗的总点数估算无需调整。
### 2. 提交层 payload(`app/image_studio_generation.py`)
- `_submit_or_resume_job()` 的 payload 从顶层 `image_base64` 迁移为 `images` 数组:
- 勾选模式 / 无参考图:`images=[{"image_base64": 主图}]`;
- 不勾选且有参考图:`images=[{"image_base64": 主图}, {"image_base64": 参考1}, ...]`,参考图按上传顺序排列;
- 本地文件一律读成 base64(复用 `ai._image_data_url`),不使用 `image_url`(SSRF 校验只放行公网地址,本地路径不可用)。
- 客户端在提交前裁剪到 cmhub 上限:主图 1 张 + 参考图最多 7 张;不得同时携带顶层旧字段与 `images`(互斥,违者 `bad_request`)。
- 同步/异步接口字段一致;异步提交、`Idempotency-Key`、轮询、退点、重试语义全部不变。
### 3. job spec 与冻结快照
- `build_job_specs()` 为每个 spec 增加 `reference_asset_ids`(有序整数列表,不含 `source_asset_id` 自身);勾选模式为固定空列表。
- `image_studio_jobs` 表新增可空列 `reference_asset_ids TEXT`(JSON 数组),走既有 additive 迁移方式;历史行为 `NULL`,语义等同「无参考图」,历史任务查看/恢复/重试不受影响。
- 「生成套图」点击时冻结:参考图列表与 prompt 同时进入 job 快照;生成过程中增删上传图只影响下一轮。
- 重试/恢复读取快照 `reference_asset_ids`:若某参考资产文件已缺失(被删除),跳过该参考图、仅以剩余图片提交,并写一条中文日志说明;主图缺失仍按现状让该 job 失败。
### 4. `{参考图规则}` 渲染(衔接 T-637 契约)
- renderer context 增加实际参考图数量;`{参考图规则}` 按提交事实渲染两种文本:
- 单图(勾选模式,或不勾选但只上传 1 张):沿用现文本「当前图片为本任务唯一主参考图;保持商品主体、款式、颜色和关键细节准确;不编造用户与参考图均未提供的信息。」
- 多图(不勾选且参考图 ≥1):「第 1 张为主商品图,请保持其主体、款式、颜色和关键细节准确;第 2–N 张仅作为风格、构图、场景参考,不得替换主商品;不编造用户与参考图均未提供的信息。」N 为实际提交总张数。
- 该表述与 cmhub 服务端固定追加的多图规则同向,不冲突、不试图覆盖。
- 提示词设置弹窗预览:预览 context 按当前套图任务的勾选状态与上传图数量计算参考图数;上传 0/1 张或勾选时显示单图文本。占位符契约(14 个变量、必需 9 个)不变。
### 5. GUI 调整(`app/gui/tabs/product_suite.py`)
- 勾选项「每张上传图分别作为主图生成」旁增加 helper 文案(次要灰色小字,参考现有 helper 样式):
`多款式/多SKU图请勾选;同一商品多角度图不勾选,其余图将作为参考图一同提交。`
- 生成确认弹窗的「逐图主图」说明行更新为新规则表述;不勾选且上传图超过 8 张时,追加一行「参考图仅取前 7 张,其余不参与本轮生成」。
- `1180x760` 及最小窗口尺寸下 helper 与弹窗文案不截断、不重叠。
### 6. 边界与超限
- 上传图 > 8:参考图取上传顺序前 7 张;确认弹窗与日志如实说明;`{参考图规则}` 的 N 用实际提交数,不用上传总数。
- 单张参考图读取/编码失败:跳过该参考图并写中文日志,不使整个 job 失败;主图读取失败维持现状(job 失败)。
- 上传仅 1 张时勾选与否行为一致(单图提交),不出现空参考数组。
## 验收标准
- [ ] 不勾选时,任一分类的生成请求 payload 为 `images` 数组:首元素为第 1 张上传图,后续为其余上传图按序(≤7 张);不含顶层旧单图字段。
- [ ] 勾选时,每个请求 `images` 为单元素数组(当前主图);白底图仍只用第 1 张、按配置数量生成;其他分类每张上传图各生成配置数量。
- [ ] 白底图默认数量为 1,允许用户调整,生成张数与配置一致;`suite_total_count()` 合计与实际 job 数一致。
- [ ] 多图与单图请求均只扣一次点;确认弹窗总点数估算与实际扣点一致。
- [ ] `{参考图规则}` 单图/多图两种渲染与实际提交张数严格一致;弹窗预览与新建 job 的最终 prompt 在同一 context 下完全相同。
- [ ] `reference_asset_ids` 随 job 冻结落库;生成开始后增删上传图不影响本轮已创建 job;重试使用快照参考图列表。
- [ ] 历史 job(`reference_asset_ids` 为 NULL)查看、恢复、重试行为不变,按单图提交。
- [ ] 重试时参考资产文件缺失:跳过缺失项、剩余图片正常提交、写中文日志;主图缺失该 job 失败。
- [ ] 上传图 > 8 时仅前 7 张作为参考提交,确认弹窗有明确中文说明。
- [ ] 勾选项旁 helper 文案按定稿文案显示,`1180x760` 与最小窗口尺寸不截断。
- [ ] 异步提交 `Idempotency-Key`、轮询、自动重试、失败退点语义与 T-564 一致,未被多图改动破坏。
- [ ] cmhub 返回 `bad_request`(含多图字段混用)按不可重试错误处理并展示中文信息。
- [ ] ②AI生成封面、AI 帮写、③「更新蝦皮」、CDP、计费展示不受影响。
## 测试要求
- 纯逻辑测试:`build_job_specs()` 勾选/不勾选 × 白底图/其他分类 × 上传 1/3/9 张的 `reference_asset_ids` 与 spec 数量矩阵;参考图排序、去除主图自身、>8 张裁剪。
- `{参考图规则}` 渲染:单图文本、多图文本(N 取实际提交数)、上传 1 张勾选与不勾选文本一致;预览与 `build_job_specs()` prompt 相等。
- 提交层:payload 使用 `images` 数组且不含顶层旧字段;单元素与多元素结构;参考图读取失败跳过并日志;主图失败 job 失败。
- 迁移:新列 additive 迁移幂等;历史 NULL 行为单图提交;快照重试路径。
- GUI:helper 文案存在与不截断断言;确认弹窗新规则说明与 >8 张提示(进 `tests/test_product_suite_gui.py`)。
## 文档同步
- 实现时更新 `docs/04-architecture.md`:商品套图多图提交规则、`reference_asset_ids` 快照语义、`{参考图规则}` 双态渲染。
- 不修改冻结的 `docs/06-tasks.md`。
## 验证
```bash
py -3.10 -m unittest tests.test_product_suite
py -3.10 -m unittest tests.test_product_suite_gui
py -3.10 -m unittest tests.test_image_studio_generation
py -3.10 -m unittest discover -s tests
py -3.10 -m ruff check app tests main.py
py -3.10 -m compileall app main.py
git diff --check
```
## 非目标
- 不做多 SKU 自动识别或图片语义分组;多图关系由用户通过勾选项声明。
- 不实现「多图联合生成一张多 SKU 拼图/色卡图」(源项目 `multi_sku` 详情图模式不迁移)。
- 不修改点数计费规则、并发计划、轮询间隔或 cmhub 协议本身。
- 不修改 T-637 占位符契约(变量清单与必需集不变,仅 `{参考图规则}` 渲染内容按事实扩展)。
- 不修改 ③「更新蝦皮」、CDP、蝦皮主图拉取。
## 执行记录
- 待实现。