backgroundbackground
CodeGraph:给编程 Agent 一张项目地图

CodeGraph:给编程 Agent 一张项目地图

CodeGraph / MCP / AI Agent / Tree-sitter / SQLite / Claude Code / Codex / Cursor

技术

2026-05-20 06:04

项目小的时候,编程 Agent 看代码的方式很朴素:grep 一下,Read 几个文件,差不多就能动手。

但项目一大,这套方式会迅速变得笨重。它不是“不够努力”,而是探索路径本身出了问题:Agent 每次都要重新扫目录、猜关键词、打开文件、读到一半发现不对,再换一个方向继续读。上下文窗口越大,它越敢读;项目越复杂,它越容易读错。

这也是我最近看到 colbymchenry/codegraph 时觉得很有意思的地方。它解决的不是“让 Agent 多读一点代码”,而是反过来问:

能不能先把项目变成一张可查询的地图,再让 Agent 按地图找代码?

这句话就是 CodeGraph 的核心。

一、先说结论:CodeGraph 不是更快的 grep

CodeGraph 是一个本地优先的代码知识图谱工具。它会把项目里的代码预先解析成:

files
symbols
call edges
import edges
inheritance edges
route edges
references

然后通过 MCP 暴露给 Claude Code、Codex CLI、Cursor、opencode 这类编程 Agent。

最短使用路径大概是:

npx @colbymchenry/codegraph

cd your-project
codegraph init -i

初始化后,项目根目录会多一个 .codegraph/,里面有配置和本地 SQLite 数据库。之后 Agent 不必每次从文件系统裸奔式探索,而是可以直接问:

这个符号在哪里?
谁调用了这个函数?
这个函数调用了谁?
改这个类会影响哪些地方?
这个功能相关的入口、代码片段和调用关系是什么?

这和 grep 的差别很大。

grep 回答的是:

哪些文本行包含 auth?

CodeGraph 回答的是:

项目里有哪些叫 auth / AuthService / login 的符号?
它们是什么类型?
在哪个文件第几行?
谁调用它?
它又调用谁?
相关代码应该读哪几段?

一个是文本匹配,一个是结构理解。

二、编程 Agent 真正的痛点:不是不会读代码,是没有地图

我们平时让 Agent 改代码,最常见的失败方式不是它完全不会写,而是它一开始就找错了上下文。

比如你问:

帮我修一下登录状态刷新后丢失的问题

一个没有项目地图的 Agent 往往会这样走:

  1. login
  2. 读几个名字很像的文件。
  3. 发现还有 authsessiontokenmiddleware
  4. 再搜一轮。
  5. 打开更多文件。
  6. 上下文越来越满,但关键调用链还没完整串起来。

这时它已经开始“看起来很努力”了,但很多 token 都花在了发现阶段,而不是判断阶段。

CodeGraph 的 README 里有一组基准数据:作者拿 6 个真实代码库对比 Claude Code Explore agent 使用和不使用 CodeGraph 的表现,平均减少约 92% 的工具调用,探索速度提升约 71%。其中 VS Code、Excalidraw、Swift Compiler 这类项目都在测试里。这个数字当然要带着 benchmark 条件看,但方向很清楚:瓶颈不只在模型推理,而在上下文检索路径。

这也是我对它最感兴趣的点。

编程 Agent 现在越来越像一个会写代码的实习生,但我们经常让它在陌生仓库里闭着眼摸。CodeGraph 做的事情更像是先给它一张项目平面图:

这里是入口
这里是调用链
这里是路由到 handler 的关系
这里是继承树
这里是可能受影响的测试

有了地图,Agent 才不必每次从门牌号开始猜楼层。

三、它内部到底做了什么

从源码看,CodeGraph 的主线可以拆成四层。

Tree-sitter 解析源码
  -> 提取 symbol 和 unresolved reference
    -> 写入 SQLite 图数据库
      -> 解析 references 变成 edges
        -> 通过 MCP 工具给 Agent 查询

1. 用 Tree-sitter 把代码变成节点

CodeGraph 支持的语言很多:TypeScript、JavaScript、Python、Go、Rust、Java、C#、PHP、Ruby、C/C++、Swift、Kotlin、Dart、Svelte、Vue、Liquid、Pascal/Delphi、Scala 等。

它不是用正则粗暴扫描函数名,而是基于 Tree-sitter 解析 AST,再用每种语言自己的 extractor 抽出结构化节点。

节点类型包括:

节点类型例子
file / module一个源码文件或模块
class / interface / struct类型定义
function / method函数和方法
property / variable / constant属性、变量、常量
routeWeb 路由
componentVue / Svelte / React Router 等组件相关节点

这一步的价值是:Agent 后面查询的不再是“某个字符串出现在哪里”,而是“某个语义符号在哪里”。

比如 Session.requestAuthService.login/api/users,在纯文本搜索里只是一些字符;在 CodeGraph 里,它们会尽量变成可以被定位、关联、遍历的图节点。

2. 用 SQLite 存成本地图数据库

CodeGraph 把索引结果放在 .codegraph/codegraph.db

它的 schema 很直接,核心表是:

作用
nodes存函数、类、方法、文件、路由等符号
edges存符号之间的关系,比如 calls、imports、extends
files存文件 hash、语言、大小、索引时间
unresolved_refs存解析阶段暂时无法确定目标的引用

同时它给 nodes 建了 FTS5 全文索引,覆盖 name、qualified_name、docstring、signature 等字段。

这就解释了为什么它能比 Agent 自己扫文件快:昂贵的解析和结构化工作提前做了,Agent 查询时只是查本地数据库。

这里还有一个非常现实的设计:better-sqlite3 是 optional dependency,如果本地原生模块不可用,会退回 WASM SQLite。README 也明确提醒 WASM fallback 会慢 5-10 倍,还可能在索引时遇到 database locked。这个细节说明它不是一个只在 demo 里好看的工具,而是在认真处理真实机器上的安装和性能差异。

3. 把 unresolved reference 解析成边

AST 解析只能告诉你“这里调用了一个叫 foo 的东西”,但 foo 到底指向哪个定义,需要第二阶段解析。

CodeGraph 的 resolver 会按几类策略尝试:

  1. 框架特定规则。
  2. import-based resolution。
  3. name matching。

比如前端项目里,组件引用、路由文件、导入重命名、barrel export 都会让“名字对应关系”变复杂。后端项目里,Express、FastAPI、Django、Laravel、Spring、Rails、ASP.NET、Gin、Axum 等框架又各有路由写法。

CodeGraph 的 README 里专门列了 framework-aware routes:它会识别很多 Web 框架里的路由形态,并把 route 节点用 references 边连到 handler。

这个能力很关键。

很多时候我们问:

这个接口从 URL 到业务逻辑是怎么走的?

普通搜索会把你带到一堆字符串附近。CodeGraph 更接近于回答:

URL pattern
  -> route node
    -> controller / handler
      -> service method
        -> repository / external call

这才是 Agent 真正需要的上下文。

4. 通过 MCP 暴露成 Agent 能理解的工具

CodeGraph 的 MCP 工具面设计得很克制,不是把所有内部能力一股脑甩给 Agent,而是按常见意图拆了几类:

工具适合问什么
codegraph_search某个符号在哪里
codegraph_context这个任务相关的核心上下文是什么
codegraph_explore深入理解一个陌生模块或主题
codegraph_callers谁调用了这个函数
codegraph_callees这个函数调用了谁
codegraph_impact改这个符号可能影响什么
codegraph_node查看某个符号的详情或源码
codegraph_files看索引后的项目文件结构
codegraph_status看索引状态、节点数、边数、后端类型

这里最值得注意的是 codegraph_contextcodegraph_explore 的区分。

codegraph_context 更像日常任务入口:给它一个 bug、功能或重构任务,它会组合搜索、图遍历和代码片段,输出一个相对紧凑的上下文。

codegraph_explore 更像深度探索:它会做更深的图遍历,把相关源码按文件分组,尽量返回连续代码段、关系图和额外文件线索。源码里甚至会根据项目文件数量动态调整输出预算,避免小项目也被塞一大坨上下文。

这个细节很有意思。它说明 CodeGraph 不是只追求“召回更多代码”,而是在控制 Agent 的上下文饮食。

四、它解决的不是一个痛点,而是一串连锁反应

我觉得 CodeGraph 最有价值的地方,是它把编程 Agent 的几个老问题串起来解决了。

1. 降低探索成本

没有 CodeGraph 时,Agent 的探索成本是实时发生的:

每次任务
  -> 扫目录
  -> grep
  -> Read
  -> 再 grep
  -> 再 Read

有 CodeGraph 后,成本前置:

初始化索引一次
  -> 后续任务查 SQLite 图

这不是免费午餐,只是把成本从“每次对话重复付”变成“项目级别预付”。对于长期维护的大项目,这个取舍通常很划算。

2. 降低上下文污染

Agent 最大的敌人之一,是读了太多“相关但不关键”的代码。

上下文不是越多越好。上下文太多以后,模型会被噪音拖着走:它知道很多文件,却不知道哪几个才是决策核心。

CodeGraph 的 contextexplore 会做排序、分组、截断、按文件聚合,还会尽量给出完整的相关代码段,减少 Agent 为了补 line number 或补上下文再次 Read 文件。

这背后的判断很对:

好上下文不是体积大,而是结构对。

3. 让调用链变成一等公民

软件系统里,真正难的经常不是“函数在哪里”,而是:

谁会走到这里?
这个分支从哪个入口进来?
这个方法是不是被某个框架反射调用?
改了这个类型,哪些测试最可能受影响?

这些问题天然是图问题。

如果你只用文本搜索,就会不断在文件之间跳。CodeGraph 把 callsimportsextendsimplementsreferences 这些关系显式存成边,就能让 Agent 直接沿图走。

这也是 codegraph_callerscodegraph_calleescodegraph_impact 这些工具的意义:它们把“追调用链”从手工阅读变成结构查询。

4. 让变更影响分析更早发生

很多 Agent 改代码翻车,是因为它只读到了要改的函数,却没读到依赖它的调用方。

codegraph_impact 的思路是从目标符号反向找 incoming edges,也就是找依赖这个符号的地方。对于 class、interface、module 这类容器节点,它还会往内部子节点展开,避免只看到了类本身而漏掉类方法的调用者。

这不是类型检查,也不是测试替代品,但它能在动手前提醒 Agent:

你要改的不是一个孤立函数。
这些调用方、子类、测试、路由也可能被波及。

对大项目来说,这个提醒很值钱。

五、它为什么特别适合 MCP

MCP 的本质,是把外部能力以工具形式接到模型工作流里。问题是:并不是所有工具都适合给 Agent 用。

一个好的 MCP 工具要满足几件事:

  1. 输入简单。
  2. 输出结构化。
  3. 不要求模型自己做太多二次加工。
  4. 能减少后续工具调用,而不是制造更多调用。

CodeGraph 在这方面做得比较清醒。

比如 server-instructions.ts 里直接告诉 Agent:

找符号用 search
理解任务用 context
看调用方用 callers
看影响面用 impact
不要一上来 grep
不要把 narrow question 丢给 explore

这点很重要。很多 MCP 项目的问题不是工具不强,而是模型不知道什么时候该用哪个工具。CodeGraph 把“工具选择策略”写进 server-level instructions,相当于给 Agent 配了一份使用手册。

更微妙的是,它还在 codegraph_explore 里按项目大小给探索调用次数做预算。小项目少调用,大项目多一点,但也有上限。这是在主动对抗 Agent 的探索欲。

一个编程 Agent 很容易陷入“再看一眼”的循环。CodeGraph 的设计倾向是:

看够了就停,开始综合判断。

这很工程。

六、怎么在真实项目里用它

如果是我在一个中大型项目里接入 CodeGraph,我会把它放在三个场景里。

1. 新项目 onboarding

先看索引状态:

codegraph status

再看结构:

codegraph files --max-depth 3

然后让 Agent 用 codegraph_contextcodegraph_explore 回答具体问题:

这个项目的认证流程怎么走?
订单创建从 API 到数据库经过哪些模块?
前端路由和后端接口分别在哪里定义?

这里不要问太空泛的“介绍一下项目”。越具体,图检索越有用。

2. Bug 修复

我的理想顺序是:

codegraph_search 找入口
codegraph_callers 看谁调用
codegraph_callees 看它依赖什么
codegraph_context 组合任务上下文
修改代码
跑测试

这样 Agent 不会只盯着报错文件,而是先把调用链看一遍。

3. 重构前评估影响面

重命名、拆模块、改公共方法签名之前,先查:

codegraph_impact

如果影响面很大,再让 Agent 根据结果拆任务,而不是直接开始大改。

CodeGraph 的 CLI 里还有 codegraph affected,可以根据变更文件沿依赖关系找可能受影响的测试文件。这个思路很适合接到 pre-commit、CI 或本地验证脚本里。

七、它的边界也要看清楚

CodeGraph 很有用,但它不是银弹。

第一,它是静态分析,不是运行时真相。动态调用、反射、依赖注入、字符串拼路由、框架魔法,都可能让解析变成 best effort。

第二,它不是类型检查器。它能告诉你“这些符号有关联”,但不能保证改完一定正确。TypeScript compiler、lint、unit test、e2e test 还是要跑。

第三,它依赖索引质量。文件被 exclude、语言暂不支持、生成代码太大、WASM fallback 太慢,都会影响结果。README 里也明确提到,如果 better-sqlite3 装不上,WASM SQLite fallback 会明显变慢。

第四,它的索引有短暂延迟。MCP server 会用文件 watcher 自动同步,但刚保存完马上查询,可能还没来得及更新。它的 server instructions 里也提醒了这一点。

所以我更愿意把它定位成:

CodeGraph 是 Agent 的结构化上下文层,不是代码正确性的最终裁判。

它帮 Agent 找到更好的起点、更完整的路径、更小的上下文切片。最后能不能改对,还是要回到编译器和测试。

八、我真正喜欢它的地方

很多 AI 编程工具的方向,是继续扩大上下文窗口,继续让模型读更多东西。

CodeGraph 的方向更克制:不要一上来读更多,先把代码变成可以查询的结构。

这很像数据系统里的一个老道理:查询慢,不一定是机器不够强,也可能是你没有建索引。

编程 Agent 也是一样。

当项目只有几十个文件时,Agent 现读现搜没什么问题;当项目有几千、几万文件时,继续让它每次从文件系统开始探索,就像让数据库每次全表扫描。

CodeGraph 做的事情,就是给代码库建一组面向 Agent 的索引:

符号索引
调用索引
依赖索引
路由索引
文件结构索引
影响面索引

它不让 Agent 变得全知,但让 Agent 少一点盲目。

我觉得这会是编程 Agent 工具链里越来越重要的一层:模型负责推理和生成,编译器负责验证,测试负责回归,而 CodeGraph 这种工具负责把项目结构以低成本、可查询的方式递给模型。

最后可以用一句话记住它:

不要让 Agent 每次都重新认识你的项目;给它一张会自动更新的地图。

参考资料: