CodeGraph:让AI理解代码仓库的神器

26 阅读12分钟

Agentic Search

像Claude Code、Codex、Cursor、OpenCode、Trae等编码Agent能够对我们的项目进行功能实现、代码重构、bugfix等操作的前提是:他需要读懂你的代码仓库。

通常来说用户输入自然语言提问:"请帮我查询一下下单流程是怎样的,怎么保证订单状态的一致性",这个时候Agent首先需要理解什么是下单流程,他会把这个提问query rewrite为对应的关键字,如:order|orderStatus|orderProcess,然后通过这些关键字去查找对应的代码片段:

  • grep 关键词 -> read file -> grep 关键词 -> read file -> ...(loop) -> answer

通过多轮查询将碎片化的信息组织成有逻辑的上下文。

image.png

但是这种查询方式存在以下问题:

  • AI 需要不断 grep 扩读原始仓库代码得到想要的结果,耗时和token消耗比较大
  • 纯文本的代码片段是碎片化的,难以产生关联,比如函数的调用链路、页面与组件的关联关系等,AI 更容易关注到局部信息,而忽略掉与关键字无关的潜在内容
  • 业务语义到实际代码是如何关联的?业务是由业务团队人为定义的,而代码是由开发人员的具体实现,同一个功能可能有不同的代码实现,反之同一个函数名称也可能承载不同的功能

CodeGraph

CodeGraph是一种通过静态分析(Tree-sitter等)把代码仓库转换成可检索、可遍历的代码知识图谱(由实体和关系组成),沉淀文件、函数、方法、类、调用关系、依赖关系、字段读写、状态流等上下文,提供相关API给调用方检索,让AI不再只靠关键词搜索,而是能够真正理解代码之间的关联关系。

以下是一些比较知名的代码知识图谱方案:

项目一句话定位核心技术图如何设计如何召回相关代码更新方式最突出的特点
GitNexus功能全面的代码知识图谱与 Graph RAG 平台Tree-sitter + LadybugDB;支持浏览器 WASM文件、函数、类、调用关系,并预计算功能社区和执行流程BM25 关键词 + 向量语义检索 + RRF 融合,再结合调用链和功能社区重新执行分析;增量能力仍在持续演进检索、影响分析、流程分析和可视化比较完整
code-review-graph面向代码审查和变更风险分析Tree-sitter + SQLite,可选向量模型函数、类、导入、调用、继承、测试关系,并识别代码社区与执行流程全文检索 + 可选语义向量 + 图遍历,重点寻找变更影响范围Watch、Git Hook 和增量更新最适合 PR 审查、测试缺口和风险评分
codebase-memory-mcp追求极致性能和语言覆盖的代码图后端Tree-sitter + Hybrid LSP + SQLite,静态二进制除代码符号外,还建模 HTTP 路由、基础设施资源及跨服务关系名称/正则搜索、图遍历、Cypher 查询和代码搜索后台自动同步,也可共享压缩后的图数据库语言覆盖广、查询快、依赖少
CodeGraph为 AI 编程 Agent 提供始终最新的精准代码上下文Rust 原生解析内核 + Tree-sitter + SQLite/FTS5以文件和代码符号为节点,以调用、导入、继承、实现和框架路由为关系先用全文搜索定位入口,再沿调用关系扩展;一次返回源码、调用路径和影响范围监听文件变化,只同步发生变化的部分本地运行、自动保鲜,并把复杂查询收敛成一个 explore 工具
Graphify将代码、文档、PDF、配置等统一生成可浏览知识图谱Tree-sitter;非代码内容可使用 LLM 提取概念级图谱;每条边标记为“源码提取”或“推断”;使用 Leiden 划分社区不使用向量库,主要通过子图、邻居和最短路径召回支持手动更新、Watch 和 Git Hook数据源最丰富,图关系可解释、便于可视化和团队共享

对比了业内多种方案,我们最终采取了CodeGraph这套方案,它包含完整的benchmark测试,并且产品化做得比较好,也符合我们在25年底前期探索的基于LocAgent的理论实践(在这之前我们已经基于这套理论实现了一版CodeGraph),而且是一套比较纯粹的、纯工程侧的基础能力。

它的核心流程可以概括为:FTS负责做精确符号和关键词定位,CodeGraph 负责沿调用关系、引用关系继续扩展上下文。两者结合后,召回结果可解释、确定性强。

  • 解析代码 → 建立符号关系图 → 自动增量同步 → 为 Agent 一次性返回精准上下文

业务实践

为了增强前端语义,我们制定了 CodeGraph 图的标准:实体和边的设计

  • 实体是构建图的基本单元,如文件、目录、函数等
  • 边描述了实体与实体之间的关系,如包含、调用、继承等

实体类型

通用实体

通用实体用于描述代码库的基础结构和语言层面的符号关系,适用于不同语言

实体含义说明
file文件所有代码、配置、资源的承载单元
directory目录仓库结构基础实体
function函数或 inline callback语言侧面的可调用逻辑单元
method方法类、对象或框架对象中的方法
class语言侧面的类型与继承结构
import导入定义模块依赖和符号引用入口
export导出定义模块对外暴露的符号

框架实体(Vue/Mpx/React)

框架实体用于描述前端应用中由 Vue、Mpx、React 等框架或运行时约定产生的语义(前端特定语义)。这些实体通常不是纯语言 AST 能完整表达的,需要结合框架 parser、配置解析和约定识别。

实体含义说明
app应用入口应用启动、挂载和全局配置入口
page页面入口前端业务页面或路由页面
componentUI 组件Vue/Mpx 组件或 React component
reactive_state局部响应式状态,例如 prop、data、ref、useState、computed组件或页面内部状态
global_state全局状态,例如 store、model、全局 atom跨页面、跨组件共享状态
event_channel事件通道,例如 emit/on、event bus组件事件、页面事件或全局事件通信

关系类型

通用边

通用边描述跨技术栈都成立的结构、依赖和调用关系。

含义说明
contains结构包含关系目录、文件、类、方法、函数等层级关系
invokes普通调用关系函数、方法、callback 之间的调用
imports模块依赖关系文件或模块之间的导入依赖
extends继承关系子类继承父类
resolves_to配置或符号解析到目标实体,例如 route 解析到 pageimport/export、路由、配置等解析结果
depends_on通用依赖关系,通常用于较弱或无法更具体归类的依赖兜底依赖边,适合解析置信度较低的关系

框架边(Vue/Mpx/React)

框架边描述前端框架特有的 UI 组合、页面跳转、数据读写、事件副作用、服务调用和平台能力。

含义说明
navigates_to导航关系,例如页面跳转、路由跳转、参数跳转page、route 之间的跳转链路
reads_data读取局部或全局状态页面、组件、函数读取 reactive_state 或 global_state
writes_data写入局部或全局状态页面、组件、函数写入 reactive_state 或 global_state
provides_toprovider 向 consumer 提供依赖框架依赖注入或上下文提供关系
injects_fromconsumer 从 provider 注入依赖框架依赖注入或上下文消费关系

基于实体和边的结构化表达,最终我们就获得了一张完整的、用于描述代码仓库结构与语义的一个完整关系图,他能清晰的表达不同职责的代码之间的关联关系。

image.png

视图分层

为了更好的对仓库进行理解,我们做了视图抽象:可以通过视图分层聚焦到某一块的内容进行查看,这一块儿的分层是基于Vue/Mpx/React等前端框架来设计的,主要分为四层:

以下示例基于开源项目vue-admin-better进行分析生成:

  • L1:BaseGraph层,最基础、最底层的、基于前端项目的全实体graph,如下图(基于GitNexus渲染)

image.png

  • L2:PageGraph层,只展示页面语义单元,描述了整个CodeGraph中有多少个Page实体,这些实体之间有什么路由关联关系,可以由那些普通实体进行跳转(也就是包含了路由graph)

image.png

  • L3:Page层,只展示对应页面下的组件语义单元,一个页面可以包含多个组件,并且页面自身也有一些额外的逻辑,比如页面自身的方法、data数据、props等

image.png

  • L4:Component层,基于某个组件(Component)展开之后的完整的子图,包含该组件内部的函数、方法、Hook、Store 访问、副作用、数据流、状态流等真实关系

image.png

image.png

覆盖场景

公共场景(跨语言)

场景说明
结构导航浏览目录、文件、类、方法、函数层级
调用链分析追踪函数和方法之间的调用路径
依赖分析分析文件级 import 关系和模块依赖
符号定位根据文件名、函数名、方法名、类名定位代码位置

前端场景(Vue/Mpx/React)

前端上下文召回:为 AI Coding 找到相关页面、组件、状态、跳转链和接口链路

场景说明
页面跳转分析追踪页面跳转和参数传递
组件使用分析分析页面和组件之间的渲染关系
数据流向分析分析局部状态和全局状态的读取、写入和副作用

应用到开源CodeGraph,适配内部技术项目

Benchmark

为验证前端语义增强的实际收益,我们选取了乘车码、乘客小程序、tianji-sale-car、VS Code 和 Excalidraw 共 5 个项目、17 个真实代码理解问题进行测试。每个 Case 分别执行 4 次 Agent 任务(claude code + deepseek-v4-flash),并采用四轮中位数进行统计。

测试包含三组:

  • Without:不使用 CodeGraph。
  • Main:使用Github主干版本 codegraph 1.0.1
  • CodeGraph:使用加入 Page、Component、Service 等前端语义增强后的候选版本 codegraph 1.0.3内网版本)。

测试模型为 deepseek-v4-flash。

指标WithoutMainCodeGraphCodeGraph vs MainCodeGraph vs Without
总耗时2119.6s1672.4s1430.6s-14.5%-32.5%
总成本¥22.735¥16.302¥13.097-19.7%-42.4%
Total Tokens57.45M33.18M27.39M-17.4%-52.3%
工具调用764.5320.0321.5+0.5%-57.9%

具体的数据见:

image.png

改进后的CodeGraph VS without

image.png

改进后的CodeGraph VS 原始的CodeGraph VS without

总的来说,前端语义增强取得了明确的正向收益。相较 Main(原始的upstream),CodeGraph 在工具调用量基本持平的情况下,总耗时下降 14.5%、成本下降 19.7%、Token 消耗下降 17.4%。这说明收益并非简单来自减少工具调用,而是语义图帮助 Agent 更快定位页面、组件、状态和服务之间的关系,减少了无效代码阅读和上下文扩张。

相较完全不使用 CodeGraph,语义增强版本的耗时下降 32.5%、成本下降 42.4%、Token 消耗下降 52.3%、工具调用下降 57.9%,说明图召回对于大型代码仓库中的 Agent 代码理解具有显著价值。

存在的问题

CodeGraph提供的是高效查询、组织代码上下文、提供给AI Agent的能力,它是一个由工程侧产生的确定性的结果,在绝大部分场景下,他是由Agent自主调用的(cli命令、mcp、skill等)、不影响最终产出结果准确性的工具(只会影响耗时、Token消耗等)。

image.png

但是在我们的实际应用场景中,决定查询质量的第一步往往是用户输入的query

  • 如果这个query是一个比较确定symbol,比如函数、组件、页面的名字等,那么这种查询是非常高效的。

image.png

  • 如果这个query是一个比较抽象的业务场景,比如某个功能的流程是怎样的、涉及到多个业务语义场景,那么在很大程度上取决于Agent的query rewrite能力,如果Agent对用户query做到很好的理解,基于query拆分出来symbol是精确的、那么最终组织生成的代码上下文是比较可靠的,反之Agent则可能因为拿不到精确的内容而退化到使用find、glob、grep等工具进行文本扩读,按照AI自己的理解去组织代码上下文,得到对应的结果。

image.png

因此我们最近在尝试构造Business Knowledge System,它是一个面向业务领域的知识库,是一个描述各业务实体之间的关联关系的拓扑结构。

我们前后端可以基于这个拓扑结构生成关于代码仓库的知识图谱,如LLM Wiki生成的一些关于代码仓库的业务解释性的md文件,通过解析这些文件,得到对应的业务语义与代码symbol(如页面、组件、函数的地址等)的一个关联关系(索引)。建立业务语义与相关代码子图之间的关系,使同一组关系支持双向查询

  • 业务 -> 代码(Top-down):从 Stage、Page 等业务语义定位前端代码入口;
  • 代码 -> 业务(Bottom-up):从 symbol、组件、文件、模块或 API 反查业务归属和影响场景。

这就是业务语义与代码的双向可追溯关系。

这个 BKS 是跨仓库、技术栈的,比如前端后端在同一个PRD里,针对同一个业务场景有不同的具体代码实现。在抽象层,业务语义其实是一样的,而在具体的代码查询(CodeGraph)则可以根据对应的团队技术栈进行优化实现。这样我们不仅能够回答某个功能对应到哪些代码、也能反过来回答当前的代码是归属于哪些业务的,这样修改一个功能,我们就能反推会影响哪些业务模块儿。理论上讲这能解决所有场景:Coding、issue定位、bugfix、知识问答等。