Files
cmshoppe/docs/tasks/T-658.md
T

137 lines
11 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.
---
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、蝦皮主图拉取。
## 执行记录
- 待实现。