- 步骤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% 达标)
119 lines
4.8 KiB
Python
119 lines
4.8 KiB
Python
"""库级端到端试运行驱动(真实 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="既有系统开发语言(默认 java,None=自动探测)")
|
||
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()
|