一句话:tree-sitter AST 提取器 + networkx 图 + Leiden 社区检测 + 10 种导出格式 + 9 种 Agent 平台集成脚本。 零 LLM 依赖,"智能"部分全部外包给宿主 Agent(Claude Code / Codex / OpenCode / ...)。
| 文件 | 行数 | 作用 |
|---|---|---|
extract.py | 2804 | tree-sitter AST 提取(22 语言)—— 真正的发动机 |
export.py | 1001 | HTML / SVG / GraphML / Cypher / Obsidian 导出 |
__main__.py | 972 | CLI + 9 种 AI 平台 install/uninstall 脚本 |
analyze.py | 537 | god nodes(度数排序)+ surprising connections(跨社区边) |
detect.py | 502 | 文件扫描 + 类型识别(含论文启发式)+ .graphifyignore |
serve.py | 364 | MCP stdio server(7 个工具) |
ingest.py | 297 | URL 抓取:twitter/arxiv/github/youtube/pdf/image/web |
hooks.py | 198 | git post-commit/checkout 自动重建(纯 AST) |
security.py | 197 | SSRF 防护 + URL 校验 + redirect 再验证 |
watch.py | 183 | 文件监听,代码变更增量重建 |
transcribe.py | 182 | whisper 视频/音频转写(可选) |
report.py | 175 | GRAPH_REPORT.md 生成 |
cache.py | 154 | SHA256 文件级缓存(markdown 剥 YAML frontmatter) |
cluster.py | 137 | Leiden / Louvain 社区检测 + 大社区拆分 |
benchmark.py | 129 | token reduction 计算 —— 71.5× 来源 |
build.py | 87 | dict → networkx.Graph 组装 |
| (其余小文件:validate / wiki / __init__ / manifest · 共 ~130 行) | ||
| 合计 | 8,237 | 20 个 .py 文件 · MIT License · PyPI: graphifyy |
# 核心 networkx tree-sitter >= 0.23.0 tree-sitter-{python,javascript,typescript,go,rust,java,c,cpp,ruby, c-sharp,kotlin,scala,php,swift,lua,zig,powershell, elixir,objc,julia} # 22 种语言 # 可选 extras mcp # MCP server neo4j # 直推图数据库 pypdf # PDF 文本提取 graspologic # Leiden(否则回落 Louvain) python-docx # Office faster-whisper # 视频转写 yt-dlp # YouTube 下载
__main__.py 的 main() 只有 install / query / benchmark / save-result / hook 等子命令
(__main__.py:780-968)—— 根本没有 "build graph" 子命令。
完整 pipeline 是被 skill.md 驱动的:宿主 Agent 按 skill.md 的 step 1..N,用内联
python3 -c "from graphify.X import Y" 挨个调各模块。这种"skill 脚本驱动"设计
把 LLM 部分合法地外包给宿主 Agent,Python 包就完全 offline。
# 实际执行顺序(由 skill.md 协调) detect.detect(root) # detect.py:329 扫描+分类 ↓ 并行 ├── extract.extract(code_paths) # extract.py:2639 tree-sitter AST └── [宿主 Agent subagent 读 PDF / 图 / markdown] → JSON cache.save_semantic_cache(...) # cache.py:119 build.build([ast_result, semantic_result]) # build.py:84 cluster.cluster(G) # cluster.py:59 Leiden/Louvain analyze.god_nodes + surprising_connections # analyze.py:42/61 report.generate(...) # report.py:14 export.to_html / to_json / to_obsidian # export.py:329/285/447
extract.py(2804 行) —— 真正的发动机架构模式:LanguageConfig dataclass(extract.py:24-60)+ 通用
_extract_generic()(extract.py:645-1010)+ 每种语言一个配置实例 + 少数语言特有的 walker。
class LanguageConfig: class_types # 哪些 tree-sitter 节点类型算"类" function_types # 哪些算"函数" import_types call_types name_field # 默认从哪个 field 拿名字 body_field function_boundary_types # 递归到这里停 import_handler # 每种语言 import 语法不一样,单独回调 resolve_function_name_fn # C/C++ 用 declarator 解包 extra_walk_fn # JS 箭头、C# namespace 等特殊分支
13 种语言走通用路径(extract.py:419-627),7 种独立手写(因为 tree-sitter 节点命名差太大):
extract_go(190 行)extract_rust(168 行)extract_zig、
extract_powershell、extract_objc、extract_elixir、extract_julia。
两遍 walk:
contains/method/inherits 边;进 function 记下 body 挂到 function_bodies 列表label_to_nid 建 calls 边_resolve_cross_file_importsextract.py:2110-2240,130 行 —— 整个项目 唯一真正跨文件推理的地方。
Pass 1:扫所有 Python 文件的 AST 节点,建 stem_to_entities[stem][ClassName] = nid 全局映射表。
Pass 2:对每个文件找 from .models import Response,在映射表里查到 Response 的 nid,然后对当前文件里的每个 class,添加 DigestAuth --uses--> Response 的 INFERRED 边。
这是"god nodes / surprising connections"最后能出东西的关键 —— 没这步,Python 项目全是 contains 边的垃圾。其他 21 种语言都没做这个,只有结构 + 同文件 calls。
detect.py(502 行) —— 文件扫描_looks_like_paper()(detect.py:68)用 13 条正则(arxiv / doi / \cite{} / \d{4}\.\d{4,5})判断一个 .md 是不是学术论文转出来的 —— 命中 ≥3 条升级为 PAPER_SENSITIVE_PATTERNS(detect.py:33-40)过滤 .env / .pem / id_rsa / aws_credentials —— 默认不读密钥文件_SKIP_DIRS(detect.py:238)自动跳 venv / node_modules / .git / dist / build.graphifyignore 支持向上查找到 .git 目录为止(detect.py:266)convert_office_file(detect.py:194)把 .docx / .xlsx 转 markdown sidecar,哈希命名避冲突cache.py(154 行) —— SHA256 内容缓存file_hash = SHA256(content) ⊕ SHA256(resolved_path)(cache.py:20)—— 路径加进哈希防同内容碰撞graphify-out/cache/{hash}.json,os.replace 原子写cluster.py(137 行) —— Leiden / Louvaingraspologic.partition.leiden,失败 fallback 到 networkx.community.louvain_communities(cluster.py:22-52)_MAX_COMMUNITY_FRACTION = 0.25:超过总节点 25% 的社区二次 Leiden 拆分(cluster.py:93)redirect_stdout(cluster.py:11-20,issue #19)analyze.py(537 行) —— "智能"层其实都是 networkx 一行题。
god_nodes(analyze.py:42)= dict(G.degree()) 排序,过滤掉文件节点 + concept 节点,取 top_n。
所谓"god nodes" = 度数最高的真实实体。_is_file_node(analyze.py:11-38)过滤三类伪节点:文件级 hub、.method() 方法桩、孤立 func()。
_surprise_score()(analyze.py:131-184)是一个朴素加权:
semantically_similar_to 关系 ×1.5serve.py(364 行) —— MCP 服务器暴露 7 个 MCP 工具:query_graph、get_node、get_neighbors、get_community、god_nodes、graph_stats、shortest_path(serve.py:156-226)。
_score_nodes(serve.py:42)就是把问题 split 成 >2 字符的词,
在 label 里子串匹配 +1 / source_file +0.5,排序取 top3 当 BFS 起点。
没有 embedding、没有 BM25、甚至没有 stemming。对中英混合语料很不友好。
export.py(1001 行) —— 十种输出to_json / to_cypher / to_html(交互 vis.js 单文件)/ to_obsidian(每节点一 md,每社区一 hub,带 wikilinks)to_canvas(Obsidian Canvas)/ push_to_neo4j(bolt 协议直推)/ to_graphml(Gephi/yEd)/ to_svgMAX_NODES_FOR_VIZ = 5_000(export.py:19)—— 超过就不出 HTMLsecurity.py(197 行) —— SSRF 防护validate_url 解析 hostname → getaddrinfo → 拒绝
is_private / is_reserved / is_loopback / is_link_local,加黑名单 metadata.google.internal
(security.py:14-64)。_NoFileRedirectHandler 重写 HTTPRedirectHandler,
每次 redirect 都重新校验一遍 —— 防 open-redirect SSRF。写一个抓取模块就自带这种层次的防护,不常见。
"GraphRAG" 按微软原论文定义:LLM 抽实体 + LLM 抽关系 + LLM 写社区摘要 + 分层答案合成。Graphify 对齐情况:
| GraphRAG 环节 | Graphify 做法 | 状态 |
|---|---|---|
| LLM 抽实体 | tree-sitter AST(代码)/ 宿主 Agent subagent(非代码) | 否 |
| LLM 抽关系 | AST + 字符串 heuristics | 否 |
| 社区检测 | Leiden / Louvain | 是 |
| LLM 写社区摘要 | 只有 label 模板,宿主 Agent 可能帮忙写 | 否 |
| 分层答案合成 | BFS + term-frequency 子串匹配 | 否 |
结论:这不是 GraphRAG,是"一个有置信度标签的 tree-sitter 代码图谱工具 + Obsidian 导出器 + 9 个 Agent 平台的 SKILL.md 胶水"。 技术本身合格(8k LOC 大部分在 extract.py 真的干活),但把它叫 "GraphRAG AI 架构助理" 是营销话术。
拆开 benchmark.py 看(benchmark.py:1-130):
_CHARS_PER_TOKEN = 4 corpus_tokens = corpus_words * 100 // 75 # 1 词 ≈ 1.33 token query_tokens = BFS 子图渲染文本长度 // 4 reduction_ratio = corpus_tokens / avg(query_tokens of 5 个固定英文问题) # 5 个固定问题(benchmark.py:55-61) "how does authentication work" "what is the main entry point" "how are errors handled" "what connects the data layer to the api" "what are the core abstractions"
= "整个语料的 token 数" 除以 "单次 BFS 子图的 token 数"。不包含建图时烧掉的 subagent token。
对代码库很好看(AST 零 token + 后续查询便宜),对论文/截图混合语料有水分 —— 首次建图那一次 PDF subagent 读取的 token 会把 ratio 稀释。
而且固定 5 个英文问题 + 子串匹配选起点 —— 换一个中文项目可能选不到起点直接返回 0。
extract.py:24。通用 walker + 每语言差异填配置。13 种语言共用 200 行通用代码。
cache.py:20。"改 metadata 不触发重抽取"这种细节。
每条边打 EXTRACTED/INFERRED/AMBIGUOUS,INFERRED 再带 0.6-0.95 的 confidence_score,报告里直接出现 "avg confidence: 0.78",诚实度拉满。
extract.py:2110。让 god nodes 变得有意义的关键。
export.py:329。内嵌 vis.js,无需服务器,发一个文件给客户直接看。
__main__.py:49-105。Claude/Codex/Cursor/OpenCode/Aider/Copilot/OpenClaw/Factory/Trae/Antigravity/Gemini。
security.py:14-64。默认写一个 URL 抓取模块就有这种层次的防护,业界少见。
cluster.py:93。>25% 超大社区二次 Leiden 拆,防退化。
_extract_python_rationale(extract.py:1011)基于 docstring 的 "X because Y" heuristics 脆得很。_make_id 用 re.sub(r"[^a-zA-Z0-9]+", "_", combined)(extract.py:14)—— 对中文/非 ASCII 标识符直接 collapse 成空串,中文代码库识别不了。_is_file_node 启发式(analyze.py:11-38)在非 Python 语言下容易误伤 —— Go/Rust 函数都是 () 结尾,又按 degree 判断……补丁摞补丁。graphifyy(两个 y,因为 graphify 被占),看得出作者赶时间。你的工作看板现在用的是 ~/Projects/code/20260319-gitnexus/(来源 abhigyanpatwari/GitNexus,17.8k stars,浏览器端 WASM 方案)。
| 维度 | GitNexus(你已有) | Graphify |
|---|---|---|
| 代码规模 | 31,367 行 TypeScript | 8,237 行 Python |
| 架构 | 浏览器 WASM 零服务器 Tree-sitterLadybugDBSigma.js |
Python CLI + networkx 内存图 |
| 图数据库 | LadybugDB(Cypher 查询) | JSON 文件 |
| 类型系统 | symbol-table.ts + type-env.ts + type-extractors/ —— 真的做类型推导 |
无 |
| Python MRO | mro-processor.ts —— 方法解析顺序 |
无 |
| 框架识别 | framework-detection.ts |
无 |
| 入口打分 | entry-point-scoring.ts |
无 |
| 跨文件 resolver | resolvers/ + resolution-context.ts + named-binding-extraction.ts |
只有 Python 做了 130 行 |
| 社区检测 | Leiden + cluster-enricher.ts |
Leiden/Louvain |
| 可视化 | Sigma.js Web UI @ 4090 | 单 HTML + vis.js |
| 多项目面板 | Web UI 常驻 | 每项目一目录,一次性 |
| MCP Server | gitnexus/src/mcp/ |
serve.py(7 工具) |
| 非代码输入(PDF/图/视频) | 无 | 支持(外包给 subagent) |
| Obsidian 导出 | 无 | 有(每节点一 md + 社区 hub) |
| Agent 平台集成 | Claude plugin + Cursor integration | 9 种平台 |
| 诚信度标签 | 未知 | EXTRACTED/INFERRED/AMBIGUOUS 三档 |
| LLM 依赖 | LangChain + 多家 LLM(可选增强) | 无硬依赖(委托宿主 Agent) |
gitnexus/src/core/ingestion/ 目录(一瞥)ast-cache.ts call-processor.ts call-routing.ts cluster-enricher.ts community-processor.ts entry-point-scoring.ts export-detection.ts filesystem-walker.ts framework-detection.ts heritage-processor.ts import-processor.ts language-config.ts mro-processor.ts named-binding-extraction.ts parsing-processor.ts pipeline.ts process-processor.ts resolution-context.ts structure-processor.ts symbol-table.ts tree-sitter-queries.ts type-env.ts type-extractors/ resolvers/ workers/
光是 ingestion 里就有 20+ 个处理器。在代码静态分析这件事上 GitNexus 已经把 Graphify 吊打了。
GitNexus 在代码静态分析维度领先 Graphify 一个量级:真类型系统、MRO、框架识别、入口打分、完整 resolver —— 这些 Graphify 全都没有。 替换过去你会失去跨项目 Web UI、看板集成、浏览器 WASM 零服务器架构,换来的只是一套更弱的单项目 AST 抽取和几个你不需要的 Obsidian 模板。
GitNexus 继续作为工作看板代码图谱主干,Graphify 作为偶尔跑一次的单项目深度报告工具(需要 Karpathy 式 /raw 混合语料场景时)。两者并不冲突。
confidence_score —— 给 GitNexus 的边也加上,报告诚实度立即拉满cache.py:10-17)—— 细节体验_looks_like_paper(detect.py:68)—— 13 条正则,简单有效_SENSITIVE_PATTERNS(detect.py:33-40)—— 安全默认值security.py:14-90)—— 如果 GitNexus 要加 URL 抓取功能直接移植cluster.py:93)—— 防 Leiden 出一个超大社区ingest.py 的 URL 分类抓取(297 行:twitter/arxiv/github/youtube/pdf/image/web)—— 给 GitNexus 补非代码输入时有现成逻辑我可以继续深入看 GitNexus 的 gitnexus/src/core/ingestion/pipeline.ts 和
type-extractors/,给你出一份 "GitNexus 现状 + 可增强点" 的专门评估 —— 这份评估完成后,
决定要不要把上述 Graphify 的借鉴项真正落到 GitNexus 里。