diff --git a/docs/design.md b/docs/design.md index 6e10272..022103f 100644 --- a/docs/design.md +++ b/docs/design.md @@ -17,6 +17,7 @@ 9. [数据模型与 Provenance 层](#9-数据模型与-provenance-层) 10. [异常处理策略](#10-异常处理策略) 11. [通信语言与文档规范](#11-通信语言与文档规范) +12. [Phase 5:Writer / QA 子系统](#12-phase-5writer--qa-子系统) --- @@ -1710,3 +1711,73 @@ Step 3(关联推理)中部分功能推理失败: - 不得将客户数据、公司信息上传至外部公开仓库 - API Key 配置在环境变量或配置文件中,不得硬编码在源码 - 确认所有依赖的许可证类型,禁止使用盗版软件 + +--- + +## 12. Phase 5:Writer / QA 子系统 + +> 本章记录在 phase5/writer-qa 分支上落地的「Writer / QA 子系统」实现,作为设计 §6(Writer Agent 详细设计)与 §7(QA Agent 详细设计)的工程落地补充。实现严格遵循设计基线,并诚实标注了范围边界与推后项。 + +### 12.1 目标与范围 + +**目标**:打通「结构化源 → 单章内容生成 → 渲染 → 注入 Word 模板 → QA 校验 → 闭环仅重失败章」的最小可用链路。 + +**范围内**: +- 单章内容生成(WriterAgent 调用 InferenceEngine 产出 `ChapterContent`)。 +- 内容块渲染(`render_chapter_blocks` 将 `ChapterContent` 转 `Block` 序列)。 +- Word 注入(`DocxInjector.inject` 将章节内容写入模板对应 `{{section:id}}` 占位符)。 +- QA 校验(确定性维度:`traceability` / `placeholder_residue` / `chapter_completeness`)。 +- QA 闭环(`QALoop` 仅对失败章重新生成,受 `QALoopController(DEFAULT_MAX_QA_ROUNDS=3)` 约束)。 + +**范围外(诚实标注)**: +- **语义 QA 为探针**:内容准确性 / 幻觉检测 / 规则遵守等 LLM 语义维度,经 `ChapterScorer.llm_evaluators` 钩子注入,默认中性分,尚未接入真实推理(设计 §7.5 已声明待 Phase5)。 +- **图表生成**:本实现不生成图形/图表,仅支持表格/列表/段落/提示框等文本型 Block(设计 §6.4 的 table/list/paragraph/note)。 +- **跨章引用一致性**:设计 §7.2 第 4 项「关联一致性」由 `WriterState.cross_refs` 记录,但端到端语义校验(与 ImpactReport 逐条比对)超出本实现范围,留待人工质量门禁与后续里程碑。 + +### 12.2 组件与数据流 + +``` +StructuredSource + │ build_contexts(structured_source, samples_dir) + ▼ +list[ChapterContext] (每章:chapter_id / 数据选择器 / 模板标记) + │ WriterAgent.generate_chapter(ctx) 逐章串行 + ▼ +ChapterContent(blocks: list[ContentBlock] + source_uris) + │ render_chapter_blocks(content) + ▼ +list[Block](DocxInjector.Block:paragraph/heading/table/list/note) + │ DocxInjector(template_path).inject(sections, meta) + ▼ +Document(注入后 Word 文档) + │ QALoop(DEFAULT_MAX_QA_ROUNDS=3) + │ ├─ QAValidator 校验各章(仅确定性维度) + │ └─ 仅对失败章:WriterAgent.generate_chapter → render → 重新注入 + ▼ +最终 Document + QAReport +``` + +关键约定: +- 逐章**串行**生成(设计 §6.8.1 基线约束),`WriterState` 跨章共享摘要与关键表结构供后章引用。 +- QA 闭环**仅重失败章**,不重新生成全部,受最大轮次护栏约束,禁止无限重试(设计 §7.4 护栏)。 + +### 12.3 实现期发现的真实 API 适配(避坑记录) + +实现过程中,以下真实接口与设计/规范文档存在偏差,已按真实接口落地,此处统一记录供后人避坑: + +1. **`DocxInjector` 真实接口**:为 `DocxInjector(template_path).inject(sections: dict[section_id, list[Block]], meta) -> Document`。**不存在** `inject_blocks` 方法;`sections` 字典的键是 `{{section:id}}` 中的 `id`(不含 `section:` 前缀)。 +2. **`InferenceEngine.chat_structured`** 签名为 `chat_structured(*, session_id, prompt, variables, schema, retry_count=2) -> StructuredResult`,返回对象含 `.data`(解析后的 dict)与 `.status`(ok / parse_error / failed)。调用时必须关键字传参。 +3. **`RagService.retrieve_*`** 为**同步**方法(非 async)。调用方在同步编排链路中直接调用即可,无需 `await`。 +4. **`map_template`** 消费真实的 `ParsedTemplate.sections`(元素类型为 `ChapterMarker`,含 `type` / `name` / `level`)。映射按文档顺序:遇到 `heading` 起一章;其后的 `section:` 占位符归属该章 → `chapter_id = id`、`section_placeholder = "section:"`。 +5. **`ChapterScorer.score(chapters: list[ChapterArtifact], source: StructuredSource)`**:QA 层需要 `ChapterContent → ChapterArtifact` 适配器(由 `ChapterContent.blocks` 拼接得到 `text`,并从各 block 收集 `source_uris`);设计 §7.5 的 `ChapterArtifact` 并非直接由 Writer 产出,需经适配。 +6. **真实 `DocxInjector.Block`** 已支持 `table` / `list` / `note` 类型;表格注入会渲染 `caption` 段落(caption 非空时在其上方/下方生成说明段落),非空 caption 不应被丢弃。 + +### 12.4 垂直切片状态 + +**FakeLLM 模式已打通(可回归)**: +- 运行 `python scripts/run_phase5_slice.py --fake`,使用 `FakeLLMClient` 驱动整条链路,产出 `samples/phase5-slice/output.docx`。 +- 该切片覆盖:build_contexts → 逐章生成 → 渲染 → 注入 → QA 闭环(仅重失败章),并附带 `QAReport`。 + +**真实 LLM 生成 + 人工质量门禁(待人工执行项,P5-T10 推后)**: +- 真实推理接入(DeepSeek / Qwen 等)与端到端人工质量门禁(内容准确性 / 格式精度 / 规则遵守的人工判读)不在本自动实现范围内,标记为推后项。 +- 真实 LLM 接入点已预留(`InferenceEngine` 默认实例 + `PromptRegistry` 注入),人工执行时仅需提供可用模型配置与 `scripts/run_phase5_slice.py` 的非 `--fake` 路径。