feat(ai-outfit): 左栏新增「标题生成」(看图→提示词→AI 文字标题→写回A列) (§19.18)

AI 穿搭页左栏新增独立的「生成标题」流程:用户写标题提示词,AI 看该行
衣服图(目录行取首图)生成电商标题,逐行立即写回 Excel A 列并刷新明细表;
完成后重载 Excel,紧接「开始生成」跑图即用新标题。移除原「最终生成要求预览」
腾出版面(docs/11 §17)。

- ai_text_service.py:AiTextClient 复用图像服务 HTTP 管道做文本输出;
  chat/gemini 带图视觉,images/images_edits 明确报错;extract_text 取首条标题
- ai_title.py + TitleResult:单行编排,never raises
- excel_service.write_title_result:只写 A 列、不动 D/E/F
- config_service:title_model 默认 + load/save_title_prompt + 默认标题话术
- 面板:标题生成组(提示词+标题模型下拉+保存+生成标题)置于话术组上方;
  _TitleWorker 顺序逐行+立即回填+刷新;与「开始生成」互斥;删预览相关组件
- 测试:文本解析/payload、generate_title(单文件/目录首图/失败)、
  write_title_result、面板标题组存在且无预览;全套 py37 通过

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 11:04:38 +08:00
co-authored by Claude Opus 4.8
parent 313e71b774
commit 2fa488bd21
12 changed files with 921 additions and 86 deletions
+26
View File
@@ -1279,3 +1279,29 @@
- [x] 保留「新请求间隔」启动节流;图片并发数 >1 时日志/缩略图完成顺序允许与文件名排序不同
- [x] 补测试:Excel 行顺序处理、目录内图片并发、单文件行不并发、已存在跳过、部分失败聚合、UI label/default/config 兼容
- [~] 验证:语法检查、`test_outfit_batch.py`、`test_ai_outfit.py`、`test_ai_outfit_panel.py` 通过;全套 `python -m unittest discover -s tests` 当前被工作区未提交的 `packaging/default_config/ai_models.json` 改动阻塞(模型顺序与出厂模板规范不一致),待清理该文件后重跑
### 19.18 AI 穿搭左栏「标题生成」 — docs/11 §17
前置阅读:
- `docs/11-ai-outfit.md`(§17 标题生成、§7 提示词、§10 界面、§4.1 目录行)
- `src/services/ai_image_service.py`(`AiModelConfig`/`image_to_data_url`/`detect_api_type`/`normalize_api_url`/`build_payload`/`generate`,复用 HTTP 管道)
- `src/core/ai_outfit.py`(`render_prompt`/`looks_like_directory`/`list_directory_images`)
- `src/services/excel_service.py`(`read_all_rows`/`write_outfit_result`,COL_TITLE)
- `src/services/config_service.py`(`load_ai_models`/`load_outfit_prompt`/`DEFAULT_CONFIG`)
- `src/app/widgets/ai_outfit_panel.py`(`_build_left`/`_OutfitWorker`/预览相关方法)
背景:
「添加印花」批量导出的 Excel,A 列「标题」是占位印花名。需在 AI 穿搭页左栏新增**独立**的「生成标题」:用户写标题提示词,AI 看该行衣服图(视觉)生成电商标题,逐行回填 Excel A 列并刷新 GUI;之后「开始生成」跑图即用新标题。已确认:写回 A 列;独立按钮;看图(视觉);独立「标题模型」下拉;逐行各生成 1 条、第 n 条回填第 n 行 A、重载刷新 GUI。
任务:
- [ ] `src/services/ai_text_service.py`(新建):`AiTextClient(config, session=None)` 复用 `ai_image_service` 管道;`generate_text(prompt, image_path=None)`(chat/gemini 带图视觉文本输出,images/images_edits 抛 `AiTextServiceError`);`extract_text_from_response`(chat/gemini 取文本,返回第一条标题:首个非空行、去序号/引号、单行化)
- [ ] `src/core/ai_title.py`(新建):`render_title_prompt(template, task)`(替换 `{title}`/`{product_id}`,不加图片输出要求尾巴);`generate_title(task, prompt_template, model_config, api_client=None) -> TitleResult`(目录行取 `list_directory_images` 首图,无图失败,never raises)
- [ ] `src/core/models.py`:新增 `TitleResult`(task/success/generated_title/error/attempts)
- [ ] `src/services/excel_service.py`:新增 `write_title_result(excel_path, row_index, title)`(只写 A 列并保存,不动 D/E/F)
- [ ] `src/services/config_service.py`:`DEFAULT_CONFIG` 增 `title_model`;新增 `load_title_prompt`/`save_title_prompt`(`title_prompt.txt`)+ `DEFAULT_TITLE_PROMPT`
- [ ] `src/app/widgets/ai_outfit_panel.py`:移除预览块(`_sample_combo`/`_preview_*`/`_refresh_preview`/`_fill_sample_combo`/`_reload_sample_rows` 及信号连接);新增「标题生成」组(提示词编辑 + `标题模型`下拉 + 保存 + 生成标题)置于话术组上方;`apply_config`/`_emit_config` 接 `title_model`+标题提示词;`_TitleWorker` 顺序逐行生成+立即回填+刷新明细表标题列,完成后重载 Excel;「生成标题」与「开始生成」互斥
- [ ] 测试:`tests/test_ai_text_service.py`(文本解析含多行只取第一条、images_edits 抛错、payload 含图);`tests/test_ai_title.py`(render、单文件/目录首图/无图失败/异常);`tests/test_excel_service.py` 加 `write_title_result` 只改 A;删预览后 `ai_outfit_panel` 离屏可构建
- [ ] 验证:相关单测 + 全套 py37 通过(`test_config_service` 的 `ai_models.json` 顺序失败属并行 §19.13 遗留,与本改动无关);离屏冒烟(mock `AiTextClient`):选印花 Excel → 生成标题 → A 列改写、明细表刷新、D/E/F 未动、面板无预览控件