5.5 KiB
5.5 KiB
RAG 影响调查接入(Task 5)实现报告
日期:2026-08-29 范式步骤:Agent 实现(RAG 迭代 Task 5,含评审修订 D1/D3)
真实代码要点(先读后改)
src/genesis/server/service.pyGenesisService.__init__:新增rag: ImpactRAG | None = None、use_rag: bool = False、rag_db_path: str | None = None、embedder=None(默认get_embedder("fake"))。保存self.rag/self.use_rag;rag=None为不启用(向后兼容默认关闭)。upload_file:当file_type == "existing_system"且self.rag is not None,解压后self.rag.index_dir(session_id, path);异常try/except+_LOGGER.warning不阻断上传(D1 上传即索引,scope=session_id)。run_impact改为async def run_impact(self, session_id, use_rag=None):eff = self.use_rag if use_rag is None else use_rag。eff 且 self.rag is not None时走 RAG 路径——engine为空则build_inference_engine();取requirements_text = rec.files["requirements"]["name"] or "要件定義";await ImpactAgent(engine=..., rag=..., use_rag=True).run_impact(...);写status=awaiting_impact_confirm+impact_summary={"rag_enabled": True, "impact_llm": ...},并尽量保留impact_report_path。否则走原确定性ImpactAgent().run(...)路径(行为不变)。
src/genesis/server/app.pycreate_app新增rag=None;为 None 时内部构造RagStore(data_root/rag.db) + ImpactRAG(..., get_embedder("fake"))(先mkdir(parents=True)防止unable to open database file),以rag=rag, use_rag=False注入GenesisService。- Critical 修复:
_FakeEngine.chat_structured改为async def(与真实InferenceEngine一致)。 start_impact改为async def start_impact(sid, use_rag=False),await service.run_impact(sid, use_rag=use_rag)。
src/genesis/chat/agent.py:_parse_and_confirm与_run_impact中两处self.service.run_impact(...)以asyncio.run(...)包裹(兼容同步消息处理,避免协程未执行)。tests/test_server_service.py:两处svc.run_impact(...)改为asyncio.run(svc.run_impact(...)),保持全绿。tests/test_impact_rag_e2e.py(新增,D3 强验证):真实GenesisService+ 临时SessionStore+ 临时data_root+ 异步FakeEngine(捕获 prompt)+ImpactRAG(RagStore(tmp/rag.db), FakeEmbedder()),use_rag=True。- 辅助:
zipfile构造含src/KnownOrder.java(内容含「订单创建调用 MyBatis」)的 existing_system zip;requirements/template 用glob定位sample/下真实文件。 - 流程:create_session → upload requirements → upload template → upload existing_system → run_parse → confirm_parse(→ impact_running)→
asyncio.run(svc.run_impact(sid, use_rag=True))。 - 核心断言:
FakeEngine.captured含「KnownOrder」(上传源码被 RAG 检索并注入 LLM prompt);上传后rag.retrieve(sid, "订单创建", k=1)命中非空(D1 验证);另起会话rag=None且默认use_rag时走确定性路径、captured is None且不含 RAG 小节标题(向后兼容)。
- 辅助:
验证状态
python -m pytest tests/test_impact_rag_e2e.py -q:2 passedpython -m pytest tests/test_server_service.py -q:20 passedpython -m pytest tests/ -k "server or impact or chat" -q:228 passed(无回归;最初test_server_api因rag.db目录不存在报OperationalError,已通过Path(rag_db).parent.mkdir(...)修复)
自我审查(顾虑)
create_app现在总会创建data_root/rag.db(即使use_rag=False);默认app = create_app()会在工作区生成data/server/rag.db文件,属轻微副作用,可接受(向后兼容且关闭时不参与影响调查)。chat/agent.py用asyncio.run包裹run_impact是最小侵入方案;若未来聊天层整体异步化,可改为直接await。- 覆盖配置
fail_under=99%,单文件运行 e2e 会因采样不足触发覆盖率告警(非测试失败);以-o addopts=""运行可获干净结果。 - RAG 路径不依赖
_rebuild_source(仅用 requirements 文件名 + RAG 检索),与 Brief 一致;确定性路径仍保留_rebuild_source。
修复后验证(复审 Minor 3 项最小化修复)
日期:2026-08-30 范式步骤:反馈迭代(RAG 迭代 Task 5 复审 Minor 修复)
修复项
- 死存储(service.py):移除
GenesisService.__init__的rag_db_path与embedder两个未使用参数及其赋值(rag/use_rag保留)。已确认全仓无调用方传入这两个参数(仅app.create_app传rag=)。 - 引擎构建竞态(service.py):
run_impact中引擎懒构建改为双重检查锁:__init__新增self._engine_lock = threading.Lock(),构建处with self._engine_lock: if self.engine is None: self.engine = build_inference_engine()(新增import threading)。 - 文档措辞(docs/design.md):将"端点同步改为
async def"改为"端点改为async def"(原端点本就是同步 def,去掉"同步"歧义)。
验证命令
python -m pytest tests/test_impact_rag_e2e.py tests/test_server_service.py -q
结果摘要
- 22 passed(test_impact_rag_e2e 2 + test_server_service 20),无回归。
- 注:覆盖率门禁
fail_under=99%在仅运行这两个文件时会触发(项目级全量门禁,与本次修复无关,非测试失败)。按报告既有建议以-o addopts=""运行可获干净结果。 - 默认路径语义与
use_rag=False行为保持不变。