Files
2026Technology-Competition/scripts/run_trial.py
T
lhl d01e1b720f feat(writer): 输出语言一致性保障 + 用户可选输出语言
- 步骤0: 新增中文镜像模板 scripts/make_zh_template.py 与 samples/概要设计书模板_中文.docx(7章锚点原样保留)
- 步骤1: config.WriterConfig.output_language→Settings.writer;GenerationContext.output_language + to_vars.language_instruction(zh/ja/auto);writer_agent【语言约束】改引变量;context_builder/orchestrator/run_trial 透传 --output-language
- 步骤A: 新建 src/genesis/writer/language.py(detect_script/resolve_expected_language/find_language_violations);WriterAgent.generate_chapter 按期望语言强制、违规重试、耗尽硬失败;max_retries 默认 1→2
- 步骤B: _format_impact 影响调查标签按 output_language 本地化(zh 新建/变更/删除/警告)
- 步骤C: eval scorer 第 11 维度 language_consistency(不可验证=满分,不拉低总分);ChapterArtifact.expected_language;QAValidator.validate_doc 透传;QALoop.run 透传 output_language
- 测试: test_zh_template/test_language_plumbing/test_writer_language/test_scorer_language/test_language_coverage,并更新 test_phase5_e2e
- 全量 pytest 424 passed / 99.15%(覆盖率门槛 99% 达标)
2026-08-25 23:30:24 +08:00

119 lines
4.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""库级端到端试运行驱动(真实 LLM / 离线 Fake)。
用法:
python scripts/run_trial.py [--fake] [--output out.docx] ...
默认输入为 samples/ 下既有样本(追加改修·股票场景 + sunOnly 既有系统 + 真实概要设计书模板)。
真实模式需先在 .env 配置 GENESIS_INFERENCE__API_KEY(见 README「运行说明」),
缺失时抛 LLMNotConfiguredError 并给出配置提示;--fake 为离线确定性引擎,无需 key。
输出:
概要设计书 docx + 影响调查书 impact-report.json(默认写至 output/,不入库)。
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from types import SimpleNamespace
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT / "src") not in sys.path:
sys.path.insert(0, str(ROOT / "src"))
from genesis.impact.impact_agent import impact_report_to_dict # noqa: E402
from genesis.parsers.source_aggregator import SourceParser # noqa: E402
from genesis.writer.orchestrator import WriteOrchestrator # noqa: E402
class FakeEngine:
"""离线确定性引擎:章节正文固定为占位文本(用于无 key 验证整条管线)。"""
def chat_structured(self, *, session_id, prompt, variables, schema, retry_count=2):
return SimpleNamespace(
data={
"title": variables["title"],
"blocks": [{"type": "paragraph", "text": "自动生成内容"}],
},
status="ok",
)
def _parse_args(argv: list[str] | None) -> argparse.Namespace:
p = argparse.ArgumentParser(description="概要设计书自动生成试运行")
p.add_argument("--requirement", default=str(ROOT / "samples" / "要件定義_追加改修_股票.xlsx"),
help="要件定义 Excel(可多个,逗号分隔)")
p.add_argument("--template", default=str(ROOT / "samples" / "概要設計書テンプレート.docx"),
help="概要设计书 Word 模板")
p.add_argument("--rules", nargs="*", default=[
str(ROOT / "samples" / "概要設計做成説明書.docx"),
str(ROOT / "samples" / "記入規則.docx"),
], help="写入规则/记入规则 docx(可多个)")
p.add_argument("--existing-system", default=str(ROOT / "samples" / "existing-system"),
help="既有系统源码目录(追加/改修场景)")
p.add_argument("--language", default="java", help="既有系统开发语言(默认 javaNone=自动探测)")
p.add_argument("--output-language", default="auto",
choices=["auto", "zh", "ja"], help="输出语言:auto=与标题一致,zh=简体中文,ja=日文")
p.add_argument("--samples-dir", default=str(ROOT / "samples"), help="样本资产根目录")
p.add_argument("--output", default=str(ROOT / "output" / "output.docx"), help="概要设计书输出路径")
p.add_argument("--impact-report", default=str(ROOT / "output" / "impact-report.json"),
help="影响调查书 JSON 输出路径")
p.add_argument("--fake", action="store_true", help="离线 Fake 引擎(无需 API key")
return p.parse_args(argv)
def main(argv: list[str] | None = None) -> dict:
args = _parse_args(argv if argv is not None else sys.argv[1:])
if args.fake:
engine = FakeEngine()
else:
from genesis.inference.factory import build_inference_engine # noqa: E402
engine = build_inference_engine()
ss = SourceParser().parse(
requirement_paths=[args.requirement],
template_path=args.template,
rule_paths=list(args.rules),
existing_system_path=args.existing_system,
existing_system_language=args.language,
)
out_path = Path(args.output)
out_path.parent.mkdir(parents=True, exist_ok=True)
orch = WriteOrchestrator()
contents = orch.generate(
ss, str(out_path),
samples_dir=args.samples_dir,
engine=engine,
template_path=str(Path(args.template)),
output_language=args.output_language,
)
report = ss.impact_report
if report is None:
raise RuntimeError("影响调查未执行(未提供既有系统或门控未触发)")
report_path = Path(args.impact_report)
report_path.parent.mkdir(parents=True, exist_ok=True)
report_path.write_text(
json.dumps(impact_report_to_dict(report), ensure_ascii=False, indent=2),
encoding="utf-8",
)
summary = dict(report.summary)
print(f"章节数: {len(contents)}")
print(f"影响调查: {json.dumps(summary, ensure_ascii=False)}")
print(f"概要设计书: {out_path}")
print(f"影响调查书: {report_path}")
return {
"chapters": len(contents),
"summary": summary,
"output": str(out_path),
"impact_report": str(report_path),
}
if __name__ == "__main__":
main()