CodeGraph · agent 的代码索引后端
为 Claude Code、Cursor、Codex、opencode、Hermes 预建的语义代码知识图谱——本地、确定性、零 API。一次 MCP 调用拿到入口点、相关符号、调用关系和源码。
CodeGraph
colbymchenry/codegraph · v0.9.4 · MIT一句话定位:给 coding agent 装一个“预先建好的代码地图”。当 agent 需要理解代码时,不再 grep 满仓库,而是向 CodeGraph 的 MCP 服务发一次结构化查询,一个调用拿到入口点、相关符号、调用关系和源码片段。作者强调一个关键设计哲学——抽取过程是确定性的,源自 AST 而非 LLM 总结,因此结果稳定、可复现、可进 git。
分层流水线架构
CodeGraph 的核心是一条清晰的单向流水线,src/index.ts 里的 CodeGraph 类把各层串起来,对外暴露 init / indexAll / sync / searchNodes / getCallers / getImpactRadius / buildContext 等 API:
数据模型:22 种节点 × 12 种边
图的表达力来自精心设计的类型系统(src/types.ts)。节点(NodeKind)覆盖 22 种符号——从 function/method/class 到 interface/trait/route/component;边(EdgeKind)覆盖 12 种关系:
// 12 种关系,每条边还带 provenance:'tree-sitter' | 'scip' | 'heuristic'
export type EdgeKind =
'contains' | 'calls' | 'imports' | 'exports'
| 'extends' | 'implements' | 'references' | 'type_of'
| 'returns' | 'instantiates' | 'overrides' | 'decorates';
interface Edge {
source: string; target: string; kind: EdgeKind;
line?: number; provenance: Provenance; // 谁推断出这条边
}
抽取层:19+ 语言的 WASM 解析→ 深度页
CodeGraph 用 web-tree-sitter + tree-sitter-wasms 跑 WASM 语法,不依赖任何原生编译,因此“一个二进制到处跑”。它对工程细节的打磨令人印象深刻:
- 按需加载语法:只为项目里真实出现的语言编译 WASM grammar,控制内存。
- Worker 每 250 文件回收一次(
WORKER_RECYCLE_INTERVAL):因为 WASM 线性内存只增不减,唯一回收堆的办法就是杀掉 isolate 重建。 - 1MB 文件上限 + 10s 解析超时:跳过压缩 bundle / vendored 大文件,避免撑爆 WASM 堆。
- Git 感知发现文件:
git ls-files --recurse-submodules,尊重.gitignore与子模块。 - 对 Svelte / Vue / Liquid / Delphi 表单(.dfm)这类 tree-sitter 搞不定的格式,写了专用 extractor。
解析层:tree-sitter 搞不定的,才是真功夫→ 深度页
这是 CodeGraph 最有价值的部分。tree-sitter 只能告诉你“这里有个调用”,却无法跨文件解析“它到底调的是谁”,更无法理解框架的动态分发。ReferenceResolver 用一套分层策略来补这个洞:
- ① 内置过滤:跳过 stdlib / 内建函数(JS 内建、React hooks、Python 内建、Go stdlib…)。
- ② 框架模式:识别 14+ 框架(Express / NestJS / Django / Laravel / Rails / Spring / Gin / Axum / ASP.NET / Vapor / SvelteKit / Vue-Nuxt / Drupal…),把 URL 路由 route 节点 连到处理函数。这里用的是对“去注释后源码”的正则,而非完整 AST,省开销。
- ③ import 解析:按语言扩展名顺序解析(TS 会试
.ts/.tsx/.d.ts/index.ts…),并读tsconfig的paths别名、Cargo workspace 成员。 - ④ 名称匹配:精确名 → 限定名 → 文件路径三级匹配,用路径邻近度给候选打置信分。
- ⑤ 回调合成(最妙):见下。
emitter.on('mount', handler) 和 emitter.emit('mount') 之间的隐式调用。CodeGraph 主动“合成”这些边——它识别注册器(/^on[A-Z]|subscribe|addListener/)与派发器(/emit|trigger|notify|dispatch/),按字段名/事件字符串配对,凭空补出 React 重渲染、EventEmitter、Vue/Svelte 组合式的动态调用边,并打上 provenance:'heuristic' 标注合成来源与注册位置。存储层:node:sqlite + FTS5→ 深度页
CodeGraph 直接用 Node 内置的 node:sqlite(DatabaseSync,真正的 SQLite 编进了 Node),外面包了一层 better-sqlite3 形状的接口。因为自带 Node runtime,所以 WAL、FTS5 全平台可用。它的 PRAGMA 调优很“数据库工程师”:
db.pragma('journal_mode = WAL'); // 读不阻塞写,MCP+git hook+CLI 可并发
db.pragma('synchronous = NORMAL'); // 配合 WAL 安全
db.pragma('cache_size = -64000'); // 64MB page cache
db.pragma('mmap_size = 268435456'); // 256MB 内存映射 I/O
db.pragma('busy_timeout = 5000'); // 锁等待 5s,骑过正常 sync
schema 设计同样克制:edges 表只建 (source,kind) 与 (target,kind) 两个复合索引,靠左前缀扫描覆盖正反向查询,故意不建多余的单列索引以减轻写入负担;unresolved_refs 表把引用解析延后到抽取之后,避免抽取阶段 N+1 查符号表;nodes_fts 用 FTS5 虚拟表 + 触发器自动同步全文索引。
服务层:10 个 MCP 工具,与自适应预算
CodeGraph 通过 stdio 暴露 10 个 MCP 工具给 agent,每个都为“一次调用解决一类问题”而设计:
codegraph_context
给定一个任务描述,一次返回入口点 + 相关符号 + 关系 + 源码——替代昂贵的探索 agent。
codegraph_trace
追踪 from→to 的调用路径,包含动态分发的跳点,附带 provenance / 注册位置元数据。
codegraph_explore
一次性勘察多个相关符号,按文件分组返回源码,受自适应输出预算约束。
codegraph_impact
分析改动某符号的“影响半径”——多步 BFS 找出所有受牵连的代码。
codegraph_callers / callees
谁调用了它 / 它调用了谁,支持多层深度,秒级返回。
codegraph_search
FTS5 驱动的全文符号搜索,带 kind 加权与路径相关性打分。
一个被低估的细节:explore 工具的自适应预算会随项目规模缩放——小项目单次给 18K 字符、大项目(≥25k 文件)给到 38K,每行还带 cat -n 式行号前缀,让 agent 能直接引用 file:line 而无需回读文件。还有一条硬不变量:单文件字符上限在任何档位都不能下降——这是被一个真实回归逼出来的(excalidraw 的 415KB App.tsx 曾因上限太低只返回不到 1% 内容,迫使 agent 退回去 Read)。
增量同步与安装器
FileWatcher 用原生 OS 事件(FSEvents / inotify / ReadDirectoryChangesW)+ 防抖,让图随写随更新;在 WSL2 /mnt 与网络盘上自动降级为 git hook(post-commit / merge / checkout)。安装器(@clack/prompts 交互式)会自动探测并配置 Claude Code、Cursor、Codex、opencode、Hermes——比如给 Cursor 注入 --path 解决其子进程 cwd 错误的问题。
实测收益(v0.9.4,7 个真实仓库中位数)
对比 Claude Opus 4.7 在“有/无 CodeGraph”下回答同一架构问题。增益随仓库规模放大:
↑ token 削减幅度。Excalidraw 案例:工具调用 79→3,耗时 2m58s→48s。
对 coding agent 生态的意义
CodeGraph 把“代码理解”从 每次会话临时探索 变成 一次索引、长期复用的基础设施。它选择 100% 本地 + 确定性抽取,意味着结果可复现、可进 git、无隐私顾虑——这让它有资格成为多种 agent 共享的“代码索引标准件”。它证明了一件事:agent 性能的下一个数量级,可能不在更大的模型,而在更好的检索基建。