Files
2026Technology-Competition/scripts/run_trial.py
T
lhl 6372bb17b9 feat(run): 试运行脚本 + 运行说明(真实 LLM 默认 / --fake 离线)
- scripts/run_trial.py:库级端到端驱动(argparse 默认指向 samples/ 股票追加改修+sunOnly+真实模板)
  - --fake 离线 FakeEngine(无 key)/ 默认 build_inference_engine 真实 LLM(读 .env)
  - SourceParser → WriteOrchestrator 门控自动影响调查 → output/output.docx + impact-report.json
  - 暴露 main(argv) 供测试
- tests/test_run_trial.py(fake 模式断言 13 章 + summary 16/5/8/3/50/0 + 文件产出 + docx 注入)
- .gitignore 新增 output/(试运行产物不入库)
- 新建 README.md(安装 / .env 配置 / 离线与真实试运行 / 输出 / 测试)
- 全量 364 passed / 99.28%;fake 试运行 PASS(13 章、summary 与 MVP 基线一致)
2026-08-24 11:05:45 +08:00

116 lines
4.6 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("--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)),
)
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()