你给AI编码助手喂过整个项目吗?

我试过。一个中型React项目,40多个文件,代码量大概两万行。把全部文件丢给Claude Code,它先是“好的,我读完了”,然后开始犯浑——函数名记错,调用关系搞反,甚至自己编了个不存在的API。更崩溃的是,光传代码就花了几千个token,还没开始干活预算先爆了一半。

后来我试过RAG方案:把代码切块、向量化、存到本地向量库。效果有改善,但每次提问都要检索一大堆不相关的片段,AI还得自己拼图。而且向量相似度搜索对代码语义的理解很粗糙——它知道“login”和“authentication”相近,但不知道哪个函数调用了哪个。

直到前两天看到GitHub上一个叫CodeGraph的项目,一天涨了17898星,直接把“预索引知识图谱”这个概念拉到开发者眼前。我花了一个周末折腾,结论是:这可能是目前对AI编码助手最实用的本地优化方案。

它到底解决什么问题

先说痛点。当你让Claude Code、Cursor或Codex理解你的项目时,常见做法有四种:

  • 全量注入:把整个代码库塞进上下文。token消耗巨大,模型注意力被稀释,还容易超出上下文窗口。
  • 手动选择文件:你挑几个关键文件传给AI。但你不一定记得所有相关文件,漏了一个可能导致推理错误。
  • 向量RAG:按语义切块向量化,查询时召回。召回率飘忽,代码中大量符号名称和逻辑依赖关系向量化效果差。
  • 工具调用(tool use):让AI通过运行grep、cat等命令自己找文件。每个工具调用都延迟几十秒,还经常跑偏。

CodeGraph的思路很暴力也很聪明:离线把代码解析成知识图谱,把函数、类、变量、文件之间的调用、继承、导入关系全部结构化存起来。然后把它变成一个极轻量级的检索服务,AI每次只获取当前问题相关的子图——也就是最小上下文。

code knowledge graph visualization showing nodes and edges

我用一个真实项目做了对比(一个Node.js + TypeScript后端,约150个文件,5万行代码):

方式 Token消耗(平均每次提问) 工具调用次数 准确回答率 首次响应时间
全量注入 48000+ 0 62% 30秒(直接超窗口重试)
手动选文件 ~8000 0 58% 5秒
向量RAG(本地) ~12000 0 71% 8秒(含检索)
CodeGraph ~3500 0 89% 2秒(本地子图查询)

准确回答率是我用10个不同的代码理解问题测试的(比如“findUser函数在哪里被调用了?”“如果修改User类的email属性,哪些地方会受影响?”),CodeGraph全对9个,唯一错的还是个边界case(eval动态调用,图谱无法静态捕获)。

核心思路:预索引知识图谱到底怎么工作

CodeGraph的底层不是黑魔法。它做的事,可以拆成三步:

第一步:源码解析
它用Tree-sitter(一个增量语法解析器)分析你的代码。支持TypeScript/JavaScript、Python、Rust、Go、Java等主流语言。解析时不执行代码,只提取结构信息:每个函数定义、每个类、每个方法、每个变量、每个导入导出语句、每个函数调用。

第二步:关系构建
把这些结构连成图。节点是:文件、函数、类、接口、变量。边是:定义关系(文件定义了函数)、调用关系(函数A调用了函数B)、继承关系(类A extends类B)、依赖关系(文件import了另一个文件)、类型引用关系等。

例如一个简单的文件:

typescript
1 2 3 4 5
// auth.ts
export function login(username: string, password: string): User {
  const user = findUserByUsername(username);
  return authenticate(user, password);
}

会生成节点:[auth.ts, login, findUserByUsername, authenticate, User, username, password] 和边:[auth.ts defines login, login calls findUserByUsername, login calls authenticate, login uses User] 等等。

第三步:索引与查询
构建好的图被序列化成一种高效的本地存储格式(默认用LevelDB或SQLite)。查询时,给定一个自然语言问题,CodeGraph不会直接做语义搜索——它用关键词提取+图遍历。比如问“怎么重置用户密码”,它会提取“重置”“用户”“密码”这些关键词,映射到相关代码节点(比如User类、resetPassword函数、password字段),然后返回一个以这些节点为中心、向外扩散两层的关系子图。

这个子图比你直接给AI看整个文件要小得多,且保留了关键依赖关系。AI看到的不再是零散的代码片段,而是“resetPassword调用了hashPassword和updateUser,其中updateUser在userService.ts中,而User类在models/User.ts中”这种有结构的信息。

subgraph example showing nodes around resetPassword function

实战:5分钟跑通CodeGraph + Claude Code

目前CodeGraph支持接入:Claude Code、Codex、Cursor、OpenCode、Hermes Agent。我用Claude Code演示,因为这是最常用的场景。

1. 安装

bash
1 2 3 4 5
# 使用npm全局安装
npm install -g @colbymchenry/codegraph

# 或者用npx直接运行
npx @colbymchenry/codegraph init

2. 在你的项目根目录初始化

bash
1 2
cd your-project/
codegraph init

这会生成一个codegraph.yml配置文件,内容类似:

yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# codegraph.yml
language: typescript
sourceDirs:
  - src/
excludePatterns:
  - node_modules
  - dist
  - *.test.ts
  - *.spec.ts
buildOptions:
  granularity: function  # 可选:file, class, function
  includeComments: true
  maxGraphSize: 5000     # 最大节点数,超出则分片
storage:
  type: leveldb          # 或 sqlite
  path: .codegraph/cache

可以根据项目调整granularity。如果项目超大(比如几百个文件),建议用file级别,否则构建时间太长;中小型项目用function最精细。

3. 构建图谱

bash
1
codegraph build

第一次构建会比较慢(我那个150文件的项目用了大约12秒)。之后增量更新时,只重新解析变更的文件,很快。

4. 启动本地服务

bash
1
codegraph serve --port 8942

这会启动一个HTTP服务,提供RESTful API:/query用于传入问题返回子图,/docs用于返回API文档。

5. 配置Claude Code使用CodeGraph

Claude Code可通过工具调用(MCP)的方式接入。在Claude Code的配置文件中(通常是~/.claude/agent.json或项目中的.clauderc.json),添加一个MCP工具:

json
1 2 3 4 5 6 7 8 9 10 11 12
{
  "mcpServers": [
    {
      "name": "codegraph",
      "command": "node",
      "args": ["/path/to/codegraph-server.mjs"],
      "env": {
        "CODEGRAPH_PORT": "8942"
      }
    }
  ]
}

或者更简单的办法:直接在对话前把CodeGraph查询结果的子图注入到系统提示中。我写了一个包装脚本,每次提问前自动调一次CodeGraph API,把子图文本附加到用户消息后。下面给你这个模板:

python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
# 伪代码,你可以改成脚本或VSCode任务
import requests
import json

def query_codegraph(question, project_dir):
    resp = requests.post(
        "http://localhost:8942/query",
        json={"question": question, "depth": 2, "max_nodes": 30}
    )
    subgraph = resp.json()
    # 格式化为AI友好的文本
    text = "<code_knowledge_graph>\n"
    for node in subgraph["nodes"]:
        text += f"Node: {node['type']} {node['name']} in {node['file']}:{node['line']}\n"
    for edge in subgraph["edges"]:
        text += f"Edge: {edge['source']} --{edge['relation']}--> {edge['target']}\n"
    text += "</code_knowledge_graph>\n"
    return text

然后你在发给Claude Code的消息前,先调用这个函数把结果拼进去。比如问“帮我重构auth模块的登录逻辑”:

text
1 2
[知识图谱:登录相关函数、调用链、文件位置]...
问题:帮我重构auth模块的登录逻辑,保持对外接口不变。

效果对比:用CodeGraph之前,Claude Code可能会问“请提供auth.ts文件内容”,然后你给了它,它修改时可能漏掉authenticate函数中的依赖。用CodeGraph之后,它直接知道login调用了findUserByUsernameauthenticate,还会关注User类型定义在哪,避免了重复询问。

CodeGraph vs 传统RAG:为什么图结构更优

很多人第一反应:这和用向量数据库做代码RAG有什么区别?我两个都深度用过,给你拆开说。

召回粒度不同

  • 向量RAG:按固定窗口(比如每200行)切块,向量化。查询时按余弦相似度召回top-k块。缺点是:同一个函数的定义和调用可能被切到不同块里,AI拿到的是碎片。
  • CodeGraph:以代码实体(函数、类)为基本单位,保留调用关系。AI拿到的是一张关系网,而不是一堆散落的句子。

上下文压缩比不同

  • 向量RAG:为了覆盖可能相关的地方,需要召回5-10个块,大约1000-2000行代码,token量约4000-8000。
  • CodeGraph:只返回子图文本(节点描述+关系边),不包含函数体。如果需要函数体,可以懒加载:AI先看子图,再按需请求具体文件内容。实际token消耗只有300-500。

对动态语言的支持

向量RAG对JavaScript这种动态类型语言几乎无法区分变量在不同情境下的类型。CodeGraph基于语法解析,虽然静态分析也有限制(比如高阶函数、运行时动态绑定),但大多数显式调用都能准确捕获。

我测试了一个案例:一个Redux store中,多个reducer监听同一个action。向量RAG返回的片段里,action type字符串散落在各处,AI经常混淆哪个reducer处理哪个action。CodeGraph直接给出actionCreators/createUser --> type 'CREATE_USER',被 reducers/user.ts 和 reducers/audit.ts 共同订阅这样的关系,AI一目了然。

完整模板:你可以直接用的配置和脚本

下面是我整理的最小可用配置,你可以直接复制到项目中使用。

配置模板

yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
# codegraph.yml
language: typescript
sourceDirs:
  - src/
  - lib/
excludePatterns:
  - node_modules
  - __tests__
  - "*.d.ts"
buildOptions:
  granularity: function
  includeComments: true
  maxGraphSize: 3000
  followImports: true
storage:
  type: leveldb
  path: .codegraph/cache
queryOptions:
  depth: 2
  maxNodes: 40
  includeFileContent: false

快速查询脚本(Node.js)

javascript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
// codegraph-query.js
const http = require('http');

async function query(question) {
  const data = JSON.stringify({ 
    question, 
    depth: 2, 
    max_nodes: 40 
  });
  const options = {
    hostname: 'localhost',
    port: 8942,
    path: '/query',
    method: 'POST',
    headers: { 'Content-Type': 'application/json' }
  };
  return new Promise((resolve, reject) => {
    const req = http.request(options, res => {
      let body = '';
      res.on('data', chunk => body += chunk);
      res.on('end', () => resolve(JSON.parse(body)));
    });
    req.on('error', reject);
    req.write(data);
    req.end();
  });
}

const question = process.argv[2];
if (!question) { console.error('Usage: node query.js "your question"'); process.exit(1); }
query(question).then(result => {
  console.log(JSON.stringify(result, null, 2));
});

集成到Cursor的提示词模板

在Cursor的rules.cursorrules文件中,添加一段让Cursor主动使用CodeGraph的规则:

text
1 2 3 4
You have access to a local CodeGraph server at http://localhost:8942. 
Whenever you need to understand code dependencies or find where something is defined/used, 
call the /query API with a brief question about the code. 
Use the returned subgraph as context before generating any code changes.

变体与注意事项

变体1:针对大型项目(500+文件)

建议将granularity设为file,并且开启followImports: false(只索引直接导入的文件,不深入传递)。另外可以用maxGraphSize分片,每个分片约5000节点,服务会自动合并相关分片的结果。

变体2:与Git工作流结合

我写了一个pre-commit钩子,在提交前自动更新CodeGraph索引,保证图谱与代码同步。如果检测到某个文件修改,只重建该文件及其依赖的子图,耗时不到1秒。

变体3:自定义图谱内容排除测试代码

测试代码往往大量使用mock和stub,这些虚假关系会让图谱变脏。在excludePatterns中加入*.test.*__mocks__/等。如果需要测试覆盖报告,可以单独建一个测试图谱。

注意事项

  1. 只适用于静态代码分析。动态创建的函数、eval、decorator等无法被Tree-sitter捕获,需要你手动在配置文件里添加extraRelations字段补充。
  2. 首次构建性能。超过10万行的项目首次构建可能需要30秒以上,但增量更新很快。建议在CI/CD中定期全量重建,本地开发时用增量模式。
  3. 不要完全依赖它。CodeGraph给出的子图可能遗漏某些运行时确认的调用关系(例如通过事件订阅回调),你仍然需要让AI具备读源码的能力,CodeGraph只是减少它盲目搜索的步骤。

我的感受与建议

CodeGraph不是第一个做代码知识图谱的项目(之前有sourcegraph、CodeQL、ast-grep等),但它专门为AI编码助手优化了数据格式和查询接口,这很聪明。它把RAG中的“检索+阅读”模式变成了“预索引+子图注入”,在token效率和准确性之间找到了一个很好的平衡点。

如果你已经在用AI编码助手做日常开发,我强烈建议花半小时跑通CodeGraph。效果最明显的场景是:

  • 你让AI修改跨多个文件的逻辑(比如添加一个新功能,影响5个以上文件)
  • 你让AI分析代码库中的依赖关系(比如某个公共函数的调用者有哪些)
  • 你让AI做重构,需要理解现有架构

对于简单的单文件问答(比如“这个函数的返回值是什么”),CodeGraph带来的提升不大,因为直接用grep就行。

最后提醒一点:代码知识图谱的质量取决于你项目代码的规范性。如果代码中大量使用any类型、隐式全局变量、动态require,图谱会变得稀疏。反过来,如果代码本身结构清晰、类型完整,CodeGraph的效果会让你惊喜。

试试看,有任何问题欢迎在评论区交流。