graphify 实战:用 clone 与 merge-graphs 构建跨仓库知识图谱
本文围绕 graphify 的 /graphify skill 参考文档 GitHub clone and cross-repo merge 展开,讲解如何用 graphify clone 拉取远程仓库、用 graphify merge-graphs 把多个仓库或多个本地子目录的知识图谱合并成一张可查询的跨仓库图谱,并结合 cli.py 与 build.py 的源码,说明节点前缀、社区 ID 偏移、同名目录冲突等合并细节的真实实现,读完即可独立完成"多仓库 / monorepo 图谱合并 + 合并后查询"的完整工作流。
适用场景
该参考文档对应两类典型输入:
- 用户给出一个或多个
https://github.com/...URL,需要先克隆再建图; - 用户指定多个本地子目录(monorepo 或 multi-service 布局),希望把它们合并进同一张图。
两种场景最终都落在同一条路径上:先为每个代码库单独产出 graph.json,再用 merge-graphs 合并,之后所有代码库问题直接对合并图执行 graphify query——无需重新抽取,也没有体积门槛(fast path)。
Step 0:用 graphify clone 克隆 GitHub 仓库
单仓库
LOCAL_PATH=$(graphify clone <github-url> [--branch <branch>])
# 后续所有步骤都以 LOCAL_PATH 作为扫描目标
多仓库(跨仓库图谱)
# 分别克隆,各自跑完整 pipeline,再合并
graphify clone <url1> # → ~/.graphify/repos/<owner1>/<repo1>
graphify clone <url2> # → ~/.graphify/repos/<owner2>/<repo2>
# 对每个本地路径运行 /graphify,产出各自的 graph.json,然后合并:
graphify merge-graphs \
~/.graphify/repos/<owner1>/<repo1>/graphify-out/graph.json \
~/.graphify/repos/<owner2>/<repo2>/graphify-out/graph.json \
--out graphify-out/cross-repo-graph.json
默认克隆目录是 ~/.graphify/repos/<owner>/<repo>,重复运行时直接复用已有克隆。源码中 clone 子命令的完整签名是:
graphify clone <github-url> [--branch <branch>] [--out <dir>]
源码实现:浅克隆 + 缓存复用
实现位于 cli.py 的 _clone_repo,几个关键行为可以从源码直接确认:
- URL 规范化:自动补全
.git后缀,并用正则从 URL 中解析出owner和repo;不是可识别的 GitHub URL 会直接报错退出。 - 浅克隆:首次克隆执行
git clone --depth 1,指定分支时追加--branch <branch>,以控制缓存体积;分支名以-开头会被拒绝,避免被误认为选项。 - 缓存复用:目标目录已存在时,不再克隆,而是执行
git pull(带分支时为git pull origin -- <branch>),pull 失败只打警告不中断流程;不存在时执行浅克隆,失败则退出并输出 git 的 stderr。 - 自定义目录:
--out <dir>可覆盖默认的~/.graphify/repos/<owner>/<repo>位置。 - 命令最后会把本地路径打印到 stdout,所以文档里
LOCAL_PATH=$(graphify clone ...)的 shell 捕获写法成立,供后续步骤直接引用。
跨仓库合并:merge-graphs 的工作方式
merge-graphs 接收两个及以上的 graph.json 路径,可选 --out 指定输出位置,实现位于 cli.py。除了把几张图拼在一起,它还处理了若干跨仓库合并特有的"脏活",这些都是从源码注释和对应测试中确认的行为:
1. 节点 ID 加仓库前缀,并记录 repo 属性
合并时每个输入图都会经过 build.py 的 prefix_graph_for_global:
- 所有节点 ID 被改写为
repo_tag::<原ID>,边及其方向属性_src/_tgt同步重写; - 每个节点写入
repo属性(值为该图的 repo tag),这正是文档所说"每个节点携带repo属性,可按来源过滤"的实现来源; - 原 ID 保留在
local_id属性中,方便回溯;label 保持不变用于展示。
2. 同名目录冲突:repo tag 去重
若两张图各自位于同名目录下(例如 src/graphify-out 与 frontend/src/graphify-out 朴素取目录名都得到 src),前缀相同的不同节点会被 nx.compose 悄悄合并、产生虚假的跨仓库边(上游 issue #1729)。distinct_repo_tags 专门解决这个问题:标签冲突时先用"父目录_目录名"展宽(如 frontend_src),再追加序号后缀保证唯一,并打印一行 note 提示。回归测试见 test_merge_graphs_cli.py 中 test_merge_graphs_same_named_repo_dirs_do_not_collapse。
3. 社区 ID 偏移,避免跨仓库社区互相吞并
每个输入图的 community 都是从 0 开始编号的,直接携带合并会让不同仓库的同号社区在聚合视图里融成一个元节点(#3014)。合并循环按输入顺序给每个图施加递增的 community_offset:第一个图偏移为 0,之后每个图的偏移取"当前最大值 + 1"。原社区 ID 保存在节点的 local_community 属性中,见 prefix_graph_for_global。
4. 输入类型归一化、方向与超边保持
- 各仓库的
graph.json可能由不同抽取路径在不同时间写出,directed / multigraph 标志不一致会导致nx.compose崩溃(#1606);合并器把每个输入统一归一化为无向简单Graph,测试test_merge_graphs_mixed_directed_and_multigraph用 DiGraph、Graph、MultiGraph 三个混合输入验证了这一点。 - 加载时会把旧格式
edges键归一化为links(#738),并用_src/_tgt标记恢复原始边方向(#2261),写出前再还原回source/target。 - 超边(hyperedges)在 compose 前被逐图收集、统一前缀重写(成员 ID 与超边 ID 都加 repo 前缀,见 build.py),合并后重新以"取并集 + 按 ID 去重"的方式挂回,避免只有最后一个输入的超边存活(#2484/#2485),并以双槽位(
graph.hyperedges与顶层hyperedges)原子写出,保持与常规graph.json一致的形状。
5. 跨仓库共享类型的接线
合并后还会执行 cross_repo_types.py 的 link_shared_type_declarations:由于所有节点 ID 都带仓库前缀,两个仓库各自声明的同一契约类型会表现为两个互不相连的节点。该步骤对"namespace 与 name 都相同、且分属不同 repo"的类型声明之间添加 same_type_as 边——只加边、不合并节点,这样跨仓库遍历可以跨越边界,而两个可能已经发生漂移的副本仍各自保留成员与来源。模块 docstring 给出了一组实测数字:一对声明了 1440 与 262 个类型的 .NET 服务中,namespace+name 匹配只产生 7 对,全部是共享的 EventManager.Models.*Event 契约,说明该匹配相当保守。合并完成时终端会输出类似:
linked 7 type declaration(s) shared across repos
Merged 2 graphs -> N nodes, M edges
Written to: graphify-out/cross-repo-graph.json
6. 参数速查
| 命令 | 参数 | 说明 |
|---|---|---|
graphify clone <url> |
--branch <b> |
克隆指定分支;已有缓存时转为 git pull origin -- <b> |
--out <dir> |
覆盖默认克隆目录 ~/.graphify/repos/<owner>/<repo> |
|
graphify merge-graphs g1 g2 [...] |
--out <file> |
合并输出路径;不指定时默认为 graphify-out/merged-graph.json |
| 位置参数 | 至少 2 个 graph.json,否则打印用法并退出;每个输入都会做体积上限检查 |
monorepo / 多服务本地子目录的合并方式
文档特别指出一个坑:/graphify skill 流水线把所有中间产物与最终产物写到当前工作目录的 graphify-out/。如果先 cd core/ 再 cd service/ 分别跑 skill,第二次运行会直接覆盖第一次的输出(clobber 同一个目录)。
正确做法是改用 CLI 逐个子目录抽取——graphify extract 会把 graphify-out/ 放在被扫描路径内部:
graphify extract ./core/ # → ./core/graphify-out/graph.json
graphify extract ./service/ # → ./service/graphify-out/graph.json
graphify extract ./platform/# → ./platform/graphify-out/graph.json
# 按你实际配置了哪家的 API key,追加对应 backend
# --backend gemini|kimi|openai|deepseek|claude-cli
# 然后在项目根目录合并:
graphify merge-graphs \
./core/graphify-out/graph.json \
./service/graphify-out/graph.json \
./platform/graphify-out/graph.json \
--out graphify-out/graph.json
注意这条路径与"多 GitHub 仓库"路径共享同一个合并器,因此上述前缀、社区偏移、类型接线等机制同样生效;由于各子目录名字天然不同,distinct_repo_tags 取到的标签也自然可区分。
合并之后的快速路径:直接查询
一旦 graphify-out/graph.json(跨仓库合并图)存在,后续任何代码库问题都走 fast path:直接对合并图执行 graphify query,不需要重新抽取,也不受体积门槛限制。日常可用的几个动作:
- 按来源过滤:合并图每个节点带
repo属性,可据此只关注某一仓库/子目录的节点;节点上的local_id可还原该节点在原始图中的 ID。 - 跨仓库追溯:共享契约类型之间有
same_type_as边,从生产者仓库的节点可以一步跳到消费者仓库中的同型声明。 - 再合并/扩展:新增一个子仓库时,只需对其单独
graphify extract,再用merge-graphs把旧合并图与新图重新合并即可。
小结
这条参考文档给出的是 graphify 处理"不止一个代码库"场景的标准配方:clone(浅克隆 + 目录缓存 + pull 复用)负责把远程仓库落到 ~/.graphify/repos/<owner>/<repo>;对每个库独立跑 pipeline 得到各自 graph.json;merge-graphs 负责合并,并在底层完成节点 ID 前缀化(repo 属性、local_id 回溯)、repo tag 去重、社区 ID 偏移、图类型归一化、边方向恢复、超边并集重建与跨仓库共享类型接线。本地多子目录则用 graphify extract <dir>/ 规避 skill 流水线的固定输出目录冲突。合并图落盘后,graphify query 的 fast path 让所有跨仓库问题无需再抽取即可回答。相关实现与回归测试可分别参见 cli.py、build.py、cross_repo_types.py 以及 test_merge_graphs_cli.py、test_cross_repo_shared_types.py。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00