graphify GitHub 仓库克隆与跨仓库图谱合并指南:clone、逐仓 extract 与 merge-graphs 全流程实战
本篇指南讲解 graphify 处理多代码源的核心工作流:当用户抛出一个或多个 https://github.com/... 地址,或点名要合并的多个本地子目录时,如何借助 graphify clone、逐仓抽取与 graphify merge-graphs,把彼此独立的仓库/服务折叠进同一张可查询知识图谱。读完你将掌握 graphify 的克隆缓存复用机制、graphify-out/ 输出目录约定、合并时节点前缀与 repo 归属的底层逻辑,以及合并完成后跳过重复抽取直接查询的"快速路径"用法。
本文内容主体源自仓库中由 skillgen 生成的技能参考文档 github-and-merge.md(其源片段位于 tools/skillgen/fragments/references/shared/github-and-merge.md,同一内容会被物化到 agents、amp、claude、codex、kiro、vscode 等各平台技能目录下),并结合 graphify 的 CLI 源码与测试做了实现级扩充。
什么时候用到这份指南
以下两类请求出现时,就进入了"GitHub 克隆 / 跨仓库合并"场景,应加载这份流程:
- 用户传入了一个或多个 GitHub 仓库 URL(
https://github.com/...),期望针对这些外部仓库建图; - 用户点名了多个本地子目录(monorepo 的多个模块、多服务代码库等),期望合并为一张图。
无论哪种情况,最终产物都是同一个:在某个根位置生成一份 graphify-out/graph.json,让后续所有代码问题直接命中它,无需重复抽取。
先理解输出约定:graphify-out/ 与"快速路径"
graphify 的整套流水线(检测 → AST 抽取 → 语义抽取 → 合并 → 建图 → 聚类 → 落盘)会产出 graph.json 与各类辅助文件,统一存放在名为 graphify-out/ 的目录里。关键差异在于这个目录落在哪里:
- 技能(skill)流水线:把全部中间与最终产物写到当前工作目录下的
graphify-out/。因此在同一工作目录下对多个子目录分别运行技能,会互相覆盖同一个输出目录——这正是多子目录场景必须改走 CLI 的原因; - CLI 直接抽取:
graphify extract <path>默认把graphify-out/建在被扫描路径内部。源码中的约定写得非常明确:"The user-facing contract is<out>/graphify-out/",未显式传--out时输出根即目标路径本身(cli.py),因此graphify extract ./core/产出的就是./core/graphify-out/graph.json。
一旦 graphify-out/graph.json 存在,"快速路径"即刻接管:后续任何代码库问题直接以 graphify query 查询该图即可——不再重复抽取、不再受图规模门禁约束(这也是 always_on/agents-md.md 中给 Agent 的固定指引:graph.json 在则先 graphify query)。
Step 0 —— 克隆 GitHub 仓库(仅当拿到 GitHub URL 时)
单仓库克隆
LOCAL_PATH=$(graphify clone <github-url> [--branch <branch>])
# 之后所有步骤都以 LOCAL_PATH 作为目标路径
graphify clone 会做两件重要的事:把仓库克隆到本地缓存目录,并把最终路径打印到标准输出(因此可以直接用命令替换 $(...) 捕获)。如果只想复制裸命令形态,等价写法为:
graphify clone https://github.com/owner/repo
多仓库克隆(跨仓库图谱的前置)
# 逐仓克隆,每仓分别跑完整流水线,最后再合并
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>,实现位于 cli.py 的 _clone_repo:
- URL 规整:自动去掉末尾
/与.git后缀,统一补成<url>.git交给 git;只有匹配github.com[:/]<owner>/<repo>形态的 URL 才会被接受,否则报error: not a recognised GitHub URL并退出; - 重复运行复用:目标目录已存在时不再全量克隆,而是执行
git -C <dest> pull拉取最新(配合--branch时拉取指定分支)。所以对同一 URL 反复执行clone是廉价的增量更新,而非重新下载整个仓库。首次克隆使用git clone --depth 1(浅克隆,只取最新快照),兼顾速度与体积; - 分支安全校验:
--branch值若以-开头会被判定非法并中止,避免分支名被误解析为 git 选项; --out <dir>:可选自定义目标目录,跳过默认缓存位置;默认仍以~/下的缓存为准。
clone 命令的 CLI 入口在 cli.py,其用法即 graphify clone <github-url> [--branch <branch>] [--out <dir>],克隆完成后打印仓库路径(失败时非零退出)。
克隆参数速查
| 参数 | 含义 | 默认值 | 说明 |
|---|---|---|---|
<github-url> |
待克隆的 GitHub 仓库地址 | 必填 | 须匹配 github.com[:/]owner/repo,自动容忍 .git 后缀 |
--branch <b> |
指定拉取的分支 | 仓库默认分支 | 首次克隆时以 --branch 传 git;复用缓存时执行 git pull origin -- <b>;以 - 开头会报错 |
--out <dir> |
自定义克隆目标 | ~/.graphify/repos/<owner>/<repo> |
显式给出后不再写入默认缓存目录 |
多仓库 / 多子目录:先逐仓建图,再统一合并
多仓库(跨仓库图)总览
对克隆下来的每个仓库,先分别产出各自的 graph.json,再把它们喂给 merge-graphs。merge-graphs 产出的合并图里,每个节点都带有一个 repo 属性,用于标识它来自哪个源仓库,方便后续按来源过滤。
本地多子目录:避开 skill 的输出目录冲突
monorepo 或多服务布局(例如根下有 core/、service/、platform/)是最容易踩坑的场景:如第一节所述,技能流水线把输出写进当前工作目录的 graphify-out/,若对每个子目录分别跑一次技能,会反复覆写同一个输出目录。正确做法是用 CLI 直接对每个子目录抽取,让每个子目录得到自己内部的 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 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
graphify extract:无头全流水线抽取
extract 是面向 CI/脚本的"一次到位"命令,内部依次执行检测 → 代码 AST 抽取 → 文档/PDF/图片的语义抽取 → 合并 → 建图 → 聚类并写出全部产物(cli.py)。这与走 Claude Code 子代理的技能路径不同:它直接调用并行抽取实现,使用当前已配置 API Key 的后端。
其完整用法(cli.py):
graphify extract <path> [--backend gemini|kimi|claude|openai|deepseek|ollama]
[--model M] [--mode deep] [--out DIR|--output DIR]
[--google-workspace] [--no-cluster] [--no-gitignore]
[--code-only] [--no-dedup] [--max-workers N]
[--token-budget N] [--max-concurrency N]
[--api-timeout S] [--postgres DSN] [--cargo]
[--allow-partial] [--timing]
| 参数 | 作用 |
|---|---|
--backend <name> |
选择语义抽取的后端,按你已配置密钥/环境的供应商填写。graphify 的 LLM 后端注册表中至少包含 kimi、gemini、openai、deepseek、claude-cli(另处理 ollama/azure/bedrock),见 llm.py 与 llm.py;其中 claude-cli 走本机 Claude CLI,不依赖远程密钥 |
--out DIR / --output DIR |
输出目录;不传则把 graphify-out/ 建在被扫描路径内部 |
--code-only |
只跑代码 AST 通道,跳过语义抽取 |
--no-dedup |
关闭实体去重(配合增量合并与建图收缩保护) |
--no-cluster / --no-gitignore |
跳过聚类 / 不读取 gitignore |
--max-workers / --max-concurrency / --token-budget |
性能与预算旋钮,缺省用库默认值 |
--google-workspace / --postgres DSN / --cargo |
接入 Google Workspace 文档、Postgres 元数据、Cargo 工程内省等附加语料源 |
合并图谱:graphify merge-graphs 的语义与源码内幕
命令形态与默认输出
graphify merge-graphs <graph1.json> <graph2.json> [...] [--out merged.json]
--out 缺省为 graphify-out/merged-graph.json;需要至少两个输入图,否则打印用法并退出;任一输入路径不存在即报错中止(cli.py)。合并前每个输入文件都会经过 _enforce_graph_size_cap_or_exit 容量门禁校验(cli.py),与 query 等命令共用同一规模保护,避免无保护的超大图被装载。成功后会打印汇总:Merged N graphs -> X nodes, Y edges,并用原子写落盘(cli.py)。
输入兼容性:图的类型归一
逐仓生成的 graph.json 可能来自不同抽取路径、不同时刻,因此在 directed / multigraph 标志上未必一致。merge-graphs 会把每个输入归一为无向简单图(nx.Graph)再组合——混合 DiGraph / MultiGraph / MultiDiGraph 输入不再触发 NetworkX 的 compose 崩溃。归一逻辑见 cli.py,并由 tests/test_merge_graphs_cli.py 中的 test_merge_graphs_mixed_directed_and_multigraph 专门覆盖。同时,旧版本可能把边写成 edges 键(新版是 links),读取时会做键名兼容;每条边的真实方向通过 _src/_tgt 标记保留,避免无向重载丢失方向(cli.py)。
节点前缀与 repo 标签:为什么必须"打标签"
合并的核心机制在 build.py 的 prefix_graph_for_global:每个输入图的节点 ID 都会被重写为 repo_tag::<原始ID>,同时:
- 展示用 label 保持不变,便于阅读;
- 每个节点新增
local_id记录原始 ID,可以无损还原; - 每个节点被标记
repo = repo_tag,这正是文档所说"按来源过滤"的数据基础; - 边及
_src/_tgt方向标记同步重写到带前缀的新 ID; - 图外挂的超边(hyperedges)成员 ID 与超边自身 ID 也会套用同一前缀映射表,避免跨仓库合并后超边悬空或同名碰撞;
- 聚类
community整型 ID 通过community_offset平移进共享编号空间,原划分保留在local_community,防止两个仓库各自的 0 号社区在合并后被错误融合成一个元节点。
repo_tag 由 build.py 的 distinct_repo_tags 生成:它不能简单取 graphify-out/ 的父目录名——src/graphify-out 与 frontend/src/graphify-out 会撞成同一个 src,同干同名节点(两个仓库里的 app)就会被 nx.compose 静默合并,制造出根本不存在的跨运行时调用。因此算法会先把撞名标签用其上层目录加宽(如 frontend_src),再以序号后缀兜底,保证每个输入图拥有唯一前缀。
跨仓库共享类型的打通:same_type_as
前缀策略保证了节点不串仓,代价是"同一个契约类型"在两个仓库里会以两个互不相连的节点出现——而消息总线代码里,恰好这段关系才是最有价值的跳转:生产者在一个仓库引用 SyncProductUpsertToSearchEvent,消费者在另一个仓库实现 IConsumer<SyncProductUpsertToSearchEvent>。为此 cross_repo_types.py 的 link_shared_type_declarations 会为命名空间与名称都相同、且来自不同仓库的类型声明节点之间补一条 same_type_as 边(关系上下文记为 cross_repo)。要求命名空间一致是为了只匹配"真正同声明"的类型,而不是撞了短名的两个无关类——源注释记录的真实案例中,两个 .NET 服务的 1440/262 个类型经此规则只命中 7 对,全部是共享的 EventManager.Models.*Event 契约。需要强调的是该阶段只加边、不合并节点:两个仓库持有的契约副本可能已经各自漂移,保留独立节点才不会掩盖分叉。合并入口会在 compose 完成后执行该步骤并打印 linked N type declaration(s) shared across repos(cli.py)。
边与超边的完整性
每个输入图的超边会先被收集、随节点一起前缀化并去重,最后统一回挂到合并图上(避免 nx.compose 只保留最后一个输入超边列表的旧缺陷,见 cli.py);写盘时 hyperedges 同时写入 node_link_data 嵌套的 graph 槽与顶层槽,确保新旧读取方一致。
合并之后的"快速路径":直接查询
一旦根位置的 graphify-out/graph.json 就位(无论来自单仓库抽取还是上面的多图合并),后续代码问题就走快速路径——直接 graphify query 查询合并图,不再重新抽取、不再受规模门禁约束。技能侧的具体做法(如 references/query.md 所述)是:先检查 graphify-out/graph.json 是否存在,存在则直接读取并跑图遍历,而不是先跑一遍抽取。
由于合并图中的每个节点都带 repo 归属,跨仓库查询天然可以回答"这个符号分别在哪些仓库里出现""两个服务共享了哪些契约类型""一次跨仓库调用链长什么样"这类单一仓库图答不了的问题。
配套验证与延伸阅读
- CLI 入口与实现:graphify/cli.py(
_clone_repo)、graphify/cli.py(merge-graphs)、graphify/cli.py(extract与输出目录约定); - 合并语义核心:graphify/build.py(前缀化)、graphify/build.py(去重 repo 标签)、graphify/cross_repo_types.py(跨仓库共享类型);
- LLM 后端清单:graphify/llm.py;
- 回归测试:tests/test_merge_graphs_cli.py(混合有向/多重图归一)、tests/test_cross_repo_shared_types.py(跨仓库共享类型加边)、tests/test_merge_graphs_cli.py 其余用例覆盖前缀冲突与节点存活等行为;
- 技能参考的物化产物:graphify/skills/agents/references/github-and-merge.md,同一份参考内容也出现在 amp、claude、codex、copilot、kiro、vscode、windows 等平台的
graphify/skills/<平台>/references/目录中;源片段位于 tools/skillgen/fragments/references/shared/github-and-merge.md,由 tools/skillgen/gen.py 依据 tools/skillgen/platforms.toml 组装分发; - Agent 侧的图谱使用约定:graphify/always_on/agents-md.md。
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 StartedRust0627
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