从混合检索到 Agentic GraphRAG:代码仓库问答的设计笔记

1143 字
6 分钟
从混合检索到 Agentic GraphRAG:代码仓库问答的设计笔记

这篇笔记由 2026 年 3 月的项目阶段记录整理成文,项目代码与时间线可在 CodeRag 查看。

做代码仓库问答时,我很快发现:只把代码切块后放进向量库,能够回答“这段代码大概在做什么”,却很难稳定回答“谁调用了它”“一条请求怎样穿过多个模块”“修改这个符号会影响哪里”。原因并不神秘——代码的核心信息不只存在于文本相似度里,还存在于结构关系里。

因此,这个项目的目标不是再做一个聊天壳,而是构建一条能够在文本证据与图关系之间主动切换的检索链路。

1. 先把代码变成两种可检索表示#

第一种表示是文本。仓库文件经过读取、清洗和切分后,分别进入 BM25 与向量索引:

  • BM25 擅长精确命中函数名、类名、配置键与报错文本;
  • 向量检索擅长处理自然语言问题和代码描述之间的语义差异;
  • 两路结果通过 RRF(Reciprocal Rank Fusion)合并,减少对某一路分数尺度的依赖。

第二种表示是符号图。Tree-sitter 负责从不同语言的语法树中抽取函数、类、方法与导入关系,再将 CALLSIMPORTSOWNSHERITAGE 等边写入 KuzuDB。这样,系统不需要让大模型“猜”调用关系,而是可以执行邻居扩展和路径搜索。

2. 为什么需要 Agent 路由#

并不是每个问题都需要 GraphRAG。比如“README 中如何启动服务”通常用关键词检索就够了;“解释 handle_requestsave_order 的调用路径”才需要符号消歧和图搜索。

我把流程拆成多个职责清晰的节点:

  1. router 判断是直接回答、普通 RAG、GraphRAG 还是调用路径问题;
  2. symbol_suggest_tool 对短名进行前缀与模糊匹配,避免同名符号误判;
  3. retrieve_tool 执行语义或混合检索;
  4. expand_graph_tool 沿调用、导入和归属边扩展上下文;
  5. call_path_tool 查询两个符号之间的路径;
  6. answer 只基于整理后的 context_pack 生成回答;
  7. verify 检查证据是否足够,必要时改写查询并重试一次。

这里最重要的不是节点数量,而是“决策可以被观察”。前端能够看到 route、attempts、节点调用顺序、引用片段和原始 trace。调试时,我可以分辨到底是路由选错、符号消歧失败、检索召回不足,还是回答阶段没有遵守证据。

3. 上下文不是越多越好#

图扩展很容易带来新的问题:一个高连接度符号可能在两跳内扩出大量节点。如果把所有结果交给模型,既浪费上下文,也会稀释真正相关的证据。

我的处理方式是给扩展设置明确边界:限制深度、限制每类边的数量、保留初始命中与扩展命中的来源,并在组装 context_pack 时优先保留能直接回答问题的符号与代码片段。换句话说,图不是为了“查得更多”,而是为了“补齐文本检索缺失的关系”。

4. 自验证应检查证据,而不是检查文风#

第一次实现验证节点时,很容易写成“你觉得回答好不好”。这类检查太主观,也不能驱动下一步动作。更有效的方式是检查结构化条件:

  • 路径类问题是否真的得到路径结果;
  • 回答中的关键结论是否有对应 citation;
  • 符号存在歧义时是否已经完成消歧;
  • 检索结果是否覆盖问题中的关键实体。

只有检查失败时才触发一次重试,并提高检索范围或改写查询。有限循环比无限“反思”更容易控制延迟与成本。

5. 这次实践留下的结论#

代码 RAG 的质量不只取决于模型。解析质量、符号命名、混合检索、图扩展边界、引用格式和运行轨迹共同决定了系统是否可信。Agent 的价值也不是让每个问题都经过复杂流程,而是让系统能够根据问题类型选择最小充分路径,并在证据不足时知道下一步该做什么。

参考资料#

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

从混合检索到 Agentic GraphRAG:代码仓库问答的设计笔记
https://github.com/yeliheng-010/CodeRag
作者
叶立恒
发布于
2026-03-06
许可协议
CC BY-NC-SA 4.0