首页
/ graphify 从 GitHub URL 到跨仓库知识图谱:clone、extract 与 merge-graphs 完整实战指南

graphify 从 GitHub URL 到跨仓库知识图谱:clone、extract 与 merge-graphs 完整实战指南

2026-09-07 10:35:48作者:殷蕙予

本指南对应 graphify 技能参考文档中的核心工作流之一。当你把一或多个 https://github.com/... 的仓库链接丢给 graphify,或想把本地多个子目录(monorepo/多服务布局)合并成一张可查询的图谱时,本指南就是操作手册:先讲透 graphify clone 的克隆语义与缓存复用,再分别演示「单仓库图谱」「多仓库跨仓库图谱」「多本地子目录图谱」三条生产级路径,最后结合仓库源码解释合并图为何会带 repo 属性、节点 ID 如何被改写,以及合并完成后如何用快速路径直接 graphify query。读完你就能把任意多个代码库的 graph.json 合并为一张带仓库来源标签、可过滤可查询的统一图谱。

在 graphify 的 8 份按需加载参考文档中,github-and-merge.md 是唯一一份专门处理「输入来源」的参考:它决定了一张图的原材料从哪里来、以什么目录结构落地、又在何时被合并成更大的统一图。该参考是全部平台的共享片段,经 skillgen 渲染后逐平台发布,例如本文分析的 Windows 平台成品 与它的单一事实来源片段 fragments/references/shared/github-and-merge.md。graphify 每次把代码库(连同文档、SQL schema、配置与 PDF)解析为可查询知识图谱后,graph.json 就是后续一切查询的入口——而如何让多份 graph.json 各归其位、最后合成一张,正是本文主题。

Step 0:从 GitHub URL 克隆仓库(仅在收到 URL 时执行)

当用户只给了代码库的本地路径时,本步骤整段跳过;只有用户明确传入了 https://github.com/... 形式的 URL,才需要先把它落到本地磁盘。clone 子命令的定位很明确——看 graphify/main.py 的帮助文案,它被描述为 "clone a GitHub repo locally and print its path for /graphify":克隆完成后把本地路径打到标准输出,供后续所有步骤当作目标路径使用。

单仓库:一行拿到可扫描的本地路径

LOCAL_PATH=$(graphify clone <github-url> [--branch <branch>])
# Use LOCAL_PATH as the target for all subsequent steps

关键点:

  • 命令替换graphify clone 会把克隆落地路径打印到 stdout,所以用 $(...) 捕获后,LOCAL_PATH 就是后续 /graphifygraphify extract 的目标;
  • --branch <branch>:可选,指定要检出的分支;不传则用仓库默认分支;
  • 完整用法还支持 [--out <dir>],即自定义克隆目录,见下节。

仓库落地约定:为什么重复运行不会重复克隆

graphify 默认把仓库克隆进 ~/.graphify/repos/<owner>/<repo>,且重复运行同一 URL 时复用已有克隆。这一点有明确的源码实现依据:查看 graphify/cli.pyclone 的底层实现,其 docstring 写明 "Clones into ~/.graphify/repos/<owner>/<repo> by default so repeated runs on the same URL reuse the existing clone (git pull instead of clone)"——也就是说首次运行执行真正的 git clone,后续运行走增量更新逻辑,而不是把整个仓库再拉一遍。底层命令形态如下(cli.py 中实际构造的 git 命令):

git clone --depth 1 [--branch <branch>] -- <git_url> <dest>

其中 --depth 1 表示浅克隆(只取最近一次提交历史),这对建立知识图谱而言足够——graphify 分析的是代码结构与语义,不需要完整 git 历史。

落地目录规律<owner><repo> 取自 GitHub URL,因此两个不同 owner 下同名仓库、或同一 owner 下不同仓库,天然落在互不冲突的目录里,不会互相覆盖。

多仓库(跨仓库图谱):先各自建图,再 merge

跨仓库图谱的标准姿势是「分治 → 合并」三段式:逐个克隆 → 对每个本地路径跑完整管线产出各自的 graph.json → 用 merge-graphs 合并。参考文档给出的完整流程:

# Clone each repo, run the full pipeline on each, then merge
graphify clone <url1>   # → ~/.graphify/repos/<owner1>/<repo1>
graphify clone <url2>   # → ~/.graphify/repos/<owner2>/<repo2>
# Run /graphify on each local path to produce their graph.json files
# Then merge:
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 clone <url1> 注释里标注的目标路径——clone 输出的是克隆根目录(如 ~/.graphify/repos/<owner1>/<repo1>),而真正喂给 merge-graphs 的是每个克隆内部的 graphify-out/graph.json。也就是说,每个仓库需要先经历完整抽取管线(AST 解析 + 语义标注 + 建图),才会产生可供合并的图文件。

merge-graphs 的 CLI 约束也很直接,在 graphify/cli.py 的对应分支实现中:必须提供 2 个及以上输入图路径,输出路径通过 --out 指定,默认值是 graphify-out/merged-graph.json_GRAPHIFY_OUT 目录下的 merged-graph.json)。换句话说,--out 是可选的,不写也有安全的默认落点;写成 graphify-out/cross-repo-graph.json 只是为产物起一个语义更清晰的名字。

多本地子目录(monorepo / 多服务布局):绕过输出目录冲突

当用户不是给 GitHub URL,而是点名了几个本地子文件夹要合并成一张图时,参考文档给出了一个极其重要、容易踩坑的背景知识:

技能管线会把所有中间产物与最终产物写到当前工作目录的 graphify-out/。如果对每个子文件夹分别跑一遍技能,它们会互相覆盖同一个输出目录

也就是说,若在仓库根目录跑 /graphify ./core、再跑 /graphify ./service,两次都写根目录的 graphify-out/,前一次结果会被后一次冲掉。这正是文档建议「绕过技能,直接用 CLI」的原因——CLI 的 graphify extract <path> 会把 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
# Add --backend gemini|kimi|openai|deepseek|claude-cli depending on which API key you have set

# Then merge at the project root:
graphify merge-graphs \
  ./core/graphify-out/graph.json \
  ./service/graphify-out/graph.json \
  ./platform/graphify-out/graph.json \
  --out graphify-out/graph.json

要点拆解:

  • 输出就地隔离./core/ 的产物落在 ./core/graphify-out/graph.json./service/ 的落在 ./service/graphify-out/graph.json……彼此零冲突,这是多个 extract 能并行/串行各自安全完成的前提;
  • 合并发生在项目根:三个子图就绪后,在项目根目录执行 merge-graphs,把三份图合成 graphify-out/graph.json——此时根目录这份才是后续统一查询的总图;
  • extract 是无头完整抽取:看 graphify/main.pyextract 的说明,它是 "headless full extraction (AST + semantic LLM) for CI/scripts",即不依赖交互式技能、适合脚本与 CI 的完整抽取入口。它和技能管线的区别只是输入输出位置的组织方式,抽取深度一致。

--backend:语义阶段用哪个 LLM

extract 默认会自动探测("default: whichever API key is set"),但显式指定更可控。文档示例注释中给出的选择是 gemini|kimi|openai|deepseek|claude-cli——核心依据是你现在配了哪家 API key。结合 CLI 帮助文本(graphify/main.py),extract --backend 实际可接受的名字集合还包括 claudeollama(其中 openai 后端还可以通过 OPENAI_BASE_URL 指向 llama.cpp、vLLM、LM Studio 等自托管 OpenAI 兼容服务)。实践建议:结构抽取(AST 部分)始终本地免费执行,--backend 只决定文档/论文/图片等内容的语义标注由哪家模型完成,按你环境里实际生效的 key 选即可。

合并图的底层语义:repo 属性、ID 前缀与来源过滤

参考文档承诺了一个关键行为,而它由源码逐条兑现:

Each node in the merged graph carries a repo attribute so you can filter by origin.

这意味着合并后你随时可以回答「这条依赖关系属于哪个仓库」这类溯源问题。真实实现比「加一个属性」更细致,可拆成三层来看:

第一层:合并前先给每个输入图打上唯一的 repo 标签。graphify/build.py 中,merge-graphs 会为每个输入图计算一个 "unique, human-meaningful repo tag"。这里还有个隐蔽的坑:不能直接用 graphify-out 的父目录名当标签——src/graphify-outfrontend/src/graphify-out 的父目录同名时标签就撞车了,因此有专门的 distinct_repo_tags 逻辑保证每个输入图拿到互不相同的标签。

第二层:节点 ID 全量加前缀。 同一个函数在 graphify/build.py 里把每个节点的 ID 改写成 <repo_tag>::<原ID> 形式,同时:

  • 保留原始标签(label)不变,仅用于展示;
  • 为每个节点附加 local_id 属性,以便从带前缀的 ID 还原出原始 ID;
  • 边及边的方向属性(_src/_tgt)同步改写以匹配新的带前缀 ID;
  • repo 属性被设置到每个节点上——这就是过滤与溯源的锚点。

第三层:跨仓库类型有机会再缝合。 前缀化之后,两个服务各自声明的同名契约类型会以「两个独立节点」的形式出现在合并图中——这在消息总线架构里恰恰是价值所在:生产者在一个仓库引用事件类型、消费者在另一个仓库实现它。合并图并不会自动把它们糊在一起,而是提供再加工的空间,相关逻辑见 graphify/cross_repo_types.py 的模块说明("Join the same declared type across repositories in a merged graph")。

什么时候更该走 merge 而不是单目录直扫? 连 graphify 自己的去重模块都在提示你:当同一路径下的抽取会把「名字相同但实体不同」的节点误判为重复时,graphify/dedup.py 会直接建议 "run 'graphify extract' per subfolder and merge with 'graphify merge-graphs'"——这正是本文这套多子目录工作流的另一大适用场景:让每个子目录先独立成图、保留各自实体,再由 merge 阶段统一按需关联。

合并完成后的快速路径:免重抽取直接 query

整份参考文档在结尾给出一个让「后续所有对话都变快」的机制:

Once graphify-out/graph.json exists, the fast path above takes over: any codebase question runs graphify query directly on the merged graph — no re-extraction, no size gate.

翻译成实际操作就是:合并完成后,一旦目标路径(根目录的 graphify-out/graph.json)已经存在,后续任何关于代码库的问题都直接走 graphify query 查询这张合并图——不再重新抽取,也不再受仓库体量门槛限制。这对跨仓库场景是决定性的体验提升:你不需要在每次提问时重新 AST 解析三个仓库,一次抽取 + 一次合并的成本被摊薄到此后无数次查询中。

命令速查与语义对照

场景 命令形态 产物位置
克隆单个 GitHub 仓库 graphify clone <url> [--branch <b>] [--out <dir>] 默认 ~/.graphify/repos/<owner>/<repo>,stdout 打印路径
克隆仓库(指定分支) graphify clone <url> --branch main 同上,检出指定分支
单仓库本地抽取 graphify extract <LOCAL_PATH> <LOCAL_PATH>/graphify-out/graph.json
多子目录分别抽取 graphify extract ./core/ ./service/ ./platform/(各自单独执行) ./core/graphify-out/graph.json 等,互不覆盖
合并多份图 graphify merge-graphs <g1> <g2> [...] --out <out>.json 默认 graphify-out/merged-graph.json;需 ≥ 2 个输入图
合并后查询 graphify query(基于已存在的 graphify-out/graph.json 免重抽取、无体量门槛

补充一个与本文主题呼应的仓库内旁证:graphify/__main__.py 的 CLI 帮助里还有一条 merge-driver——一个用于 git 三方合并时对两份 graph.json 做 union-merge 的合并驱动。它和本文的 merge-graphs 分属不同场景(前者服务 git merge 钩子、后者服务知识图谱的显式合并),说明「图文件合并」是 graphify 中一等公民的操作,既有面向用户的命令入口,也有面向版本控制集成的底层支持。

适用前提与边界说明

  • 本文全部命令以当前仓库代码为准,克隆与合并的落地目录、默认输出路径随版本可能演进,动手前可用 graphify --helpgraphify extract --help 复核;
  • graphify clone 依赖本机 git 可用;--depth 1 浅克隆意味着不携带完整历史,若你的分析依赖历史提交信息,需要自行调整克隆策略;
  • 合并图的 repo 过滤依赖合并时附加的属性与 ID 前缀,因此请始终使用 merge-graphs 合并、而不要手工拼接 graph.json
  • --backend 的可选模型列表以 graphify/main.py 的帮助文本为准,实际生效值取决于你环境中配置的 API key。

通过这套工作流,你可以把「N 个 GitHub 仓库」或「monorepo 里的 N 个服务目录」稳定地折叠成一张带来源标签的统一知识图谱,再用 graphify query 享受随问随答的快速路径——这正是 graphify 跨仓库代码理解能力的入口所在。

登录后查看全文
热门项目推荐
相关项目推荐