Graphify 解决的是单仓结构查询
Graphify(Graphify-Labs/graphify)把仓库编成一张能查的代码图,让 Agent 少 grep、少读文件。2026 年这一拨工具不少,CodeGraph、GitNexus 也是同一路:本地 tree-sitter 抽出 class / method / import / call,再通过 CLI 或 MCP 交给模型。
FastAPI 那种单仓、源码都在、提交完再问「这个类谁在用」的仓库,这套有用。我日常改的是超大 Android 多仓、默认靠 AAR / Maven 分发的工程,对不上。Java、Kotlin 不是抽不出来,是图的工作方式和日常问题不是一类:
- 图抄的是语法树上的直接调用。EventBus、接口回调、匿名
onStart这种运行时分发,抄不下来。 - 图只索引磁盘上已有的
.java/.kt,不索引 AAR / JAR。没打开的仓在图里是空白,而且不标明「这里缺源码」。 - 新鲜度默认跟本机 git commit / checkout,不是正在改的工作区,也不是
git push。 - 建图范围由命令扫到的目录决定,不是由「这次需求涉及哪几个仓」决定。
过期或残缺的图比没有更糟,Agent 会当权威用。这类日常真正缺的是「源码在不在」和「现在这棵树、这一跳运行时对不对」。
Graphify 实际在做什么
产品是 Python 包 graphifyy(CLI 仍叫 graphify),Apache 2.0,YC S26。开源引擎在本地跑。app.graphify.com 是托管版,图会建到云上,未公开的代码不要用。
它分三层:
| 层 | 做什么 | 大仓场景 |
|---|---|---|
| Pass 1 代码 | 本地 tree-sitter,不走 LLM | Java / Kotlin 的 class、method、import、直接 call 能进图 |
| Pass 2 音视频 | 本地 whisper | 基本用不上 |
| Pass 3 文档 / PDF / 图 | 模型抽 INFERRED 边 | 只有设计文档已经在仓库里的 md / pdf 才有价值 |
产物在 graphify-out/:graph.html、GRAPH_REPORT.md、graph.json。边带 EXTRACTED / INFERRED / AMBIGUOUS。纯代码可以 --code-only 离线建图。
它不是 IDE 的 Find Usages,也不是 LSP。官方自己的分界是:LSP 做一跳;图做多跳「改这里会炸什么、A 怎么连到 B」。前提是那一跳得先被 Pass 1 抄下来。Android 超大仓里经常连一跳都不全,不是 MCP 没接好。
AST 抄得下什么,抄不下什么
源码对解析器是一堆文字。tree-sitter 先把它变成语法树,树上挂的是:这是一个类、这是一个方法、这一行在直接调用 foo.bar()。Graphify 建图,基本就是把树上能看见的「谁调用了谁」抄下来。
A 方法里写了 B.doWork()
→ 图上有一条边:A ──调用──► B.doWork这叫静态、看得见的调用。问「谁调用了 doWork」,图可以给答案。这是 Graphify 的主场。
人追 bug 时问的通常是另一件事:点击、进度、生命周期进来之后,谁在什么时候真正执行。EventBus、接口回调、队列里的匿名 onStart,源码里就不是这种写法。
EventBus 和接口回调
常见写法:
发:EventBus.getDefault().post(StatusEvent)
收:@Subscribe fun onStatus(e: StatusEvent)树上能看见的只有:有人调用了 post,有个方法叫 onStatus。没有「post 会跑到 onStatus」这条边。中间是运行时按事件类型去对的,AST 不跑程序,对不上。
所以不是「EventBus 三个字检索不到」,而是:
- 发事件的那一行、收事件的那个方法,当普通函数,图里可能都在;
- 「这个 post 会打到那个 subscribe」连不上;
- 问谁发的、谁收的、中间怎么串,图给不出完整答案。
点击回调一样。图看见的是 setOnClickListener(注册)。真正执行是用户点下去之后系统回头调 listener,图里通常没有「点击 → 业务方法」。了解eventbus、观察者模式 讲的就是这类机制:按类型或接口分发,不是写死调用。
匿名对象:图停在准备阶段
Android / Kotlin 里大量逻辑不写在具名 class 里:
queue.add(object : Task(startMs) {
override fun onStart() {
runEnterAnim()
}
})人读代码会说:入口是 queue.add,真正执行是后面某个时刻调到 onStart()。Graphify 早期 Kotlin 抽取器(issue #2347)抽出外层函数后不往函数体里走,object : Xxx { ... } 不当 class 处理,于是 onStart 和内部调用都不存在。changelog 后来补过成员抽取。节点在了,图仍然很难告诉你谁在什么时候触发 onStart。那是队列、进度、生命周期,不是源码里的直接 calls。
具名 object 单例和这种匿名表达式不是一回事,见 Kotlin中的objectdeclaration、objectexpression、companionobject和顶层函数。
枚举常量:类型在,状态值曾经不在
状态按钮这类代码问的是 DONE 时走哪段 UI,谁把状态写成 RUNNING。早期图只有枚举类型节点,没有 NONE / RUNNING / DONE(issue #1700)。Java 半边在 0.9.10 补了定义节点,Kotlin enum_entry 随后也补了。有定义节点不等于能画出完整状态机。官方自己也说过,usage 边一开始不在范围内。
从入口到真正执行,经常停在这三层:XML / 生命周期触发、EventBus 或接口回调、匿名 override 与枚举分叉。Graphify 擅长的是中间那层具名 class 的 calls / imports。运行时那一跳看不见,图就会停在 queue.add 或 TaskStatus 类型上。
图会装完整:它扫的是磁盘,不是需求
「装完整」不是「每次提问都把 20 个仓塞进模型」。建图和提问是两步:
- 建图:把某个目录树里的
.java/.kt扫进graph.json。 - 提问:在这张已经建好的图上走几步,只把相关小块交给模型。
Graphify 不是多仓管理工具,不知道你开了哪些仓。对哪个目录跑 /graphify <路径>,它就扫那个目录下能看见的源码。AAR / JAR / .class 不进图。没打开、不在磁盘上的仓当不存在,也不会标「这里缺源码」。缺口是空白,不是「未索引」。Agent 会以为这个类没有调用方、没有实现,其实实现在没打开的 AAR 后面。
对一个超大多仓工程的日常开发维护来说:已打开源码大约 2 万级 Java+Kotlin 文件,另有大量 XML。日常默认态是聚合工程,大量模块仍是 Maven / AAR,磁盘上只有被切成本地源码的仓。调用链经常是:业务源码 → 接口在 AAR 里 → 实现又在另一个没打开的仓。
反编译 AAR 再喂给 Graphify 理论上能做,但不该当日常:R8 名对不上、没有注释、和当前二进制版本对不齐、体量爆炸。该做的还是反查模块 → 把 AAR 依赖切成本地源码 → 同步真正源码到磁盘。刚打开一个仓,图也不知道,除非再重建。
多仓下的开发问题
开了 20 个仓、需求只用 3 个,这种情况怎么办?
范围可以收,默认不会帮你只扫 3 个。
| 怎么跑 | 进图的是什么 |
|---|---|
多仓工程根目录 /graphify . | 已经打开、磁盘上有的源码都会扫。开了 20 个就是 20 个 |
/graphify ./modules/xxx 只指那 3 个目录,或分别建再 merge | 可以只索引这 3 个 |
根目录 .graphifyignore 排除另外 17 个 | 也能只留 3 个 |
graphify query 不会把 20 个仓全文塞进模型。但若图是按 20 个仓建的:建图成本按 20 个付;查询可能串到另外 17 个仓的同名类;那 17 个仓里过期或无关的边仍可能被当成相关。
Graphify 没有「当前任务只用这 3 个子仓」这种一等能力。要窄,得自己选路径或写 ignore。这 3 个仓若还调用第 4 个没打开的 AAR,图在这 3 个仓内部可以较完整,跨到 AAR 的那一跳仍然是断的,看起来像后面没了。
日常工作是某一个已打开的业务仓、一条链路,不是整个超大 App 仓的架构地图。官方 HTML 图大约 5k 节点就开始劝退。XML、skin、资源几乎不进图,而 Android 入口经常在 XML。没有适合「按已打开源码仓」的一等用法。
新鲜度跟 commit,不跟工作区
增量是 --update(按文件 hash),自动更新是本机的 post-commit / post-checkout。git pull 之后还要自己跑一次 graphify update .。git push 对开源引擎什么也不做。本地改了还没 commit、Agent 刚改完下一句就问、刚同步下来一个仓、刚把某个模块切成本地源码,图经常是旧的。
图默认也不是远端图数据库。开源引擎的产物是本机 graphify-out/graph.json。graphify hook install 装的是这个 clone 的本地 git hook,在你自己的机器上后台重建,不打到一台共享服务器。100 人每人 /graphify 或 commit 一次,默认是 100 台电脑各自改自己的文件,不是 100 次请求去更新同一份库。
官方给团队的三条路:
| 怎么维护 | 图在哪 | 100 人会怎样 |
|---|---|---|
| 每人自己建 | 各人磁盘上的 graphify-out/ | 互不影响。别人的 commit 不会自动更新你的图,git pull 之后还要自己 graphify update . |
把 graphify-out/ 提交进 git | 仓库里的文件,靠 pull 同步 | 官方推荐的共享方式。hook 仍在各人机器上跑;并发改 graph.json 靠官方 merge driver 做并集合并。超大仓把这份生成物当公共文件提交,git 历史会很难看 |
| 托管版,或自己起一台 MCP | 云上,或某台 HTTP 服务 | app.graphify.com 连上仓库后,push 才触发云上重索引。未公开代码不要用。自建 python -m graphify.serve ... --transport http 也可以,但谁 rebuild、图对应哪次 revision,得自己定 |
没有「每个人敲一次命令,远端图库就更新一次」这种一等能力。要共享,要么提交 graph.json 当文件,要么自己养一台服务。
图找不到时,模型会不会自己再找
会,但有前提。Graphify 默认在劝 Agent 别再找。
| 情况 | 模型还能不能补 |
|---|---|
源码在磁盘上,Agent 仍可用 rg / 读文件 | 能。搜事件类名、@Subscribe、post(,人怎么追它怎么追 |
| 按 Graphify 设计在用(先问图,少 grep) | 容易停在残缺图上,把「图里没边」当成「没有调用」 |
| 源码还在 AAR 里,本地根本没有 | 图找不到,rg 也找不到,模型只能猜 |
Claude 上甚至有严格模式,第一次读源码会被拦住、改去查图。图越不完整,Agent 越可能自信地错。把 Graphify 当额外工具、仍允许搜文件,关键一跳还是靠原来的办法,只是多付了建图成本。
缺边时图不会报「未索引」或「运行时分发」。它就给出一张残图,Agent 很容易把这张图当成完整知识。
和 CodeGraph、GitNexus 的差别
三者都用 tree-sitter,都宣称省 token,赌注不同。换产品补不上运行时分发和源码不在磁盘这两处,能拉开差距的主要是新鲜度和许可。
| Graphify | CodeGraph | GitNexus | |
|---|---|---|---|
| 赌什么 | 范围:代码加文档 / PDF / 图 | Agent 摩擦:一张活的代码图 | 分析深度:impact / taint / DI |
| 存储 | graph.json | SQLite + FTS5 | LadybugDB |
| 新鲜度 | git hook / --update | OS file watcher,保存即更新 | 重索引 / detect_changes |
| JAR / AAR | 不索引字节码 | 不索引字节码 | 不索引字节码 |
| 许可 | Apache 2.0 | MIT | PolyForm Noncommercial |
独立评测里有一句很准:过期的代码图比没有更糟。Graphify 默认跟本机 commit 走;CodeGraph 才是为「边改边问」设计的。对未提交改动,CodeGraph 强一档,但 watcher 也只覆盖已经在磁盘上的源文件。仓没打开、还是 AAR,换谁都没用。
GitNexus 更像程序分析,但非商用协议,上班写代码默认不能用,除非单独买商用授权。先排除。
公开数字也要打折。Graphify 的 71.5× token 来自 52 个文件的混合语料,不是 2 万级 Java / Kotlin 的 App 仓。LOCOMO / LongMemEval 是记忆检索榜,不是 Android 调用图质量。CodeGraph 在 OkHttp 上的数字更相关,那也只是单个开源库,不是几万文件的多仓 App 工程。
和现在常用办法比一下:
| 问题类型 | 现在怎么做 | 上图索引有没有增量 |
|---|---|---|
| 类在 AAR 里,本地没有源码 | 把 AAR 切成本地源码 | 无,图更会漏 |
| 单方法 / 单文件 | rg + 读文件 | 负,多一层过期索引 |
| 从入口到触发的完整链路 | 按执行顺序读,区分准备和执行 | 弱,AST 看不到触发 |
| 改一个符号的影响面 | IDE Find Usages / rg | CodeGraph 可能有用;Graphify 一般 |
| 模块 onboarding + 设计文档已在仓库 | 人读 + 报告 | Graphify 这才对口 |
| 跨仓「谁依赖谁」 | 多仓模块清单 / 依赖图 | 图扫不全没打开的仓 |
Graphify 想省的是 Agent 少 grep。这边的成本在:源码在不在本地、入口在 XML / 回调里、跨仓跨 AAR、改的是未提交中间态。文档加代码一张图、神节点、社区划分,对写 onboarding 有用,对日常改代码几乎用不上。多数 Agent 也不是官方一等集成,还要自己接 skill / MCP。
什么时候才值得试
默认不需要。尤其不要对超大聚合工程全仓 /graphify .,不要用托管版,不要指望它替代「打开本地源码 → rg → 按执行链路读」,也不要同时装 Graphify + CodeGraph + GitNexus。
值得试的窄场景要同时满足:
- 单个已经切成本地源码的业务仓,大约几百到两三千源文件;
- 旁边有能进仓库的 md / 设计文档,不是只在需求平台 / 在线文档里;
- 问题是「这块模块怎么拼起来的 / 文档和实现对不上」,不是「这个点击从 XML 到 runnable 怎么走」;
- 接受
--code-only,不把 PDF / 视频塞进去。
以后如果真要上代码图:先把目标仓切成本地源码;只为 Agent 少 grep、看 blast radius,用 CodeGraph 扫那一个仓;文档和代码必须连在一起,再对那个仓 Graphify --code-only + 少量 md。验证也要窄:选一个已打开的业务模块,抽查 EventBus 订阅、layout → Activity、Hilt 注入这三跳在不在图里。这三跳缺了就停,不必再装进默认 MCP。
现在这套(打开本地源码 → rg → 按执行顺序读当前文件)不是落后替代,是更贴场景的办法。差的不是 grep,是图里根本没有那些边。
相关笔记
- Agent Workspace:把一次性 AI 对话变成可积累的研发系统
- Agent MCP 完全指南
- Agent Skills 完全指南
- 子代理工作流:上下文隔离、证据压缩与责任边界
- 了解eventbus
- 观察者模式
- Kotlin中的objectdeclaration、objectexpression、companionobject和顶层函数
- RxJava:把异步事件做成可订阅的管道
评论