69 lines
7.4 KiB
Markdown
69 lines
7.4 KiB
Markdown
# 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)。
|
||
|
||
偏差说明:<如有写在这里,无则写"无">
|
||
```
|