docs/how-it-works.mdGraphify 之所以省 token,是因為「建一次圖,之後都讀圖」。建圖的第一次要花 token(文件那部分),之後每一次查詢都讀壓縮過的 graph.json,而不是掃原始檔。在一個 52 檔案的混合語料上,每次查詢比直接讀原始檔省 71.5 倍 token。
Graphify 把輸入分成三類,用三種不同的方式處理:
graphify-out/converted/ 下的 Markdown sidecar(Office 檔用 [office] extra;Google Workspace 需 --google-workspace)。**Pass 1 — Code structure (free, no API calls)**
Tree-sitter parses your code files and extracts classes, functions, imports,
call graphs, and inline comments. This runs locally with no LLM involved.
25 languages supported. SQL files get special treatment: tables, views,
foreign keys, and JOIN relationships are extracted deterministically.
**Pass 2 — Video and audio (local, no API calls)**
Video and audio files are transcribed with faster-whisper. Transcripts are
cached — re-runs skip already-processed files.
**Pass 3 — Docs, papers, images (Claude subagents, costs tokens)**
Claude runs in parallel over markdown, PDFs, images, and transcripts. Each
subagent reads a batch of files and outputs a JSON fragment: nodes, edges,
and any group relationships. The fragments are merged into a single graph.
社群是用 Leiden 演算法 找出來的——一種「把節點依邊密度分群」的圖分群方法。彼此連線很多的節點會落進同一個社群。
不需要 embedding。Claude 抽出的語意相似邊(semantically_similar_to)本來就在圖裡,直接影響社群形狀——圖的結構本身就是相似度訊號,沒有獨立的 embedding 步驟、也沒有向量資料庫。這是 Graphify「不是向量索引」的核心主張。
Communities are found using the Leiden algorithm — a graph-clustering method
that groups nodes by edge density. Nodes with many connections between them
end up in the same community.
**No embeddings needed.** The semantic similarity edges that Claude extracts
(semantically_similar_to) are already in the graph, so they influence
community shape directly. The graph structure is the similarity signal —
there's no separate embedding step or vector database.
每一條關係都貼三種標籤之一:
| 標籤 | 意義 |
|---|---|
EXTRACTED | 直接在原始碼找到(例如一個函數呼叫、一個 import)——confidence 固定 1.0 |
INFERRED | Claude 做的合理推論,帶 confidence_score(0.0–1.0) |
AMBIGUOUS | 不確定,在報告中標記供人工檢視 |
INFERRED 用離散級距:0.95 近乎確定(明確跨檔引用)→ 0.85 強證據 → 0.75 合理 → 0.65 弱 → 0.55 臆測。
| Tag | Meaning |
|------------|--------------------------------------------------|
| EXTRACTED | Found directly in the source (e.g. a call, import)|
| INFERRED | A reasonable inference, with confidence_score |
| AMBIGUOUS | Uncertain — flagged in the report for review |
EXTRACTED edges always have confidence 1.0. INFERRED edges use a discrete
rubric: 0.95 / 0.85 / 0.75 / 0.65 / 0.55.
省 token 的幅度取決於語料大小——圖越大,省得越多:
| 語料 | 檔案數 | 節省倍數 |
|---|---|---|
| Karpathy repos + 論文 + 圖片 | 52 | 71.5× |
| graphify 源碼 + Transformer 論文 | 4 | 5.4× |
| httpx(合成 Python 函式庫) | 6 | ~1× |
6 個檔案還在 context window 內,圖的價值是「結構清晰」而不是「壓縮」;到 52 個檔案,省下的 token 就快速累積。repo 裡每個 worked/ 資料夾都有原始輸入與真實產出,可以自己跑來驗證。
ProcessPoolExecutor 平行抽取(繞過 GIL 的真多處理);文件/論文/圖片用平行 Claude 子代理。84 個程式碼檔的語料,平行 AST 抽取比循序快約 1.66 倍。graphify-out/cache/。輸出 graph.json 用 NetworkX 的 node-link 格式。每個節點有:
id — 穩定識別符label — 人可讀名稱file_type — code / document / paper / image / rationalesource_file — 來自哪個檔案每條邊有:source、target、relation(動詞片語,如 calls、imports、semantically_similar_to)、confidence、confidence_score(僅 INFERRED)、source_file。連接 3+ 節點的群組關係(hyperedges)放在 G.graph["hyperedges"]。
The output graph.json uses NetworkX's node-link format. Each node has:
- id — stable identifier
- label — human-readable name
- file_type — code, document, paper, image, rationale
- source_file — where it came from
Each edge has:
- source, target — node IDs
- relation — verb phrase (e.g. calls, imports, implements, semantically_similar_to)
- confidence — EXTRACTED, INFERRED, or AMBIGUOUS
- confidence_score — float (INFERRED only)
- source_file — where the relationship was found
Hyperedges (group relationships connecting 3+ nodes) live in G.graph["hyperedges"].
Graphify 的核心理念是「建一次圖,之後都讀圖」。三遍處理是理解整個系統的鑰匙:
社群偵測用 Leiden 演算法,不需要 embedding。信任標籤(EXTRACTED/INFERRED/AMBIGUOUS)讓你知道每一條關係的可信度。
graphify extract worked/httpx/raw。觀察輸出:Pass 1 處理所有檔案,Pass 3 被跳過,token cost = 0。graphify extract worked/mixed-corpus/raw。觀察 Pass 3 被觸發,token cost > 0。GRAPH_REPORT.md,比較 nodes/edges 數量與社群分佈。| 錯誤訊息 | 原因 | 解決方式 |
|---|---|---|
faster-whisper not found | 未安裝影音轉錄依賴 | 執行 pip install graphify[audio] |
Pass 3: no LLM provider configured | 語料有文件但未設定 API key | 設定 ANTHROPIC_API_KEY 或 OPENAI_API_KEY 環境變數 |
Leiden: empty communities | 圖中沒有邊或節點太少 | 檢查 extract 階段是否正確產生 edges |
SHA256 cache: stale entries | 快取與實際檔案不同步 | 執行 graphify extract --no-cache 清除快取重跑 |
前面的 Worked Example 比較兩份語料的抽取差異;這次是同一份程式庫、時間軸上的差異:你要分析一個跑了 15 年的 ERP 系統(如 benchmarks 頁的 ERPNext 場景),回答「核心抽象這 15 年怎麼演化」。
graphify extract .(AST-only,零 LLM),產生多份 graph.json——15 年 × 52 週 = 780 份。graphify report 取 god nodes(Client、Request 這類核心抽象)與社群數。graphify diff old-graph.json new-graph.json(graph_diff 的 CLI)看節點/邊的增減——「2020 年多了 payment 社群、2023 年 gateway 社群大漲」。graph_diff 給的是「結構層的差異」,補足 git log 只有文字 diff 的盲點。這是把 graphify 從「單次快照工具」升級成「長期演進儀表板」的用法。文裡說「Leiden 依邊密度分群」,但實作上「把社群偵測弄到可用」的是圍繞它的工程:
leiden()(比 Louvain 多了 refinement phase,社群更凝聚);缺套件時退回 networkx Louvain。random_seed=42、trials=1——否則同樣的圖每次跑社群編號會亂跳。semantically_similar_to 邊承載(Claude 抽取),圖結構本身就是相似度——沒有向量資料庫,也無法做 cosine 檢索。社群形狀 = 「邊密度」,不是「向量距離」。semantically_similar_to 邊,或抽取的 relation 標錯,社群就會歪掉——而且歪得很自然,看不出來。最常見的案例是「兩個 repo 的 Block class 被分進不同社群」:這不是 bug,是「該邊的抽取沒成功或 confidence 太低」的結果。看到奇怪的社群分界,先查邊,再質疑演算法。| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
| 同一個 repo 重跑兩次,社群成員變了 | 分群輸入不確定(節點順序被 dict 順序影響)或隨機種子被改 | 確認走的 _partition 有排序重組 + seed 固定;不要手動改 G.nodes() 順序 |
| 整個 repo 幾乎一個大社群 | 檔案級 hub(如 CLAUDE.md 連到全部)把大家拉在一起 | 檢查是否有 doc-hub 節點;用 exclude_hubs_percentile 排除超集線器 |
| 社群數比上次少很多 | 抽取失敗導致邊變少(語意子代理超時、文件缺) | 看 GRAPH_REPORT 的 EXTRACTED/INFERRED 比例;對比 token 統計,找出哪類檔案沒抽到 |
| token 成本異常高 | Pass 3 把不該送 LLM 的檔送出去(分類錯:如 .apm.yml 被當文件) | 檢查 classify_file 結果;套件 manifest 應被導向 CODE |
| 社群命名讀起來很怪 | 用了預設命名(最高度數成員),沒跑 LLM 命名 | 設定 LLM 後端跑 Step 5;或接受「預設名 = 結構真相」並手動覆寫 |
前面的 Worked Example 比較兩份語料、做架構考古。這次是同一個專案的完整生命週期:你有一個 500 檔的混合語料(200 Python + 150 Markdown + 50 PDF + 60 SQL + 40 圖片),要從零建圖、跑多次增量、追蹤社群演化、最後產出一份「專案知識演進報告」。
graphify extract corpus/——Pass 1 處理 260 個程式碼/SQL 檔(零 LLM),Pass 2 跳過(無影音),Pass 3 處理 240 個文件/圖片(花 token)。記下 spend ledger 與 EXTRACTED/INFERRED 比例。graphify extract corpus/——觀察 manifest.json 的 changed/cached 數字,確認增量模式生效。追蹤圖的 nodes/edges/社群數變化。graphify report 取 god nodes 清單,比較三次增量後的變化。「核心抽象有沒有變?新增的社群代表什麼子系統?」graphify extract corpus/ --no-cache——全量重建消除增量累積的去重偏差,比較重建前後的圖差異。| 面向 | 考量 | 實務建議 |
|---|---|---|
| 效能 | 三遍處理中 Pass 3(LLM)是瓶頸;SHA256 快取在增量模式下跳過未變檔案 | 純程式碼語料完全跳過 Pass 3;用 --code-only 強制跳過;監控 graphify-out/cache/ 大小避免快取膨脹 |
| 品質 | Leiden 社群偵測的確定性依賴排序重組 + 固定 seed;語意抽取品質影響社群形狀 | 不要手動改 G.nodes() 順序;用 graphify extract --verbose 監控每檔案的抽取結果;定期跑 graphify report 檢查 god nodes |
| 安全 | Pass 3 的 LLM 處理攻擊者可控的文件內容 | <untrusted_source> 雜湊戳記包覆 + _neutralise_injection_sentinels() 解除 jailbreak 記號;這是 table-stakes 防禦,不是無敵 |
| 面向 | 本文(運作原理) | 相關文 | 差異說明 |
|---|---|---|---|
| 三遍處理 | Pass 1/2/3 的概覽與 token 節省數字 | 架構總覽 | 架構頁列模組職責表,不深入三遍處理的內部機制 |
| 社群偵測 | Leiden 演算法 + 確定性 + hub 排除 | 程式碼對照 · analysis | 程式碼對照頁逐函數講 _partition()、exclude_hubs_percentile、relabel |
| 信任標籤 | EXTRACTED/INFERRED/AMBIGUOUS + 離散級距 | 增量更新 | 增量頁的去重步驟也使用信任標籤做合併決策 |
| Token 經濟 | 省 token 倍數表(52 檔 71.5×) | 效能基準 | 效能基準用 LOCOMO/LongMemEval 做跨系統比較;運作原理頁只做內部基準 |
graphify extract --verbose 監控一次完整建圖的 Pass 1/2/3 分佈,並判斷哪個 Pass 是瓶頸--no-cache)