ARCHITECTURE.md + AGENTS.mdGraphify 是一個「Claude Code skill + Python 函式庫」的組合:skill 指揮函式庫,函式庫也可以獨立使用。
detect() → extract() → build_graph() → cluster() → analyze() → report() → export()
每個階段都是自己模組裡的一個函數。它們用純 Python dict 與 NetworkX 圖溝通——沒有共享狀態,除了 graphify-out/ 之外沒有副作用。這個「每個模組只管一件事、介面乾淨」的設計,是整個 codebase 最好懂的地方。
| 模組 | 函數 | 輸入 → 輸出 |
|---|---|---|
detect.py | collect_files(root) | 目錄 → 過濾後的 [Path] |
extract.py | extract(path) | 檔案路徑 → {nodes, edges} dict |
build.py | build_graph(extractions) | extraction dict 列表 → nx.Graph |
cluster.py | cluster(G) | 圖 → 每個節點帶 community 屬性的圖 |
analyze.py | analyze(G) | 圖 → 分析 dict(god nodes、surprises、questions) |
report.py | render_report(G, analysis) | 圖 + 分析 → GRAPH_REPORT.md 字串 |
export.py | export(G, out_dir, ...) | 圖 → Obsidian vault、graph.json、graph.html、graph.svg |
callflow_html.py | write_callflow_html(...) | graphify-out 檔案 → Mermaid 架構/呼叫流 HTML |
ingest.py | ingest(url, ...) | URL → 存進 corpus 目錄的檔案 |
cache.py | check_semantic_cache / save_semantic_cache | 檔案 → (cached, uncached) 分割 |
security.py | 驗證 helpers | URL / 路徑 / label → 通過驗證或拋錯 |
validate.py | validate_extraction(data) | extraction dict → schema 錯誤時拋錯 |
serve.py | start_server(graph_path) | 圖檔路徑 → MCP stdio server |
watch.py | watch(root, flag_path) | 目錄 → 變更時寫 flag 檔 |
benchmark.py | run_benchmark(graph_path) | 圖 → 語料 vs 子圖 token 比較 |
每個 extractor 回傳同樣的形狀,validate.py 在 build_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 個步驟——這是給想貢獻的人的入門路線圖:
extract.py 加一個 extract_<lang>(path) -> dict,遵循既有模式(tree-sitter 解析 → 走訪節點 → 收集 nodes/edges → 第二遍呼叫圖產生 INFERRED calls 邊)。extract() dispatch 與 collect_files() 註冊副檔名。detect.py 的 CODE_EXTENSIONS 與 watch.py 的 _WATCHED_EXTENSIONS。pyproject.toml 加 tree-sitter 套件。tests/fixtures/ 加 fixture 檔、在 tests/test_languages.py 加測試。所有外部輸入在使用前都會先經過 graphify/security.py:
| 輸入 | 驗證 |
|---|---|
| URL | validate_url()(只允許 http/https)+ _NoFileRedirectHandler(擋 file:// 轉址) |
| 抓取的內容 | safe_fetch() / safe_fetch_text()(大小上限、逾時) |
| 圖檔路徑 | validate_graph_path()(必須解析到 graphify-out/ 內) |
| 節點 label | sanitize_label()(去除控制字元、上限 256 字元、HTML escape) |
每個模組一個測試檔(tests/test_*.py)。跑法:
pytest tests/ -q
全部都是純單元測試——不碰網路、不寫 tmp_path 以外的檔案系統。
有趣的是,Graphify 用自己的 product 管理自己:repo 裡有 graphify-out/,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 依賴、最後寫測試。這是開源專案「可擴充性」的具體體現。
my-project/,每個檔案有一個類別和幾個函數。graphify detect my-project/ — 檢查輸出的 .graphify_detect.json,確認偵測到的檔案數量與類型。graphify extract my-project/ — 觀察 AST 抽取階段(Pass 1)的輸出,注意 nodes/edges 的數量。graphify-out/graph.json,確認每個節點有 id、label、source_file 屬性。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 error | extractor 回傳的 dict 格式不符 | 檢查 nodes 是否有 id 和 label,edges 是否有 source、target、relation |
validate.py 在 pipeline 的哪個階段被呼叫pytest tests/ -q 跑通所有測試與前面的「從零跑完 pipeline」不同,這次的情境是長期、自動、可稽核:你維護一個 200+ 檔、8 種語言(Python/TS/Go/Rust/SQL/Bash/Java/Pascal)的 monorepo,團隊要「每次 merge 之後圖自動更新、PR 時能看到這支 PR 影響哪些節點」。
.graphifyignore(排除 build 產物與 vendor 目錄),並在 AGENTS.md 註明「先讀 graphify-out/GRAPH_REPORT.md 再回答」。graphify extract .——純程式碼語料,Pass 3 被跳過,建圖零 LLM、可確定性重現。graphify hook install(或對應平台的 PreToolUse hook)——每次 git commit 自動跑 graphify update .,走 build_merge 增量路徑,只有變更的檔案被重抽。graphify path / graphify explain 回答「改 parser.py 會波及誰」——甚至配合 graphify update 產生的 graph_diff 輸出對比 base branch。graphify extract --check-drift(或對照 built_at_commit)確認圖跟得上程式碼,防止「圖過期」。架構頁說「每個模組只管一件事、介面乾淨」,但「乾淨介面」不等於「資料一定對」。真正讓圖正確的是藏在函數裡的防護: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.json 或 graph.json 缺失;或命令列誤加 --no-cache | 確認 manifest + graph.json 都在;移除 --no-cache;首次跑永遠全量是正常 |
| 新增語言後 dispatch 沒生效 | 副檔名只註冊了一半:_DISPATCH、CODE_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]" 等 |
LanguageConfig、不改 engine.py,能否支援一種全新語言?請指出哪些語言特性(如 C# partial class、Swift extension)必然要動 engine,並說明理由。deduplicate_by_label」並用 docstring 說明為何不用。你認為「保留死碼 vs 刪除死碼」的工程取捨,在開源專案裡該如何拿捏?前面的 Worked Example 是「從零跑完 pipeline」和「CI 自動化」。這次是完整的專案生命週期:你維護一個 2000+ 檔的技術文件庫(含 Python 程式碼 + Markdown 文件 + SQL migration + 論文 PDF),要從零建圖、跑增量更新、用 SQL 做統計分析、最後把圖接進多個 AI agent。
graphify extract corpus/——Python 走 Pass 1(零 LLM)、Markdown/PDF 走 Pass 3(花 token),產出 graph.json + GRAPH_REPORT.md + graph.html。graphify extract corpus/——manifest.json 自動偵測到已有圖,進入增量模式。只有變動的檔案被重抽,成本趨近於零。SELECT community, COUNT(*) as size FROM nodes GROUP BY community ORDER BY size DESC 看社群大小分佈,找出過大社群。graphify extract corpus/ --no-cache 全量重建,消除增量累積的去重偏差。| 面向 | 考量 | 實務建議 |
|---|---|---|
| 效能 | 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 |
graphify extract,並解釋每階段的輸出(detect.json → extraction chunks → graph.json)pytest tests/ -qgraphify query、graphify path、graphify explain 三個指令回答「某個函數被誰呼叫」「兩個模組間的最短路徑」