架構總覽

14 個模組、七階段 pipeline、怎麼新增一個語言抽取器
來源:ARCHITECTURE.md + AGENTS.md

定位

Graphify 是一個「Claude Code skill + Python 函式庫」的組合:skill 指揮函式庫,函式庫也可以獨立使用。

Pipeline:七個階段、一個函數一個模組

detect()  →  extract()  →  build_graph()  →  cluster()  →  analyze()  →  report()  →  export()

每個階段都是自己模組裡的一個函數。它們用純 Python dict 與 NetworkX 圖溝通——沒有共享狀態,除了 graphify-out/ 之外沒有副作用。這個「每個模組只管一件事、介面乾淨」的設計,是整個 codebase 最好懂的地方。

模組職責表

模組函數輸入 → 輸出
detect.pycollect_files(root)目錄 → 過濾後的 [Path]
extract.pyextract(path)檔案路徑 → {nodes, edges} dict
build.pybuild_graph(extractions)extraction dict 列表 → nx.Graph
cluster.pycluster(G)圖 → 每個節點帶 community 屬性的圖
analyze.pyanalyze(G)圖 → 分析 dict(god nodes、surprises、questions)
report.pyrender_report(G, analysis)圖 + 分析 → GRAPH_REPORT.md 字串
export.pyexport(G, out_dir, ...)圖 → Obsidian vault、graph.json、graph.html、graph.svg
callflow_html.pywrite_callflow_html(...)graphify-out 檔案 → Mermaid 架構/呼叫流 HTML
ingest.pyingest(url, ...)URL → 存進 corpus 目錄的檔案
cache.pycheck_semantic_cache / save_semantic_cache檔案 → (cached, uncached) 分割
security.py驗證 helpersURL / 路徑 / label → 通過驗證或拋錯
validate.pyvalidate_extraction(data)extraction dict → schema 錯誤時拋錯
serve.pystart_server(graph_path)圖檔路徑 → MCP stdio server
watch.pywatch(root, flag_path)目錄 → 變更時寫 flag 檔
benchmark.pyrun_benchmark(graph_path)圖 → 語料 vs 子圖 token 比較
怎麼記:前七個(detect→export)是主線 pipeline,後八個是圍繞它的服務(ingest 進料、cache 加速、security/validate 把關、serve/watch 對外)。程式碼對照區會逐函數講:主線服務層分析與報告輸出

抽取輸出 Schema

每個 extractor 回傳同樣的形狀,validate.pybuild_graph() 之前強制檢查:

{
  "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"}
  ]
}

如何新增一個語言抽取器

官方文件列出的 5 個步驟——這是給想貢獻的人的入門路線圖:

  1. extract.py 加一個 extract_<lang>(path) -> dict,遵循既有模式(tree-sitter 解析 → 走訪節點 → 收集 nodes/edges → 第二遍呼叫圖產生 INFERRED calls 邊)。
  2. extract() dispatch 與 collect_files() 註冊副檔名。
  3. 把副檔名加進 detect.pyCODE_EXTENSIONSwatch.py_WATCHED_EXTENSIONS
  4. pyproject.toml 加 tree-sitter 套件。
  5. tests/fixtures/ 加 fixture 檔、在 tests/test_languages.py 加測試。

安全入口

所有外部輸入在使用前都會先經過 graphify/security.py

輸入驗證
URLvalidate_url()(只允許 http/https)+ _NoFileRedirectHandler(擋 file:// 轉址)
抓取的內容safe_fetch() / safe_fetch_text()(大小上限、逾時)
圖檔路徑validate_graph_path()(必須解析到 graphify-out/ 內)
節點 labelsanitize_label()(去除控制字元、上限 256 字元、HTML escape)

測試

每個模組一個測試檔(tests/test_*.py)。跑法:

pytest tests/ -q

全部都是純單元測試——不碰網路、不寫 tmp_path 以外的檔案系統。

AGENTS.md:這個 repo 自己怎麼用 graphify

有趣的是,Graphify 用自己的 product 管理自己:repo 裡有 graphify-out/,AGENTS.md 規定:

展開英文原文:AGENTS.md
This project has a graphify knowledge graph at graphify-out/.

Rules:
- Before answering architecture or codebase questions, read graphify-out/GRAPH_REPORT.md
- If graphify-out/wiki/index.md exists, navigate it instead of reading raw files
- After modifying code files in this session, run `graphify update .`

也就是「先讀圖、再回答」——Dogfooding 的最好示範。本站做這個教學網站時,也是用同樣的邏輯在組織內容。

教學解說

Graphify 的架構核心是「一個 pipeline、七個階段、每個階段一個純函數」。理解這個架構的關鍵在於:所有模組之間只透過 Python dict 和 NetworkX 圖物件溝通,沒有共享狀態。這代表你可以獨立測試每個模組,也可以替換任何階段的實作。

14 個模組分兩組:前七個(detect→export)是主線 pipeline,後八個是圍繞它的服務。服務層的作用是「進料(ingest)、加速(cache)、把關(security/validate)、對外(serve/watch)」。

新增語言抽取器的 5 步驟是 contributor 入門路線圖:先寫 extractor 函數、再註冊副檔名、加 tree-sitter 依賴、最後寫測試。這是開源專案「可擴充性」的具體體現。

Worked Example:從零跑完架構 pipeline

  1. 準備語料:建立一個包含 3 個 Python 檔案的目錄 my-project/,每個檔案有一個類別和幾個函數。
  2. 執行 detectgraphify detect my-project/ — 檢查輸出的 .graphify_detect.json,確認偵測到的檔案數量與類型。
  3. 執行 extractgraphify extract my-project/ — 觀察 AST 抽取階段(Pass 1)的輸出,注意 nodes/edges 的數量。
  4. 檢查產出:查看 graphify-out/graph.json,確認每個節點有 idlabelsource_file 屬性。
  5. 驗證社群:查看 GRAPH_REPORT.md 中的社群數量,確認 Leiden 演算法正確分群。
預期產出graph.json(知識圖譜資料)、graph.html(互動視覺化)、GRAPH_REPORT.md(摘要報告)。純程式碼語料零 LLM 成本。

常見錯誤與診斷

錯誤訊息原因解決方式
ModuleNotFoundError: tree_sitter未安裝 tree-sitter 依賴執行 pip install graphify[code]pip install tree-sitter
extract.py: no nodes extracted語料中沒有支援的語言檔案檢查 CODE_EXTENSIONS 列表,確認副檔名被支援
build_graph: empty graph所有檔案的抽取結果為空--verbose 重新跑 extract,查看每個檔案的抽取狀態
validate: schema errorextractor 回傳的 dict 格式不符檢查 nodes 是否有 idlabeledges 是否有 sourcetargetrelation

練習與驗收清單

① 進階真實情境 Worked Example:為 8 語言 monorepo 架設 CI 自動建圖管線

與前面的「從零跑完 pipeline」不同,這次的情境是長期、自動、可稽核:你維護一個 200+ 檔、8 種語言(Python/TS/Go/Rust/SQL/Bash/Java/Pascal)的 monorepo,團隊要「每次 merge 之後圖自動更新、PR 時能看到這支 PR 影響哪些節點」。

  1. 建立 repo 慣例:在 repo 根目錄放 .graphifyignore(排除 build 產物與 vendor 目錄),並在 AGENTS.md 註明「先讀 graphify-out/GRAPH_REPORT.md 再回答」。
  2. 首次全量graphify extract .——純程式碼語料,Pass 3 被跳過,建圖零 LLM、可確定性重現。
  3. 裝 commit hookgraphify hook install(或對應平台的 PreToolUse hook)——每次 git commit 自動跑 graphify update .,走 build_merge 增量路徑,只有變更的檔案被重抽。
  4. PR 影響分析:用 graphify path / graphify explain 回答「改 parser.py 會波及誰」——甚至配合 graphify update 產生的 graph_diff 輸出對比 base branch。
  5. CI 把關:在 CI 跑 graphify extract --check-drift(或對照 built_at_commit)確認圖跟得上程式碼,防止「圖過期」。
為什麼選這條路徑:這是「程式碼永不離開機器」的主場——8 種語言全部走 tree-sitter 確定性抽取,零 token、可重現、可 commit 進 repo。相較於把整份語料每次送 LLM,增量 + SHA256 快取讓每次 merge 的建圖成本趨近於零,而且「圖與 commit 綁定」讓團隊永遠知道圖的新鮮度。

② 深入原理擴充:純函數 pipeline 背後的「隱形正確性工程」

架構頁說「每個模組只管一件事、介面乾淨」,但「乾淨介面」不等於「資料一定對」。真正讓圖正確的是藏在函數裡的防護:build_from_json 的 500 行裡有 ID 正規化(非字串 ID、legacy _semantic_id_remap、doc/AST twin 重映射)、檔名消歧(不同目錄同名檔加最短唯一後綴)、shrink guard(新圖更小就拒絕覆寫)。validate.py 在 build 前強制 schema;security.py 在 ingest 前消毒 URL。換句話說:「正確」是防護網疊出來的,不是純函數自動保證的

大家以為建圖正確、但其實有誤的案例:很多人以為「build_graph() 之後的圖就是對的,可以直接查」。實際上 跨檔 calls 邊只對「有 resolution 支援」的語言成立——resolution.py 寫了 tsconfig alias、workspace packages、JS 路徑解析,但冷門語言(Pascal、Terraform、Apex)沒有同等級的跨檔解析,它們的 import 邊可能停在檔案級、不會精確指到類別。因此「某條 calls 邊是 EXTRACTED」只代表 AST 有證據,不代表端點解析正確。查圖時看到斷掉的邊,先懷疑 resolution 而非懷疑抽取。

③ 診斷式疑難排解表

症狀可能原因解決方案
CI 中 graphify extract 每次重抽全部檔案沒進入增量模式:graphify-out/manifest.jsongraph.json 缺失;或命令列誤加 --no-cache確認 manifest + graph.json 都在;移除 --no-cache;首次跑永遠全量是正常
新增語言後 dispatch 沒生效副檔名只註冊了一半:_DISPATCHCODE_EXTENSIONS_WATCHED_EXTENSIONS 三處沒同步對照「新增語言 5 步驟」逐一檢查註冊點;重跑 detect 看分類結果
某些檔案「默默消失」不出現在圖detect 過濾生效:noise 目錄、ignore 規則、敏感檔被排除detect() 回報的 skipped_sensitive / ignored / unclassified 欄位,而不是猜
圖節點數比上次少,export 卻不報錯shrink guard 已擋下覆寫(回傳 False)但呼叫端沒檢查確認上一輪 chunk 檔未遺失;檢查 to_json 回傳值;確認後再 --force
ModuleNotFoundError 只發生在某一語言該語言的 tree-sitter grammar 是 optional extra([sql][pascal][terraform]裝對應 extra:pip install "graphify[sql]"

④ 進階挑戰題

  1. 只靠改 LanguageConfig、不改 engine.py,能否支援一種全新語言?請指出哪些語言特性(如 C# partial class、Swift extension)必然要動 engine,並說明理由。
  2. 假設你想在 cluster 與 analyze 之間插入「自訂節點排序」步驟。以「沒有共享狀態」的介面原則,這個步驟的函數簽名該長什麼樣?輸入輸出為何?
  3. build.py 保留「被取代的 deduplicate_by_label」並用 docstring 說明為何不用。你認為「保留死碼 vs 刪除死碼」的工程取捨,在開源專案裡該如何拿捏?

① 專案級端到端 Worked Example:大型文件庫建圖專案——建圖 + 增量更新 + SQL 分析 + 跨 agent 整合

前面的 Worked Example 是「從零跑完 pipeline」和「CI 自動化」。這次是完整的專案生命週期:你維護一個 2000+ 檔的技術文件庫(含 Python 程式碼 + Markdown 文件 + SQL migration + 論文 PDF),要從零建圖、跑增量更新、用 SQL 做統計分析、最後把圖接進多個 AI agent。

  1. 首次全量建圖graphify extract corpus/——Python 走 Pass 1(零 LLM)、Markdown/PDF 走 Pass 3(花 token),產出 graph.json + GRAPH_REPORT.md + graph.html。
  2. 觀察抽取比例:查看 GRAPH_REPORT 的 EXTRACTED/INFERRED/AMBIGUOUS 比例——純程式碼部分應 100% EXTRACTED,文件部分有 INFERRED。這告訴你哪些邊值得信賴。
  3. 設定增量排程:用 cron 每晚跑 graphify extract corpus/——manifest.json 自動偵測到已有圖,進入增量模式。只有變動的檔案被重抽,成本趨近於零。
  4. SQL 即席分析:把 graph.json 倒進 SQLite(用 Docker MCP SQLite),跑 SELECT community, COUNT(*) as size FROM nodes GROUP BY community ORDER BY size DESC 看社群大小分佈,找出過大社群。
  5. 跨 agent 整合:在 Claude Code 裝 graphify skill(Step 0-9),在 OpenCode 也裝同一套——兩個 agent 都先讀圖再回答,且圖是同一份(共用 graphify-out/)。
  6. 季度重建:每季跑一次 graphify extract corpus/ --no-cache 全量重建,消除增量累積的去重偏差。
預期產出:一份「與 commit 同步」的知識圖譜,任何 agent 隨時可查;SQL 分析提供社群健康度儀表板;季度重建確保圖不漂移。整個專案的 LLM 成本集中在首次建圖與文件語料的 Pass 3,之後增量更新成本趨近於零。

② 效能/品質/安全深度

面向考量實務建議
效能14 個模組的 pipeline 串行執行;大語料(>10K 檔)的 build_graph 耗時可能達分鐘級ProcessPoolExecutor 平行抽取(已內建);對超大語料分批 extract 再 merge;監控 manifest.json 的大小(它記錄所有檔案 hash)
品質抽取品質直接決定圖品質;tree-sitter 解析失敗的檔案會靜默跳過graphify extract --verbose 看每個檔案的抽取狀態;定期檢視 AMBIGUOUS 邊比例(>10% 要查原因);用 review.md 記錄已知的圖錯誤
安全 ingestion 用戶端 URL 可能觸發 SSRF;MCP server 路徑穿越所有 URL 經過 validate_url() 白名單;MCP 路徑強制在 graphify-out/ 內;ingest 前的 <untrusted_source> 包覆防 prompt injection

③ 文件間比較對照表

面向本文(架構總覽)相關文差異說明
Pipeline 階段七階段:detect→export + 八個服務模組運作原理運作原理深入講三遍處理的 Pass 1/2/3 細節;架構頁只列模組職責
新增語言5 步驟路線圖程式碼對照 · extractors架構頁是概覽;程式碼對照頁逐函數講 extractor 的 tree-sitter 走訪邏輯
安全設計簡述 security.py 四個驗證函數安全模型安全模型頁逐條威脅面解析(SSRF、XSS、prompt injection 等),深度遠超架構頁
增量更新未涵蓋增量更新增量更新是獨立的設計文件,架構頁只描述靜態 pipeline

④ 互動式檢核清單