芥末
发布于 2026-04-28 / 0 阅读
0
0

code-review-graph:用代码图谱降低 AI 读代码的 Token 成本

AI(Artificial Intelligence,人工智能)编程工具在大型项目里有一个很隐蔽的成本:大量 token 并没有花在真正生成代码上,而是花在反复“读代码”上。

让 Claude Code 修一个问题,它可能先读 package.json,再读入口文件、业务模块、测试文件;问某个函数还有谁调用,它可能通过 grep 找一轮,再把一批文件全文打开。单次看起来不多,但在大型仓库、多人协作和频繁迭代的场景里,这部分上下文成本会快速累积。

code-review-graph 解决的正是这个问题:提前把代码库解析成一张“代码地图”,让 AI 编程工具先查询结构化图谱,再决定需要读取哪些源码文件。

它不是替代 Claude Code 的模型能力,而是给模型准备一层更适合检索代码结构的基础设施。

code-review-graph 是什么

code-review-graph 是一个面向 AI 代码审查和代码理解的开源工具。它会扫描代码库,提取函数、类、导入关系、调用关系、继承关系等信息,然后把这些信息存入本地 SQLite 数据库,形成一张可查询的代码图谱。

Claude Code、Cursor、Windsurf、Codex 等工具可以通过 MCP(Model Context Protocol,模型上下文协议)访问这张图谱。这样,AI 不需要每次都从文件系统里盲目搜索,而是可以先问图谱:

  • 修改某个函数会影响哪些调用方?
  • 哪些测试可能覆盖到这段逻辑?
  • 某个模块和支付、鉴权、订单等业务之间有哪些依赖?
  • 哪些函数调用频率高但缺少测试?
  • 某个类的继承链和使用范围是什么?

整体工作流可以理解为:

flowchart LR
    Repo[代码仓库] --> Parser[Tree-sitter 解析]
    Parser --> AST[抽象语法树 AST]
    AST --> Extract[提取函数、类、导入、调用、继承]
    Extract --> Graph[(SQLite 代码图谱)]

    AI[Claude Code / Cursor / Codex 等] --> MCP[MCP 工具层]
    MCP --> Graph
    Graph --> Result[相关函数、文件、测试、依赖结果]
    Result --> AI
    AI --> Source[按需读取源码]

这里的关键变化是:AI 从“先读文件再理解”,变成“先查结构再读文件”。

在公开 benchmark 中,一个包含 27,732 个文件的 Next.js monorepo 做代码审查时,传统方式可能需要让 AI 探索大量文件;接入图谱后,最终只需要读取十几个真正相关的文件。项目给出的平均 token 节省约为 6.8 倍,极端场景会更高,但实际收益和项目规模、任务类型强相关。

为什么 grep 和 read 不够用

Claude Code 自带的 grepreadglob 已经能完成很多代码检索任务,但它们本质上还是文本工具。

举个例子,想知道 calculatePrice 被谁调用,文本搜索可以找到包含这个字符串的文件,但它无法稳定区分下面几种情况:

// 真正的函数调用
const price = calculatePrice(order)

// 注释
// calculatePrice will be removed later

// 字符串
const fnName = "calculatePrice"

// 同名导入或同名局部变量
import { calculatePrice } from "./legacy"

模型可以继续读文件、推理上下文,但这会带来两个问题:

  1. 需要消耗更多 token,因为它要读入更多文件内容。
  2. 结果容易受上下文噪音影响,因为无关代码越多,模型越容易偏离真正的问题。

code-review-graph 的做法是先用 Tree-sitter 解析源码。Tree-sitter 是一种增量语法解析器,能把源码变成抽象语法树(AST,Abstract Syntax Tree)。有了语法树之后,工具就能更准确地识别“这是一个函数定义”“这是一次函数调用”“这是一个类继承关系”,而不是只看文本是否匹配。

对比可以更直观看出来:

维度Claude Code 原生检索code-review-graph
检索基础文件名、文本、正则函数、类、调用、依赖组成的图
查询方式搜索后继续读文件图遍历后按需读文件
上下文成本每次任务重新探索一次建图,多次复用
跨文件关系依赖模型读文件后推理图谱中直接保存关系
影响分析需要 AI 自行判断可直接查询影响半径
持久化通常只在当前会话有效SQLite 文件可跨会话复用

原生工具适合临时探索,例如找某个配置项、定位某段文本、改一个独立脚本。代码图谱更适合大型仓库里的结构化问题,例如跨模块重构、底层函数修改、代码审查、测试影响分析。

核心机制:把代码库变成一张图

代码库本身是文件和目录的集合,但程序真实运行时更像一张关系网。函数会调用函数,类会继承类,模块会导入模块,测试会覆盖业务逻辑。

code-review-graph 做的事情就是把这些隐含关系显式存下来。

一个简化后的图谱模型大致如下:

图元素示例作用
节点文件、函数、类、方法、测试表示代码实体
调用、导入、继承、包含、依赖表示实体之间的关系
属性文件路径、行号、语言、名称、复杂度支持定位和排序
存储SQLite本地持久化、可重复查询
接口MCP 工具提供给 AI 编程工具调用

例如下面这段代码:

// pricing.ts
export function calculatePrice(order: Order) {
  const discount = getDiscount(order.userId)
  return order.amount - discount
}

// checkout.ts
import { calculatePrice } from "./pricing"

export function createCheckout(order: Order) {
  const price = calculatePrice(order)
  return createPayment(order, price)
}

图谱里会形成类似这样的关系:

flowchart LR
    A[pricing.ts] --> B[calculatePrice]
    B --> C[getDiscount]

    D[checkout.ts] --> E[createCheckout]
    E --> B
    E --> F[createPayment]
    D -. imports .-> A

当 AI 问“修改 calculatePrice 会影响哪里”时,图谱不需要重新全文搜索整个仓库,而是沿着调用边反向查找调用方,再继续追踪上层调用、相关文件和测试。

Blast Radius:改动影响半径分析

code-review-graph 里一个很实用的能力是 Blast Radius,也就是“影响半径”。

软件开发中,很多 bug 并不是因为改动本身写错了,而是因为没有看清它会影响哪些上游调用方、业务入口和测试。尤其是工具函数、底层服务、共享组件,一处修改可能扩散到很多模块。

影响半径分析的思路是:从被修改的函数或文件出发,沿着图谱中的关系边向外扩展,找出直接和间接受影响的实体。

flowchart BT
    Changed[被修改函数 calculatePrice]
    Caller1[createCheckout]
    Caller2[previewOrder]
    Caller3[refundEstimate]

    API1[POST /checkout]
    API2[GET /order/preview]
    API3[POST /refund]

    Test1[checkout.test.ts]
    Test2[order-preview.test.ts]
    Test3[refund.test.ts]

    Caller1 --> Changed
    Caller2 --> Changed
    Caller3 --> Changed

    API1 --> Caller1
    API2 --> Caller2
    API3 --> Caller3

    Test1 --> API1
    Test2 --> API2
    Test3 --> API3

这种查询对 AI 编程很关键。没有图谱时,模型通常会扩大搜索范围,读入一批看起来相关的文件;有图谱后,它可以先拿到一份更精确的“必读清单”:

  • 哪些函数直接调用了改动点;
  • 哪些业务入口间接受影响;
  • 哪些测试文件可能需要运行或补充;
  • 哪些模块耦合较深,修改风险更高。

风险评分也可以建立在这些结构信息上。比如调用方越多、跨模块越多、测试覆盖越弱,风险就越高。这里不需要把评分理解成绝对正确的数学结论,它更像一个排序信号,帮助 AI 和工程师优先查看高风险区域。

增量索引和持久化为什么重要

如果每次对话都重新解析整个仓库,图谱本身也会变成新的负担。code-review-graph 的工程设计重点之一是增量更新:文件没有变化就不重新解析,只处理变更过的部分。

这让它更适合放进日常开发流程。

flowchart LR
    Change[文件变更] --> Detect[检测变更文件]
    Detect --> Parse[只解析变更文件]
    Parse --> Update[更新相关节点和边]
    Update --> SQLite[(SQLite 图谱)]
    SQLite --> Query[AI 查询最新代码结构]

公开资料中提到,一个约 2,900 个文件的项目重建索引可在 2 秒以内完成。对中大型项目来说,这意味着图谱可以长期维护,而不是一次性的临时分析结果。

SQLite 的选择也比较务实:它不需要单独部署数据库服务,仓库本地就能保存索引文件,适合开发机、CI(Continuous Integration,持续集成)任务和 AI 编程工具共享使用。

MCP 让代码图谱变成 AI 可调用工具

MCP 可以理解成 AI 应用调用外部工具的一套标准接口。Claude Code 通过 MCP 不只可以读文件、跑命令,也可以调用外部服务暴露的结构化查询能力。

code-review-graph 通过 MCP 暴露查询工具后,AI 可以把“理解代码结构”拆成明确动作:

sequenceDiagram
    participant User as 开发者
    participant Claude as Claude Code
    participant MCP as code-review-graph MCP
    participant DB as SQLite 图谱
    participant FS as 源码文件

    User->>Claude: 修改 calculatePrice,并判断影响范围
    Claude->>MCP: 查询 calculatePrice 的调用方和相关测试
    MCP->>DB: 图遍历
    DB-->>MCP: 返回函数、文件、测试清单
    MCP-->>Claude: 返回结构化结果
    Claude->>FS: 只读取必要源码
    FS-->>Claude: 返回相关文件内容
    Claude-->>User: 给出修改方案和风险点

这比“让模型自己在文件系统里摸索”更稳定,因为 MCP 返回的是结构化结果,模型不需要从大量无关文本里猜关系。

支持的工具和语言

code-review-graph 不只面向 Claude Code。安装命令会自动检测并配置多种 AI 编程环境,包括 Claude Code、Codex、Cursor、Windsurf、Zed、Continue、OpenCode、Antigravity、Qwen、Qoder、Kiro 等。

语言支持覆盖也比较广,公开说明中提到支持 23+ 种语言,包括:

语言类型示例
前端和脚本JavaScript、TypeScript、Python
后端和系统Go、Rust、Java、Kotlin
移动端Swift
智能合约Solidity
数据分析Jupyter Notebook

多语言支持对 monorepo 很重要。现实中的大型仓库经常不是单一语言:前端 TypeScript、后端 Go、脚本 Python、移动端 Swift、基础设施配置混在一起。如果图谱只能覆盖其中一小部分,跨模块分析价值会大打折扣。

安装和基本使用

最小可用流程很简单:

pip install code-review-graph
code-review-graph install
code-review-graph build

这三条命令分别完成:

命令作用
pip install code-review-graph安装命令行工具
code-review-graph install自动配置 Claude Code 等支持的 AI 工具
code-review-graph build在当前项目根目录构建代码图谱

通常应该在项目根目录执行 build,这样工具才能从完整仓库中提取跨文件关系。

构建完成后,可以直接在 Claude Code 里提出结构化问题,例如:

修改 src/pricing/calculatePrice.ts 会影响哪些测试?
分析 auth 模块和 payment 模块之间的依赖关系。
找出被调用最多但缺少测试覆盖的函数。
这个 service 的上游调用方有哪些?按风险从高到低列出来。
如果重构 checkout 流程,哪些文件必须一起阅读?

这类问题用纯 grep 很难稳定回答,因为它们问的不是“某个字符串在哪里”,而是“代码实体之间是什么关系”。

适合和不适合的场景

code-review-graph 的价值和项目规模、任务类型高度相关。它不是所有场景都需要装的一层工具。

场景是否适合原因
中大型 monorepo 代码审查适合文件多、依赖复杂,图谱能减少无效读取
修改底层公共函数适合需要快速判断调用方、测试和风险
跨模块重构适合依赖关系比文本匹配更重要
梳理遗留系统适合图谱能帮助定位核心模块和耦合关系
几十个文件的小项目不一定适合直接全文读取成本不高
单文件脚本修改不适合建图收益小,流程反而变重
临时改文档或配置不适合文本搜索足够
高动态语言的运行时调用分析谨慎使用静态解析可能漏掉反射、装饰器、动态导入等关系

一个简单判断标准是:如果一次任务经常需要 AI 打开很多文件才能确定影响范围,图谱就可能有收益;如果任务本身只涉及一两个文件,原生工具更直接。

和其他代码理解方案的区别

AI 代码理解常见方案大致有三类:

方案做法优点局限
文本搜索grep、正则、全文索引简单、无需预处理不理解语法和调用关系
向量检索把代码切块后做 embedding适合语义搜索对精确调用链不够稳定
代码图谱保存函数、类、调用、依赖关系适合影响分析和结构查询依赖解析器质量,需要维护索引

向量检索适合问“哪里实现了类似功能”,代码图谱适合问“这个函数影响谁”。两者不是互斥关系,成熟的 AI 编程基础设施通常会同时需要语义检索和结构化图谱。

code-review-graph 更偏向结构查询,因此在代码审查、重构风险分析、测试影响判断上更有优势。

需要注意的限制

小项目收益有限

几十个文件的小仓库里,AI 直接读取相关文件通常也不会消耗太多 token。接入图谱会增加安装、配置、索引维护成本,收益可能不明显。

静态解析无法覆盖所有运行时行为

Tree-sitter 擅长解析语法结构,但并不等于完整运行时分析。对于 Go、Rust、Java 这类静态结构较清晰的语言,调用关系更容易提取;对于 Python、JavaScript 等动态特性较多的语言,下面这些情况可能导致图谱不完整:

# 动态获取函数
handler = getattr(module, handler_name)
handler(request)

# 装饰器改变调用路径
@route("/checkout")
def create_checkout():
    ...

# 运行时注册
registry.register("payment", PaymentService)

遇到这类代码时,图谱结果应该作为高质量线索,而不是唯一依据。关键修改仍然需要结合测试、运行时日志和人工判断。

多一层基础设施就多一层维护成本

接入后需要维护 SQLite 索引、MCP 配置、文件监听或增量更新流程。团队使用前可以先在一个中等规模项目里试运行,确认索引更新速度、查询准确性和现有开发流程是否匹配。

token 节省不是固定值

6.8 倍是公开数据中的平均表现,不应该当成每个项目都能达到的固定承诺。代码审查、跨模块分析、大型仓库收益更明显;单文件编辑、简单 bugfix、文档修改则不会有太大变化。

实践建议

在中大型项目里使用时,可以按这样的方式落地:

阶段建议
试用选一个 500 文件以上、依赖关系较复杂的项目
构建在项目根目录执行 code-review-graph build
验证让 AI 查询几个已知函数的调用方,对比人工认知
接入确认准确性后,再配置到主要 AI 编程工具
维护根据团队流程接入文件保存、提交前检查或 CI
评估观察代码审查任务的读文件数量、token 消耗和修改准确性

更推荐从“影响分析”这类明确任务开始,而不是一开始就让它承担所有代码理解工作。比如:

我准备修改 UserService.createUser,请先查询影响半径,不要直接改代码。
根据代码图谱列出 checkout 模块最核心的 10 个函数,并说明它们的调用关系。
找出 payment 模块中被多个业务入口调用、但测试覆盖较弱的函数。

这类提示能让 AI 优先使用结构化查询能力,减少无目的地读文件。

小结

code-review-graph 的核心价值是把代码库里的隐含结构变成可查询资产。函数、类、调用、继承、依赖这些关系一旦被持久化,AI 编程工具就不必每次从零开始扫描文件,而是可以先通过图谱定位关键区域,再读取必要源码。

它尤其适合中大型项目中的代码审查、影响半径分析、跨模块重构和遗留系统理解。对于小项目、单文件修改和纯文本任务,Claude Code 原生工具通常已经够用。

当模型能力越来越接近时,上下文组织方式会变得更重要。把“喂完整文件”改成“查询代码结构后按需读取”,往往比单纯调整 prompt 更能降低 token 成本,也能减少无关上下文带来的判断偏差。

资源:


评论