feat(writer): 章级管道重试兜底 + 表格 caption 语言检查 + 文档收尾

#1/#2 真实 LLM 随机方差兜底:orchestrator.generate 新增 chapter_attempts(默认 3),
单章 WriterGenerationError 不连坐整次 run,重试耗尽才抛错(不吞错)。新增
tests/test_orchestrator_retry.py(前 N 次失败后恢复 / 耗尽仍抛错)。

#3 表格 caption 语言检查:find_language_violations 现检 table.caption(生成正文需跟随
输出语言),rows/headers 仍照抄源不检;QA 维度经 ChapterArtifact.blocks=(type,text,caption)
同步生效。修复真实 zh 输出中表格说明引用日文源表名绕过强制的问题。

文档收尾:README 新增 --output-language/中文模板用法;design.md §6.2.1 输出语言控制、
§7.2 校验清单 10→11 项;计划验收勾选;.gitignore 加 .opencode/。
全量 pytest 431 passed / 99.15%
This commit is contained in:
lhl
2026-08-26 00:49:36 +08:00
parent 84d3b87da3
commit 1925ef7239
8 changed files with 178 additions and 25 deletions
+43 -2
View File
@@ -79,15 +79,56 @@ def test_no_violation_ja_expected_japanese_paragraph():
def test_heading_and_table_blocks_excluded():
# heading 与 table(照抄原文)即使含中文也不算违规
# heading 与 table 的 rows/text(照抄原文)即使含中文也不算违规
blocks = _blocks(
("heading", "機能一覧表"),
("table", "機能ID 機能名"), # 表格不检
("table", "機能ID 機能名"), # 表格数据不检
("paragraph", "本機能は注文処理を行う。"),
)
assert not find_language_violations(blocks, "ja")
def _blocks_with_caption(*specs):
"""(type, text, caption) 构造内容块。"""
out = []
for i, (t, text, caption) in enumerate(specs):
out.append(ContentBlock(block_id=str(i), type=t, text=text, caption=caption))
return out
def test_table_caption_checked_zh_expected():
# 表格 caption 是生成正文(非源数据):zh 期望下含日文假名 → 违规
blocks = _blocks_with_caption(
("table", "", "TB001(訂単テーブル)与 TB002(即時行情テーブル)为既有表,本次变更涉及列定义调整。"),
)
assert find_language_violations(blocks, "zh")
def test_table_caption_checked_ja_expected():
# ja 期望下 caption 为纯中文(无假名 ≥12)→ 违规
blocks = _blocks_with_caption(
("table", "", "这是一段纯中文的表格说明内容,应当被判定为语言违规。"),
)
assert find_language_violations(blocks, "ja")
def test_table_caption_matching_language_not_flagged():
# 与期望语言一致的 caption 不违规
blocks = _blocks_with_caption(
("table", "", "表5-1 功能一览表(展示既有与新规功能清单)"),
)
assert not find_language_violations(blocks, "zh")
def test_table_rows_still_excluded_even_with_caption():
# 表格 rows 照抄源数据:即使含假名也不因 rows 判违规;仅 caption 参与检查
blocks = _blocks_with_caption(
("table", "", "表5-1 功能一览"),
)
blocks[0].rows = [["機能ID", "機能名"], ["F001", "注文処理"]]
assert not find_language_violations(blocks, "zh")
def test_short_chinese_not_flagged_under_threshold():
# 长度<12 的短术语不误杀
blocks = _blocks(("paragraph", "中文术语"))