graphify 源码阅读笔记
一个让 AI 用图查询替代 grep 的 Claude Code Skill。本文对应项目 Graphify-Labs/graphify,分支
v8。我从 v0.9.0 一路读到 v0.9.38,下面是我的阅读笔记,不是教程,更不是软文。
地址: https://github.com/Graphify-Labs/graphify
一、先把几个判断摆出来
- 代码图构建零 LLM 成本。36 种语言的源码全部走本地 tree-sitter 解析,纯字符串加 AST 操作,不发任何 API 请求。
- 它不是向量索引,是一张真正的图。节点是类、函数、模块,边是
calls/imports/inherits/mixes_in。每条边都带置信度标签EXTRACTED/INFERRED/AMBIGUOUS。 - 基准数据有出处。
BENCHMARKS.md自己写明所有竞品在同模型(Kimi K2.6)、同预算、同裁判下跑,LOCOMO 召回率 0.497,差不多是 mem0 的十倍,建图成本归零。
这三条结论先列在前面。后面所有内容都为它们提供依据。
二、为什么我想搞清楚这件事
我用 Claude Code 已经一年多。每次让 AI 分析一个陌生仓库,最让我心疼的就是 token。看着它逐文件 Read,一次性塞几十个文件进上下文,我看着心里明白,这种模式走到大仓库会崩。仓库越大,AI 不光看不到全貌,连已经看到的都忘了。
我的判断是,代码理解这件事如果能做得结构化一些,让 AI 不再把所有内容塞进上下文,而是先建一份能反复查询的索引,体验会有质变。这个想法从去年就有,断断续续试过几种方案,效果都不理想。
graphify 正好是我想找的那个思路。先用 tree-sitter 把代码解析成图,再让 AI 查图。抱着"看看它是怎么做的"的心态。
三、整体架构,七步流水线

打开 ARCHITECTURE.md,作者用一句话把整个系统串起来。
detect() → extract() → build_graph() → cluster() → analyze() → report() → export()
七步流水线。每一步是一个独立的 Python 模块,模块之间通过纯 dict 和 NetworkX 图对象通信。没有共享状态,也几乎没有隐藏副作用(除了往 graphify-out/ 写文件)。
我看着这套设计觉得最难的地方在于分层的纪律。每一步都是纯函数,IO 边界清楚。加一门新语言不需要改其它模块,加一个新的输出格式也不会影响提取逻辑。这种分层纪律在 production 代码里其实非常少见。
下面这张表是模块职责的速查(来源 ARCHITECTURE.md)。
| 模块 | 函数 | 输入 → 输出 |
|---|---|---|
detect.py |
collect_files(root) |
目录 → 过滤后的文件路径列表 |
extract.py |
extract(path) |
文件路径 → {nodes, edges} 字典 |
build.py |
build_graph(extractions) |
提取字典列表 → nx.Graph |
cluster.py |
cluster(G) |
图 → 带 community 属性的图 |
analyze.py |
analyze(G) |
图 → 分析 dict(God 节点、惊喜连接) |
report.py |
render_report(G, analysis) |
图 + 分析 → GRAPH_REPORT.md 字符串 |
export.py |
export(G, out_dir, ...) |
图 → graph.json / graph.html / Obsidian |
下面我挑几个我看得最久的模块说说。
1. detect.py 文件收集的双重过滤
detect.py 不复杂,但藏着两个值得提的细节。
它会自动读取每一级目录下的 .gitignore,再合并项目根目录的 .graphifyignore(.graphifyignore 优先级更高,包括 ! 否定模式)。它还提供 --no-gitignore 开关。某些项目里被 git 忽略的生成代码反而值得索引,比如前端项目的 dist/。
我猜这个设计的潜台词是,过滤规则应该尊重项目的现有约定。这种细节在 production 工具里特别重要。你不会想用一个会偷偷爬进 node_modules/ 的工具。
2. extract.py 分语言的 AST 提取器注册表
extract.py 是整个项目的调度核心。它本身不长(500 行左右),但通过 from graphify.extractors import ... 引入了 28 个语言专属的提取器。每个提取器都是 extract_<lang>(path: Path) -> dict 这种统一签名的函数。
我重点翻了 graphify/extractors/engine.py,它是大多数语言的通用骨架。核心思路分三步,见源码 graphify/extractors/engine.py。
# 1. 用 tree-sitter 解析
tree = parser.parse(bytes_source)
root_node = tree.root_node
# 2. 遍历 AST,收集 symbols 和 references
# 收集的是"声明事实"(SymbolDeclarationFact)和"使用事实"(SymbolUseFact)
# 3. 调用 _resolve_symbol_references(...) 做跨文件符号解析
# 把模块 A 里的 `import foo` 和模块 B 里的 `def foo():` 配成一条边
这一步产出的 {nodes, edges} 字典长这样,节选自 ARCHITECTURE.md。
{
"nodes": [
{
"id": "routing.py::APIRouter",
"label": "APIRouter",
"source_file": "routing.py",
"source_location": "L2210"
}
],
"edges": [
{
"source": "routing.py::APIRouter",
"target": "routing.py::get",
"relation": "method",
"confidence": "EXTRACTED"
}
]
}
3. confidence 标签从代码层就分清"看到"和"猜到"
这是 graphify 我觉得最值得借鉴的设计之一。每条边从出生那一刻就打上置信度标签。
| 标签 | 含义 | 例子 |
|---|---|---|
EXTRACTED |
源码里写明的 | import x、func() 直接调用 |
INFERRED |
推断出来的 | 跨文件的 call graph 二次遍历 |
AMBIGUOUS |
不确定的 | 命名冲突、LLM 输出的边界情况 |
源码里执行这个打标动作的位置在 graphify/extract.py 的 extract() 主函数,调用 call_graph_second_pass() 之后给边补 confidence="INFERRED"。EXTRACTED 是默认值,只有二次推断才覆盖这件事。
这说明 graphify 在意 AI 看到的结果里,哪些是真证据,哪些是猜测。
4. cluster.py Leiden 算法给代码自动分社区
cluster.py 用 Leiden 社区检测算法(graspologic 库,需要 Python < 3.13;3.13+ 走降级实现)。社区命名默认是 Community N 占位,由 IDE 内的 AI 助手或单独的 graphify label 子命令补上。
我自己在几个仓库跑下来,社区数量一般控制在几十到几百之间,对应真实项目的子系统划分。如果嫌粒度太粗或太细,可以用 --resolution 1.5(更细)或 --exclude-hubs 99(把超 hub 节点排除再分)。源码在 graphify/cluster.py。
5. analyze.py 从图里挖出什么值得看
analyze.py 做的事情很轻。计算每个节点的 degree,找出 degree 最高的 top-N(God 节点),找出跨社区的高权重边(惊喜连接),再生成 4 到 5 个"图能优先回答的问题"。源码在 graphify/analyze.py。
我看着这一步觉得它是 graphify 真正有产品感的地方。它知道用户打开报告最先想看什么,所以先把最值得看的那一页铺好。
6. serve.py 把图暴露成 MCP 工具
这是 graphify 另一个让我眼前一亮的点。serve.py 实现了一个完整的 MCP(Model Context Protocol)服务器,对外暴露这些工具。
query_graph(question)自然语言查子图get_node(name)拿一个节点的元数据get_neighbors(name)拿邻居节点shortest_path(a, b)拿最短路径list_prs()/get_pr_impact()PR 看板
支持两种 transport,--transport stdio 本地单用户(默认),--transport http 团队共享,可以挂在容器里供多人访问。源码在 graphify/serve.py。
启动命令示例。
# 本地 MCP
python -m graphify.serve graphify-out/graph.json
# 团队 HTTP(带 API key)
python -m graphify.serve graphify-out/graph.json \
--transport http --host 0.0.0.0 --port 8080 \
--api-key "$SECRET"
把建图和用图完全解耦,是这套设计最优雅的部分。建图是 CPU 密集型,可以扔 CI 跑。用图是 IDE 行为,可以走 MCP 协议和 AI 助手对话。两者不需要绑在同一个进程里。
四、Skill 指令的精妙之处是约束而非命令

graphify 在 AI 助手里跑,不靠 AI 自动发现这个工具,而是靠安装到宿主 AI 助手配置目录里的 Skill 文件。Skill 文件本质上是一段精心写过的 Prompt。源码在 graphify/skill.md 和 graphify/cli.py。
让我摘一段 cli.py 里 PreToolUse 钩子会注入给 AI 助手的"软提示"。
MANDATORY: graphify-out/graph.json exists. You MUST run
`graphify query "<question>"` before grepping raw files.
Only grep after graphify has oriented you,
or to modify/debug specific lines.
注意几个用词。
“MANDATORY” 是大写的,但语气是"必须做 X,然后才能做 Y",不是"禁止 Y"。它给助手留了 escape hatch(修改和调试特定行)。
“graphify has oriented you” 这个短语把"图查询"定义成"理解阶段",把"读文件"定义成"修改和调试阶段"。两个阶段各司其职。
同一文件里还有一个 _READ_DENY 变体,开启 --strict 模式时第一次直接 deny 原始 Read 调用,但每 session 只触发一次,所以助手永远不会被卡死。
这段 Prompt 写得相当克制。它没有试图用惩罚性语言逼迫助手,也没有无限循环拦截。它只在用户想读源码之前塞一句"你查图了吗?",然后放手。
Skill 设计的核心是,在正确的时间点给 AI 正确的提醒。控制 AI 并不是它的目的。
1. Skill 文件和 README 的边界
我顺手对比了一下 graphify/skill.md(安装到 IDE 的)和仓库根的 README.md。两者内容高度相关但目的完全不同。
README.md 面向人类开发者,介绍怎么装、怎么用、怎么贡献。skill.md 面向 AI 助手,介绍什么时候触发它、它能做什么、做完之后怎么告诉主人。
同样的功能,写 README 时强调易用性,写 Skill 时强调无歧义触发条件。graphify 在两份文档之间做了清晰的边界,避免了 AI 看到 README 后无所适从的问题。
五、踩坑指南,production 用之前先想清楚这些
我在本地用 uv tool install "graphifyy[all]" 装了一遍,挑了几个值得提醒的坑。
1. 包名和命令名不一样
pyproject.toml 里写的是 name = "graphifyy"(两个 y),但命令叫 graphify。
# 错误
uv tool install graphify # 找不到这个包
# 正确
uv tool install graphifyy # 装包
graphify install # 用命令
为什么这个坑值得提。如果你想用 uvx 临时跑,必须写 uvx --from graphifyy graphify install,不能直接写 uvx graphify install(uv 会把第一个词当成包名去找)。
2. PowerShell 里 /graphify 会炸
在 PowerShell 里 /graphify . 会被解析成以根目录开头的路径,正确写法是 graphify .(不带斜杠)。这个坑在 README 的 Troubleshooting 里有专门一节,作者显然踩过。
3. Leiden 社区检测对 Python 版本敏感
pyproject.toml 里 leiden = ["graspologic; python_version < '3.13'"],意思是用 Python 3.13+ 时不装 Leiden,走降级实现。如果你的项目依赖 3.13,又想要 Leiden 社区检测,目前只能降 Python 或者 fork graspologic。
4. graph.json 默认上限 512 MiB
环境变量 GRAPHIFY_MAX_GRAPH_BYTES 可以覆盖这个上限(支持 "700MB"、"2GB" 之类的字符串,也支持纯字节数)。对于几十万的代码库这个限制碰不到,但如果做整个 mono-repo 全索引,最好提前确认。
5. 查询日志默认不开启
GRAPHIFY_QUERY_LOG_ENABLE=1 开启查询日志到 ~/.cache/graphify-queries.log,记录每次 query / path / explain 的问题和语料路径。如果团队用,建议在 onboarding 文档里写清楚这个变量,避免被合规审计抓到。
6. --force 是覆盖,不是重建
--force 允许新的 graph.json 在节点数少于旧版本时仍然覆盖,不会主动清理已经不存在的旧节点。如果重构删了一批文件,要彻底清理得用 graphify extract . --force,再跑一次完整提取。
六、横向对比,同类项目里它处于什么位置
既然 wiki/references.md 列了几个对标项目,我把它们拉到一起比较一下。
1. 数据来源和输入支持
| 工具 | 代码 | 文档 | 视频 | arXiv | |
|---|---|---|---|---|---|
| graphify | 36 种 tree-sitter | MD/RST/HTML | 本地解析 | 本地 whisper | 支持 |
| mem0 | 不支持 | 支持 | 需手动 | 不支持 | 不支持 |
| supermemory | 不支持 | 支持 | 支持 | 部分 | 不支持 |
| GraphRAG | 不支持 | 支持 | 支持 | 不支持 | 部分 |
graphify 是这里面唯一代码图构建零 LLM 成本的工具。其它几家本质是文档或对话向量化,代码只是其中一种文本。
2. 检索形态
| 工具 | 本质 | 检索方式 |
|---|---|---|
| graphify | NetworkX 知识图 | 图遍历加关键词混合 |
| mem0 | 向量索引 | 余弦相似度 |
| supermemory | 混合向量加图 | 相似度加时间衰减 |
| GraphRAG | 知识图加向量 | 社区摘要加向量 |
向量索引在语义相似上很强,但在"两个东西到底怎么连"上很弱。graphify 的图遍历能直接给一条带边类型的路径,这是向量系统做不到的。
3. 成本曲线
这一点 graphify 自己有基准(BENCHMARKS.md),我自己也复算过。
| 套件 | 指标 | graphify | 对比系统 |
|---|---|---|---|
| LOCOMO 召回率 | recall@10 | 0.497 | mem0 0.048, BM25 0.362 |
| LOCOMO QA | 准确率 | 45.3% | supermemory 49.7%(贵 11×) |
| LongMemEval-S | QA 准确率 | 76% | dense RAG 76%(并列) |
| LOCOMO 摄取 | USD | ~$1.40 | supermemory $15.67 |
| 图构建 | LLM credits | $0 | 其它按 token 计费 |
一个细节。这些数字是在 Kimi K2.6 一个模型、同预算、同裁判下测出来的,并且裁判的 Cohen’s kappa 是 0.81。不是某个团队自己跑、自己评。
4. 与 LLM 配合的姿势
graphify 的角色是 LLM 的前置索引。代码部分完全本地、零 LLM,非代码部分(文档、PDF、图片)才走 LLM 语义提取。这种"分层用 LLM"的设计,在我自己的项目里也想借鉴。能本地算的就不扔给云端。
七、我的判断与建议
适用场景。
- 你在 Claude Code / CodeBuddy / Cursor 里工作,仓库是陌生的大型项目,需要快速建立 mental model。
- 团队需要一个共同的代码地图,可以挂在 CI 上,每次 PR 自动 rebuild。
- 你对代码隐私敏感,源码不能出境(graphify 的代码部分完全本地,能满足这一点)。
不那么适用的场景。
- 仓库非常小,小于 1000 行。树建立索引的成本可能比 Read 几遍文件还高。
- 主要是文档或对话语料库,代码占比极低。这种情况直接用向量 RAG 更合适。
- 你希望 AI 助手主动理解,但你不想手动触发 Skill。graphify 默认按需触发,有
--strict模式但仍有限制。
我自己的期待,几条非常具体的。
- Leiden 在 Python 3.13+ 上能用。
leidenalg纯 Python 版的稳定性还要继续观察。 - 如果 graph.json 持续变大,是否考虑分片存储。现在所有查询都是把整个 JSON 加载到内存里,几 GB 之后就开始吃力。
- Skill 设计可以加一个
--quiet模式,CI 跑时不要每次都打印 100 行进度。
最后一句。"AI 读代码"这件事,graphify 给出了我目前最认可的工程做法。它把代码变成图,让 AI 查图。它不会让 AI 突然变聪明,但能让 AI 不再那么浪费 token 和你的耐心。
参考文献
- Graphify-Labs/graphify GitHub 仓库
- graphify PyPI 包(graphifyy)
- ARCHITECTURE.md 模块职责与流水线说明
- BENCHMARKS.md LOCOMO / LongMemEval 基准结果
- Wikipedia:Signs of AI writing humanizer-zh skill 的判别依据
- Traag et al., “From Louvain to Leiden” (2019)
- tree-sitter 官方仓库
- NetworkX 文档
- Model Context Protocol 规范
- mem0 仓库
- Microsoft GraphRAG 仓库
- The Memory Layer Safi Shamsi 的技术书籍
更多推荐

所有评论(0)