# 核销表 · 第5环 写文案

> 90分级验收清单（孙总方法论·硬门禁）。已吸收独立交叉验证修正（⚠️标注处）。
> 规则：做完一个勾一个，每勾必附证据（SQL结果/日志行/截图）。auto=机器判定，human=倩倩姐/北哥目视。

## 模块目标
文案生成做到：①只展示≥90分合格文案 ②合格不足时先展示后台补 ③品牌词每条植入 ④模型升 opus-4-8 ⑤503真退避重试。

## Ralph 修改步骤（先全列，按序执行）

| # | 改什么 | 落点 |
|---|--------|------|
| S1 | run_text_generation 存库前过滤：`if not (c.get('passed') and c.get('score',0)>=90 and c.get('banned_word_status')!='hard_block'): continue`；合格数<目标时返回 needs_replenish=True | pipeline_io.py:96-118 |
| S2 | Celery 主函数检查 needs_replenish：合格<text_count 时 apply_async 补充子任务（同 task_id 补 deficit），主任务不等待直接推 task_done SSE；子任务跑完追加写库+推 text_candidate SSE | 先 grep `@app.task`/`celery.task` 确认入口文件 |
| S3 | build_product_dict 补 brand_keyword 透传（`product.brand_keyword or ''`），与 custom_prompt 同行 | pipeline_steps.py:58-68 |
| S4 | ORM Product 加 `brand_keyword` 列（String(64) nullable）+ alembic migration 008 | product.py:39 + alembic/versions/008_brand_keyword.py（新建） |
| S5 | products.py ProductCreate DTO 补 brand_keyword；_fmt_product 序列化补；create/update 写库赋值 | products.py:24-97 |
| S6 | 前端 dto.ts Product/CreateProductRequest 补 brand_keyword；ProductForm 加品牌词输入框 | dto.ts:40-62 + ProductSubComponents.tsx:138-165 |
| S7 | MODEL_TEXT 默认值改 claude-opus-4-8（**三处必须全改**：gemini_factory.py:166 + config.py:27 + .env:20） | 见左 |
| S8 | _call_llm 捕获 httpx.HTTPStatusError，503/429 做指数退避重试（≤3次，2^attempt 秒），其他异常仍 return '' 降级 | text_variants.py:22-39 |
| S9 | 前端 ProductForm 补 custom_prompt 文本域（品牌词下方，rows=3，label='自定义指令（可选）'） | ProductSubComponents.tsx:154 |

⚠️ **交叉验证补强（执行前必看）**
- **S7 三处缺一假通过**：若只改 gemini_factory 默认值、.env 没改，log 里 model 仍是旧值且不报错。执行后 `docker exec clover_worker env | grep MODEL_TEXT` 确认环境变量已生效。
- **S8 双保险盲点**：text_variants.py:136 `dedupe_copies(...)[:count]` 的截断在 S1 过滤之前，截断后列表仍可能含 hard_block（dedupe 只去重不过滤）。S1 存库前过滤是兜底，但需确认 generate_text_variants 返回值本身的口径——单保险还是双保险，执行时定。

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

> 🔄 2026-06-15重大变更：评分从机械5维→AI评委真读文案7维(忠于富贵理论)；合格线90→**80(临时线)**。
> 已用 task54 端到端真验(product_id=1,text_count=3,image_count=0)，证据见下。

- [x] **AC-1**〔auto〕✅ 合格线过滤生效：task54 入库5条 score 80~86 全≥80 且无 hard_block。阈值 constants.py QUALITY_PASS_SCORE=80。
- [x] **AC-2**〔human〕✅ 先展示+后台补：task54 首轮2条<目标3→触发补充→达标即停(日志"库内合格=N/3 replenished")。
- [x] **AC-3**〔auto〕✅ 品牌词全链路：临时配 product_id=1 brand_keyword='龙石'跑 task54 → 5条 title+content 全含'龙石'。**已按倩倩姐2026-06-15要求清回NULL(品牌词客户填不预置)**。
- [x] **AC-3b**〔auto〕✅ 反向断言：task54 `content NOT LIKE '%龙石%'` 条数=0(全植入)。
- [ ] **AC-4**〔human〕前端有品牌词框+自定义指令域：录入保存 → GET 产品响应 brand_keyword/custom_prompt 一致。(待前端浏览器验)
- [x] **AC-5**〔auto〕✅ 实际调 opus-4-8：ai_call_logs task54 model='claude-opus-4-8' success=1；worker env MODEL_TEXT=claude-opus-4-8。
- [x] **AC-6**〔auto〕✅ 503真退避：task51~54 日志多次出现"LLM 返回 503，第1/4次重试，等待5s"后恢复。
- [x] **AC-7**〔human〕✅ 前端最后一公里(2026-06-15浏览器真验)：admin登录 localhost:3000 → /tasks/54/text → 用户视角看到 5/5 条文案全展示 + 每条 7维评分条(情绪张力/痛点人群/产品聚焦/买点转化/开头钩子/标题点击/合规性+总分)真实渲染；总分80~86全过线；顶部飞轮偏好回填("已选41次信号")同时生效。ScoreDimBars 成功消费后端 dims 数组。

## 🔴 后续待补正式工作（倩倩姐2026-06-15拍板·质量优先级）
- **提升文案生成质量把分数自然顶上去，不靠继续降评分线**。80是临时观察值。
  方向：优化生成prompt/角度正交/卖点翻译成买点的深度。质量优化是独立工作项，非本环已完成范围。

## ✅ M1 质量优化（2026-06-15 去套路化·已真跑验收）

> 针对"后续待补正式工作"做的第一轮质量优化：①去掉收尾套路句 ②卖点翻译加深。
> 改动落点：`_text_prompt.py`（_PERSONA 去写死范例+收尾铁律 line16；_STRUCTURE_RULES 收尾策略池A-F line74-81；build_prompt 卖点翻译范例 line159；兜底模板去套路 line203）。
> 真跑证据：task65(3条)+task66(5条)=8条样本，product_id=1 真瓶身，image_count=0。

- [x] **Q1**〔auto+human〕✅ 收尾套路句清零：8条收尾**0条**含"买不买跟我没关系/东西放这了"(目标≤1)。
- [x] **Q2**〔human〕✅ 收尾多样性达标：8条覆盖 **A留白/B反问/C场景延续/D克制回购** 四种策略(目标≥4种)，无单一策略霸屏。
- [x] **Q3**〔auto〕✅ 分数不降：8条 score_json 总分 82~92 全≥80(80=合格线，未动)。
- [x] **Q4**〔auto〕✅ 无违禁：8条 banned_word_status 全 pass，无 hard_block。
- [x] **Q5**〔human〕✅ 卖点翻译到位：全为可感知使用感受("熬夜后脸没那么黄""推开不卡纹路""洗面奶就能洗掉不用卸")，无裸功效词堆列。
- ⚠️ 旁证：两次跑均碰 apiports 503 短时过载致单任务少出1条(目标6实得3/5)，503退避重试生效但过载窗口内仍掉条——网络层问题非质量问题，已记 eval_score 列设计留NULL(真分在score_json)。

## 待北哥/倩倩姐定义（不阻断本环代码）
- 合格不足的上限重试次数（现默认补到达标或 MAX_REPLENISH_ROUNDS=3 即止，实测生效）。
