按《参赛成果物提交规范·赛道一》§6 红线: - samples/ 目录改名 sample/(git mv,保留历史) - 10 个中日文样本文件 + docs 参赛手册 PDF 重命名为 ASCII (requirements_*/template_*/rules_*/contestant-handbook.pdf) - tests/test_zh_template.py 硬编码绝对路径 D:\00_project\Genesis 改为相对路径 - 全局更新 21 个活动文件引用;历史日志/审查文档不改(追加说明记录) 全量 pytest 431 passed / 99.15%
78 lines
3.1 KiB
Python
78 lines
3.1 KiB
Python
"""#1/#2:orchestrator 章级重试兜底测试。
|
||
|
||
问题:真实 LLM 输出有随机方差(如 ja 跑 function_list 单次输出疑似纯汉字被判违规),
|
||
WriterAgent 内部 max_retries=2 耗尽后抛 WriterGenerationError → 整次 run_trial 死亡。
|
||
修复:orchestrator.generate 增加章级管道重试(chapter_attempts),单章失败不连坐整次运行。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from pathlib import Path
|
||
from types import SimpleNamespace
|
||
|
||
import pytest
|
||
|
||
from genesis.data_models import StructuredSource
|
||
from genesis.parsers.word_template_parser import WordTemplateParser
|
||
from genesis.writer.exceptions import WriterGenerationError
|
||
from genesis.writer.orchestrator import WriteOrchestrator
|
||
|
||
|
||
class _FlakyEngine:
|
||
"""前 N 次调用抛错(模拟随机硬失败),之后成功。"""
|
||
|
||
def __init__(self, fail_calls: int):
|
||
self.fail_calls = fail_calls
|
||
self.calls = 0
|
||
|
||
def chat_structured(self, *, session_id, prompt, variables, schema, retry_count=2):
|
||
self.calls += 1
|
||
if self.calls <= self.fail_calls:
|
||
raise RuntimeError(f"模拟第 {self.calls} 次调用失败")
|
||
return SimpleNamespace(
|
||
data={
|
||
"title": variables["title"],
|
||
"blocks": [{"type": "paragraph",
|
||
"text": "本機能はFakeLLMにより生成された十分な説明内容であり、書込規則を満たす。"}],
|
||
},
|
||
status="ok",
|
||
)
|
||
|
||
|
||
def _ss() -> StructuredSource:
|
||
template = Path("sample/phase5-slice/template.docx")
|
||
if not template.exists():
|
||
pytest.skip("样本模板缺失,跳过")
|
||
parsed = WordTemplateParser().parse(str(template))
|
||
return StructuredSource(template=parsed, tables=[], rule_docs=[],
|
||
image_analyses=[], existing_system=None, comments=[])
|
||
|
||
|
||
def test_generate_recovers_stochastic_single_chapter_failure(tmp_path):
|
||
"""前 3 次调用失败(跨章/章内重试),后续成功 → 整次生成不崩溃。"""
|
||
out = tmp_path / "o.docx"
|
||
engine = _FlakyEngine(fail_calls=3)
|
||
# chapter_attempts=3,内部 max_retries=2 → 单章最多约 3×2 次调用
|
||
contents = WriteOrchestrator().generate(
|
||
_ss(), str(out), samples_dir="nonexistent_dir_xyz",
|
||
engine=engine, template_path=str(Path("sample/phase5-slice/template.docx")),
|
||
)
|
||
assert contents, "应成功产出章节"
|
||
assert out.is_file()
|
||
|
||
|
||
def test_generate_hard_fails_after_chapter_attempts_exhausted(tmp_path):
|
||
"""始终失败 → 章级重试耗尽后仍抛 WriterGenerationError(不吞错)。"""
|
||
out = tmp_path / "o.docx"
|
||
|
||
class AlwaysFail(_FlakyEngine):
|
||
def chat_structured(self, *, session_id, prompt, variables, schema, retry_count=2):
|
||
self.calls += 1
|
||
raise RuntimeError("always fail")
|
||
|
||
with pytest.raises(WriterGenerationError):
|
||
WriteOrchestrator().generate(
|
||
_ss(), str(out), samples_dir="nonexistent_dir_xyz",
|
||
engine=AlwaysFail(0), template_path=str(Path("sample/phase5-slice/template.docx")),
|
||
chapter_attempts=1,
|
||
)
|