"""Phase 5 垂直切片运行脚本(CLI)。 用法: python scripts/run_phase5_slice.py --template --output [--fake] - --fake: 使用内置 FakeEngine(无需 LLM 客户端/API Key),用于验证管线打通。 - 不带 --fake: 使用真实 InferenceEngine(),需先配置 LLM 客户端(人工质量门禁)。 管线: WordTemplateParser → StructuredSource → WriteOrchestrator.generate → docx。 """ from __future__ import annotations import argparse from types import SimpleNamespace from docx import Document from genesis.data_models import StructuredSource from genesis.parsers.word_template_parser import WordTemplateParser from genesis.writer.orchestrator import WriteOrchestrator class FakeEngine: """切片验证用假引擎:返回固定章节内容,避免真实 LLM 调用。""" def chat_structured(self, *, session_id, prompt, variables, schema, retry_count=2): return SimpleNamespace( data={ "title": variables.get("title", "章"), "blocks": [{"type": "paragraph", "text": f"【{variables.get('title','')}】自动生成的内容(fake 模式)"}], }, status="ok", ) def _build_source(template_path: str) -> StructuredSource: parsed = WordTemplateParser().parse(template_path) return StructuredSource( tables=[], template=parsed, rule_docs=[], image_analyses=[], existing_system=None, comments=[], ) def _main() -> None: parser = argparse.ArgumentParser(description="Phase 5 垂直切片运行脚本") parser.add_argument("--template", required=True, help="概要设计模板 docx 路径") parser.add_argument("--output", required=True, help="输出 docx 路径") parser.add_argument("--fake", action="store_true", help="使用 FakeEngine(无 LLM)") parser.add_argument("--samples-dir", default="sample", help="RAG 样本目录") args = parser.parse_args() source = _build_source(args.template) engine = FakeEngine() if args.fake else None # None → 真实 InferenceEngine()(需客户端配置) orch = WriteOrchestrator() contents = orch.generate( source, args.output, samples_dir=args.samples_dir, engine=engine, template_path=args.template, ) loaded = Document(args.output) non_empty = [p.text for p in loaded.paragraphs if p.text.strip()] remaining = sum(1 for p in loaded.paragraphs if "{{" in p.text) print(f"[slice] 生成章节数: {len(contents)}") print(f"[slice] 输出路径: {args.output}") print(f"[slice] 注入校验: {'OK' if non_empty and remaining == 0 else 'CHECK'}") print(f"[slice] 非空段落数: {len(non_empty)}; 残留占位符: {remaining}") preview = "\n".join(non_empty[:5]) print(f"[slice] 文本内容预览:\n{preview[:400]}") if __name__ == "__main__": _main()