不用向量不调 LLM,graphify 用一棵 AST 树把整个代码库变成知识图谱

发布于 2026年07月16日 21:23 #Agent 基建#Github 解读 原文链接

不用向量不调 LLM,graphify 用一棵 AST 树把整个代码库变成知识图谱 封面图
  • graphify 用 tree-sitter AST 解析代码,确定性抽取节点和边,无需 LLM 和向量库
  • 管线分 detect、extract、build、cluster 等阶段,模块间无共享状态,通过 Python dict 和 NetworkX 传递
  • 每条边标注 EXTRACTED、INFERRED、AMBIGUOUS 三级 confidence,实现可审计的代码关系追踪
  • 跨 20+ 平台兼容,通过 Skills 标准实现 Claude Code、Cursor 等 Agent 的自动触发
  • 代码层 AST 解析零 LLM 成本,社区检测用 Leiden 算法,大图性能受限于 NetworkX 纯 Python 实现

我在 GitHub 趋势榜上连续几天看到 Graphify-Labs/graphify,月增 13000+ Star,总 Star 逼近 9 万。一个「把代码变成知识图谱」的 Skill,凭什么在 Skills 生态里杀出重围?我拆了它的源码,发现这东西的技术选型跟主流完全反着来。

大家好,我是若风。

一句话定位

graphify 是一个 AI 编程助手 Skill,你在 Claude Code / Cursor / Codex 里敲 /graphify .,它就把整个项目(代码、文档、PDF、数据库 schema)变成一张知识图谱。然后你不 grep 文件了,你 query 这张图。

听起来又是一个 RAG?不是。它的核心反差在于,代码层完全不用 LLM,不用 embedding,不用向量库

为什么不用向量

这是理解 graphify 的钥匙。

传统代码 RAG 的做法是把文件切片、embedding、存向量库,查询时算余弦相似度召回。这条路有几个绕不开的坑,graphify 全部避开了:

第一,向量召回是模糊的。你问「认证流程连了哪些数据库」,embedding 可能给你一个带 auth 字样的文件,但那个文件到底 import 了什么、calls 了什么,向量不知道。

第二,embedding 要花钱。每次 reindex 都要调 API,代码一改就得重算。

第三,向量库是黑盒。你不知道为什么它召回这个文件不召回那个,没法审计。

graphify 的做法是,代码层用 tree-sitter 做 AST 解析,确定性地抽取节点和边。一个 import 语句就是一条 imports 边,一个函数调用就是一条 calls 边,一个 class B(A) 就是一条 inherits 边。这些都是从源码语法树里直接读出来的,不猜,不推断,不需要 LLM。

你想想看,这其实是个很朴素的洞察,代码本身就是结构化数据。你有一棵 AST 树,你就有节点和边,你就有图。干嘛要绕道向量?

管线拆解

graphify 的架构文档 ARCHITECTURE.md 写得很干净,一条流水线,每个阶段一个模块一个函数,模块间用 Python dict 和 NetworkX 图传递,没有共享状态:

detect() → extract() → build_graph() → cluster() → analyze() → report() → export()

我逐个拆。

detect:文件收集

graphify/detect.pycollect_files(root) 遍历目录,返回过滤后的文件列表。它会读 .gitignore,也支持自定义 .graphifyignore,语法跟 gitignore 完全一样,包括 ! 取反规则。两个文件 merge 时 .graphifyignore 的 pattern 优先级更高,但只排除不纳入,不会把 .gitignore 已排除的文件重新加回来。

extract:核心引擎

这是整个项目最重的模块。graphify/extract.py 是总调度入口,真正的解析器在 graphify/extractors/ 目录下,按语言一个文件,目前有 28 个提取器覆盖约 40 种语言。

每个提取器返回统一的 schema:

{
  "nodes": [
    {"id": "unique_string", "label": "human name", "source_file": "path", "source_location": "L42"}
  ],
  "edges": [
    {"source": "id_a", "target": "id_b", "relation": "calls|imports|uses|...", "confidence": "EXTRACTED|INFERRED|AMBIGUOUS"}
  ]
}

这里有一个我很喜欢的设计,confidence 三级标签。每条边都标了它是怎么来的:

  • EXTRACTED:源码里明确写了的,比如 import 语句、直接函数调用
  • INFERRED:通过第二轮 call-graph 分析推断出来的,比如跨文件的间接调用
  • AMBIGUOUS:不确定的,在报告里标红让人工 review

这个标签的价值在于可审计。你看到一条边写着 APIRouter → Dependant [uses] [INFERRED],你就知道这条关系是 graphify 推的,不是源码里直接写的。向量 RAG 给不了你这个透明度。

build:三层去重

graphify/build.py 的节点去重做了三层,源码注释写得很清楚:

  1. 文件内(AST 层),每个提取器维护一个 seen_ids 集合,同一个文件里重复的 class/function 定义只保留第一次出现
  2. 文件间(build 层),NetworkX 的 G.add_node() 天然幂等,同一个 ID 加两次后者覆盖前者。这里有个有意的设计,semantic pass 的节点会覆盖 AST pass 的节点,因为 semantic 节点携带更丰富的跨文件上下文
  3. 缓存合并(Skill 层),在调用 build() 之前,Skill 会用 seen set 按 node["id"] 做一轮合并,把缓存命中的和新增的提取结果统一去重

还有一个细节引起了我的注意。build.py 里有一个 _EDGE_LANG_FAMILY 字典,定义了跨语言族的概念边界。比如 .py 属于 py 族,.ts 属于 js 族,.java 属于 jvm 族。它会在边循环里做一个 phantom-edge guard,丢掉跨语言族的 INFERRED calls

为什么要这么做?因为一个 Python 文件里的 import time 可能意外绑定到一个叫 time.ts 的文件(issue #1749),或者跨语言的 INFERRED 边把不相关的东西连起来(issue #1547)。这个 guard 保证了只有真实的跨语言互操作(比如 TS→JS 的模块引用、C 实现到头文件的调用)才会保留。

cluster:社区检测

graphify/cluster.pyLeiden 算法做社区检测,把图里的节点分成若干子系统(community)。Leiden 没装的时候降级到 Louvain(NetworkX 内置)。

社区检测的输出很有意思。graphify 会给每个社区打标签,然后把你的代码库拆成可视化的「子系统」。那个 graph.html 交互式可视化里,同色节点属于同一个 community,点进去能看到这个子系统里最重要的节点是什么。

源码里有个很务实的处理。_suppress_output() 函数把 graspolec 库的 stdout/stderr 重定向到 devnull,因为 graspologic 的 leiden() 会输出 ANSI 转义序列(进度条、彩色警告),在 Windows PowerShell 5.1 上会搞坏滚动缓冲区(issue #19)。这种平台兼容性细节,README 不会告诉你,但源码会。

security:不做后门

graphify/security.py 是我读到的最认真的安全层之一。所有外部输入都过安全验证:

  • URL 验证 validate_url(),只允许 http/https,_NoFileRedirectHandler 阻止 file:// 重定向
  • 内容抓取 safe_fetch(),二进制 50MB 硬上限,文本 10MB 硬上限
  • 图文件路径 validate_graph_path(),必须 resolve 到 graphify-out/ 内部,防止路径穿越
  • 节点标签 sanitize_label(),剥控制字符,256 字符截断,HTML 转义
  • 还有一个 memory-bomb 防护,_MAX_GRAPH_FILE_BYTES = 512 MiB,拒绝加载超大 graph.json 防止 json.loads 吃光内存

对于一个要解析你整个代码库的工具,这种安全意识是对的。

跨 Agent 兼容:Skills 标准的胜利

graphify 的定位描述里写着一串名字,「Claude Code, Codex, OpenCode, Cursor, Gemini CLI, and more」。我数了一下 install 文档,支持 20+ 个平台

这不只是「装个插件」这么简单。不同平台的接入机制完全不同:

  • Hook 平台(Claude Code、Gemini CLI)用 PreToolUse hook,在搜索类工具调用前自动触发,把助手引导到查图路径
  • 指令文件平台(Codex、OpenCode、Cursor)写持久化指令文件(AGENTS.md.cursor/rules/),让助手每次都优先查图
  • Cursor 比较特殊,写 .cursor/rules/graphify.mdcalwaysApply: true,不需要 hook

这说明一件事,Skills 标准正在脱离单一平台变成开放协议。一个 Skill 能同时在 Claude Code 和 Cursor 里跑,靠的不是谁的恩赐,是各家都在往兼容的方向走。谁先成为事实标准,谁就掌握了 Agent 生态的入口。

benchmark 要怎么看

graphify 在 README 里放了一张 benchmark 表,LOCOMO recall@10 达到 0.497,吊打 mem0(0.048)和 supermemory(0.149)。

我得说,这个数字很好看,但你要知道它是怎么来的。

打开 BENCHMARKS.md,第一行就写着,「graphify’s own harness」。对,测试框架是 graphify 自己写的。所有竞争系统(mem0、supermemory)作为 adapter 跑在同一个 harness 里,同一个模型(Kimi K2.6),同一个预算。裁判也是 Kimi K2.6,跟第二个独立裁判做了盲测对比,agreement 90.6%,Cohen’s kappa 0.81。

这个设计有没有问题?有。自己当裁判自己当被测,哪怕做了盲测和双裁判校验,利益冲突是客观存在的。kappa 0.81 说明两个裁判一致性高,但不说明裁判本身没有偏向性。

但话说回来,graphify 的一个数据是硬的,没法注水,graph build 的 LLM credits 是 $0。因为代码层纯 AST 解析不调 API。而 supermemory 的 ingest 成本是 $15.67,mem0 是 $3.48。这个对比不需要裁判,是算术题。

几个你没看 README 就不知道的坑

安装名称有陷阱。 PyPI 包名是 graphifyy(双 y),不是 graphify。但 CLI 命令是 graphify(单 y)。README 里专门加粗警告了,其他 graphify* 的 PyPI 包都不是官方的。如果你用 uvx graphify install 会报错 No solution found,因为 uv tool run 把第一个词当包名读,你要写 uvx --from graphifyy graphify install

Mac 上别用 pip install。 graphify 的 Skill 运行时会从 graphify-out/.graphify_python 解析 Python 路径。如果你用 pip 装在了系统 Python 里,但 Skill 解析到了另一个环境,就会 ModuleNotFoundError: No module named 'graphify'。用 uv tool installpipx install 隔离环境可以完全避开。

Leiden 算法不支持 Python 3.13+。 这个在 optional extras 表里,leiden extra 的注释是「Python < 3.13 only」。如果你跑的是最新的 Python 3.13,社区检测会降级到 Louvain,效果会差一点。这是一个还没解决的兼容性问题。

issue #371 暴露的性能瓶颈。 有贡献者提出用 igraph 的 C 后端替换 NetworkX 计算 betweenness centrality,预计 ~100x 加速。这说明 graphify 当前在大图上的图算法性能受限于 NetworkX 的纯 Python 实现。对于百万行级别的代码库,构建图可能会慢。

它代表了什么

graphify 背后有一个我越来越确信的判断,Agent 时代的代码理解层正在分化成两条路线

一条是「语义派」,靠 LLM + embedding,让模型去「理解」代码含义,适合做模糊问答和意图理解。另一条是「结构派」,靠 AST + 图算法,确定性地抽取代码的事实结构,适合做精确的依赖追踪和影响分析。

graphify 选的是结构派,而且把结构层做成了零 LLM 成本的公共底座。代码改了,AST 重跑一遍就行,不花一分钱 API。语义层(文档、PDF、视频的 semantic pass)按需叠加,你配了 backend 才花钱。

这个分层的价值在于,当你需要精确答案的时候,你查的是事实不是概率graphify path "FastAPI" "ModelField" 告诉你这两个概念之间隔着 3 跳,每跳的关系是什么,confidence 是什么级别。这不是向量相似度能给你的。

YC S26 的背景、3 天 3 个 release 的迭代速度、30 个贡献者、20+ 平台兼容,这些数字说明 graphify 不只是一个「好用的 Skill」,它在试图定义 Agent 理解代码库的标准方式。至于能不能成,得看 Skills 生态本身的走向,以及 Leiden/图算法这一层在超大代码库上能不能扛住性能。

如果你今天要在 Claude Code 或 Cursor 里理解一个陌生的代码库,graphify 值得一试。特别是你对向量 RAG 的「黑盒召回」感到不放心的时候,AST 知识图谱给你的是一条可审计、可追溯、零成本的替代路径。

评论互动

© 2026 王若风的技术博客 · Powered by Astro