Theme / v0.9.56

Graphify

Codebase 知識圖譜

實戰範例

實戰範例 007:用 graphify merge-graphs 合併多個子專案圖譜

monorepo 多服務、或多 repo 跨專案場景,把各自建的子圖譜合成一份可橫向查詢的總圖

背景

graphify 預設每次 extract 都針對單一目錄建一份圖譜。但實務上有兩種場景需要「跨圖譜查詢」:

  1. monorepo 多服務services/auth/services/billing/services/export/ 各自獨立維護,想看「auth 的 verify() 跟 billing 的 charge() 是怎麼連起來的」
  2. 多 repo 跨專案:上游 SDK 是獨立 repo、下游服務也是獨立 repo,想看「SDK 的 client 升級會炸到下游哪些 callers」

把全部 source 丟進一次 extract 不是好辦法。子專案各有自己的 .graphifyignore、版本節奏、CI pipeline,混在一起會失去邊界。graphify merge-graphs 給你「分別建圖、需要時合併」的工作流:每個子專案維護自己的圖譜,要做橫向分析時才 merge 成一份總圖。

💡 與範例 002 差異:002 是把整個 monorepo 一次 extract . 建成單一圖譜。本範例處理的是「子專案各自獨立建圖、再合併」的場景,適合子專案有不同建圖設定或不同 repo 的情況。


適用情境

  • monorepo 內子專案有各自的 .graphifyignore、建圖頻率、或需走語意層 vs AST-only 的差異
  • 跨 repo 場景:SDK repo + 應用 repo,想看 client 升級的下游影響
  • 微服務架構:每個服務獨立 repo,但想分析 service-to-service 的呼叫關係
  • 想做「跨服務死碼分析」或「跨服務 god node 識別」

完整流程

步驟 1:分別為子專案建圖

情境:monorepo 內有 services/auth/services/billing/services/export/ 三個子專案,各自有獨立 setup。

cd ~/team-monorepo

# 子專案 1:auth(純 AST,不需要語意)
cd services/auth
graphify extract . --code-only --output graphify-out/auth.json

# 子專案 2:billing(含 docs,需要 LLM 語意層)
cd ../billing
export ANTHROPIC_API_KEY=sk-ant-...
graphify extract . --output graphify-out/billing.json

# 子專案 3:export(純 AST)
cd ../export
graphify extract . --code-only --output graphify-out/export.json

每個子專案輸出獨立的 graph.json。各自維護新鮮度(hook / update / watch)。

步驟 2:合併三份子圖譜

回到 monorepo 根目錄,把三份合在一起:

cd ~/team-monorepo

graphify merge-graphs \
  services/auth/graphify-out/auth.json \
  services/billing/graphify-out/billing.json \
  services/export/graphify-out/export.json \
  --out graphify-out/merged.json

merge-graphs 做三件事:

  1. 讀進每份子圖的節點跟邊
  2. 對相同 ID 的節點取聯集(node id 相同就合併屬性)
  3. 寫出合併後的 JSON

輸出類似:

Merging 3 graphs...
  services/auth/graphify-out/auth.json    (412 nodes, 1024 edges)
  services/billing/graphify-out/billing.json (687 nodes, 1543 edges)
  services/export/graphify-out/export.json   (298 nodes, 612 edges)

Resolving shared nodes...
  Shared node IDs: 23 (across auth & billing)
  Shared node IDs: 8 (across billing & export)
  → merged as unified nodes, attributes combined

Output written: graphify-out/merged.json
  Total nodes: 1366
  Total edges: 3151
  (union of all subgraphs; shared nodes deduplicated)

步驟 3:對合併圖做橫向查詢

合併圖的重點價值在「跨子專案的查詢」。把 merged.json 餵給 graphify query:

graphify query --graph graphify-out/merged.json "auth 的 verify() 怎麼連到 billing 的 charge()?"

graphify 在合併圖上跑 BFS,找到跨子專案的路徑:

Subgraph (5 nodes, 6 edges):
  AuthMiddleware.verify()        [auth]
    --calls--> RequestContext.current_user()  [auth]
    --calls--> InvoiceService.for_user()      [billing]
    --calls--> InvoiceService.charge()        [billing]
    --calls--> PaymentGateway.charge()        [billing]

跨子專案的呼叫鏈一次拉出來。這是單獨看任一份子圖譜看不到的視角。

步驟 4:跨服務的 god node 識別

合併圖的好處是 god node(高連結數節點)計算更準。單獨看 auth 圖譜,RequestContext.current_user() 可能 in-degree 只有 4,不算 god node。合併後 in-degree 跳到 18,立刻浮現為 god node:

head -50 graphify-out/GRAPH_REPORT.md

合併後的報告會列出跨服務的 god node,是重購的高價值目標。

步驟 5:把合併自動化進 CI

子專案各自 push 後,CI 自動重建子圖、再合併成總圖:

.github/workflows/merge-graphs.yml:

name: Merge Subproject Graphs
on:
  push:
    branches: [main]
    paths:
      - 'services/**'
jobs:
  merge:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install graphify
        run: pipx install graphifyy
      - name: Build subgraphs
        run: |
          for svc in services/auth services/billing services/export; do
            (cd "$svc" && graphify extract . --code-only --output graphify-out/sub.json)
          done
      - name: Merge
        run: |
          graphify merge-graphs \
            services/auth/graphify-out/sub.json \
            services/billing/graphify-out/sub.json \
            services/export/graphify-out/sub.json \
            --out graphify-out/merged.json
      - name: Commit merged graph
        run: |
          git add graphify-out/merged.json
          git commit -m "chore(graphify): update merged graph" || echo "no changes"
          git push

main branch 永遠帶著最新的合併圖。reviewer 跟新人都看得到跨服務視角。

步驟 6:用 HTTP MCP server 共用合併圖

把合併圖開成團隊共用的 MCP server:

python -m graphify.serve graphify-out/merged.json \
  --transport http \
  --host 0.0.0.0 \
  --api-key "$TEAM_GRAPHIFY_KEY"

每人的 IDE 指向同一個 URL,助理用同一份合併圖回答問題,避免每人各自重建。


真實輸出範例

$ graphify merge-graphs \
    services/auth/graphify-out/auth.json \
    services/billing/graphify-out/billing.json \
    services/export/graphify-out/export.json \
    --out graphify-out/merged.json
Merging 3 graphs...
  services/auth/graphify-out/auth.json    (412 nodes, 1024 edges)
  services/billing/graphify-out/billing.json (687 nodes, 1543 edges)
  services/export/graphify-out/export.json   (298 nodes, 612 edges)

Resolving shared nodes...
  Shared node IDs: 23 (across auth & billing)
  Shared node IDs: 8 (across billing & export)
  → merged as unified nodes, attributes combined

Output written: graphify-out/merged.json
  Total nodes: 1366
  Total edges: 3151

$ graphify query --graph graphify-out/merged.json "auth verify 跟 billing charge 怎麼連?"
Subgraph (5 nodes, 6 edges):
  AuthMiddleware.verify()       [auth]
    --calls--> RequestContext.current_user()  [auth]
    --calls--> InvoiceService.for_user()      [billing]
    --calls--> InvoiceService.charge()        [billing]
    --calls--> PaymentGateway.charge()        [billing]

對比傳統做法

任務 一次 extract . 整個 monorepo 分別建圖 + merge-graphs
子專案 .graphifyignore 只能設一個 各自獨立設定
子專案建圖頻率 全部綁在一起 各自控制(純 AST vs 語意層)
跨 repo 場景 不適用(路徑不連續) 各 repo 建圖、再合併
跨服務查詢 預設就有 merge 後才有
CI 成本 一次全量 子專案 incremental、CI 平行
子專案獨立分析 不容易(邊界模糊) 子圖譜可單獨查詢

merge-graphs 不是「取代」一次 extract .,而是補它。子專案獨立性強、建圖設定差異大、或跨 repo 時,merge-graphs 才是對的工具。單純 monorepo 還是建議直接 extract .


失敗處理

情境 排查
合併後節點數比預期少很多 子圖譜之間的節點 ID 衝突被誤判為同一個。檢查子圖譜的 ID 命名規則(例如是否含路徑前綴)
合併圖查不到跨服務路徑 子圖譜之間的 shared node ID 不存在,呼叫鏈在中間斷掉。確認中介層(例如 shared SDK)也被建進至少一份子圖
merge-graphs 報 JSON parse error 某份子圖譜是舊版 graphify 建的、格式不相容。重跑該子專案的 extract
合併圖太大、查詢慢 節點超過 10 萬時 query 會變慢。考慮只合併需要的子圖,或用 --code-only 減少語意節點
CI 合併後圖譜跟本地對不上 本地 hook 跑的是子圖 incremental、CI 跑的是全量。统一流程:CI 每次都從子圖全量重建再合併
共享節點屬性不一致 兩份子圖對同一個節點有不同屬性。merge 採「後寫入覆蓋」或「屬性聯集」,看 graphify 版本;檢查合併後的 GRAPH_REPORT.md 確認

內部連結


下一步

  1. 用 diagnose multigraph 診斷 same-endpoint 風險(合併多圖後的節點衝突診斷)
  2. 大型 monorepo 重購(合併圖譜作為重購分析的基礎)

關鍵學習點

  • merge-graphs 不是預設工作流,是「子專案獨立性強、跨服務查詢需求」場景的專用工具。單純 monorepo 用一次 extract . 就好。
  • 共享節點 ID 是合併成敗的關鍵。子專案之間共用的 SDK、型別、介面要先確認 ID 命名一致,否則合併會出現幽靈節點或斷鏈。
  • 合併圖的價值在「跨服務 god node」跟「跨服務呼叫鏈」。這兩個視角是單獨子圖譜給不出來的。
  • CI 自動化合併 + HTTP MCP server 共用,是大型團隊把 merge-graphs 變成日常工具的標準組合。
  • merge 後的 merged.json 也要 commit 進 repo 或開 MCP server,否則每人各自合一次、結果不一致。

v0.9.54:跨 repo 不只連型別,還能補 member call

從 v0.9.49 起,merge-graphs 會對同 namespace + name 的跨 repo shared type 建立 same_type_as;v0.9.54 再往前一步:如果某個 member call 的 receiver type 在另一個 repo,而且「跨 repo type + method」只有唯一候選,merge 時可以補出該呼叫邊。

例如 SDK repo:

namespace Shared.Payments;
public sealed class PaymentClient
{
    public Task ChargeAsync(decimal amount) => ...;
}

App repo:

await _paymentClient.ChargeAsync(order.Total);

兩邊分開建圖後再 merge-graphs,新版有機會把 app 的 ChargeAsync 呼叫連到 SDK 的真實方法。若存在兩個不歧義不足的候選,Graphify 會保守地不連,避免生成漂亮但錯誤的跨 repo path。

這也表示舊版「只靠 shared node ID 是否相同」的理解已不完整;現在還有 fully-qualified type identity 與 merge-time resolver 參與判斷。