Files

5.5 KiB
Raw Permalink Blame History

RAG 影响调查接入(Task 5)实现报告

日期:2026-08-29 范式步骤:Agent 实现(RAG 迭代 Task 5,含评审修订 D1/D3

真实代码要点(先读后改)

  • src/genesis/server/service.py
    • GenesisService.__init__:新增 rag: ImpactRAG | None = Noneuse_rag: bool = Falserag_db_path: str | None = Noneembedder=None(默认 get_embedder("fake"))。保存 self.rag/self.use_ragrag=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_rageff 且 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.py
    • create_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 ziprequirements/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 -q2 passed
  • python -m pytest tests/test_server_service.py -q20 passed
  • python -m pytest tests/ -k "server or impact or chat" -q228 passed(无回归;最初 test_server_apirag.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.pyasyncio.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 修复)

修复项

  1. 死存储(service.py:移除 GenesisService.__init__rag_db_pathembedder 两个未使用参数及其赋值(rag/use_rag 保留)。已确认全仓无调用方传入这两个参数(仅 app.create_apprag=)。
  2. 引擎构建竞态(service.pyrun_impact 中引擎懒构建改为双重检查锁:__init__ 新增 self._engine_lock = threading.Lock(),构建处 with self._engine_lock: if self.engine is None: self.engine = build_inference_engine()(新增 import threading)。
  3. 文档措辞(docs/design.md:将"端点同步改为 async def"改为"端点改为 async def"(原端点本就是同步 def,去掉"同步"歧义)。

验证命令

python -m pytest tests/test_impact_rag_e2e.py tests/test_server_service.py -q

结果摘要

  • 22 passedtest_impact_rag_e2e 2 + test_server_service 20),无回归。
  • 注:覆盖率门禁 fail_under=99% 在仅运行这两个文件时会触发(项目级全量门禁,与本次修复无关,非测试失败)。按报告既有建议以 -o addopts="" 运行可获干净结果。
  • 默认路径语义与 use_rag=False 行为保持不变。