Files
cmshoppe/docs/tasks/T-637.md
T
chengmaandClaude Fable 5 0ab110d255 docs(tasks): close two remaining gaps in T-637 spec
- restore-default validates built-in template before atomic write;
  invalid bundled template errors out without touching user template
- {差异化要求} must occupy its own line, enforced by validation, so
  empty rendering can delete the whole line without inline residue

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 14:52:35 +08:00

227 lines
19 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-637
title: 商品套图提示词设置弹窗与实时最终预览
status: TODO
phase: 7
deps: [T-634, T-636]
created: 2026-07-16
---
# T-637 商品套图提示词设置弹窗与实时最终预览
## 问题 / 背景
⑥「商品套图」当前在「商品卖点与要求」标题行右侧放置「AI 帮写」和运行时「取消」按钮,套图最终提示词则由 `app/product_suite.py::build_suite_prompt()` 硬编码。用户只能填写商品卖点,无法查看或调整平台、分类、比例、参考图及商品一致性等上下文最终如何组合,也无法确认实际提交给 cmhub 的完整提示词。
现有硬编码还存在三个明确缺口:
1. 界面显示的「白底主图,多角度呈现商品细节」等分类说明没有进入生成提示词;
2. 同一分类生成多张时没有把分类内序号、总数或差异化要求写进提示词,同源同类任务可能使用完全相同的文本;
3. 提示词预览若在 GUI 内另写一套拼接逻辑,后续容易与真实生成内容漂移。
需求是在标题同行调整按钮布局,并增加一个可编辑、可恢复默认、支持占位符插入和实时最终预览的套图提示词设置弹窗。用户原提议入口名为「提示词列表」,但第一版没有多个命名模板或列表选择;为避免名称误导,入口统一使用「提示词设置」。本任务只管理一份全局套图基础模板,不实现多模板 CRUD。
## 目标
1. 将「AI 帮写」移动到「商品卖点与要求」label 右侧,运行时「取消」紧随其后;标题行最右侧增加「提示词设置」。
2. 提供左右等宽的提示词设置弹窗:左侧编辑基础模板,右侧只读预览最终请求,默认预览白底图。
3. 通过受控占位符菜单在光标处插入变量,编辑后实时刷新预览;未知或缺失必需占位符时不能保存。
4. 提示词预览与真实套图生成调用同一个纯逻辑渲染函数,保证所见即所提交。
5. 内置默认模板随安装包发布,用户模板保存到 `data/`;恢复默认不会依赖网络,也不会覆盖其他提示词。
6. 价格、尺码、虚构参数、商品一致性等强制规则由代码追加,用户不能在模板编辑器中删除。
## 实现方案
> 推进顺序建议:先完成纯逻辑(renderer、占位符校验、默认模板、分类说明迁移进 `build_job_specs()`)并测试绿,再动弹窗 GUI,最后补打包断言;避免 20 条验收项一次性集中风险。
### 1. 主界面标题行
- 调整 `ProductSuiteTab._build_prompt_section()` 的标题行顺序:
```text
商品卖点与要求 [AI帮写] [取消] [提示词设置]
```
- 保留现有用户文案「AI帮写」,不改成「AI编写」;保持现有 AI 帮写请求、取消和状态管理不变。
- 「取消」继续只在 AI 帮写运行时显示,必须紧邻「AI帮写」,不能因最右侧新增入口而跳动到无关位置。
- 「提示词设置」放在标题行最右侧,使用次要命令样式;不与底部「生成套图」争夺主按钮层级。
- 在 `1180x760` 和项目支持的最小窗口尺寸下,标题、两个操作入口和运行时取消按钮不得重叠或截断。
### 2. 弹窗布局与交互
- 新增独立 `ProductSuitePromptDialog`(或等价职责清晰的类),不要继续扩张 `ProductSuiteTab` 为一个大方法。
- 弹窗使用可调整的左右分栏,初始比例 `1:1`,设置合理最小尺寸;左侧是可编辑模板,右侧是只读最终提示词预览。
- 第一行从左到右提供:
- 「保存」:校验通过后原子写入用户模板,并显示轻量中文成功提示;
- 「恢复默认」:二次确认后读取安装包内置模板,写盘前先执行与「保存」完全相同的占位符和结构校验——校验失败(打包缺陷、资源损坏)时显示中文错误、不写盘、不改动用户模板和编辑区;校验通过后调用与「保存」完全相同的原子写入服务立即覆盖用户套图模板,成功后同步更新编辑区、预览区和已保存基线,不要求再次点击「保存」。确认弹窗需明示会丢弃编辑区当前未保存修改;恢复或写盘失败时保留原用户模板和当前编辑内容;
- 右侧「预览分类」下拉框:默认选中「白底图」,可切换当前固定分类和当前项目已有自定义分类。
- 在「预览分类」右侧增加紧凑的「预览第 X 张」步进器:
- 范围为 `1..max(1, 当前预览分类配置数量)`,默认值为 1;
- 分类数量为 0 或 1 时固定为 1 并禁用,禁止出现 `第 1/0 张`;
- 分类数量大于 1 时允许切换序号,用于核对 `{分类序号}`和 `{差异化要求}`的真实变化;
- 切换分类时重置为第 1 张,不修改项目内真实生成数量。
- 左侧编辑区顶部提供「插入变量」菜单或按钮菜单。用户选择变量后,在当前光标位置插入,不要求手工记忆占位符。
- 右侧预览区不可编辑、不可获得保存语义;内容使用当前商品任务的真实平台、地区、语言、比例、商品 ID、分类、参考图序号和卖点。预览默认使用 `{主参考图序号}`=1、`{分类序号}`=步进器当前值、`{分类总数}`=`max(1, 当前预览分类配置数量)`;当前值为空时使用明确的中文示例值,内部 `draft_` 标识不得出现在预览中。
- 编辑内容变化后使用约 `200ms` 单次防抖刷新,连续输入不得触发大量重复渲染或造成界面卡顿。
- 关闭存在未保存修改的弹窗时,提供「保存」「不保存」「取消」三种中文选择;保存校验失败时保持弹窗打开。
### 3. 占位符契约
- 第一版只允许以下单层中文占位符,沿用项目现有 `{变量名}` 风格:
```text
{分类要求}
{套图分类}
{平台}
{国家地区}
{输出语言}
{图片比例}
{商品ID}
{主参考图序号}
{分类序号}
{分类总数}
{差异化要求}
{参考图规则}
{商品卖点与要求}
```
- `{分类要求}`、`{套图分类}`、`{图片比例}`、`{分类序号}`、`{分类总数}`、`{差异化要求}`、`{参考图规则}`、`{商品卖点与要求}`为必需占位符;缺失时预览区显示中文校验结果,「保存」禁用。
- `{套图分类}`列为必需是因为固定分类的说明文案不保证包含分类名,删掉后最终提示词可能丢失分类信息。
- `{分类序号}`、`{分类总数}`和 `{差异化要求}`列为必需,用于保证同一分类配置多张时最终提示词具有明确序号和差异化约束;分类总数为 1 时 `{差异化要求}`可以渲染为空,但模板中仍必须保留该占位符,防止以后把数量调大时退化为完全相同的提示词。
- `{差异化要求}`必须独占一行(所在行去除首尾空白后只剩该占位符),否则校验失败、「保存」禁用。这保证渲染为空时可整行定点删除,不会产生「要求:。」之类的内联残句。
- 未知占位符、未闭合花括号和渲染后仍残留的占位符都视为错误,不得静默原样发送给模型。
- 重复使用合法占位符允许保存;第一版模板**不支持字面花括号**,任何非合法占位符形式的花括号一律校验报错,不定义转义规则,也不做字符串猜测。
- 占位符替换必须使用结构化上下文字典和统一 renderer,不允许在 GUI 中连续手写 `.replace()` 拼接另一套规则。
### 4. 默认模板和分类要求
- 新增安装包内置默认模板,例如 `app/default_prompts/product_suite/base.txt`,结构参考已完成的外部源码调研,但按 cmshopee 当前字段和单参考图事实收口:
```text
{分类要求}
平台:{平台};国家地区:{国家地区};输出语言:{输出语言}。
图片比例:{图片比例}。
套图分类:{套图分类};当前为本分类第 {分类序号}/{分类总数} 张。
当前主参考图序号:{主参考图序号}。
{参考图规则}
{差异化要求}
商品ID:{商品ID}
商品卖点与要求:
{商品卖点与要求}
```
- 将当前仅存在于 GUI 的固定分类说明迁移或集中到业务层,渲染为 `{分类要求}`;GUI 通过同一来源显示说明,不能复制另一份字典。
- 自定义分类的 `{分类要求}`必须明确包含自定义分类名称,例如「生成商品套图中的『使用方法图』,按照该分类名称表达用途」,不能使用不含分类名的泛化文案。
- `{差异化要求}`至少包含分类内序号和“与本分类其他图片采用不同构图、角度或场景”的约束。若第一版没有可靠的商品语义,不自动编造具体卖点、参数或使用场景。
- 退化规则:分类总数为 1 时 `{差异化要求}`渲染为空,最终提示词不得出现“与本分类其他图片不同”这类无意义残句。
- 空行清理必须是定点操作:仅当 `{差异化要求}`渲染为空时删除该占位符所在行及其行尾(校验已保证其独占一行),不得全局压缩连续空行、修改用户主动保留的段落间距或顺带清理其他空白。
- `{参考图规则}`必须与当前实际请求一致。每个 `image_studio_job` 只有一个 `source_asset_id` 时,应明确表述当前图片是本任务唯一主参考图,不得声称未提交的其他图片也参与参考。
### 5. 强制保护规则
- 以下规则不得放入可删除的用户模板中,由业务 renderer 在模板渲染完成后统一追加:
- 保持商品主体、款式、颜色和关键细节准确;
- 不添加无依据的功能、参数、认证、价格、折扣或尺码;
- 用户和参考图均未提供的信息不得自行编造;
- 项目既有的其他内容安全约束。
- 右侧预览必须显示「用户模板渲染结果 + 强制保护规则」的完整最终文本,让用户看到真实提交内容。
- 强制规则应作为业务常量或只读内置资源集中维护,不得分别散落在弹窗、worker 和 `build_suite_prompt()`。
### 6. 持久化、初始化与恢复
- 用户模板建议保存为 `data/prompts/product_suite/base.txt`,路径必须通过 `appconfig` 统一解析,不能依赖当前工作目录。
- 安装包内置模板只读;首次运行仅在用户模板不存在或为空时复制默认值,已有用户模板不得被版本升级静默覆盖。
- 扩展现有 `app/prompts.py` 的模板读写能力或增加职责单一的套图提示词模块,复用路径防越界、UTF-8 和文件名安全纪律。
- 保存采用同目录临时文件后原子替换,写入失败时保留原用户模板并给出中文错误。
- 「保存」和「恢复默认」必须调用同一个原子写入服务;统一写盘服务不等于只允许「保存」按钮触发,禁止为恢复默认另写非原子的覆盖路径。
- 「恢复默认」只覆盖商品套图基础模板,不影响②AI生成标题/封面模板、AI工场模板、商品卖点、图片或生成历史。
- 用户模板每次加载后都必须执行与保存前相同的占位符和结构校验,覆盖用户手工编辑文件、文件损坏及未来模板契约升级等情况。
- 用户模板无效时不得静默改用内置默认模板,也不得把残留占位符提交给 cmhub:
- 打开设置弹窗时保留并展示当前无效内容及中文校验错误,允许用户修复或恢复默认;
- 点击「生成套图」时在创建 job、启动 worker 或调用 cmhub 前中止,并提示 `套图提示词模板无效,请在提示词设置中修复或恢复默认。`;
- 中止不得修改用户文件、项目卖点、图片、历史 job 或当前分类设置。
- PyInstaller 构建脚本/spec 必须显式包含内置套图默认模板;开发环境和打包程序使用同一资源读取入口。
### 7. 预览与真实生成共用渲染
- 抽取纯逻辑接口,例如:
```python
render_product_suite_prompt(template_text, context) -> str
```
- `ProductSuitePromptDialog` 只负责组装预览 context 并调用 renderer;`build_job_specs()`/`build_suite_prompt()`也必须调用相同 renderer。
- 点击「生成套图」时只读取一次当前模板,并在建立本轮 job specs 前冻结。生成过程中修改或恢复模板只能影响下一轮,不能改变已经创建或提交的任务。
- 每个 job 继续把最终渲染后的完整提示词保存到 SQLite;历史任务查看、恢复和重试使用原任务快照,不用新模板重算旧任务。
- 预览切换分类只改变预览 context,不修改当前项目分类数量、用户模板或商品卖点。
## 验收标准
- [ ] 「AI帮写」紧邻「商品卖点与要求」,运行时「取消」紧随其后;「提示词设置」位于同行最右侧。
- [ ] 点击「提示词设置」打开左右等宽弹窗,左侧可编辑、右侧只读,默认预览白底图最终提示词。
- [ ] 预览分类下拉可以切换固定分类和当前自定义分类;切换只刷新预览,不修改项目配置。
- [ ] 从「插入变量」菜单选择每个合法变量,都会插入到当前光标位置并在约 200ms 后刷新预览。
- [ ] 当前商品数据完整时,预览正确替换平台、地区、语言、比例、商品 ID 和商品卖点;主参考图序号默认为 1,分类序号使用步进器值,分类总数使用 `max(1, 当前分类配置数量)`。
- [ ] 当前预览分类数量为 0 或 1 时显示第 1/1 张且步进器禁用,不出现第 1/0 张;数量大于 1 时可切换预览第 1 至第 N 张。
- [ ] 临时草稿预览使用 `未绑定商品` 或等价中文值,不显示内部 `draft_` 标识。
- [ ] 删除任一必需占位符(含分类序号、分类总数和差异化要求)、输入未知变量、花括号未闭合、`{差异化要求}`未独占一行或渲染残留变量时不能保存,并显示中文校验错误。
- [ ] 固定分类说明实际进入提示词;自定义分类提示词明确包含自定义分类名称。
- [ ] 同一分类多张任务的最终提示词包含各自序号/总数和差异化要求,不再完全相同。
- [ ] 分类总数为 1 时,该分类最终提示词不含差异化残句;仅删除空的差异化占位符所在行,用户主动保留的其他段落空行不变。
- [ ] 参考图规则与实际单个 `source_asset_id` 一致,不声称未提交图片参与参考。
- [ ] 强制商品一致性、禁止虚构参数/价格/尺码等规则始终出现在最终预览和真实请求中,用户不能从编辑器删除。
- [ ] 保存后关闭并重启程序仍加载用户模板;已有用户模板不会被启动初始化或软件升级覆盖。
- [ ] 「恢复默认」经确认后通过统一原子写入服务立即落盘,并刷新编辑区和预览;关闭弹窗不再提示该次恢复为未保存修改,且只影响商品套图模板。
- [ ] 「恢复默认」写盘前校验内置模板;内置模板无效时显示中文错误、不写盘、不改动用户模板和编辑区。
- [ ] 弹窗存在未保存修改时关闭会出现保存/不保存/取消三选项,任何失败都不损坏原模板。
- [ ] 同一 context 下右侧预览文本与新建 job 保存的最终 prompt 完全一致。
- [ ] 生成开始后再修改模板不影响本轮已经冻结的 job;历史重试继续使用历史 prompt 快照。
- [ ] 打包程序首次运行能初始化套图默认模板,且用户无需手工创建文件即可打开弹窗和生成。
- [ ] 用户手工把模板改成缺失必需变量、未知变量或未闭合花括号后,程序不会崩溃或静默回退;点击生成会在创建 job 和调用 cmhub 前中止,并引导用户修复或恢复默认。
- [ ] `1180x760` 及项目最小窗口尺寸下,标题行和弹窗控件不重叠、文字不截断。
- [ ] SQLite schema、cmhub API、计费、并发、CDP 和③「更新蝦皮」不受影响。
## 测试要求
- 新增纯逻辑测试覆盖全部合法占位符、必需变量(含 `{套图分类}`、`{分类序号}`、`{分类总数}`、`{差异化要求}`)、未知变量、未闭合/字面花括号报错、`{差异化要求}`未独占一行报错、重复变量、强制规则追加和 Unicode 文本。
- 覆盖白底图、其他固定分类、自定义分类、正式商品、临时草稿、分类多张、分类数量为 0/1 的预览退化、分类总数为 1 的差异化退化与定点空行删除,以及单参考图 context。
- 覆盖用户模板首次初始化、不覆盖已有文件、原子保存失败保持原文件、保存和恢复默认共用写入服务、恢复成功更新已保存基线、内置模板无效时恢复默认报错且不写盘。
- 覆盖用户文件被外部改成无效模板时的加载校验、弹窗修复入口和生成前中止,断言无 job、worker、cmhub 或文件覆盖副作用。
- `tests/test_product_suite_gui.py` 覆盖标题行按钮顺序、AI 帮写运行时取消按钮、弹窗初始分栏、默认白底图、分类切换、预览序号步进器、变量插入、防抖刷新、只读预览、保存校验和未保存关闭三选项。
- 覆盖预览 renderer 与 `build_job_specs()` 最终 prompt 相等,以及生成运行中模板变更不污染本轮 job。
- 更新构建测试或脚本断言,确认内置套图模板进入 PyInstaller 数据文件。
## 文档同步
- 实现时同步更新 `docs/04-architecture.md` 的商品套图提示词事实来源、占位符契约、强制规则、模板路径和任务冻结语义。
- 在对应任务执行记录中说明内置模板和用户模板路径;不得修改冻结的 `docs/06-tasks.md`。
## 验证
```bash
py -3.10 -m unittest tests.test_prompts
py -3.10 -m unittest tests.test_product_suite
py -3.10 -m unittest tests.test_product_suite_gui
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
```
## 非目标
- 不实现多个命名套图模板的新增、重命名、删除、排序或导入导出,因此入口不命名为「提示词列表」。
- 不允许用户编辑或移除强制商品一致性和禁止虚构内容规则。
- 不调用 AI 自动改写基础模板,预览不会产生 cmhub 请求或点数消耗。
- 不修改商品卖点 AI 帮写模型、别名、超时、重试或计费。
- 不修改图片生成模型、cmhub submit/poll/download 协议、并发和超时。
- 不修改 SQLite schema、图片目录、CDP、蝦皮主图拉取或③「更新蝦皮」。
- 不在本任务中增加详情图提示词编辑器;第一版只覆盖⑥「商品套图」。
## 执行记录
- 2026-07-16 补:按全栈评审调整规格——`{套图分类}`升为必需占位符;第一版禁止字面花括号(不做转义规则);「恢复默认」改为只回填编辑区、落盘统一走「保存」;预览序号类占位符固定值约定;分类总数为 1 时差异化要求退化为空并折叠空行;补充纯逻辑先行的推进顺序建议。
- 2026-07-16 补:修正评审后仍存在的契约缺口——分类序号/总数/差异化要求升为必需变量;预览总数最小为 1,并增加分类内序号步进器;「恢复默认」改为复用统一原子写入服务立即落盘;补充无效用户模板的加载校验和生成前阻断;空行处理限定为只删除空的差异化占位符所在行。
- 2026-07-16 补:封堵两处剩余小洞——「恢复默认」写盘前先校验内置模板,校验失败报错不写盘(防打包缺陷/资源损坏写入坏模板);`{差异化要求}`必须独占一行,校验层强制,杜绝渲染为空时的内联残句。
- 待实现。