# 核销表 · 第6环 配图

> 90分级验收清单（孙总方法论·硬门禁）。已吸收独立交叉验证修正（⚠️标注处，含1处严重落点错误已修）。
> 规则：做完一个勾一个，每勾必附证据。auto=机器判定，human=目视。
>
> 🔴 **2026-06-15 倩倩姐拍板重大调整**：
> 1. **本轮目标收缩=先打通单套配图**（含去AI化+尺寸可选），3套正交A/B/C扩展挪到下一轮。
> 2. **去AI化对齐大卫xhs实测版**（`大卫/小红书系统图片定制/xhs-tool/backend/infrastructure/imagePostProcess.js` 只读素材源，运营实测去AI化能力强）。
> 3. **尺寸按大卫=3:4默认1024×1536（gpt原生免拉伸）**，原 S4/A5 写的"强缩1080×1440"**作废**——大卫±2%容差内不resize，零失真。
> 4. 三套叙事措辞由倩倩姐起草，不临时占位。
>
> 🔴 **2026-06-15 端到端验证发现（task58 codeproxy主通道实测）**：
> - **根因修复**：`.env` 图片主通道曾误配 `IMAGE_PROVIDER_PRIMARY=apiports`，但 apiports 无 gpt-image-2 的 edits 端点（返回SPA首页HTML→`Expecting value`报错），违反红线「生图主=codeproxy」。已改回 codeproxy，worker重建，task58 六图全出（provider=codeproxy/success=1）。详见记忆 clover-apiports-no-image-edits-endpoint。
> - ⚠️ **产品名占位污染产出（待北哥真数据）**：product_id=1 name="素颜霜（预置样本）"，"（预置样本）"被画进图（02特写标题）。同"龙石"类，是测试数据污染非链路bug，换真实产品名即正常。
> - ⚠️ **status enum 无 'failed'**：任务失败只能无限重试或硬改 rejected，设计缺口待补。

## 🟢 本轮施工图（单套配图打通·先列后做）

| # | 改什么 | 落点 | 对应大卫 |
|---|--------|------|---------|
| P1 | image_postprocessor 重写：加 RATIO_MAP(1:1/3:4/4:3/9:16/16:9)、±2%容差跳过、SynthID破除(缩2px裁1px边+modulate亮度1.005/饱和0.998，env SYNTHID_HARD_MODE开关)、quality 88→100/chroma4:4:4 | image_postprocessor.py | imagePostProcess.js逐项 |
| P2 | process_image 加 aspect_ratio='3:4' 参数；pipeline_io 调用处传尺寸 | image_postprocessor.py + pipeline_io.py:204 | RATIO_MAP默认3:4 |
| P3 | 生图请求尺寸对齐 1024×1536(gpt原生3:4)，确认 image_gen size 正确 | constants.py + image_gen.py | 1024×1536 |
| P4 | 配图段写 ai_call_logs(call_type='image',provider,success,latency,error_code) | pipeline_io.py:244前 | Clover自有 |

> 🔴 **2026-06-15 倩倩姐纠偏：标绿门槛=真实数据端到端跑通**：
> task58 是**占位产品**("素颜霜（预置样本）")跑的，**不算端到端成功**。下方 B1-B8 的"✅"降级为"🟡链路自测通过(占位数据)"，**必须用北哥真实数据(产品名=素颜霜/品牌词=倍分子)重跑通才能标真绿**。倩倩姐原话"标绿不能随便标，得端到端测试成功之后才能标"。
> - apiports **确实有生图接口**(后台image key已启用/有余额/历史54万tokens)，只是接法与 `/images/edits` 不同，在大卫xhs里是可用备用通道，待coder查正确接法后修成真备用。本轮主线先codeproxy。

## 🟡 验收清单（本轮·单套）— 占位数据自测通过，待真实数据复验

> 下列✅=占位产品task58链路自测通过；🔴 = 待倍分子素颜霜真实数据重跑确认。

- [x] **B1**〔auto〕单套6图入库：✅task58 `image_candidates` COUNT=6(id171-176)，role齐(hook/product_closeup/ingredient/texture/applied_proof/closer)，url非空，/app/uploads/3/58/ 6个jpg真落盘(1.1-1.35MB)。
- [x] **B2**〔auto〕尺寸3:4=1024×1536：✅task58 PIL独立读6张全部(1024,1536)，容差内未拉伸。
- [x] **B3**〔auto〕去元数据：✅task58 6张 exif全NONE。(SYNTHID缩边验证归B8 coder交叉)
- [x] **B4**〔auto〕尺寸可选真生效：✅单测 process_image('1:1')→(1024,1024)、('3:4')→(1024,1536)、传1080×1440→resize到(1024,1536)。
- [x] **B5**〔auto〕生图写日志：✅task58 `ai_call_logs` call_type='image'/provider='codeproxy'/success=1/latency_ms=345907/error_code=NULL。
- [x] **B6**〔auto〕禁降级硬失败保持：✅task57 need_product_image=True无参考图→拒绝降级(日志"拒绝降级纯文生图")，image_candidates COUNT=0，无"降级"成功字眼。
- [x] **B7**〔human〕浏览器真验：✅倩倩姐+我目视task58封面(01_hook"早八来不及"大字清晰/三卖点勾选/产品与参考图一致/场景去AI化真实/瓶身无虚假品牌词合规)+02特写(产品居中/卖点+气泡框清晰)，3:4比例画质佳。⚠️仅"（预置样本）"占位字样待真数据。
- [x] **B8**〔auto〕交叉验证：✅coder独立起脚本重核task58，结论与team-lead一致：B1(COUNT=6/role齐)、B2(6张全1024×1536/exif=None/icc=None/xmp=None)、B4=B5(provider=codeproxy/success/无Expecting value报错)真绿，B3 N/A(SYNTHID_HARD_MODE未开)。无谎报无放水。

---

## 📦 下一轮：3套正交扩展（本轮不做，归档备查）

> 单套打通后再做。叙事措辞等倩倩姐起草。原 S1/S2/S3/S7/S8 + A1/A2/A3/A4/A7/A8 归此。

### Ralph 修改步骤（下一轮）

| # | 改什么 | 落点 |
|---|--------|------|
| S1 | generate_storyboard_images 循环3次（strategy A/B/C），每次传不同 strategy_hint 影响叙事维度；返回 list[dict]→list[list[dict]] | image_gen.py:121-173 |
| S2 | plan_image_set 接收 strategy 参数，按 strategy 切换叙事描述（新增A痛点先行/B场景先行/C成分背书先行3条），写进 base_prompt 叙事链路段 | storyboard.py:119-193 + constants.py:66-70 |
| S3 | run_image_generation 对 A/B/C 各调一次，存 ImageCandidate 时 strategy 分别写 A/B/C（修掉:207硬编码'A'）；SSE strategy 随之正确 | pipeline_io.py:156-213 |
| S4 | ⚠️**强制 resize**（落点已修正，见下）：`Image.open(...).convert('RGB')` 后**立即**插入 `img=img.resize((1080,1440),Image.LANCZOS)`，并把第47-49行 `resize(original_size)` 改为 `resize((1080,1440))` | image_postprocessor.py:41 之后 |
| S5 | run_image_generation 仿 run_text_generation:77-93，三套全完后写 AiCallLog（call_type='image'，provider取实际路由，success/latency覆盖整批，单套失败不阻断，success=False写error_code） | pipeline_io.py:215 db.commit() 前 |
| S6 | ALLOW_TEXT_ONLY_IMAGE 默认 'true'→'false'，与 pipeline_io:150 硬失败对齐 | image_gen.py:70 |
| S7 | 前端 generationStore 新增 imageSets:Record<'A'\|'B'\|'C',ImageCandidate[]>，addImageCandidate 按 strategy 分流，reset/hydrate 同步 | generationStore.ts:15-122 |
| S8 | 图片选择页加 A/B/C 切换 Tab，默认套A，每套内单张可选；DTO strategy 字段已存在无需改类型 | 新建 ImageSetTabs.tsx 或 page 内联 |

⚠️ **交叉验证补强（执行前必看）**
- **🔴S4 严重修正**：原清单写"在 buf.save 之前插入 resize"是**错的**——那个位置已经过了基于 original_size 的缩放，拿到的还是 gpt-image-2 返回的 1024×1536。正确落点：`Image.open().convert('RGB')` 之后立即锁死 1080×1440，且第47-49行原 `resize(original_size)` 同步改成 `resize((1080,1440))`。
- **S1+S3 必须同步**：S1 改返回值 list→list[list]，pipeline_io.py:165 的 `enumerate(image_results)` 解包会变。**不能只做 S1 不做 S3**，中间是崩溃态。
- **S6 两层说清**：pipeline_io:150 是第一层（按产品 need_product_image 字段拦）；image_gen:70 是第二层（直调 generate_one_image 的兜底）。两层同时维护，别只改一处。

## 验收清单（做完一个勾一个）

- [ ] **A1**〔auto〕3套各6张：`SELECT strategy,COUNT(*) FROM image_candidates WHERE task_id=<id> GROUP BY strategy` → 3行，每行 count=6（或=image_count），strategy=A/B/C。
- [ ] **A2**〔auto〕SSE strategy 三值：DevTools Network 找 SSE 流，grep image_candidate，strategy 出现 A/B/C 各>0次（非全'A'）。
- [ ] **A3**〔human〕⚠️前置：S1+S2 已完成。三套 base_prompt 叙事段不同：在 plan_image_set 加 `logger.info("strategy=%s base_prompt前100字=%s", strategy, base_prompt[:100])`，确认套A含'痛点先行'/套B含'场景先行'/套C含'成分背书先行'。
- [ ] **A4**〔拆两层〕⚠️品牌词进特写图：
  - ①〔auto〕代码层：storyboard.py 中 strategy=A 的第2/6张 item 包含 brand_keyword 与 brand_keyword_rule 字段（DEBUG日志或单测断言）。**这是 pass/fail 判据**。
  - ②〔human〕效果层：下载第2/6张目视有'龙石'（best-effort，模型概率性，**不作代码验收判据**——gpt-image-2瓶身小字会糊已记录在案）。
- [ ] **A5**〔auto〕尺寸严格 1080×1440：⚠️先确认实际写盘路径（worker 日志，默认 UPLOAD_BASE_PATH='uploads/packages' 不是 /app/uploads）→ `python3 -c "from PIL import Image;print(Image.open('<实际路径>/01_hook.jpg').size)"` 必须 (1080,1440)。
- [ ] **A6**〔auto〕生图写日志：`SELECT provider,success,latency_ms FROM ai_call_logs WHERE task_id=<id> AND call_type='image'` → ≥1行，provider非空、latency_ms>0、success IN(0,1)。
- [ ] **A6b**〔auto〕⚠️日志内容正确性（S5核心差错逻辑）：三套全成功时 success=1；至少一套失败时 success=0 且 error_code 非空。
- [ ] **A7**〔auto〕无参考图硬失败：建 need_product_image=True 但产品无参考图的任务 → task status='failed'，image_candidates 无该 task 记录，worker 日志**不含**'降级为纯文生图'。
- [ ] **A8**〔human〕前端 A/B/C Tab：图片选择页有 A/B/C 可点 Tab，点击切换图片网格内容，三套 URL 不同（非平铺混在一起）。

## 待北哥定义（不阻断本环代码骨架，prompt 占位先留）
- 三套叙事维度的具体文案措辞（先用 痛点/场景/成分背书 占位，北哥方案到位再填）。
