CodeGraph学习笔记:给代码建索引,节省Token和时间
好家伙,
最近在用 AI 看项目代码时,我开始关注一件事:
text
问一个实现问题,
AI 到底花了多少力气在找代码,
又花了多少力气在解释代码?
比如我想知道:
text
一条伤害战报在哪里生成?
如果修改战报记录逻辑,哪些地方需要一起检查?
最直接的办法,是搜索 damage、report,打开几个文件,再沿着调用继续找.
这个办法能用.但项目大了以后,找入口、排除同名函数、拼接调用关系,本身就会消耗不少时间和上下文.
所以我想,给ai做一个代码搜索工具,但现在已经有成熟的开源工具了,代码索引CodeGraph
所以这篇研究一个工具:CodeGraph.
正如字典的索引,我们去查某个字"索引"的"索","索"的索引是"s" 它提前给代码建立结构索引,让后续查询能够直接定位符号,沿着已解析的关系继续追查.
然后我们再看一个实际问题:
text
使用代码索引前后,
Token 消耗和完成任务的时间,
究竟差了多少?
观察日期:2026-10-10.本文介绍
colbymchenry/codegraph,本机使用版本为 1.6.0.第 6 节引用作者公开基准,第 7 节记录本机实际测试,数据来源分别说明.
0.背景:找到文件,距离找到答案还有多远
先固定问题:
text
伤害结算完成后,
战报中的 damage.dealt 事件由哪里写入?
用文本搜索,可以先执行:
powershell
rg -n "damage.dealt" src
事件名准确时,可以直接定位写入位置.进一步还要回答:
text
这段逻辑通过哪个对象写入?
这个写入方法还有哪些调用者?
宿主能不能换掉这个对象的实现?
还要阅读定义、追踪调用并核对对象来源.代码索引可以提前整理这些关系.
1.先分清:搜字符串、找相关代码、查关系
查代码常见三种需求:
| 问题 | 需要的信息 | 常用方式 |
|---|---|---|
哪里出现了damage.dealt |
字面量出现的位置 | rg 等文本搜索 |
| 哪些代码可能与伤害战报有关 | 与问题相关的代码片段 | 关键词、语义检索等 |
| 谁调用了战报追加方法 | 符号之间已解析的关系 | 结构索引与关系查询 |
假设一个项目里有三个 add:
typescript
report.add(...)
inventory.add(...)
queue.add(...)
文本搜索会命中这三种调用.结构查询则要区分:
text
我要找的是 Report.add 的调用者.
准确度取决于工具对语言、类型和调用方式的解析能力.
代码关系图也可以作为 RAG 的检索来源,为模型提供函数调用等结构信息.
2.CodeGraph的地图里,到底有什么
符号是可以单独定位的代码实体,例如:
text
Report 类
Report.add 方法
damage 函数
runBattle 函数
每个符号还记录文件、行号和对应源码.
关系描述调用、导入、继承等连接.拿战报举例:
text
damage
│ 调用
▼
Report.add
│ 默认实现追加记录
▼
Report.entries
顺着箭头可以追执行路径,反向查询可以找调用者,作为修改时的检查清单.业务规则仍要结合源码、配置和运行行为判断.
3.从源码到索引:地图怎么建起来
CodeGraph 的基本流程可以这样理解:
text
源码文件
│
▼
解析语法,提取函数、类、方法等符号
│
▼
整理调用、导入、继承等关系
│
▼
保存到本地索引
│
▼
按问题查询,返回相关源码和关系
先看一个很小的例子:
typescript
function appendReport() {
// ...
}
function damage() {
appendReport();
}
这里需要识别两个函数定义,并将 damage 中的调用解析到 appendReport 的定义.跨文件导入、同名方法和动态调用会增加解析难度.
CodeGraph 使用 Tree-sitter 解析源码,提取符号和关系,并保存到本地 SQLite 数据库中,再进行引用解析和查询上下文整理.官方原理说明
索引可以复用,代码变化后再同步更新.工具提供自动同步和手动同步命令.官方建图与同步指南
4.实战:用索引找到一条伤害记录的来源
假设游戏里有人受到伤害,战报中出现了一条伤害记录.现在我要改记录逻辑,先得弄清两件事:
text
谁生成了这条记录?
修改记录方法,还会影响哪些地方?
项目给这类记录起的事件名是 damage.dealt.第一步用 rg 搜这个名字,定位到 src/ops.ts:281,发现它在 damage 函数中调用了 engine.report.add.
这时我们已经知道两个名字:damage 和 Report.把它们交给 CodeGraph 查询,继续找方法定义和调用关系.实际工具参数如下,projectPath 还需指定项目绝对路径:
text
codegraph_explore
query: damage Report
maxFiles: 3
核对返回的源码后,默认记录路径是:
text
damage:算好伤害,组织目标、伤害量等数据
↓ 调用
Report.add:给记录编号
↓ 追加
Report.entries:保存记录的数组
也就是说,这里的"写战报"就是往数组里加一条记录.关键位置是 src/ops.ts:281 和 src/engine.ts:28.
第二步,查询"谁还调用了这个 add".为了避开其他同名方法,指定文件:
text
codegraph_callers
symbol: add
file: src/engine.ts
limit: 6
实际返回了 runBattle、snapshot、phaseInitiative、declareInitiative、phaseEffect、phaseSort 六个调用者.例如 snapshot 写战场快照,phaseSort 写行动排序.这六个是本次查询返回的部分结果,不是全部调用者.
因此,如果只改伤害记录的数据,先看 damage;如果改公共的 Report.add,快照、排序等记录也要一起检查.索引在这里帮我们找到"还要看谁",具体影响仍要读源码确认.
还有一个实际例外:runBattle 可以接收外部传入的记录对象.项目中的 NarrativeSink 就重写了 add,会筛选记录并压缩快照,不能把默认数组实现当作所有入口的行为.第 7 节把这条完整查找任务作为测试题.
5.AI怎么用它:MCP把查询接进来
对 AI 来说,基本过程是:
text
用户提出代码问题
↓
模型选择查询工具,给出参数
↓
CodeGraph 查询索引并组织源码上下文
↓
模型根据返回结果解释实现
这次查询的参数:
json
{
"projectPath": "C:/Users/Administrator/Desktop/xxx",
"query": "src/engine.ts Report add Engine.run src/ops.ts damage",
"maxFiles": 3
}
codegraph_explore 返回相关源码、行号、调用路径和依赖信息.只查位置或调用者时可以选更窄的工具,具体取决于版本和配置.官方 MCP 文档
查询时先限定问题和范围,多个项目明确指定 projectPath.若结果提示源码截断,继续按准确符号查询.
6.使用前后,Token和时间差多少
**数据来源声明:**本节性能对比数据转引自 CodeGraph 项目作者公开的 Agent A/B 基准测试,本文未独立复现作者这组测试.这些数字不代表本项目的实际收益;自己的引擎项目实测另见第 7 节.
资料查阅日期:2026-10-10.单题数据来源为官方 README 的 Benchmark Results;多轮数据来源为作者的 上下文占用测试报告.
作者公开的单题测试使用 Claude Opus 4.8.同一项目、同一问题,分别开放或关闭 CodeGraph MCP,每组运行四次,报告中位数.两组都保留普通读取、搜索和终端工具,同时阻止通过 CLI 绕过分组限制.测试方法与结果
先说明:这里统计的Token是什么
这里统计任务累计处理的 token.下面是口径示意,数值并非实测:
text
第一轮输入:10k token
第二轮输入:14k token
第三轮输入:18k token
三轮累计输入:42k token
最后一轮输入:18k token
多轮请求可能重复携带历史,只看最后一轮会漏算.作者统计逐轮累计的输入、缓存相关输入和输出,时间取任务总墙钟时间.
单题测试:累计Token对比
以下摘取三个项目,k 为千 token.原始数值来自作者,百分比由本文按 (不使用索引 − 使用CodeGraph) ÷ 不使用索引 × 100% 计算;取整数值对应近似比例.
| 项目 | 不使用索引 | 使用CodeGraph | 累计Token约减少 |
|---|---|---|---|
| VS Code | 670k | 155k | 76.9% |
| Excalidraw | 991k | 156k | 84.3% |
| Gin | 180k | 87k | 51.7% |
数据来源:CodeGraph 官方 README 的 单题基准原始表格,作者标注测试日期为 2026-08-05.表中结果为每组四次运行的中位数.
同一组测试:完成任务的时间对比
| 项目 | 不使用索引 | 使用CodeGraph | 耗时约减少 |
|---|---|---|---|
| VS Code | 130秒 | 58秒 | 55.4% |
| Excalidraw | 162秒 | 45秒 | 72.2% |
| Gin | 46秒 | 28秒 | 39.1% |
数据来源:与上一张 token 表相同,均摘自作者的 单题基准原始表格.耗时为完成任务的墙钟时间;"耗时约减少"由本文按公开数值计算,不属于额外实测结果.
这组三个项目的累计 token 和耗时均下降.我的理解是,收益来自减少寻找实现、排除无关代码和拼接关系的工作,具体幅度受模型、问题和项目结构影响.
多轮测试:Token更少,也可能更慢
另一组三轮对话测试中,VS Code 的结果如下:
| 指标 | 不使用索引 | 使用CodeGraph |
|---|---|---|
| 累计Token | 约1.6M | 约940k |
| 总耗时 | 119秒 | 179秒 |
数据来源:作者的 上下文占用测试报告中"Throughput in the same campaign (sonnet · 3 turns)"一节.这是另一组模型与对话轮数不同的实验,不能与前面的单题测试混为同一组结果.
这一组 token 下降、总时间增加.较大的检索结果还可能持续占用上下文.评估时要同时看累计 token、上下文占用、总时间和答案质量.多轮测试及上下文测量
7.本机实测:追查伤害战报,实际省多少
这次实际测试的场景,就是第 4 节的引擎项目.让 AI 完成一道完整的查代码题:
text
damage.dealt 事件在哪里写入,最终保存到哪里?
列出公共战报追加方法至少四个不同调用者.
runBattle 能否接收另一种记录实现?
找到仓库内实际注入的例子,解释行为差异,标出文件与行号.
两组条件如下:
- 测试日期为 2026-10-10,源码 commit 为
3df3c36012e4,测试前后相关源码没有变化. - 固定 Codex CLI
0.154.0、模型gpt-5.6-sol、推理强度high;CodeGraph 为1.6.0. - 每组各跑三次独立新会话,提示词和输出要求相同,交替安排两组的执行顺序.
- 无索引组关闭全部 MCP,禁止通过 CLI 或 SQLite 查询索引,日志核查未发现绕过.索引组开放 CodeGraph MCP;两组都允许普通搜索和源码读取.
Token 直接读取 CLI 返回的 turn.completed.usage,统计整个任务的累计输入和输出,不是按源码字符数估算.CLI 统计格式
text
累计 Token = input_tokens + output_tokens
cached_input_tokens 已包含在输入中,不再重复相加.
时间从启动 CLI 进程计到退出,包括 MCP 启动、模型推理、工具执行和等待.这测的是 AI 完成查代码任务的总开销.
六次实际运行结果
| 轮次 | 不使用索引:累计Token | 使用CodeGraph:累计Token | 不使用索引:耗时 | 使用CodeGraph:耗时 |
|---|---|---|---|---|
| 1 | 309,781 | 214,178 | 134.6秒 | 105.7秒 |
| 2 | 430,940 | 179,921 | 142.7秒 | 87.8秒 |
| 3 | 855,806 | 189,425 | 175.5秒 | 92.2秒 |
| 中位数 | 430,940 | 189,425 | 142.7秒 | 92.2秒 |
数据来源: 以上为 2026-10-10 本机执行六次
codex exec --json的结果,与第 6 节作者公开的基准分开统计.原始 usage、精确耗时和工具调用记录摘录已在本地留档:codegraph-benchmark-2026-10-10/summary.json.
在这道题上,按三次中位数比较,累计 Token 减少 56.0%,耗时减少 35.4%. 百分比按未四舍五入的耗时计算.
六份答案都经当前源码核对,满足事件写入、默认存储、至少四个调用者、真实注入例子和替代实现行为这五项要求.两组都找到了 NarrativeSink,并区分了默认 entries 数组与它的私有 out、finish() 取出方式.
普通搜索组三次用了 22、21、12 次工具调用,索引组为 5、6、9 次.但普通搜索第三次调用更少,累计 Token 却最高:返回内容的大小和历史累积同样影响消耗.
本次使用已有索引,首次建图和同步成本另计.服务端缓存未强制清空,CLI 后台请求存在失败告警,工具重试和环境等待均保留在统计中.每组三次的小样本,只描述当前模型在这道题上的表现.
完整提示词、运行条件和复跑命令保存在本地测试目录的 README.md;六份回答及核对记录保存在 answers.md.
参考资料
解释源码解析、符号提取、关系解析和本地存储,对应本文"地图怎么建立"的部分.
说明 AI 可以查询哪些信息,以及工具如何返回源码和关系.
提供本文单题 token 与耗时对比的原始数据和实验条件.
补充多轮会话的结果,并解释累计 token、上下文残留及统计口径的问题.