fix(writer): auto 模式语言 fallback 改用规则文档,修复日文文档被误判期望 zh

真实 ja 试运行暴露:auto 模式 fallback 误用 impact/data 源数据(含中文元素名
止损风控等 + 汉字标签无假名)→ 把日文文档误判为期望 zh → LLM 正常日文输出被
判违规 → 硬失败。修复:fallback 改用 write_rules/design_rules(RAG 自日文作成
说明书/记入规则检索,含假名可靠判 ja);加回归测试
test_generate_chapter_auto_with_ja_rules_but_chinese_source_data_enforces_ja。
修复后 ja 真实试运行成功(7章);zh 真实试运行成功(7章);425 passed / 99.15%
This commit is contained in:
lhl
2026-08-26 00:36:54 +08:00
parent d01e1b720f
commit 3f4d17cf69
4 changed files with 47 additions and 6 deletions
+2
View File
@@ -116,3 +116,5 @@
| 2026-08-24 14:02 | 架构设计 | 输出语言一致性保障方案落盘:用户可选输出语言(auto/zh/ja)+ 中文模板镜像 + 确定性脚本检测与重试强制(A)+ impact 标签本地化(B+ QA 第11维度 language_consistencyC);关键决策:表格照抄原文、双模板锚点不变、违规硬失败 | docs/superpowers/plans/2026-08-24-language-consistency.md | x-preview-f-free (opencode) |
| 2026-08-24 14:35 | 反馈迭代 | 修复 _AI_USAGE_LOG.md 表格渲染断裂(用户报告 2026-08-09 03:30 起不以表格显示):删除表内 4 处空行与 1 处误写入的工具输出残留行(End of file);L55 摘要内嵌错误码表的 6 个管道符转义为反斜杠管;L107 被拼接的两条 2026-08-23 记录拆分为独立两行;零日志行删除,修复后全表 116 行均为有效 5 列 | _AI_USAGE_LOG.md | x-preview-f-free (opencode) |
| 2026-08-25 10:20 | Agent 实现 | 输出语言一致性 + 可选输出语言(实施中,暂停于步骤 A 修正点):步骤0 新增 scripts/make_zh_template.py 与 samples/概要设计书模板_中文.docx7章锚点镜像)+ tests/test_zh_template.py;步骤1 config.WriterConfig.output_language→Settings.writer、GenerationContext.output_language+to_vars.language_instruction、writer_agent【语言约束】引变量、context_builder/orchestrator/run_trial 透传 --output-language + tests/test_language_plumbing.py;步骤A 新建 src/genesis/writer/language.pydetect_script/resolve_expected_language/find_language_violations)并接入 WriterAgent.generate_chapter 违规重试、max_retries 默认1→2 + tests/test_writer_language.py(待修正 resolve_expected_language 标题仅假名可信) | src/genesis/config.py; src/genesis/writer/models.py; src/genesis/writer/writer_agent.py; src/genesis/writer/context_builder.py; src/genesis/writer/orchestrator.py; src/genesis/writer/language.py; scripts/make_zh_template.py; scripts/run_trial.py; tests/test_zh_template.py; tests/test_language_plumbing.py; tests/test_writer_language.py; samples/概要设计书模板_中文.docx | x-preview-f-free (opencode) |
| 2026-08-25 13:40 | Agent 实现 | 输出语言一致性 + 可选输出语言 实施完成并提交(d01e1b7):步骤A language.py 修正 resolve_expected_language(标题仅含假名时可信任,纯汉字标题回落 fallback);步骤B _format_impact 标签本地化 zh 实际区分(新建/变更/删除/警告);步骤C scorer 第11维度 language_consistency(不可验证=满分)、ChapterArtifact.expected_language、validator/qa_loop 透传 output_language;全量 pytest 424 passed / 99.15%zh 模板 fake 试运行 7 章 + 扫描零假名混入;真实 LLM 双语双跑因无 API key 暂缓 | src/genesis/writer/language.py; src/genesis/eval/scorer.py; src/genesis/qa/validator.py; src/genesis/qa/qa_loop.py; src/genesis/writer/models.py; tests/test_scorer_language.py; tests/test_language_coverage.py; tests/test_phase5_e2e.py; docs/superpowers/plans/2026-08-24-language-consistency.md | x-preview-f-free (opencode) |
| 2026-08-26 01:20 | 测试验证 | 真实 LLM 双语试运行验收(补做):zh 模板+zh 强制 → 7章成功;ja 默认 auto → 暴露 BUGfallback 误用 impact/data 源数据含中文元素名→日文文档误判期望 zh→硬失败),修复为 fallback 改用 write_rules/design_rules(日文规则文档)+ 回归测试;修复后 ja 重跑成功 7章;程序化扫描两输出:zh 1处=表5-1表格说明引用日文源表名(照抄原文范畴,可接受),ja 1处=封面字段作成日(误报)→ 均无中日混杂正文缺陷;全量 pytest 425 passed / 99.15% | src/genesis/writer/writer_agent.py; tests/test_writer_language.py; docs/superpowers/plans/2026-08-24-language-consistency.md; _AI_USAGE_LOG.md | x-preview-f-free (opencode) |
@@ -100,13 +100,20 @@ python scripts/run_trial.py
- [x] 步骤 0-3 实施
- [ ] 验收
## 验收记录(2026-08-25
## 验收记录(2026-08-25,含真实 LLM 双跑
- pytest 全量:**424 passed / 99.15%**(覆盖率门槛 99% 达标)
- pytest 全量:**425 passed / 99.15%**(覆盖率门槛 99% 达标)
- Fake 模式双模板试运行:zh 模板 + `--output-language zh` → 7 章 docxja 模板默认 → 7 章 docx,均无崩溃
- 程序化扫描 `output/zh_output.docx`:25 段落,**0 日文假名混入**(语言一致性成立)
- **真实 LLM 双跑(2026-08-26.env 已配 key**
- `--template 概要设计书模板_中文.docx --output-language zh` → 7 章 ✓,无强制硬失败
- ja 模板默认(auto)→ 首次在 function_list 硬失败(期望 zhBUG),修复 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 透传
- ⚠️ **未执行项**:真实 LLM 双语双跑需 `GENESIS_INFERENCE__API_KEY`(本环境无 key),确定性强制逻辑已由 e2e + 单测 + 覆盖率间接验证;真实双跑待 key 就绪后补。
- ⚠️ 说明:ja 真实跑曾出现一次 function_list 随机性硬失败(LLM 单次输出疑似纯汉字段落),重跑即成功 → LLM 输出方差,强制机制按设计重试/硬失败,非确定性缺陷
## 实施进度(2026-08-25 暂停于步骤 A 修正点)
+7 -2
View File
@@ -137,11 +137,16 @@ class WriterAgent:
self._chunk_source(context.structured_source) # 分块可用性验证(真实拼回留待后续)
# 步骤 A:推导本章期望输出语言(仅依赖 context,循环前置,稳定)
v = context.to_vars()
# fallback 用「规则文档」(write_rules/design_rulesRAG 自日文作成说明书/记入规则检索,
# 含假名可判日文)。不能用 impact/data(源数据):样本含中文元素名(止损风控等)、
# 影响标签为汉字无假名 → 会把日文文档误判为期望 zh(真实试运行暴露)。
expected = resolve_expected_language(
explicit=context.output_language,
title=context.title,
fallback_texts=[v.get("impact", "") or "", v.get("data", "") or ""],
fallback_texts=[
"\n".join(context.write_rules or []),
"\n".join(context.design_rules or []),
],
)
last_err: Exception | None = None
+27
View File
@@ -170,3 +170,30 @@ def test_generate_chapter_auto_unverifiable_not_enforced():
state=WriterState(["db_design"]), max_retries=1)
content = agent.generate_chapter(_ctx_with_lang("db_design", "DB 設計", "auto"))
assert content is not None
class JaRuleEngine(MixedLanguageEngine):
"""返回日文正文(含假名)。"""
def test_generate_chapter_auto_with_ja_rules_but_chinese_source_data_enforces_ja():
"""回归:真实试运行暴露——auto 模式 fallback 只用规则文档(日文),
不得用 impact/data 源数据(含中文元素名)把期望误判为 zh。
构造:标题纯汉字「機能一覧」(无假名)+ 规则文档日文(含假名)+ 源数据中文,
期望应推导为 ja;日文正文应通过、中文正文应违规。
"""
ja_rules_ctx = GenerationContext(
chapter_id="function_list", title="2. 機能一覧",
template_marker=ChapterSpec(chapter_id="function_list", title="2. 機能一覧"),
structured_source="中文源数据:止损风控、下单流程",
write_rules=["書込規則に従って記述する。"], # 日文含假名
design_rules=["設計方針は本書の通りである。"], # 日文含假名
template_styles=set(),
output_language="auto",
)
engine = MixedLanguageEngine(["本機能は注文処理を行う画面であり、詳細は以下の通り。"])
agent = WriterAgent(session_id="s", engine=engine,
prompt_registry=FakePromptRegistry(),
state=WriterState(["function_list"]), max_retries=1)
content = agent.generate_chapter(ja_rules_ctx)
assert content is not None and "本機能" in content.blocks[0].text