Files
meida/docs/开发任务包/T17-报告文案与校验.md
T

69 lines
7.4 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.
# T17 · 报告流水线·LLM 文案与生成后校验
> 对应任务:`T-P3-10`(文案结论与诊断值一致,无自相矛盾)、`T-P3-11` 🔴(**硬性禁忌违反数 = 0**;贬损词零通过)、架构 §7.1 步骤 17~19(「生成后校验不可跳过:校验失败最多重试 2 次,仍失败降级为规则直出模板文案,绝不放行违规内容」)| 分支:`task/t17-report-copy` | worktree:`/home/xsl/meida_backend-t17`
> 先读《README.md(协作规程)》,遵守全部纪律。**第四批拼流水线:T15 定 Stage 接口,你供文案与校验两个阶段,文件名与类名已定死。**
## 2. 你要创建的文件(挂点,禁差分毫)
| 文件 | 导出 | 流水线 dotted path |
| --- | --- | --- |
| `packages/report/copy.py` | 纯逻辑:结论→文案素材、模板降级渲染 | — |
| `packages/report/copy_stage.py` | `class CopyStage` | `packages.report.copy_stage:CopyStage` |
| `packages/report/validation.py` | 纯逻辑:贬损词表、一致性校验器 | — |
| `packages/report/validation_stage.py` | `class ValidationStage` | `packages.report.validation_stage:ValidationStage` |
Stage 接口以协调提交 `packages/report/pipeline.py` 骨架为准;`ctx.gateway` 是 T15 注入的 `LLMGateway`(集成测试可换 FakeProvider——参考 `tests/integration/test_vertical_slice.py` 的做法)。
## 3. LLM 文案(copy.py + copy_stage.py)
1. **Prompt**:`prompts/report_copy/v1.txt`(你新建,版本化目录规则见 `prompts/README.md`——`[system]` 段**不许含任何变量**(缓存前缀纪律,`validate_cache_prefix` 会拦);`[user]` 段放 `{dimensions_json}`)。system 定角色:形象顾问报告文案;红线写进 system:描述而非评判、不用绝对化用语、不出现贬损词、结论只能复述给定诊断值**不得新增维度结论**、简体中文。
2. **结构化输出**:`gateway.complete(scene=Scene.REPORT_COPY, prompt_vars={...}, schema=ReportCopy, user_id=..., ref_type="report", ref_id=str(report_id))`——先读 `packages/llm/gateway.py` 的 `complete` 签名与 `Scene` 枚举、`packages/llm/routing.py` 里 REPORT_COPY 场景现有配置(temperature/prompt_id 指向你的 v1)。`ReportCopy(BaseModel)`:`summary: str`、`sections: list[Section]`(Section:`title/paragraphs: list[str]`)——**schema 宽进严出的取舍写注释**(字段越少结构化成功率越高,文案主体自由度放 sections 内)。
3. **素材组装(纯函数 `build_prompt_material(result_diagnosis) -> str`)**:从 T16 的 `result["diagnosis"]["dimensions"]` 取 status=ok 的维度(skipped 不进 prompt——**LLM 看不到缺失维度就不会编造它**,这是防幻觉的第一道闸);序列化成紧凑 JSON(key+value+evidence,舍 confidence 小数位)。**总长控制**:拼好后若超 ~6000 字符,按 basic_coverage 优先级裁剪维度并记录 `dropped_keys`(对齐架构 §7.2 的裁剪思想:低重要度先丢,硬结论永不丢)。
4. **`CopyStage`(name="copy")**:无 diagnosis 产物 → 抛错(流水线顺序错);调 gateway → `result["copy"] = {"summary": ..., "sections": [...], "model": completion.model, "prompt_version": ..., "dropped_keys": [...]}`。**这里不做校验**——校验是下一阶段的独立职责(T-P3-10 的"生成后校验"是流水线步骤 18,不是文案函数的内嵌断言)。
## 4. 生成后校验(validation.py + validation_stage.py)
1. **贬损词表初版**(`validation.py` 模块常量 `PEJORATIVE_TERMS`,来源:`G:\meida\docs\合规与安全方案.md` §3「内容伦理红线」——**开工先读该节**,把"描述而非评判/不使用缺陷框架"翻译成具体词表:胖/丑/矮小/缺陷/土气/难看/显老/失败 等约 20~40 词 + 简单变形("有点胖"命中"胖"即可,不做词干)。词表带注释:这是初版内嵌版,正式版归合规词库任务(T-P0-07)。
2. **一致性校验(纯函数)**:
- `check_pejorative(text) -> list[str]`(命中词列表);
- `check_conclusion_consistency(copy: ReportCopy, dimensions: list[dict]) -> list[str]`(不一致清单):初版三条规则——① 文案中出现的**体型/脸型/季型枚举词**必须是诊断值同款(诊断说 H 型、文案写 A 型 → 不一致);② 文案提到具体色值 `#RRGGBB` 必须在色板维度 value 集合内;③ 出现"建议"句式却无对应 rule_hits 支撑的维度结论 → 标记(宽松口径,注释说明误报代价 vs 漏报代价的取舍)。
- `check_absolute_claims(text)`:绝对化用语(最/第一/彻底/百分百——prompt 禁了,校验兜底)。
3. **`ValidationStage`(name="validation")**:取 copy + diagnosis → 全部校验 →
- **全过**:`result["validation"] = {"passed": true, "checks": {...计数}}`;
- **有违规**:**重试一次**——把违规清单作为修正指令追加(messages 上下文,参考 gateway 的结构化重试机制但不能用它内部的重试——那是解析层,这是语义层,两层独立)再生成 → 再校验 → 仍违规 → **降级模板**:`render_fallback_copy(dimensions, rule_hits)`(纯函数:按维度 evidence + rule explanation 拼模板文案,零 LLM)→ `result["validation"] = {"passed": true, "degraded": true, "violations_first_pass": [...]}`。**任何出口 passed 必须为 true 或阶段抛错——绝不放行未过校验的文案**(T-P3-11 红线)。
4. 校验产物同段落库(`result["validation"]`),报告查询侧能看到是否降级。
## 5. 测试要求
- 单测 `tests/unit/test_report_copy.py`:素材组装(skipped 不进、超长裁剪记录 dropped、硬结论不裁)、模板降级渲染(有 rule_hits 时含 explanation)。
- 单测 `tests/unit/test_report_validation.py`:词表命中(含"有点胖"变形)、一致性三条规则各一正一反、绝对化用语、重试后仍违规 → 降级路径纯函数级验证。
- 集成 `tests/integration/test_report_copy_stage.py`(新文件,FakeProvider 替换 gateway——参考 test_vertical_slice):
1. 干净 LLM(Fake 返回合规文案)→ copy + validation 两阶段 passed=true;
2. **脏 LLM**(Fake 第一次返回含"微胖/最合适",修正后干净)→ 第一轮 violations 非空 → 重试通过;
3. **持续脏 LLM**(恒返贬损词)→ 降级模板 passed=true degraded=true,**最终文案零贬损词**(T-P3-11 的黄金断言)。
- 不测整条流水线(T15 集成已覆盖编排);只直接构造 ctx 调你的阶段。
## 6. 禁改清单
通用禁改之外:`packages/report/` 除你四个文件 + `prompts/report_copy/**` 外全部、`apps/**`、全部已有测试。**`prompts/report_copy/v1.txt` 归你**(新目录不与他人冲突)。
## 7. 验收标准(T-P3-10/11)
1. 校验不可跳过:任何出口要么 passed 要么抛错;
2. 贬损词零通过(含持续脏 LLM 的降级路径断言);
3. 文案-结论一致性三条规则生效;
4. LLM 看不到 skipped 维度(防幻觉得到结构保证);
5. 全部检查绿(mypy 范围同 T15 + 你的文件)。
## 8. 提交信息
```
feat(report): LLM copy generation with mandatory post-validation and fallback
report_copy/v1 prompt(系统段零变量)+ 结构化 ReportCopy + 素材组装
(skipped 不进 prompt/超长裁剪)+ 贬损词表/一致性/绝对化三重校验 +
重试一次后降级规则直出模板(T-P3-10/11)。
偏差说明:<如有写在这里,无则写"无">
```