输出语言一致性保障 + 用户可选输出语言(2026-08-24)
背景
真实 LLM 试运行暴露:生成正文曾出现中日混杂(早期第 1-2 章中文、第 3-7 章日文;prompt 渲染缺陷
修复后全日文,但属概率性改善)。盘点确认:
- 唯一保障是 prompt 层【语言约束】,无任何程序化检测(grep 全代码库证实)
- 输入侧存在中文诱导源:
_format_impact() 固定标签(受影响:、[警告])进入每章 prompt
- design.md §7.2 QA 十项清单没有语言一致性校验项
- 日文章标题多为纯汉字(「5. DB設計」无假名),仅凭标题无法判定期望语言
用户需求升级:让用户可选择输出中文还是日文(参数化),并配套中文模板。
关键用户决策(已确认)
| # |
决策点 |
结论 |
| 1 |
Excel 表格数据是否翻译 |
照抄原文——数据是源数据忠实呈现,翻译违反 §7.2 内容准确性/可追溯性 |
| 2 |
子节标题语言 |
双模板方案——提供中文/日文两套模板,按用户选择使用;锚点 id 语言中立不变 |
| 3 |
违规处理 |
重试耗尽后硬失败(WriterGenerationError),与既有失败语义一致 |
| 4 |
实施范围 |
A(强制校验)+ B(impact 标签本地化)+ C(QA 第 11 维度)+ 参数贯通,全做 |
核心设计:模板 × 语言参数 双轨制
| 层 |
日文模板(现状) |
中文模板(新增镜像) |
| H1/H2 标题 |
1. はじめに / 2.1 機能一覧表 |
1. 前言 / 2.1 功能一览表 |
| 锚点 id |
section:function_list … |
完全相同(不变) → 映射/注入器零改动 |
| 封面 |
概要設計書 / バージョン |
概要设计书 / 版本 |
实施清单(TDD,每项先 RED 后 GREEN)
0. 中文样本模板
- 新建
scripts/make_zh_template.py:python-docx 镜像日文模板结构
(Title/H1×7/H2×6/锚点原样/封面字段),产出 samples/概要设计书模板_中文.docx 入库
- 测试:解析 zh 模板断言 7 章 anchor id 与 ja 模板逐一对应
1. 语言参数贯通
config.py:新增 WriterConfig(output_language: "auto"|"zh"|"ja" = "auto") → Settings.writer
models.py:GenerationContext.output_language 字段;to_vars() 输出动态
{{language_instruction}}(zh=必须使用中文撰写 / ja=必須日本語で記述 / auto=与标题一致)
writer_agent.py:【语言约束】段落改引该变量
context_builder.py / orchestrator.py / run_trial.py:--output-language {auto,zh,ja} 逐层透传
A. 确定性脚本检测 + 失败重试【核心】
- 新建
src/genesis/writer/language.py:
detect_script(text):含假名(U+3040–30FF)→ "ja";CJK 汉字零假名 → "zh";否则 None
resolve_expected_language(explicit, title, fallback_texts):显式 > 标题假名 > 规则文档主导脚本;
单一事实来源(A 的重试校验与 C 的 QA 维度共用)
violations(...):仅检 paragraph/note/list 正文块;表格 rows 不检(照抄原文)、heading 不检(跟随模板);
违规判定 = 含 CJK 汉字零假名且长度 ≥12(防误杀短术语)
writer_agent.py:generate_chapter 重试循环内后置校验,违规按生成失败重试;
max_retries 默认 1→2(现默认无重试机会,已确认既有测试均显式传参)
B. _format_impact() 标签本地化
- 按
output_language 选标签集(zh=受影响 / ja=影響;auto 默认 ja);只影响 prompt 变量,
不影响 impact-report.json 独立产物
C. QA 第 11 维度 language_consistency
ChapterArtifact.expected_language: str = ""(上层解析后传入,scorer 不自行推导)
qa/validator.py 从 orchestrator 透传期望语言;歧义记 unverifiable 计入 detail
DEFAULT_THRESHOLDS["language_consistency"] = 1.0
- ⚠️ 已识别风险:新维度改变 score() 总分均值,
test_eval_scorer.py / test_phase5_scorer.py
具体分数断言需同步修正(诚实集成,不做旁路方法)
验收标准
- 全量 pytest ≥99% 覆盖率(当前基线 389 passed / 99.04%)
- 真实 LLM 双跑(zh/ja 各一次)+ 扫描脚本确认 output.docx 无违规件
- 提交 commit + 追加
_AI_USAGE_LOG.md
涉及文件
- 新增:
scripts/make_zh_template.py、samples/概要设计书模板_中文.docx、
src/genesis/writer/language.py、tests/test_writer_language.py
- 修改:
config.py、writer/models.py、writer/writer_agent.py、writer/context_builder.py、
writer/orchestrator.py、eval/scorer.py、qa/validator.py、scripts/run_trial.py
- 测试更新:
tests/test_phase5_writer_agent.py、tests/test_phase5_models.py、
tests/test_eval_scorer.py、tests/test_phase5_scorer.py、tests/test_run_trial.py
状态
验收记录(2026-08-25,含真实 LLM 双跑)
- pytest 全量:425 passed / 99.15%(覆盖率门槛 99% 达标)
- Fake 模式双模板试运行:zh 模板 +
--output-language zh → 7 章 docx;ja 模板默认 → 7 章 docx,均无崩溃
- 真实 LLM 双跑(2026-08-26,.env 已配 key):
--template 概要设计书模板_中文.docx --output-language zh → 7 章 ✓,无强制硬失败
- ja 模板默认(auto)→ 首次在 function_list 硬失败(期望 zh,BUG),修复 fallback 后重跑成功 7 章 ✓
- 程序化扫描真实输出:
zh_real.docx:48 非空段落,1 处含假名 = 表5-1 表格说明段落(中文句式 + 引用日文源表名 訂単テーブル/即時行情テーブル)→ 属"表格照抄原文"范畴,判定可接受
ja_real.docx:47 非空段落,1 处纯汉字 ≥12 = 封面字段「作成日: 2026-08-26」(模板元数据,非正文)→ 误报
- 结论:两输出均无中日混杂正文缺陷;检测器按设计排除 table/heading 与封面元数据
- 真实试运行暴露并修复的 BUG:auto 模式 fallback 误用 impact/data 源数据(含中文元素名 止损风控 等 + 汉字标签无假名)→ 把日文文档误判期望 zh → 硬失败。修复:fallback 改用 write_rules/design_rules(规则文档,日文含假名),并加回归测试
test_generate_chapter_auto_with_ja_rules_but_chinese_source_data_enforces_ja
- 约束链路单测覆盖:writer 重试/硬失败、QA 第 11 维度、validator 透传、qa_loop 透传
- ⚠️ 说明:ja 真实跑曾出现一次 function_list 随机性硬失败(LLM 单次输出疑似纯汉字段落),重跑即成功 → LLM 输出方差,强制机制按设计重试/硬失败,非确定性缺陷
实施进度(2026-08-25 暂停于步骤 A 修正点)
已完成
- 步骤 0(中文模板):
scripts/make_zh_template.py + samples/概要设计书模板_中文.docx(已生成入库)+ tests/test_zh_template.py(4 passed)。锚点 id 与 ja 模板逐一对应、译文正确。
- 步骤 1(参数贯通):
config.py 新增 WriterConfig.output_language→Settings.writer;GenerationContext.output_language+to_vars().language_instruction(zh/ja/auto 三档);writer_agent.py【语言约束】改引 {{language_instruction}};context_builder.build_contexts 透传;orchestrator.generate 加 output_language 参数;run_trial.py 加 --output-language。tests/test_language_plumbing.py(5 passed)。
- 步骤 A(检测+强制)部分:
src/genesis/writer/language.py 已建(detect_script/has_kana/has_cjk/resolve_expected_language/find_language_violations);writer_agent.generate_chapter 已接入期望语言推导 + 违规重试 + max_retries 默认 1→2。tests/test_writer_language.py 多数通过。
待修正(暂停点)
language.py 的 resolve_expected_language 逻辑需修正:当前实现在标题为纯汉字(如「DB 設計」,无假名)时 detect_script 返回 "zh",导致标题被误当作可靠信号、fallback 失效。
依据计划「显式 > 标题假名 > 规则文档主导脚本」,标题**仅当含假名(detect=="ja")**才可信;纯汉字标题对中/日均可能,应跳过、改看 fallback。
修正:将 resolve_expected_language 中标题分支改为 if detect_script(title) == "ja": return "ja"(不再用纯汉字标题推导 zh),随后才遍历 fallback_texts,最后返回 ""。
- 修正后重跑
tests/test_writer_language.py 应全绿(已有 2 个用例因该逻辑失败:test_resolve_falls_back_to_fallback_text、test_resolve_unverifiable_when_no_hint)。
后续(未动)
- 步骤 A 通过后→ 步骤 B(
_format_impact 标签本地化已预埋参数,待补 zh 标签集使其与 ja 实际不同 + 补测试)。
- 步骤 C:QA 第 11 维度
language_consistency + ChapterArtifact.expected_language + validator.validate_doc(expected_language=None) + DEFAULT_THRESHOLDS 加项 + 同步修正 test_eval_scorer.py/test_phase5_scorer.py 总分断言(按评审 R1:不可验证=满分 1.0)。
- 验收:全量 pytest ≥99% + zh/ja 双真实试运行 + 提交 +
_AI_USAGE_LOG.md 追加。
已落盘但未提交
- 上述步骤 0/1/A 的源码与测试改动尚未 git commit(暂停前未提交)。
评审记录(2026-08-24)
核验通过的关键假设
config.py 为 pydantic-settings + extra="ignore",新增 Settings.writer 字段无 yaml 时取默认值,零风险
- 中文模板锚点 id(section:introduction/function_list/screen_list/report_list/db_design/if_definition/batch_list)已从真实日文模板解析确认,镜像后映射/注入器零改动
_format_impact 无测试断言"受影响"字样,标签本地化安全;[新規]/[変更]/[削除]/[警告] 中日通用保留
WriterAgent.max_retries 既有测试均显式传参(2 或 1),默认值 1→2 不破坏测试
- prompt 段落名【语言约束】保留、内容改引变量 → 既有模板断言兼容
评审修正(2 处)
| # |
原计划 |
修正后 |
依据 |
| R1 |
C 维度"歧义记 unverifiable 跳过" |
不可验证 = 满分通过(score 1.0),维度恒参与均值;detail 注明 "unverifiable" |
实测 test_eval_scorer.py L154 / test_phase5_scorer.py L82 断言 total_score == 1.0——若跳过维度或给非满分,既有用例总分断言即破裂;恒定维度数 + 满分兜底可让多数既有断言不变 |
| R2 |
ChapterArtifact.title: str = "" 与 expected_language 两版表述并存 |
统一为 仅加 expected_language: str = "",scorer 完全不自行推导 |
单一事实来源原则,避免 scorer 内再出现一套标题猜测逻辑 |
评审补充(3 处说明)
| # |
说明 |
| N1 |
validate_doc 增加可选 kwarg expected_language=None(qa_loop.py L55/L69 与 test_phase5_report/test_phase5_validator 共 4 处调用方零破坏);None 时该章按 unverifiable=满分处理 |
| N2 |
max_retries 默认 2 后,orchestrator.py L72 与 qa_loop.py L28 两处真实管线在失败时会多一次 LLM 调用——与 QALoopController 外层循环叠加的成本上界 = 章 × 2 × QA轮次,可接受;实施时在 WriterAgent docstring 标注 |
| N3 |
运维注意事项写入 README/计划:zh 场景规则文档仍为日文 → auto 回落会判 ja,中文输出必须显式 --output-language zh |
遗留风险(接受)
- auto 模式对纯汉字日文正式文档(无假名标题+无假名规则)无法证明语言——保守跳过强制,由 C 维度报告层呈现 unverifiable
后续收尾(2026-08-26)
- #1/#2 章级管道重试:
orchestrator.generate 新增 chapter_attempts(默认 3),单章
WriterGenerationError 不连坐整次 run,重试耗尽才抛错(不吞错)。真实 LLM 随机方差兜底。
- #3 表格 caption 语言检查:
find_language_violations 现检 table 的 caption(生成正文需跟随
输出语言);rows/headers 仍不检(照抄源)。QA 维度通过 ChapterArtifact.blocks=(type,text,caption)
同步生效。修复真实 zh 输出中"表5-1 表格说明引用日文源表名"绕过强制的问题。
- 文档收尾:README 新增
--output-language 与中文模板用法;design.md §6.2.1/§7.2 同步(11 项校验);
.gitignore 加入 .opencode/。