Graphify vs GitNexus vs CodeGraph — 三工具架构深度对比

2.1 本章的方法:一把统一的解剖刀

比较三个来自不同语言生态、不同团队、不同商业模式的系统,最容易犯的错误是"按各家自己的说法罗列一遍"。Graphify 的文档以"管线阶段"组织,GitNexus 的文档以"抽象层"组织,CodeGraph 的文档以"用户可感知的功能"组织------直接照搬任何一家的叙事框架,都会让另外两家变形。

因此本章先建立一把外生的、统一的解剖刀:一个与三家自述无关的五层架构剖面,逐层把三个系统切开并列。

层次 该层回答的问题 该层的输出物 下游被它约束的能力
L1 存储层 Storage 图落在什么介质上、由谁保证一致性 持久化文件 / 数据库 规模上限、并发、崩溃恢复、可移植性
L2 解析层 Parsing 源码如何变成结构化事实、解析器如何被组织 每文件的节点/边草稿 语言覆盖的边际成本、跨文件消解能力
L3 图模型层 Graph Model 图的形式化定义是什么:节点/边类型、属性、身份、溯源 图 schema 表达力上界、保真度、可扩展性
L4 查询层 Query 查询在哪个引擎里执行、有哪些索引可用 查询结果 查询表达力、延迟、可组合性
L5 集成层 Integration 图以什么形态暴露给人和 Agent;进程如何存活 MCP server / CLI / hook 工作流嵌入度、运维成本

这个剖面有一个关键性质:它是自下而上单向约束的。L1 选择什么介质,直接决定 L4 能跑什么查询------文件态 JSON 里跑不出 Cypher,SQLite 里跑不出原生图遍历原语。同样,L3 的 schema 刚性决定了 L2 加一门语言要付多少代价。因此本章每一层的分析都不停留在"参数是什么",而是追问**"这个选型把下游锁死成了什么样"**。

三条贯穿全章的正交轴是:

  • 形式化轴:图模型在数学上是什么(本章 §2.5 的核心)
  • 工程后果轴:ACID、并发、崩溃恢复、可移植性、内存放大(本章 §2.3 的核心)
  • 进程形态轴:常驻还是一次性、谁触发重算(本章 §2.8 的核心)

边界声明 :本章只处理静态结构 。管线各阶段的算法流程(GitNexus 的管线阶段(README 归纳为 6 阶段、ARCHITECTURE.md 实为 19 阶段 DAG)、CodeGraph 的二趟解析与合成层、Leiden 社区切分的算法过程)属第三章;查询语言语法与 MCP 工具清单属第四章;测试/CI/许可证属第五章;性能基准的方法学批判属第七章。本章在触及这些话题时一律只取其结构含义,不展开。

本章使用的一手材料,除共享技术事实库外,另定向抓取了三份此前未进入事实库的源码级证据:Graphify 的 graphify/ids.pygraphify/build.pygraphify/multigraph_compat.pyv8 分支),以及 CodeGraph 的 src/db/wal-valve.tsmain 分支)。这四份文件把三家在图身份、图类型、WAL 治理上的真实工程取舍暴露得比任何 README 都清楚,构成本章若干最锋利的论断的基础。

2.2 架构总览:三张分层剖面图

先给出三张按上述五层重绘的结构图。图中每一格都对应可核验的模块或文件,不含臆测组件。

Graphify(Python,v8 分支)

lsp 复制代码
L5 集成  │ graphify CLI (cli.py) │ MCP stdio/http (serve.py) │ git hook (hooks.py)
         │ 20+ 助手 skill 文件 (skill-*.md, skills/, always_on/)
─────────┼──────────────────────────────────────────────────────────────────
L4 查询  │ 进程内 NetworkX 遍历 │ query / path / explain 子命令
         │ 报告生成 (report.py, analyze.py, callflow_html.py, tree_html.py)
─────────┼──────────────────────────────────────────────────────────────────
L3 图模型│ build.py → nx.Graph / nx.DiGraph │ validate.py 强制 schema
         │ ids.py 统一 ID 规范化 │ dedup.py + _minhash.py 去重
         │ cluster.py 社区属性 │ multigraph_compat.py(能力探针,未启用)
─────────┼──────────────────────────────────────────────────────────────────
L2 解析  │ detect.py 文件筛选 │ extract.py + extractors/ (tree-sitter)
         │ symbol_resolution.py, resolver_registry.py, ruby_resolution.py,
         │ pascal_resolution.py │ llm.py 语义 pass │ scip_ingest.py
         │ pg_introspect.py, cargo_introspect.py, manifest_ingest.py
─────────┼──────────────────────────────────────────────────────────────────
L1 存储  │ 进程内存:NetworkX 图对象
         │ 磁盘:graphify-out/graph.json(≤512 MiB 默认)
         │ 出口:export.py + exporters/ → Neo4j cypher.txt / FalkorDB /
         │        GraphML / SVG / Obsidian / Wiki

模块清单据 Graphify graphify/ 目录 API 列表(v8)Graphify ARCHITECTURE.md(v8),抓取于 2026-08-03。

GitNexus(TypeScript,main 分支)

lsp 复制代码
L5 集成  │ gitnexus CLI (tool.ts) │ MCP stdio │ gitnexus serve → Express
         │ (serve.ts → api.ts, mcp-http.ts) │ PostToolUse hook(仅提示)
         │ gitnexus-web/:Vite + React 瘦客户端
─────────┼──────────────────────────────────────────────────────────────────
L4 查询  │ LadybugDB 查询引擎(Cypher) │ FTS + 向量 + RRF 混合检索
─────────┼──────────────────────────────────────────────────────────────────
L3 图模型│ lbug/schema.ts:每类型一张节点表 + 单张 CodeRelation 表
         │ Unified Graph Schema(文档称 44 节点类型 / 21 关系类型)
─────────┼──────────────────────────────────────────────────────────────────
L2 解析  │ 四层抽象栈(自下而上):
         │   Tree-Sitter Queries(每语言 S-表达式,统一 capture tag)
         │   Language Providers(导入语义/类型配置/导出检查/MRO 策略)
         │   Scope-Resolution Pipeline(registry + 三层导入解析 + MRO)
         │   Unified Graph Schema
         │ worker pool 为唯一解析路径(无顺序回退)
─────────┼──────────────────────────────────────────────────────────────────
L1 存储  │ <repo>/.gitnexus/:lbug │ lbug.wal │ lbug.shadow │ lbug.lock
         │   lbug.{wal,shadow}.dirty-recovery │ gitnexus.json │ meta.json
         │ ~/.gitnexus/registry.json(全局仓库注册表,MCP 发现)
         │ 由 repo-manager.ts / lbug-adapter.ts 管理

GitNexus ARCHITECTURE.md(main)GitNexus README(main),抓取于 2026-08-03。

CodeGraph(Node + Rust kernel,main 分支)

lsp 复制代码
L5 集成  │ 自包含单二进制(内置 Node 运行时) │ src/bin/, src/installer/,
         │ src/upgrade/, src/ui/, src/telemetry/
         │ MCP server (src/mcp/) 由 Agent 自行拉起
         │ 跨会话共享后台 daemon(本地 socket),codegraph daemon 管理
─────────┼──────────────────────────────────────────────────────────────────
L4 查询  │ SQLite 查询引擎 │ src/db/queries.ts(约 94 KB)
         │ src/search/(FTS5) │ src/context/, src/graph/
─────────┼──────────────────────────────────────────────────────────────────
L3 图模型│ src/db/schema.sql:nodes / edges / files / unresolved_refs /
         │ project_metadata / schema_versions / name_segment_vocab
         │ nodes_fts(FTS5 外部内容表)+ 3 个同步触发器
         │ src/db/migrations.ts(schema 版本迁移)
─────────┼──────────────────────────────────────────────────────────────────
L2 解析  │ src/extraction/(Rust kernel + tree-sitter,另有 web-tree-sitter
         │ WASM 依赖) │ src/resolution/(跨文件消解,unresolved_refs 二趟)
─────────┼──────────────────────────────────────────────────────────────────
L1 存储  │ .codegraph/codegraph.db(SQLite,WAL 模式)+ 锁文件
         │ src/db/sqlite-adapter.ts │ src/db/wal-valve.ts(WAL 检查点阀门)
         │ src/sync/(file watcher + 增量同步)
         │ CODEGRAPH_DIR 可重定向索引目录

模块清单据 CodeGraph src/ 目录 API 列表src/db/ 目录 API 列表CodeGraph README(main)src/db/schema.sql,抓取于 2026-08-03。

三张图并排看,第一个刺眼的结构差异就出来了:Graphify 是一个"扁平的 Python 模块袋"(单层 69 个条目、几乎无子包),GitNexus 是一个"显式四层抽象栈 + monorepo 分包",CodeGraph 是一个"按职责切目录的 13 目录服务"。这不是审美差异,而是三种不同的演化预期------扁平模块袋优化的是"随时插一个新 extractor",显式抽象栈优化的是"新增一门语言时不碰上层",职责目录优化的是"一个二进制里的多个长生命周期子系统各自演进"。后文 §2.9 会回到这一点。

2.3 L1 存储层:三条介质路线与它们的工程账单

2.3.1 三方并列
维度 Graphify GitNexus CodeGraph
持久化载体 文件态 graphify-out/graph.json 嵌入式图数据库 LadybugDB(.gitnexus/lbug 嵌入式关系库 SQLite(.codegraph/codegraph.db
运行时表示 进程内 NetworkX 图对象 数据库缓冲池(默认 min(2 GiB, 80% RAM)) SQLite page cache + WAL
引擎依赖 无(纯 Python + NetworkX) @ladybugdb/core ^0.18.3(README 明言"formerly KuzuDB") node:sqlite 内置模块
容量上限 默认 512 MiBGRAPHIFY_MAX_GRAPH_BYTES 可覆盖) 单库默认 16 GiBGITNEXUS_LBUG_MAX_DB_SIZE 未声明显式上限(受 SQLite 与磁盘约束)
日志/恢复机制 无(整文件覆盖写) lbug.wal + lbug.shadow(checkpoint 暂存)+ .dirty-recovery 停放文件 SQLite WAL + wal-valve.ts 主动检查点治理
并发写模型 无内建并发控制 lbug.lock 单写锁 WAL 单写多读 + 写者背压阀
全局元数据 ~/.gitnexus/registry.json 无(每项目 .codegraph/,靠 projectPath 参数寻址)
开放数据出口 Neo4j cypher.txt / FalkorDB / GraphML / SVG / Obsidian / Wiki 无声明的通用图导出 无声明的通用图导出

据事实库 §2.2、§3.2、§4.2,并经 GitNexus ARCHITECTURE.mdCodeGraph src/db/ 列表 复核,抓取于 2026-08-03。

2.3.2 graph.json 全内存模式:512 MiB 到底意味着什么

Graphify 的存储层是三者中唯一没有数据库 的:build.py 在内存里构造 NetworkX 图,export.py 序列化成 node-link 格式的 graph.json,下一次运行再整个读回来。ARCHITECTURE.md 原文写得很直白:各阶段"communicate through plain Python dicts and NetworkX graphs",Graphify ARCHITECTURE.md(v8)

512 MiB 的默认上限对应多大的仓库?Graphify 自己的 BENCHMARKS.md 给了一个可用的换算锚点:一个 2026-06-24 的快照为 22,620 节点 / 48,710 边 / 3,758 文件 (厂商自报数据,见事实库 §2.6 与 Phase 1 §6.1)。以此为基准做一次保守推导(以下为本报告推导,非厂商声明):

  • 每文件平均产出约 6 个节点、13 条边;
  • node-link JSON 中,一个带 id / label / source_file / source_location / community 五个字段的节点,序列化后约 150--300 字节;一条带 source / target / relation / confidence 的边约 100--200 字节;
  • 则每文件的图产物约 2.2--5.4 KB
  • 512 MiB ÷ 该区间 ≈ 10 万至 24 万个源文件

单看这个数字,512 MiB 似乎相当宽裕------远超 Linux 内核级别的 7 万文件。但硬上限不是真正的天花板,真正的天花板有三道,且都比它低得多:

第一道:HTML 报告在约 5000 节点即失效。 README 明言 graph.html 超过 5000 节点后浏览器难以打开,建议改用 JSON(事实库 §2.2)。按上面每文件 6 节点换算,约 800 个源文件就会让人类可读产物退化。这不是存储限制,而是 Graphify"人可读"这条价值主张的实际作用域。

第二道:加载期的内存放大。 这是文件态方案最容易被低估的成本。Python 的 json.load() 必须先把整个 JSON 文本读入,再物化成完整的 dict/list 树,然后 networkx.readwrite.json_graph.node_link_graph() 再据此构造图对象。三份数据结构在时间上存在重叠:原始文本、解析后的 Python 对象树、NetworkX 图。而 NetworkX 的 Graph 本身是 dict-of-dicts-of-dicts 结构(见 NetworkX Graph 类文档),每个节点、每条边的属性各占一个独立 Python dict,加上 CPython 的对象头与哈希表装载因子开销。本报告判断 :一份 512 MiB 的 graph.json,其加载峰值常驻内存(RSS)应在数 GB 量级 ,推导链条为------(a) 解析后的 Python 对象树本身通常是 JSON 文本的 2--5 倍;(b) NetworkX 图对象与该对象树在构造期同时存在;© 二者叠加即 3--8 倍放大。换言之,GRAPHIFY_MAX_GRAPH_BYTES 调到文档示例的 2GB 之前,应先确认机器有 8--16 GB 空闲内存。这一推导未经实测验证,标注为推论。

第三道:写入是整文件覆盖,不是增量提交。 没有 WAL、没有事务、没有页级写入。这意味着一次 --update 的增量重抽,在磁盘层面仍是"把整张图重新序列化写一遍"。当图接近上限时,每次微小改动都要付出百 MB 级的 I/O。Graphify 设有"部分抽取失败保护"(拒绝用更小的结果覆盖已有图,需 --allow-partial 才放行,事实库 §2.4)------这个保护机制本身就是对无事务存储的补偿:因为没有原子提交,只能在应用层用启发式规则防止半成品把好图冲掉。

2.3.3 LadybugDB 的单写锁:一个仓库同时只能有一个索引者

GitNexus 的 .gitnexus/ 目录布局暴露了一个标准的影子分页 + WAL 嵌入式存储引擎,其文件语义在 ARCHITECTURE.md 中逐行注明:

复制代码
<repo>/.gitnexus/
  ├── lbug           # LadybugDB database
  ├── lbug.wal       # Write-ahead log
  ├── lbug.shadow    # Shadow sidecar (checkpoint staging)
  ├── lbug.lock      # Single-writer lock
  ├── lbug.{wal,shadow}.dirty-recovery  # parked sidecars from a crashed run; safe to delete
  ├── gitnexus.json  # lastCommit, indexedAt, stats (primary metadata file)
  └── meta.json      # legacy mirror of gitnexus.json, kept in sync

(原文引自 GitNexus ARCHITECTURE.md

三条结构性结论:

一、lbug.lock 是一个进程间的单写锁,直接约束并发拓扑。 文档原文注释为 # Single-writer lock本报告推导 :这意味着同一仓库在同一时刻只允许一个 gitnexus analyze 进程写入。对 CI 场景,若同一 runner 上并行跑多个分支的索引任务并共享工作树,必然发生锁竞争;对开发机场景,若一个终端在跑 analyze、另一个终端也敲了 analyze,第二个要么等锁要么失败。事实库 §3.2 记录的"连接池懒开、闲置 5 分钟驱逐、最多 5 并发"是读侧的并发能力,与写侧单锁并不矛盾------这是典型的"单写多读"(single-writer multi-reader)模型,与 SQLite WAL 同构。

二、.dirty-recovery 停放文件揭示了崩溃恢复策略:保守而非自动。 文档注明崩溃运行留下的 sidecar 会被"停放"(parked),并附一句 safe to delete本报告判断 :这是一种"崩溃后不自动回滚、把残留证据留给人处理"的设计。它比"静默续用可疑 WAL"安全,但代价是恢复不是全自动的------用户可能需要手动清理才能重新索引。相比之下 SQLite 的 WAL 恢复是进程打开数据库时自动完成的,无需人工介入。

三、16 GiB 单库上限 + 2 GiB 缓冲池默认值,勾勒出的目标规模远大于 Graphify。 16 GiB 相对 512 MiB 是 32 倍。但要注意这个对比并不完全公平:LadybugDB 存的是列式/表式的持久结构(含 Embedding 表和向量),而 graph.json 存的是紧凑的 node-link 文本;同一份代码图在两种介质里的体积不可直接换算。可以确定的是:GitNexus 的存储层被设计成"不需要整图入内存也能查",而 Graphify 的存储层被设计成"整图必须入内存才能查"。这是本层最根本的分野。

2.3.4 CodeGraph 的 WAL 治理:wal-valve.ts 暴露的真实工程账单

CodeGraph 用的是最"无聊"的技术------本地 SQLite------但恰恰在这里藏着本章最有信息量的一份源码。src/db/wal-valve.ts(14,947 字节)的文件头注释,几乎是一篇小型工程事故报告。以下为原文摘录(CodeGraph src/db/wal-valve.ts):

"WAL checkpoint valve --- bounds WAL growth while auto-checkpointing is deferred during a bulk index (#1231)."

"SQLite's default wal_autocheckpoint (1000 pages) re-writes hot B-tree/FTS pages into the main DB file over and over during a bulk index --- measured at ~95% of ALL disk I/O, and the difference between 45s and 19+ minutes on HDD-class storage (150 random IOPS)."

"The WAL duplicates hot pages per COMMIT, so it grows far faster than the DB (5.9GB WAL for a ~340MB DB on a 3.3k-file index)."

"a WAL file's SIZE never shrinks. After a full backfill, the writer's next commit RESTARTS the WAL from the top and the frames recycle inside the same file --- so raw size says nothing about the un-backfilled backlog."

以及关键常量:

复制代码
DEFAULT_WAL_VALVE_MB = 256      // 软阈值,触发离线 PASSIVE 检查点
HARD_CAP_MULTIPLIER = 2         // 硬上限 = 2× 软阈值,超过则暂停写者
FILE_CAP_MULTIPLIER = 4         // 文件上限 = 4× 软阈值,超过则 TRUNCATE
MAX_PAUSED_BACKFILL_PASSES = 20 // 单次暂停内最多尝试 20 轮回填
CHECK_INTERVAL_MS = 2000        // 阀门轮询周期

这段代码在本章的价值有三重:

其一,它证明"用 SQLite 存图"不是省事,是把复杂度从存储引擎选型转移到了存储引擎调教上。 关系型数据库的默认参数是为 OLTP(小事务、高频、随机)调的,而代码图构建是典型的 OLAP 式批量装载(大事务、顺序、单次)。CodeGraph 必须自己写一个 256 MB 软阈值、512 MB 硬上限、1 GB 文件上限的三级阀门,并把检查点扔到 worker 线程上跑(PASSIVE 检查点"never blocks the writer"),才能让批量索引不退化。这是嵌入式关系库路线的隐性成本,在任何 README 的对比表里都看不到。

其二,backpressure() 机制说明写者可以被主动暂停。 原文:"if the writer outruns the checkpointer past a hard cap of growth (2× soft), backpressure pauses the writer (at a safe, between-transactions boundary) until a FULL backfill lands"。本报告推导 :这意味着 CodeGraph 的索引吞吐存在一个由磁盘 IOPS 决定的自适应上限 ------在慢盘上,索引不会失败,而会主动降速。这比"跑到一半 OOM"要好,但也意味着"27k 文件 ~100 秒"这类厂商自报数字(未经独立验证,事实库 §4.6)强依赖测试机的磁盘等级,在 HDD 或网络盘上不可复现。第七章可以把这条源码证据作为基准可比性批判的硬材料。

其三,"futility latch"(徒劳闩)揭示了读写共存的真实困难。 原文记载:当一个读者钉住(pin)WAL 时,20 轮回填会全部失败,此时系统会禁用写者暂停 60 秒,"degrades to the pre-valve behavior",并附了一次真实事故记录------"22GB WAL, exit 137"(exit 137 即 OOM Kill)。这直接回答了本章的一个关键设问 :WAL 模式给 CodeGraph 的 file watcher 并发写带来了什么?答案是------WAL 让"agent 一边查、watcher 一边写"成为可能(读不阻塞写),但代价是长事务读者会阻止 WAL 回收,在大仓库上可能把 WAL 撑到数十 GB。CodeGraph 为此付出的补偿代码,就是这 15 KB 的阀门。

2.3.5 六项工程后果横评

把上述三条路线放进统一的工程账单:

工程属性 Graphify(graph.json) GitNexus(LadybugDB) CodeGraph(SQLite WAL)
ACID 保证 ❌ 无事务语义;整文件覆盖写。以"拒绝用更小结果覆盖"的启发式规则作应用层补偿 ✅ WAL + shadow checkpoint,具备崩溃一致性 ✅ SQLite 完整 ACID(WAL 模式下)
并发写 ❌ 无内建控制;靠 git merge driver 事后合并 ⚠️ 单写锁 lbug.lock,进程级串行 ⚠️ 单写多读;写者可被阀门主动背压暂停
并发读 ⚠️ 每个读者各自加载整份 JSON(内存各付一份) ✅ 连接池(懒开、≤5 并发、闲置 5 分钟驱逐) ✅ WAL 下"concurrent reads never block on a writer"
崩溃恢复 ❌ 无恢复机制;崩溃时可能留下截断的 JSON ⚠️ 半自动:残留 sidecar 停放为 .dirty-recovery,需人工删除 ✅ SQLite 打开时自动 WAL 恢复;另有 codegraph unlock 清理陈旧锁
可移植性 ✅✅ 单个 JSON 文本文件,跨平台、跨语言、可 diff、可人读 ⚠️ 二进制库文件,绑定 LadybugDB 版本 ⚠️ 二进制 SQLite 文件;README 明言"the background-server lock and the SQLite index are tied to the OS that wrote them"
备份 / 版本控制友好 ✅✅ 可直接 commit;官方提供 git union-merge driver 自动消解 graph.json 冲突 ❌ 二进制 + WAL + lock,不宜入库 ❌ 二进制 + WAL,不宜入库
内存放大比 ❌ 最差:整图必须入内存,叠加解析中间态,推估 3--8× ✅ 缓冲池可控(默认 min(2 GiB, 80% RAM)) ✅ page cache 可控;WAL 增长另由阀门约束
规模上限 512 MiB 硬上限,但实际受内存与 HTML 5000 节点先行约束 16 GiB 单库默认上限 无声明上限;已自报 Linux 内核 70k 文件 / 6.4M 关系(厂商自报,未经独立验证)

这张表指向一个清晰的规模分层 (本报告判断):Graphify 的存储层在中小仓库 (数千文件量级)内是三者中最简单、最可审计、最便携的;一旦跨过万文件级别,它的全内存模式就会从优点变成瓶颈。GitNexus 与 CodeGraph 都为大仓库做了真正的存储工程,但方向不同------GitNexus 买了一个现成的图引擎(把复杂度外包给 LadybugDB),CodeGraph 自己在通用关系引擎上手工调教(把复杂度内化为 wal-valve.ts)。这两种选择在 D8(部署与运维成本)上的差异是:外包的复杂度会随上游版本升级而变动,内化的复杂度是自己的技术债但完全可控

2.3.6 数据出口:三者中唯一的开放通道

一个在功能对比表里常被当作"可视化选项"轻描淡写、实则具有战略意义的差异:只有 Graphify 提供通用图数据出口

export.pyexporters/ 目录支持导出 Neo4j Cypher 脚本(--neo4j 生成 cypher.txt--neo4j-push bolt://... 直推)、FalkorDB、GraphML(Gephi / yEd 可读)、SVG、Obsidian vault 与 Wiki(事实库 §2.2)。GitNexus 与 CodeGraph 的 README 均未声明等价的通用图导出能力。

这一差异的战略含义有三层(以下为本报告推导):

  1. 退出成本(exit cost)不对称。 用 Graphify 建的图,可以在任何时刻搬进 Neo4j、FalkorDB 或任何吃 GraphML 的工具继续用,工具本身停止维护不会让数据变成废墟。用 GitNexus 或 CodeGraph 建的图,若要迁出,需要自己写导出器去读 LadybugDB 或 SQLite------SQLite 至少还是开放格式且 schema 已公开(schema.sql 在仓库里),LadybugDB 则依赖一个刚刚改过名的第三方引擎。退出成本排序:Graphify < CodeGraph < GitNexus。
  2. 可组合性(composability)不对称。 导出到 Neo4j 之后,Graphify 的图立刻获得 Cypher、APOC、GDS 图算法库等一整套成熟生态------这是它自身 CLI DSL 给不了的。换言之,Graphify 用"导出"换来了它在查询层欠缺的表达力,这是一个被低估的架构杠杆。第四章讨论查询表达力时应当把这条通路计入。
  3. 它把"图"当资产,而不是当缓存。 三家的存储路径隐含了对"这张图是什么"的不同判断:可 commit、可 diff、可导出、可人读的 graph.json一份工程资产.gitnexus/lbug.codegraph/codegraph.db 则是可随时重建的派生缓存(两者的 README 都默认用户会重新索引而非备份数据库)。这一判断差异会在 §2.9 收束。

说明:数据可移植性与许可证是两个正交维度。Apache-2.0 / MIT / PolyForm NC 的合规差异属第五章范畴,本节仅论数据格式层面的锁定风险。

2.4 L2 解析层:解析器如何被组织,决定了加一门语言的价格

本节只谈解析器的组织结构,不谈解析流程(属第三章)。

2.4.1 三方并列
维度 Graphify GitNexus CodeGraph
tree-sitter 宿主形态 Python 绑定,语法包作为 pyproject.toml 依赖逐个引入 CLI 用原生绑定,Web 用 WASM;vendored grammars(dart/proto/swift/kotlin) README 称语法编译进"native Rust kernel";package.json 同时列 web-tree-sitter ^0.25.3 + tree-sitter-wasms ^0.1.11
语言数 36 tree-sitter 语法 / 约 40 语言 14 语言(企业版 +OCaml) 33 语言
解析器代码组织 扁平:extract.py 中的 extract_<lang>() 函数 + extractors/ 子目录 分层:per-language tree-sitter 查询(S-表达式)+ Language Provider 接口 目录化:src/extraction/src/resolution/ 分离
跨文件消解模块 symbol_resolution.py / resolver_registry.py / 语言专用 ruby_resolution.pypascal_resolution.py Scope-Resolution Pipeline(registry + 三层导入解析 + MRO) src/resolution/ + unresolved_refs 表二趟重试
非 tree-sitter 输入通道 llm.py 语义 pass、scip_ingest.pypg_introspect.pycargo_introspect.pymanifest_ingest.pymcp_ingest.pytranscribe.pygoogle_workspace.py 无(纯代码) 无通用旁路;有框架/跨语言桥接启发式
并行执行模型 未在 ARCHITECTURE 声明 worker pool 为唯一 解析路径,--workers 0 / skipWorkers 被显式拒绝 Rust kernel"one boundary crossing per file"

模块名据 Graphify graphify/ 目录列表(v8)CodeGraph src/ 目录列表,抓取 2026-08-03;语言数与 WASM 依赖据事实库 §2.3 / §3.3 / §4.3。

2.4.2 "加一门语言"的边际成本,是本层最有解释力的指标

Graphify 的 ARCHITECTURE.md 把这个成本写成了一份五步清单(原文,Graphify ARCHITECTURE.md v8):

  1. Add a extract_<lang>(path: Path) -> dict function in extract.py ...
  2. Register the file suffix in extract() dispatch and collect_files().
  3. Add the suffix to CODE_EXTENSIONS in detect.py and _WATCHED_EXTENSIONS in watch.py.
  4. Add the tree-sitter package to pyproject.toml dependencies.
  5. Add a fixture file to tests/fixtures/ and tests to tests/test_languages.py.

这份清单的信息量在于它要求改动 4 个不同文件(extract.pydetect.pywatch.pypyproject.toml本报告判断:这是一个"低门槛、高散射"的扩展模型------写一个新 extractor 不需要理解框架抽象,任何懂 tree-sitter 的贡献者一小时就能上手,这解释了 Graphify 为何能在四个月内堆到 36 种语法、并吸引到 152+ 位贡献者(事实库 §2.8);但每加一门语言都要在四处留下痕迹,且后缀注册散落在三个模块里,长期看是漂移风险的温床。

顺带一处文档与代码的漂移 需要如实记录:ARCHITECTURE.mdextract.py 描述为单一 dispatch 模块,但 v8 分支的目录列表中同时存在 extractors/exporters/ 两个子目录。本报告推导 :代码已经在向子包化演进,而架构文档尚未同步。这类漂移本身不是缺陷,但提示读者:Graphify 在高速迭代(两周 13 个 release,事实库 §2.8),其架构文档的时效性弱于源码

GitNexus 走了相反的路。它的四层抽象栈是显式的:

复制代码
 Unified Graph Schema
           ↑
 Scope-Resolution Pipeline (registry lookup + 3-tier import resolution + MRO)
           ↑
 Language Providers (import semantics, type config, export checker, MRO strategy)
           ↑
 Tree-Sitter Queries (per-language S-expressions, unified capture tags)

(原文引自 GitNexus ARCHITECTURE.md。)

其中 "unified capture tags" 是关键机制:每种语言写自己的 S-表达式查询,但捕获名(capture tag)统一,因此上层的 Scope-Resolution Pipeline 完全不需要知道当前是 Kotlin 还是 Rust。加一门语言 = 写一组查询 + 实现一个 Language Provider 接口(导入语义、类型配置、导出检查、MRO 策略),上层零改动

代价一目了然:Language Provider 接口把"一门语言必须解释清楚的四件事"变成了准入门槛 。要接一门新语言,贡献者必须能形式化描述它的导入语义与方法解析顺序(MRO)。这解释了 GitNexus 为何只有 14 种语言------不是懒,是这道门槛把长尾语言挡在外面了。这是一个深度优先于广度 的显式取舍,与 Graphify 的广度优先恰成镜像。

CodeGraph 处在第三个位置:Rust kernel 把 20 种语言的语法编译进二进制,其余走可移植引擎回退(事实库 §4.3、Phase 1 §3.4)。加语言要么改 Rust kernel 并重新编译(高成本、高性能),要么走 WASM 回退(低成本、有边界开销)。本报告注记 :README 称 Rust kernel,package.json 却把 web-tree-sitter 列为运行依赖;二者的运行时分工未从源码验证,属事实库 §5 第 6 项空洞,本章不做推测。可确认的旁证是 GitHub /languages 显示该仓库 C 占 65.2 MB 绝对主导、Rust 仅 0.95 MB(事实库 §4.8)------C 的体量与 vendored tree-sitter 语法(tree-sitter 生成的 parser 是 C 代码)的规模相符。

2.4.3 输入通道的宽窄,是本层的第二个分野

Graphify 的模块列表里有一批在另外两家完全找不到对应物的文件:scip_ingest.pypg_introspect.pycargo_introspect.pymanifest_ingest.pymcp_ingest.pytranscribe.pygoogle_workspace.pyingest.pywiki.py

单从文件名可以确认的事实是:Graphify 的解析层不是一个"源码解析层",而是一个"多源事实摄取层" ------tree-sitter 只是其中一条通道,旁边并排着 SCIP 索引摄取(Sourcegraph SCIP 协议 是取代 LSIF 的跨语言代码索引标准)、PostgreSQL schema 内省、Cargo 元数据内省、包清单摄取、MCP 配置摄取、音视频转录、Google Workspace 接入。

需要克制的地方 :本报告未取得 scip_ingest.py 等模块的源码,其实现完整度与实际启用条件未公开 。仅凭文件名不能断言"Graphify 支持完整的 SCIP 导入"。此处如实记为存在该代码路径,实现范围未验证

即便如此,这个文件名清单与另外两家的对照仍构成一条硬结论:在 L2 层,Graphify 与 GitNexus/CodeGraph 解决的根本不是同一个问题。 后两者在解"如何把源码解析得更准",Graphify 在解"如何把一个工程组织里的所有知识载体都塞进同一张图"。这一分歧会在 §2.9 收束为三种世界观的第一条证据。

2.5 L3 图模型层:本章的核心

2.5.1 三家为什么都选了属性图而不是 RDF

一个值得注意的事实:三个独立团队、三种实现语言、三条存储路线,在图模型的形式化选择上完全一致 ------都采用带标签的属性图(Labeled Property Graph, LPG),无一采用 RDF 三元组(W3C RDF)。

  • Graphify:NetworkX 图 + 节点/边属性字典,导出目标为 Neo4j / FalkorDB / GraphML(均为属性图生态);README 明言 "Not a vector index... a real graph you traverse"(事实库 §2.1)。
  • GitNexus:每类型一张节点表 + 单张 CodeRelation 表,查询语言为 Cypher(openCypher,属性图查询语言)。
  • CodeGraph:nodes / edges 表,边带 metadata(JSON)与 provenance 属性(schema.sql)。

这不是巧合。本报告的论证有三条:

其一,代码实体天然携带大量非关系型属性,RDF 的三元组粒度会造成"属性爆炸"。 一个函数节点在 CodeGraph 的 schema 里有 20 个字段:kind, name, qualified_name, file_path, language, start_line, end_line, start_column, end_column, docstring, signature, visibility, is_exported, is_async, is_static, is_abstract, decorators, type_parameters, return_type, updated_at(原文见 CodeGraph src/db/schema.sql)。在 LPG 中这是一行;在 RDF 中这是 20 个三元组,外加一个节点 URI。以 CodeGraph 自报的 Linux 内核 200 万符号计(厂商自报,未经独立验证,事实库 §4.6),LPG 是 200 万行,RDF 是 4000 万条三元组。存储与查询代价相差一个数量级,而换来的语义互操作性在"单仓库代码分析"这个封闭域里几乎无用武之地。

其二,代码图不需要全局 URI,因为它的身份空间是封闭且本地的。 RDF 的核心价值是全局唯一标识符带来的跨数据集合并能力。但一张代码图的节点身份只在"本仓库 + 本次索引"内有意义------src/auth.py::login 这个符号在另一个仓库里指的是完全不同的东西。三家的 ID 策略都印证了这一点:Graphify 用 ids.make_id() 从名称片段拼装本地 ID(详见 §2.5.5),CodeGraph 用 nodes.id TEXT PRIMARY KEY 的本地字符串主键,GitNexus 用每类型节点表的本地主键。没有任何一家试图铸造全局可解引用的 URI,因为那个成本换不来收益。

其三,查询模式是"局部游走"而非"全局模式匹配",这正是 LPG 的强项。 代码图上的典型查询是"从这个函数出发,谁调用了它,往上三跳"------这是有起点的邻域遍历。LPG 的邻接表(Neo4j 的 index-free adjacency、SQLite 上的 idx_edges_source_kind 索引、NetworkX 的 _adj 字典)对这类查询是 O(度数)。而 RDF 的原生查询范式 SPARQL 是全局三元组模式匹配,为局部遍历付出的是全表连接的代价。三家不约而同选择 LPG,本质上是被查询局部性(query locality)这一工作负载特征逼出来的共识

值得记录的推论:第一章已经论证了三家在 tree-sitter、MCP、内容寻址缓存上的范式收敛(见共享背景 §四)。LPG 应当被追加为第六条收敛项------而且它是收敛度最高的一条,因为它连"要不要考虑 RDF"这个问题都没有出现在任何一家的文档里。这从侧面说明:代码知识图谱这个品类的形式化基础已经稳定,竞争不在"图怎么建模",而在"图里放什么、怎么标注不确定性"。而后者,正是三家分歧最大的地方。

2.5.2 类型体系对照:三种"图有多丰富"的答案
项目 Graphify GitNexus CodeGraph
节点类型定义位置 无显式类型表;节点由 label 字段自由标注 lbug/schema.ts每类型一张物理表 nodes.kind 字段(单表,类型为运行时字符串)
节点类型数 未枚举(归纳自文档:概念/文件/包/注释/文档/PDF/媒体/社区/PR 等) 文档称 44,ARCHITECTURE 实列 28--31 (含 Embedding--pdg 另加 BasicBlock 未枚举(README 归纳:函数/类/方法/路由/组件/属性/文件)
边类型定义位置 无枚举;relation 为自由字符串 CodeRelation.type 字段,实列 28--pdg 另加 6 种) edges.kind 字段,自由字符串
物理布局 内存邻接字典 + JSON node-link 多节点表 + 单关系表 单节点表 + 单边表 + FTS5 外部内容表
schema 版本管理 未声明 schema_versions 表 + src/db/migrations.ts
类型检查时机 构建期:validate.py 强制校验 建表期:物理表结构即约束 无:kind 为无约束 TEXT

三种布局各自的下游后果(本推导):

GitNexus 的"多节点表 + 单关系表"是三者中最重的,也是最快的。 每类型一张物理表意味着 MATCH (f:Function) 这样的查询直接命中一张表,不需要在混合表上过滤类型;这是列式图库的标准优化。代价是新增一个节点类型 = 一次 DDL 迁移------schema 是物理的,不是逻辑的。这解释了为什么 GitNexus 的文档里找不到任何用户自定义 schema 扩展机制(事实库 §3.1):在这个布局下,扩展 schema 不是配置问题,是数据库迁移问题。

也正因此,文档中"44 node types / 21 relationship types"与实际列出的 28--31 节点类型 / 28 关系类型之间的表述不一致 (事实库 §3.1 已记录)值得注意。本报告判断 :这类不一致在多节点表布局下比在单表布局下更危险,因为文档数字与物理表数量脱钩后,读者无法据文档判断某个类型是否真的存在。本报告统一ARCHITECTURE.md 实列为准,并如实标注文档自相矛盾。

CodeGraph 的"单节点表 + 单边表"是三者中最轻的。 加一个节点类型只需要在 nodes.kind 里写一个新字符串------零 DDL,零迁移。代价是查询时必须过滤 kind,因此 schema.sql 里配了 idx_nodes_kindidx_edges_kindidx_edges_source_kindidx_edges_target_kind 四个类型索引来补偿(原文见 CodeGraph src/db/schema.sql)。这是典型的用索引换 schema 灵活性

单边表布局还带来一个精巧的设计:唯一索引 idx_edges_identity(source, target, kind, IFNULL(line,-1), IFNULL(col,-1)) (原文,注释标注 issue #1034)。这是三家中唯一在存储层强制边去重 的机制,而且去重键包含行列位置------意味着同一对节点之间、同一 kind 的边,只要出现在源码的不同行列,就是两条独立的边。这在形式上使 CodeGraph 的图成为一个真正的多重图(multigraph),且多重性由源码位置定义。这一点在下一小节与 Graphify 形成尖锐对比。

Graphify 的"无类型表"是三者中最松的,但被 validate.py 反向收紧。 这是本章一处必须讲清的结构性反差,留到 §2.5.3 展开。

2.5.3 schema 刚性谱系:谁最刚,代价各是什么

把三者放到一条从"物理刚性"到"运行时自由"的谱系上:

复制代码
物理刚性 ◄──────────────────────────────────────────────► 运行时自由

  GitNexus              Graphify               CodeGraph
  ────────              ────────               ─────────
 每类型一张物理表      节点/边类型自由,      单表 + kind 字符串
 加类型 = DDL 迁移     但外层信封被
                       validate.py 强校验     加类型 = 零成本
 类型即表结构                                 类型无任何约束
                       "内松外紧"
                                              schema_versions +
                                              migrations.ts 兜底

Graphify 的位置最需要解释,因为它同时是"最松"和"最刚"的。ARCHITECTURE.md 给出了 validate.py 强制的抽取信封(原文引自 Graphify ARCHITECTURE.md(v8)):

json 复制代码
{
  "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"}
  ]
}

并注明:"validate.py enforces this schema before build_graph() consumes it."

结构上这是一个**"内松外紧"**的设计:

  • 外紧 :每一个 extractor(无论是 tree-sitter 的、LLM 的、还是 SCIP 摄取的)都必须交出这五个字段的节点与四个字段的边,一个不能少,格式错了直接抛异常。这道闸门是 Graphify 能同时容纳"确定性 AST 抽取"和"LLM 语义抽取"两类完全异质的生产者的唯一原因------LLM 的输出天然不可靠,没有这道强校验,图会被污染。
  • 内松label 是自由文本,relation 是自由字符串("calls|imports|uses|..." 中的省略号是文档原文),没有任何枚举约束。

下游后果(本报告推导) :这个设计让 Graphify 的扩展路径不是"改 schema",而是"加生产者" ------事实库 §2.1 已归纳出这一点,源码结构也印证了(extractors/ 目录 + 一批 *_ingest.py)。它的代价是:图中实际存在哪些 relation 值,只有跑完才知道。没有任何静态清单能回答"这张图里有多少种边"。对下游消费者(尤其是需要写查询的 Agent)而言,这是一个可发现性(discoverability)成本。相比之下,GitNexus 的 28 种关系类型是可以直接印在文档里给 Agent 看的。

CodeGraph 处在第三种状态:类型完全自由,但它是三者中唯一显式管理 schema 版本的 ------schema_versions 表(version PK, applied_at, description)加上 src/db/migrations.ts(8,008 字节,基于 src/db/ 目录列表 的推断)。本报告判断 :这说明 CodeGraph 的团队预期 schema 会长期演进,且预期用户的旧索引需要被就地升级而不是重建。这与 GitNexus/Graphify 隐含的"重新索引即可"假设不同,是一个面向长生命周期常驻索引的设计信号,与 §2.8 的 daemon 形态一脉相承。

2.5.4 一处源码级发现:Graphify 的图目前不是多重图

本章定向抓取 graphify/build.pygraphify/multigraph_compat.py 后,得到一条在任何 README 对比表里都看不到、但直接决定 D1(图模型表达力)的事实。

事实一:build.py 实例化的是简单图,不是多重图。 原文(Graphify graphify/build.py(v8),抓取 2026-08-03):

python 复制代码
G: nx.Graph = nx.DiGraph() if directed else nx.Graph()

即:directed=True 时为 nx.DiGraph,默认 directed=False 时为 nx.Graph(无向)。代码中未使用 nx.MultiGraphnx.MultiDiGraph

事实二:多重图能力目前只有探针,尚未接线。 multigraph_compat.py 的模块 docstring(Graphify graphify/multigraph_compat.py(v8))原文:

"Runtime compatibility probe for Graphify MultiDiGraph mode. Verifies that the current NetworkX runtime supports the behaviors a future opt-in --multigraph build will rely on. ... No call sites added yet; downstream multigraph PRs will gate on require_multigraph_capabilities() before enabling MDG mode."

其错误信息中亦写明:"Default simple graph mode remains available."

事实三:重复边被显式折叠。 build.py 中的 dedupe_edges() 原文 docstring:

"Collapse exact parallel edges by (source, target, relation), keeping the first occurrence."

且在无向模式下,同关系的反向重复边(a→b 与 b→a)会被直接丢弃:

python 复制代码
if not G.is_directed() and G.has_edge(src, tgt):
    existing = edge_data(G, src, tgt)
    if existing.get("relation") == attrs.get("relation") and (
        existing.get("_src") == tgt and existing.get("_tgt") == src
    ):
        continue

这三条事实合起来意味着什么(本报告推导):

在当前 v8 分支的默认配置下,Graphify 的图中任意一对节点之间只能存在一条边 。若 Acalls Bimports B,NetworkX 的 add_edge() 语义是后写覆盖属性------两种关系无法共存于一条边上,只会保留其中一种。同理,A 在三个不同位置调用 B,在图上仍只是一条 calls 边,调用点的多重性丢失。

这与 CodeGraph 形成尖锐对比:后者的唯一索引把 (source, target, kind, line, col) 作为边身份,同一对节点之间的多次调用是多条边,且各自带行列位置 。GitNexus 的 CodeRelation 单关系表同样天然支持多重边(同一对节点可有多行)。

因此在 D1(图模型表达力)这一维上可以确立一条判据:CodeGraph 与 GitNexus 的图是多重图,Graphify 的默认图是简单图。对于"这个函数在哪几行调用了那个函数""这两个模块之间有几种耦合关系"这类查询,Graphify 的默认图在结构上无法回答。

必须同时给出的限定条件(避免过度解读):

  1. --multigraph 已在路线上,探针代码已就位(_build_probe_graph()key="calls:a.py:L1" 这样的键构造并行边),说明团队已识别该限制并在推进。本报告陈述的是当前 v8 快照的状态,非永久性质。
  2. dedupe_edges() 的去重键包含 relation,说明不同 relation 的边在进入图之前是被保留的,丢失发生在 NetworkX 层而非去重层。
  3. 本报告未取得 export.py 源码,无法确认 graph.json 序列化时是否有额外的边合并/展开逻辑。此点标注为未验证
2.5.5 节点身份:ids.py 揭示的保真度代价

图模型的另一半是身份------两个抽取结果何时指向同一个节点。三家的处理方式差异同样显著,而 Graphify 这一侧有源码级证据。

graphify/ids.py 的模块 docstring 原文(Graphify graphify/ids.py(v8),抓取 2026-08-03):

"Single source of truth for node-ID normalization. Three independent producers must agree on node IDs or the graph splits a single entity into disconnected ghost nodes : 1. The AST extractor (extract._make_id) ... 2. The semantic subagents (LLM) --- follow the node-ID spec in the skill prompt. 3. The graph builder (build._normalize_id) ..."

"Historically the normalization recipe was copy-pasted ... which is exactly how the recurring ID-drift bug class crept in (#811 Unicode collapse, #550 same-filename collisions, #1033 AST-vs-LLM file-node mismatch, #1104)."

规范化配方(原文函数体):

python 复制代码
def normalize_id(s: str) -> str:
    s = unicodedata.normalize("NFKC", s)
    s = re.sub(r"[^\w]+", "_", s, flags=re.UNICODE)
    s = re.sub(r"_+", "_", s)
    return s.strip("_").casefold()

这段十行代码承载了 Graphify 全部的实体消解逻辑,其工程后果值得逐条拆解(以下为本报告推导):

其一,casefold() 使节点 ID 大小写不敏感。 在 Go 中,标识符首字母大小写决定导出性(Config 是导出的,config 不是),二者是语义上不同的实体 ;在 C、C++、Java、Rust 中大小写同样敏感。经 casefold() 后,Configconfig 折叠为同一个节点 ID。这是一类静默的实体合并------不会报错,只会让图上少一个节点、多一堆本不该存在的边。

其二,re.sub(r"[^\w]+", "_") 使所有非单词字符等价。 a.ba-ba/ba::ba b 全部规范化为 a_b。这在跨语言场景下风险更高:C++ 的 ns::fn 与 Python 的 ns.fn 会撞成同一个 ID。

其三,团队完全知道这个问题,并且为此付出了持续代价。 docstring 里点名的四个 issue 编号(#811 Unicode 折叠、#550 同名文件碰撞、#1033 AST 与 LLM 的文件节点不匹配、#1104)是该 bug 类反复复发的直接证据。把配方收敛到单一模块,是止血而非治本------它保证三个生产者用同一套规则,但没有改变规则本身的有损性。

其四,"三个独立生产者"这个前提本身是 Graphify 架构的必然产物。 因为它同时接纳 AST 抽取与 LLM 语义抽取(这是它多模态能力的来源,见 §2.4.3),所以必须让一个概率性生产者(LLM)与一个确定性生产者(tree-sitter)在同一个 ID 空间里对齐。这是"内容广度"这一优势的直接代价,其成本以 ID 漂移 bug 的形式出现在 issue 列表里。

对照另外两家:

  • CodeGraphnodes.id TEXT PRIMARY KEYqualified_name 字段,并在索引层同时建了 idx_nodes_nameidx_nodes_qualified_nameidx_nodes_lower_name(原文见 CodeGraph src/db/schema.sql)。idx_nodes_lower_name 的存在是一个精确的设计信号 :CodeGraph 把大小写不敏感当作查询期的一个可选视图 (一个额外索引),而不是存储期的强制折叠 。ID 本身保持原样、区分大小写;想做不敏感匹配时走那个索引。这在保真度上严格优于 Graphify 的方案,且成本只是一个索引。
  • GitNexus 的身份消解发生在 Scope-Resolution Pipeline 中,通过 registry 查找 + 三层导入解析完成,并附带置信度(下节)。它不做字符串规范化式的折叠,而是做作用域解析 ------两个同名符号是否是同一个,由作用域链决定,不由字符串形状决定。这是三者中语义上最正确的做法,也最贵(需要 Language Provider 提供导入语义与 MRO 策略)。

D2(抽取保真度与可溯源性)的第一条判据由此确立 :身份消解策略的正确性排序为 GitNexus(作用域解析)> CodeGraph(原样保存 + 可选不敏感索引)> Graphify(有损字符串规范化),而其实现成本排序恰好相反。

2.5.6 provenance 与置信度:三种承认"我可能错了"的方式

这是三家分歧最精彩、也最有理论价值的一处。共同前提 :静态分析无法完全消解动态语言的调用关系、反射、依赖注入、框架约定入口。三家都承认了这一点------这正是共享背景 §四把"置信度标记边"列为范式收敛项的原因。但承认的方式完全不同,而方式的差异直接决定了下游消费者能做什么。

Graphify GitNexus CodeGraph
机制类型 定性三态标签 定量置信度分数 属性透传 + 合成来源标记
取值 EXTRACTED / INFERRED / AMBIGUOUS 0.95(同文件)/ 0.9(import 作用域)/ 0.5(全局) provenance 字段(如 'heuristic')+ metadata.synthesizedBy(如 swift-objc-bridge
语义承载 边的认识论地位(显式陈述 / 合理推断 / 存疑) 解析路径的先验可靠度 边的生成者身份
落点 每条边的 confidence 字段(schema 必填) 导入解析阶段的产出 edges.provenance 列 + idx_edges_provenance 索引
人类介入设计 AMBIGUOUS 边被汇总进 GRAPH_REPORT.md 供人工复核 未声明 未声明专门的人工复核通道
可组合性 ❌ 三态不可加权、不可排序运算 ✅ 可乘、可阈值、可排序 ⚠️ 字符串标签,可分组不可运算

(据事实库 §2.1、§3.3、§4.1、§4.2;Graphify 三态定义原文见 ARCHITECTURE.md v8。)

Graphify:定性标签 ------ 面向人的认识论标注

ARCHITECTURE.md 的定义表原文:

Label Meaning
EXTRACTED "Relationship is explicitly stated in the source (e.g., an import statement, a direct call)"
INFERRED "Relationship is a reasonable deduction (e.g., call-graph second pass, co-occurrence in context)"
AMBIGUOUS "Relationship is uncertain; flagged for human review in GRAPH_REPORT.md"

三个标签划的不是"多大概率对",而是**"这条边的知识来源是什么类型":源码显式陈述、系统推断、系统承认不确定。这是一个认识论分类**而非概率估计。

它的强项是语义清晰、不可伪造。"这条边是 import 语句写着的"和"这条边是我们第二趟推的"是两个性质不同的断言,用 0.95 和 0.9 表达反而会丢失这个区别------0.95 与 0.9 之间的 0.05 差距暗示了一种可比性,而"源码写了"与"系统猜的"之间并不存在这种连续性。

build.py 中有一处细节强化了这一点。当边缺失 confidence 枚举但带有遗留的 confidence_score 浮点数时,代码注释原文写道:

"A confidence_score float with no confidence enum backfills confidence: \"INFERRED\" --- never EXTRACTED (alias recovery is not provenance) and never a threshold mapping of the float."

这行注释是本节最有价值的一手材料 :Graphify 明确拒绝把浮点分数映射回三态标签,理由是"别名恢复不是溯源"。本报告判断 :这表明团队把 provenance 视为一个不可从数值反推的一等语义,而非置信度的粗粒度离散化。这是一个思想上相当清醒的立场。

它的弱项同样明显:三态不可运算 。一条路径由 2 条 EXTRACTED 边加 1 条 INFERRED 边组成时,整条路径的可靠度是多少?定性体系给不出答案,只能给出"这条路径包含推断边"这样的定性提示。对需要排序候选答案的 Agent 来说,这是一个真实的能力缺口。

GitNexus:定量分数 ------ 面向机器的排序信号

三层导入解析的 0.95 / 0.9 / 0.5按解析路径赋予的先验可靠度:同文件内解析最可靠(0.95),import 作用域内次之(0.9),退化到全局名称匹配时最不可靠(0.5)。

这套设计的强项是可组合 :路径可乘、候选可排序、结果可按阈值过滤。配合 GitNexus 的混合检索(BM25 + semantic + RRF,K=60,事实库 §3.2),置信度天然可以进入排序函数。这与 GitNexus 的整体设计目标高度一致------它有 17 个 MCP 工具、有 Cypher、有向量,是三者中最"给机器用"的系统,那么置信度是数值就顺理成章。

它的弱项是语义被压平 。0.5 这个数字同时可能表示"三个同名候选选了一个"和"完全没解析出来给了个默认值"------数值抹掉了不确定性的类型 。而且 0.95/0.9/0.5 这三个值是人为标定的先验常数,不是从数据中学出的后验概率 ,把它们当概率相乘在数学上是没有依据的。本报告判断:这是一个"看起来可运算、实际上语义模糊"的设计,其可组合性优势部分是表面的。

CodeGraph:属性透传 ------ 面向溯源审计的生成者标记

CodeGraph 既不给标签也不给分数,而是在边上记录这条边是谁造出来的 。跨语言桥接合成的边带 provenance: 'heuristic'metadata.synthesizedBy(稳定 channel 名,如 swift-objc-bridge、React Native Codegen spec),见事实库 §4.1。

这个方案有两个被低估的优点:

  1. idx_edges_provenance 索引(原文见 CodeGraph src/db/schema.sql)使 provenance 成为一等查询维度。 schema.sql 中显式建了这个索引(原文见 CodeGraph src/db/schema.sql),意味着"把所有启发式合成的边捞出来"是一次索引扫描而非全表扫。这是三者中唯一把溯源做成可高效检索维度的------Graphify 的三态存在边属性里但没有索引(NetworkX 无边属性索引),GitNexus 的置信度也未声明专门索引。
  2. channel 名是稳定的、可枚举的、可禁用的。 知道一条可疑边来自 swift-objc-bridge 这个具体的启发式,比知道它"置信度 0.5"有用得多------前者可以定位到具体代码、可以针对性关闭、可以为该 channel 单独做回归测试。

它的弱项是默认边没有可靠性信息 。只有合成边才带 provenance:'heuristic';一条普通的 calls 边并不携带"这次解析有多确定"的元数据。CodeGraph 用另一种方式补偿了这个缺口:它公布逐语言的实测跨文件覆盖率 (Python 100% ... Liquid 73.8%,厂商自报,未经独立验证,事实库 §4.6)。本报告判断 :这是把不确定性从边级 上移到了语言级------它不告诉你"这条边有多可信",而告诉你"这门语言的边整体上有多少被解析出来"。这是一种统计学意义上的诚实,但它无法回答"我手上这条边可不可信"。

三种哲学的收束

从"下游消费者如何决策"这个问题回看,三种设计各自服务于不同的消费者:

消费者 需要的信息形态 最匹配的机制
人类审阅者("这张图哪里需要我核实") 定性分类 + 待办清单 GraphifyAMBIGUOUS 汇总进 GRAPH_REPORT.md
排序型 Agent("这 5 个候选先看哪个") 可运算的标量 GitNexus:0.95/0.9/0.5 可进排序函数
审计与调试者("这条错边是哪个启发式造的") 生成者身份 + 可检索 CodeGraphprovenance + idx_edges_provenance

本报告判断 :这三者不是同一个问题的三个答案,而是三个不同问题的各自最优解 。要求任何一家覆盖另外两家的场景,都需要它在架构上做实质性增补------Graphify 要加数值维度并为边属性建索引,GitNexus 要给分数配语义类型,CodeGraph 要把 provenance 从"仅合成边"扩展到全部边。三条路都不难,但都没人做,说明三家对"谁是主要消费者"的判断是坚定且不同的。这构成 §2.9 架构反推的第二条证据。

D2 的第二条判据 :溯源机制的覆盖率 排序为 Graphify(每条边必填,schema 强制)> GitNexus(导入解析路径产出)> CodeGraph(仅合成边);溯源机制的可操作性排序为 CodeGraph(有索引、可定位到具体启发式)> GitNexus(可排序)> Graphify(仅可分类)。这两个排序恰好相反,第八章打分时不应把它们混为一维。

2.6 L4 查询层:执行引擎在哪里,决定了表达力的上界

本节不讨论查询语言语法与 MCP 工具清单(属第四章),只讨论查询在哪个引擎里跑、有哪些索引可用这一结构问题。

维度 Graphify GitNexus CodeGraph
查询执行位置 Python 进程内,NetworkX 图对象上 LadybugDB 查询引擎进程内 SQLite 查询引擎进程内
前置条件 整图必须先反序列化入内存 打开数据库连接(连接池,≤5 并发) 打开数据库连接(WAL,读不阻塞写)
图原生原语 ✅ NetworkX 全套算法(最短路、连通分量、中心性等) ✅ Cypher 变长路径、模式匹配 ⚠️ 无原生图原语;路径遍历需在 queries.ts 中用递归 SQL / 应用层循环实现
全文检索 ❌ 无(有 _minhash.py,用途为去重非检索) ✅ FTS 索引 + BM25 + 向量 + RRF(K=60) ✅ FTS5 虚拟表 nodes_fts(external content,3 触发器同步)
向量检索 ❌ 官方 description 明言 "no vector store" ✅ 独立 Embedding ❌ 仅 FTS5
索引结构 无(内存字典即索引) 每类型节点表 + FTS + 向量索引 13+ 个显式 B-tree 索引(idx_nodes_*idx_edges_*idx_unresolved_*idx_files_*
外部查询语言 ❌(导出 Neo4j 后可用 Cypher) ✅ Cypher ❌ 不暴露 SQL

三条结构性推论:

其一,Graphify 的查询延迟结构与另外两家根本不同。 另外两家是"打开连接 → 查 → 关",单次查询的成本与图规模关系不大(有索引)。Graphify 是"加载整图 → 查",第一次查询要付整图反序列化的代价 。对 CLI 一次性使用(graphify query "..." 跑完就退出),这个代价每次都要付;对 python -m graphify.serve 常驻 MCP 服务,则只付一次。这解释了 Graphify 为什么要提供 MCP server 与 --transport http 团队共享模式------它不只是集成便利,更是把加载成本摊薄的必要手段。这是存储层选型(L1)对集成层形态(L5)的跨层约束,是本章五层剖面单向约束性质的一个具体例证。

其二,CodeGraph 用关系引擎跑图查询,付的是"路径查询"的代价。 SQLite 没有变长路径原语;要回答"从 A 到 B 有哪些调用路径",只能用递归 CTE 或在应用层做 BFS。src/db/queries.ts94,743 字节目录列表)------这个体量本身就是证据:图遍历逻辑没有被引擎承担,而是被手写进了近 10 万字节的查询模块 。GitNexus 用 Cypher 一行能写的东西,CodeGraph 需要在 queries.ts 里实现。这是"用通用关系库存图"的第二笔隐性账单(第一笔是 §2.3.4 的 WAL 治理)。

其三,name_segment_vocab 表暴露了一个 Graphify / GitNexus 都没有的机制。 schema.sql 中有一张 WITHOUT ROWIDname_segment_vocab(segment, name) 表,注释用途为"供 prompt-hook 图驱门控(标识符分词 materialized)"(CodeGraph src/db/schema.sql,事实库 §4.2)。本报告推导 :这是把"用户 prompt 里出现的词是否命中图中已知标识符"做成了一次索引查表------用于在调用图查询之前 判断"这个问题值不值得查图"。这是一个纯粹为 Agent 工作流优化的存储结构,在另外两家的 schema 中没有对应物。它印证了 CodeGraph 的存储层不是通用图存储,而是为单一消费者(Agent)定制的存储

2.7 L5 集成层:图以什么形态贴到工作流上

本节只讨论结构(进程如何被拉起、hook 挂在哪、暴露面如何组织),工具清单与调用语义属第四章。

维度 Graphify GitNexus CodeGraph
MCP server 承载进程 python -m graphify.serve,独立 Python 进程 gitnexus MCP stdio,或 gitnexus serve 的 HTTP bridge Agent 自行拉起;可跨会话共享后台 daemon
传输 stdio,另支持 --transport http(团队共享) stdio + HTTP(mcp-http.ts stdio + 本地 socket(daemon 共享)
多项目寻址 每次指定图文件路径 ~/.gitnexus/registry.json 全局注册表 MCP 工具的 projectPath 参数
人类可读产物 graph.htmlGRAPH_REPORT.md、callflow HTML、SVG、Obsidian vault gitnexus-web 浏览器 UI(Vite + React 瘦客户端) ❌ 无(文档站 + CLI)
编辑器/Agent 挂载方式 graphify install 注册 skill;仓库内 16 个 skill-*.md 覆盖 20+ 平台 agent 插件目录;PostToolUse hook src/installer/ + src/upgrade/ 自管安装与升级
hook 类型 git hook(commit 时重建 + merge driver)+ PreToolUse PostToolUse(仅提示重索引 无需 hook(daemon + OS 事件)

三者的集成层反映出对"谁是用户"的三种回答

Graphify 的集成层里躺着 16 个 skill-*.md 文件skill-agents.mdskill-aider.mdskill-amp.mdskill-claw.mdskill-codex.mdskill-copilot.mdskill-devin.mdskill-droid.mdskill-kilo.mdskill-kiro.mdskill-opencode.mdskill-pi.mdskill-trae.mdskill-vscode.mdskill-windows.mdskill.md),外加 skills/always_on/ 两个目录(目录列表)。这是把"适配每一个 Agent 平台"当成一等工程任务 ------不是提供一个通用 MCP 端点让各家自己接,而是为每个平台手写一份提示词/技能定义。这一策略的可扩展性显然有限(平台数线性增长的维护负担),但它换来的是开箱即用的深度集成

GitNexus 的集成层里有一个全局注册表 ~/.gitnexus/registry.json ,用途注明为 "Global repo registry (MCP discovery)"。本报告推导 :这是三者中唯一在架构上把"多仓库"设为一等公民的机制------MCP server 通过注册表发现本机所有已索引仓库,配合 group 级工具实现跨仓查询。Graphify 有 global_graph.py(文件名提示存在全局图概念,但源码未取得,实现范围未公开 ),CodeGraph 则用 projectPath 参数按需寻址、不维护注册表。

CodeGraph 的集成层最薄,因为它把复杂度都推给了 daemon。 没有 skill 文件、没有注册表、没有人类可读产物------默认只暴露 codegraph_explore 一个工具(实定义 8 个,其余 7 个需 CODEGRAPH_MCP_TOOLS 白名单启用)和一个会自己保持新鲜的索引。这不是功能缺失,是设计选择的对偶面:当索引永远是新鲜的、当一次调用就能拿到答案时,集成层不需要那么多东西。

2.8 进程模型与运行时形态:三种"何时算图"的答案

2.8.1 三方并列
维度 Graphify GitNexus CodeGraph
基本形态 Python CLI(一次性进程)+ 可选常驻 MCP server Node CLI(一次性进程)+ 可选 serve HTTP 自包含单二进制 + 跨会话共享后台 daemon
常驻进程 graphify.serve gitnexus serve 默认有 (本地 socket 共享;CODEGRAPH_NO_DAEMON=1 可禁用)
文件变更感知 watch.py写 flag 文件,非 OS 原生事件 无 file watcher、无 daemon 原生 OS 事件(FSEvents / inotify / ReadDirectoryChangesW)
去抖 未声明 不适用 2000msCODEGRAPH_WATCH_DEBOUNCE_MS,钳制 100ms, 60s
重算触发者 用户命令 / git hook / watch flag 用户或 CI 显式命令;PostToolUse hook 仅提示 系统自动
离线编辑补偿 --update 增量重抽 lastCommit == HEAD 早退 连接时 catch-up(size, mtime) + content-hash 对账
陈旧提示 部分抽取保护(拒绝用更小结果覆盖) staleness hints(比较 indexed lastCommit 与 HEAD) 去抖窗口内的 ⚠️ 横幅,提示 agent 直接 Read
运行时依赖 Python + uv(uv tool install graphifyy Node `^22.18.0

据事实库 §2.4、§3.4、§4.4,并经 CodeGraph READMEGitNexus package.json 复核,抓取 2026-08-03。

2.8.2 Graphify 的 flag 文件:一个被低估的架构信号

ARCHITECTURE.mdwatch.py 的描述只有一行:watch(root, flag_path) --- "directory → writes flag file on change"。

这个签名说明了什么(本报告推导)watch.py 不是一个"检测到变更就重建图"的守护进程,而是一个"检测到变更就在磁盘上留个记号"的信号器。真正的重建由别的东西(CLI 的下一次调用、skill 的 PreToolUse hook)读到 flag 后触发。

这是一个面向 skill 生命周期而非面向进程生命周期的设计。Graphify 的自我定位是"a Claude Code skill backed by a Python library"(ARCHITECTURE.md 首句原文)------skill 是在 Agent 的某个 hook 点被短暂唤起的,不是常驻的。flag 文件正是跨越"两次短暂唤起"传递状态的最简手段:无需 IPC、无需进程存活、无需端口。

代价是新鲜度是"下次用到时才补"而非"实时" 。在两次 Agent 调用之间,图是陈旧的;只有当 skill 再次被唤起并看到 flag,才会重算。本报告判断:这在 D4(索引新鲜度)上明显弱于 CodeGraph,但它是一个与"skill 模型"内在一致的选择,不是疏忽。

2.8.3 GitNexus 的"无 daemon":一个需要小心解读的空缺

GitNexus 是三者中唯一完全没有自动重索引机制的:无 file watcher、无 git hook、无常驻 daemon(事实库 §3.4,ARCHITECTURE.md 明确将实时文件监听列为路线图 TODO)。PostToolUse hook 的作用被限定为"检测过期索引并提示 agent 重索引"。

不应把这解读为单纯的功能缺失,理由有三:

  1. 它与 lbug.lock 单写锁是自洽的。 如果有一个后台 watcher 在持续写库,而用户同时在终端敲 gitnexus analyze,单写锁必然造成冲突。没有 daemon,锁就永远不会成为用户可见的问题。 这是一个把并发问题在架构层消灭而非在运行时解决的选择。
  2. 它与 CI/批处理定位一致。 gitnexus analyze 是一个可以放进 CI 流水线的幂等命令;lastCommit == HEAD 早退使得重复调用几乎零成本。对"每次 CI 跑一遍、把索引作为构建产物"的用法,daemon 是多余的。
  3. 它与浏览器模式一致。 GitNexus 有一条另外两家完全没有的部署路径------零安装的浏览器端运行(gitnexus.vercel.app,代码不上传服务器,Phase 1 §2.2)。浏览器里根本不存在"文件监听"这个概念。一个必须同时支持浏览器与 CLI 的架构,很难把 daemon 设为默认前提。

但代价是真实的 :在交互式开发场景下,GitNexus 的索引新鲜度完全依赖人的纪律或 CI 的节奏。当 Agent 基于陈旧图作答时,错误是静默的------PostToolUse hook 只能"提示",不能保证 Agent 会照做。这是本章能为 D4 提供的最明确判据:三者在索引新鲜度上的差距不是参数差距,是架构范式差距(自动 vs 半自动 vs 手动)。

2.8.4 CodeGraph 的 daemon:收益、代价与逃生舱

CodeGraph 的进程模型是三者中最"重"的,README 原文给出的流程是:

复制代码
agent writes src/Widget.ts
  → watcher fires (<100ms)
  → debounce (default 2s)
  → sync; Widget.ts is in the index
  → next agent query sees it

配合连接时 catch-up(原文):"When the MCP server (re)connects, codegraph runs a fast (size, mtime) + content-hash reconciliation against the working tree before answering the first query --- so edits made while no MCP server was running (a git pull from the terminal, edits from another editor, a previous agent session that exited) get absorbed on the next session's first tool call."

这两层机制合起来,构成了一个完整的新鲜度闭环 :在线编辑走 watcher,离线编辑走 catch-up,去抖窗口内的空档走 ⚠️ 横幅提示 Agent 直接读文件。三条通路把"图可能是陈旧的"这个风险压到接近零。这是 D4 维度上三者中唯一的完备方案。

代价同样清晰,且 README 自己把它们都写出来了:

代价一:生命周期管理成为用户可见的负担。 CLI 里出现了 codegraph daemon("Manage background daemons --- pick one to stop")与 codegraph unlock("Remove a stale lock file that's blocking indexing")两个命令。一个需要用户手动 stop daemon、手动清 stale lock 的系统,其运维成本严格高于一个跑完就退出的 CLI。 这是 D8(部署与运维成本)上的直接扣分项。

代价二:跨平台/跨文件系统的脆弱性。 README 明言三处限制:(a) WSL2 下"the local socket CodeGraph uses to share one background server across sessions is unreliable",回退为进程内服务;(b) 网络共享与 WSL2 /mnt 上 WAL 可能无法启用,此时"reads can block on writes",建议把项目移到本地盘;© "the background-server lock and the SQLite index are tied to the OS that wrote them"。本报告判断:daemon + 本地 socket + SQLite 文件锁这三样东西都是操作系统强相关的,把它们叠在一起,必然在跨边界场景(WSL2、网络盘、容器挂载)上出现退化。另外两家因为不常驻,天然免疫。

代价三:内存常驻与多项目并存。 README 未公布 daemon 的内存占用(未公开 ,尝试路径为 README 全文,未见相关数字)。可确认的结构事实是:daemon 通过 projectPath 参数服务多个项目------"query any project that has a .codegraph/ index by passing projectPath --- so a monorepo where only some services are indexed, or a second repo, works in one session"。本报告推导 :这意味着一个 daemon 可能同时持有多个项目的 SQLite 连接与 watcher,其内存与文件句柄占用随打开项目数增长。而 CODEGRAPH_DIR 环境变量(用于给同一棵树的不同平台各建一份索引,如 .codegraph-win)的存在,进一步说明同一工作树上可能并存多份索引。

逃生舱是完备的CODEGRAPH_NO_DAEMON=1 可完全关掉共享服务器(每个会话独立进程),codegraph sync 可在禁用 watcher 时手动同步,codegraph uninit 可移除项目索引。本报告判断:提供逃生舱本身是成熟工程的标志------它承认 daemon 模型不是普适的,并为不适用场景保留了退路。

2.8.5 进程模型对"工作流嵌入度"的影响

把三种进程模型映射到开发者的实际体验上:

场景 Graphify GitNexus CodeGraph
改一行代码后立刻问 Agent 需 flag 被读到才更新;skill 唤起时补算 图是陈旧的,除非人工重跑 2 秒后自动新鲜
git pull 后第一次提问 git hook 已在 commit 时重建(本地提交);拉取他人提交需 --update lastCommit != HEAD,给出 hints,需人工跑 catch-up 自动吸收
切分支 需重算 增量重跑(缓存复用) watcher 捕获批量变更,去抖后一次同步
CI 中一次性生成索引 ✅ 天然适配 ✅✅ 最适配(幂等 + 早退) ⚠️ daemon 模型在 CI 中是多余的
零安装试用 需 Python + uv ✅✅ 浏览器直接跑 需装二进制
多人共享一份索引 graph.json 可 commit + union merge ⚠️ 二进制库不宜入库 ⚠️ 二进制库不宜入库;索引与写它的 OS 绑定

判断 :三种进程模型的适配场景几乎不重叠。CodeGraph 优化的是单开发者 + 单机 + 长会话交互 ;GitNexus 优化的是CI/批处理 + 跨仓库 + 零安装试用 ;Graphify 优化的是skill 生命周期内的一次性调用 + 可版本控制的图资产。任何"哪个更好"的问法在这一层都是错的,正确的问法是"你的工作流是哪一种"。

2.9 小结:三条架构路线与三种世界观

本章用一把外生的五层解剖刀(L1 存储 / L2 解析 / L3 图模型 / L4 查询 / L5 集成)加一条进程形态轴,把三个来源各异的系统切成并列剖面。到此可以把散落在各层的证据收束:三家在每一层的选择,都不是偶然的工程偏好,而是对一个隐含问题的不同回答------"代码知识图谱到底是个什么东西"。把这个问题反向追问到架构根部,得到三条清晰的决策树。

Graphify 的架构在优化"广度与可携带性"。 它的存储是单文件 JSON、解析层是多源摄取袋、图模型是内松外紧的校验信封、集成层是二十多个手写 skill 文件。这些选择共同指向一个目标:让"建图"这件事尽可能低门槛、跨平台、可版本控制、可随时迁出。flag 文件而非常驻进程、导出 Neo4j/FalkorDB 而非锁定自有格式、整图可 commit 而非二进制缓存------每一条都在压低退出成本。它在 D1(图模型表达力)上付出的代价是默认的简单图(非多重图),在 D7(规模)上付出的代价是 512 MiB 全内存模式,但它用"资产可携带"换来了另外两家给不了的自由。

GitNexus 的架构在优化"给机器用的精度与规模"。 它买了一个现成的图引擎(LadybugDB,16 GiB 单库),把查询表达力交给 Cypher,把置信度做成可运算的标量,把多仓库做成注册表一等公民,还额外铺了一条零安装的浏览器端路径。这些选择共同指向一个目标:让图成为一个"机器可查、可排序、可跨仓聚合"的数据库。它在 D8(运维成本)上比 Graphify 重(二进制库 + 单写锁),但比 CodeGraph 轻(无 daemon);在 D7 上凭借外包的存储引擎轻松覆盖大仓库。

CodeGraph 的架构在优化"新鲜度与单二进制自治"。 它把复杂度内化为 SQLite WAL 阀门、把索引做成跨会话常驻的 daemon、把存储层定制为 Agent 专用(name_segment_vocab、idx_edges_provenance),并把 schema 版本迁移当成长期演进的一等公民。这些选择共同指向一个目标:让图"永远新鲜、开箱即跑、自己照顾自己"。它在 D8 上付出最高代价(daemon 生命周期、stale lock、跨 OS 脆弱性),在 D1 上拿到最完整的多重图表达力,在 D7 上凭借自调教的 WAL 与无声明上限支持到 Linux 内核级别的自报规模。

三种世界观由此浮现。 Graphify 把代码知识图谱当作"一份可版本控制的工程资产"------人可读、可 diff、可迁出;GitNexus 把它当作"一台给机器查询的图数据库"------可排序、可跨仓、可在浏览器里跑;CodeGraph 把它当作"一个自己保持新鲜的常驻服务"------Agent 不问它新不新,它永远新。这不是优劣排序,而是三种对"图谱本质"的不同本体论判断,它们各自的内部是自洽的。

落到本章主责的评估维度:

  • D1 图模型表达力。 在"是否为多重图"这一具体判据上,CodeGraph(唯一索引强制边去重、含行列位置)与 GitNexus(单关系表天然多重边)并列最高,Graphify 默认简单图居末------但 Graphify 在 provenance 的定性标注完整性上反而最齐(每条边必填三态)。表达力的"广度"与"保真度标注"是两个正交子维度,第八章打分时不应合并为一维。
  • D7 规模上限与性能。 Graphify 的 512 MiB 硬上限 + 全内存模式决定了它的最优作用域在中小仓库(数千文件量级);GitNexus 的 16 GiB 单库与 CodeGraph 的无声明上限 + WAL 调教都面向大仓库,二者在同一量级,差异在"外包存储引擎"还是"内化存储调教"。
  • D8 部署与运维成本。 Graphify 最低(CLI + 单文件 JSON,无 daemon、无锁、可入库);GitNexus 居中(无 daemon,但二进制库 + 单写锁,CI 最适配);CodeGraph 最高(daemon 生命周期管理、stale lock 清理、跨 WSL2/网络盘退化)。这一排序与 D7 几乎完全反向:规模能力是用运维成本换来的。
相关推荐
废弃的小码农2 小时前
功能测试--Day07--Python编程基础
开发语言·python
fengci.3 小时前
Microweber CMS 未授权路径穿越漏洞(CVE-2026-65694)
android·开发语言·前端·学习·php
程序员zgh3 小时前
C++ 拷贝赋值运算符 详解
c语言·开发语言·c++
念何架构之路3 小时前
restartmanager-重启管理子系统
java·开发语言
隔窗听雨眠3 小时前
生产级Kubernetes集群部署完全指南:从kubeadm到高可用架构落地
容器·架构·kubernetes
SomeB1oody4 小时前
【RustyML入门】2.0. 经典机器学习
开发语言·后端·机器学习·rust·教程
Embedded-Xin5 小时前
Rust学习——Cargo工具
linux·学习·架构·rust·嵌入式
一个数据大开发5 小时前
Skill、Tool、SOP、MCP 文章合集
大数据·人工智能·知识图谱
0566465 小时前
Python高级——解释器执行原理与虚拟环境工程化实战
开发语言·python