Graft 程式碼代理程式的 codebase map
Graft 使用 tree-sitter 解析 repository,建立可供 coding agent 透過 MCP 查詢的 codebase map,避免每次 session 重新搜尋相同結構。
程式碼代理程式使用的程式碼庫地圖是什麼
供程式碼代理程式使用的程式碼庫地圖,是儲存庫的持久索引。代理程式會查詢這份索引,而不是在每次新工作階段中從頭開始使用 grep 搜尋。Graft 是這種概念的一種實作。它會使用 tree-sitter 解析程式碼,建立一個包含相互連結 Markdown 節點的資料夾,以及每個符號的連結圖,並透過 MCP(model context protocol,程式碼代理程式呼叫外部工具時使用的標準介面)提供擷取工具。
Graft 不是 proxy,也不是 gateway。代理程式與模型 API 之間不會經過任何中介層。這份地圖是磁碟上的資料夾,代理程式會直接讀取。這項差異決定了你要解決的問題:自架 token gateway 會計量並路由你已經送出的請求;地圖則會改變你總共需要送出的請求數量。
這項技術早於這個工具,也會延續到工具停止使用之後。先學會這項技術,再了解實作細節。
讓 coding agents 反覆探索結構並消耗 context
觀察一個已經看過某個 repository 50 次的 agent 開始工作。它先列出目錄,再以 grep 搜尋 symbol。接著開啟 3 個檔案,找出函式的定義位置,再開啟第 4 個檔案,確認哪些程式碼會呼叫它。這些都不是工作本身,而是熟悉結構的過程;每次 session 都必須使用 input tokens 支付這項成本。
原因很簡單。模型在不同 session 之間沒有記憶。Agent 對目錄結構所學到的一切,都存在 session 結束時會被丟棄的 context window 中。因此,每次都必須從零開始,以完整成本重新探索。在大型 repository 中,熟悉結構的成本可能高於實際修改:需要 10 次 tool call 才能找到程式碼,修改只需 1 次。熟悉結構與修改各佔這項成本的一半,因此要求 agent 只執行能奏效的最小修改的 skill,適合搭配 map 使用,而不是在兩者之間擇一。
Map 將探索工作從模型移到磁碟,藉此打破這個循環。Parser 只需掃描 repository 1 次,記錄每個 symbol 的定義位置,以及各 symbol 之間的呼叫關係,並在程式碼變更時持續更新這份記錄。Agent 提出 1 個問題,就能取得包含檔案與行號的答案。反覆探索會變成低成本的查詢。
你已經在使用較弱的做法。說明慣例的 AGENTS.md 可避免 agent 每次重新推導你的慣例。產生的 map 則可避免 agent 重新推導 repository 的結構。差別在於由誰撰寫。你手動撰寫 instruction file,因此內容可以保持精簡。Parser 會產生 map,因此能涵蓋 10000 個檔案。若要了解 session 中 context window 的實際使用方式,請參閱Claude Code 如何使用其 context window。
Graft 實際建立的內容
根目錄下的同一個 graft/ 資料夾中,會建立 2 個成品。
第一個是以互相連結的 Markdown 撰寫的節點圖,每個節點各有 1 個檔案。每個節點包含簡明的英文摘要、從原始碼擷取的重要邏輯程式行「crux」、附有內容雜湊的確切原始碼檔案、連往其他節點的具型別 wikilink(depends_on、part_of、uses、implements),以及可在重新產生後保留的 notes 區段,讓你記錄剖析器無法推斷的背景資訊。
第二個是 graft/.graph/wiring.json,也就是 tree-sitter 擷取的每個 symbol 結構圖,其中包含定義、參照,以及彼此之間的呼叫邊。
這種分離方式很重要,因為只有其中一半需要模型。graft build 完全由 tree-sitter 處理,從不呼叫 LLM(large language model),因此結果具決定性且不產生成本。graft build --deep 會加入文字摘要與每個 symbol 的 crux,而這些內容需要呼叫模型並產生成本。
語言支援分為不同層級,而層級會告訴你呼叫圖的可信程度。TypeScript、JavaScript、Python、Go 和 Java 支援具 scope awareness 的跨檔案解析。Rust、C、C++、C#、Ruby、PHP、Kotlin、Scala、Swift、Elixir、Solidity、OCaml、Zig 和 Dart 會取得 symbols 與通用呼叫邊,這表示某個邊可能只是名稱相符,而不是已解析的參照。若要使用編譯器等級的邊,必須透過 --lsp 選擇啟用,並搭配 rust-analyzer 或 gopls 等 language server。
安裝 Graft 並固定版本
Graft 需要 Node.js 20 或更新版本,採用 MIT 授權。 截至 2026 年 8 月,目前版本為 0.10.1,第一個發布版本 0.1.0 的日期為 2026 年 7 月。請將它視為仍在早期階段的軟體。
npm install -g @nanonets/graft@0.10.1
npm ls -g @nanonets/graftnpm ls -g 應輸出 @nanonets/graft@0.10.1。請刻意固定該版本。單獨使用 npm install -g @nanonets/graft 時,會在執行當下解析 latest 標籤;如果專案每月發布多個 minor 版本,你的同事週一安裝的工具可能會與你週二使用的不同。固定版本可讓所有人的 CLI 旗標與圖形格式保持一致,並由你決定升級時機。
接著,將它整合到你擁有的 repository:
cd /path/to/your/repo
graft init --dry-run
graft initgraft init 會詢問要整合哪些 coding agent,然後建立圖形。請先執行 --dry-run,並查看它計畫修改的檔案清單,因為其中部分檔案位於 repository 外部。graft init 具備冪等性,不會覆寫現有設定,因此再次執行是安全的。
截至 2026 年 8 月,這項整合支援 Claude Code、Cursor、Codex、GitHub Copilot、Google Gemini、Kiro、Windsurf 與 AdaL。Claude Code 的整合最完整:包含 MCP server 項目、顯示圖形大小與過時程度的 statusline、重新建立圖形的 post-edit hooks,以及位於 .claude/ 下的 skill 檔案。其餘工具會取得 instruction 或 rule 檔案,告知 agent 可使用這些工具。因此,「支援」表示 Graft 會寫入整合設定;如果 agent 略過自己的 rules 檔案,也會略過這份地圖。這就是 agent 忽略你為它撰寫的指示 的一般原因,在此同樣適用。
哪些內容會寫入儲存庫,哪些內容不應納入 git
執行 graft init 後,預期會看到以下內容:
graft/:markdown 節點圖與graft/.graph/wiring.json。系統會替你加入.gitignore。.mcp.json:註冊 graft MCP server,讓 Claude Code 啟動它。.claude/settings.json:就地合併,加入 statusline 與 post-edit hooks。AGENTS.md、GEMINI.md、.github/copilot-instructions.md、.cursor/rules/graft.mdc、.kiro/steering/graft.md、.windsurf/rules/graft.md與.adal/skills/graft/SKILL.md:將以 marker 包圍的區段附加到與所選 agents 相符的檔案。~/.codex/config.toml、~/.codex/hooks.json與~/.codex/hooks/graft/graft-hooks.cjs:寫入整台機器,且只有選取 Codex 時才會建立。graft init --no-global會略過這些檔案,graft init --no-hooks則單獨略過 hook shim。
圖形是快取,類似 node_modules。不要將它提交。它會在幾秒內從程式碼重新產生,幾乎每次編輯都會變更;提交後,一行修正就可能變成數百個檔案的差異,而且沒有 reviewer 會閱讀。請提交設定連接內容,包括 AGENTS.md 與 .mcp.json。團隊成員複製儲存庫後,執行 graft build,即可取得自己的本機圖形。
第一次提交前,確認 ignore 規則已正確寫入:
grep -n graft .gitignore
git status --shortgrep 應輸出包含 graft/ 的行,而 git status --short 在 graft/ 下方不應列出任何內容。若輸出中出現 graft/ 下的檔案,表示 ignore 項目遺失,或在其他位置被覆寫。請在提交前修正,因為 git 一旦開始追蹤檔案,之後的 .gitignore 編輯不會停止追蹤該檔案。
如果你想手動註冊 MCP server,或將其固定在已安裝的相同版本,設定項目很簡短:
{
"mcpServers": {
"graft": {
"command": "npx",
"args": ["-y", "@nanonets/graft@0.10.1", "mcp"]
}
}
}代理程式呼叫的擷取工具,而不是 grep
Graft 透過 MCP 提供 6 個工具。graft_find_code 會依工作描述傳回排名後的節點,並附上檔案與行號。graft_file_api 會傳回檔案中的所有簽章,但不包含函式本體。graft_trace_calls 會逐層追蹤數層深度的呼叫者或被呼叫者。graft_find_all 會依符號分組傳回正規表示式比對結果。graft_repo_map 會先提供不熟悉儲存庫的概覽。graft_check_freshness 會回報圖形是否仍與程式碼相符。
每個工具都有對應的 CLI 版本。您可以透過 CLI 檢查代理程式實際取得的內容:
graft map .
graft ask "where do we validate the refresh token"
graft skeleton src/auth/session.ts
graft callers validateRefreshToken
graft callers validateRefreshToken --direction out
graft grep "refresh_token" --jsongraft ask 應列印包含 file:line 參照的排名節點,而不是檔案內容。這就是完整機制:代理程式取得指標後,只需開啟一個檔案,不必讀取 10 個檔案才能找出正確檔案。若您想自行查看圖形,graft viz 會在 localhost 上開啟互動式檢視器。如果針對一個您能在 30 秒內回答的問題,graft ask 沒有傳回有用結果,表示圖形已過期,或您的語言屬於廣泛支援層級;在這種情況下,這份對映也無法協助代理程式。
有一項成本很容易被忽略。整個工作階段中,每個請求的 system prompt 都會注入 6 個工具定義。無論代理程式是否使用這份對映,您都必須支付這項成本。對於小到足以放入 context 的儲存庫,這項固定成本可能高於它所節省的探索成本。
程式碼變更時,圖譜會發生什麼事
結構更新成本低,而且會自動執行。Graft 讀取的是工作樹,而不是 git,因此尚未提交的編輯與已暫存的編輯,對它而言都同樣可見。執行查詢時,只會重新剖析 stat 已變更的檔案。專案文件指出,這項額外成本約為 3 ms;每次回合結束時重新建置,也只會處理程式碼已移動的檔案。設定 GRAFT_NO_REFRESH=1,或傳入 --no-refresh,即可直接使用磁碟上的圖譜,不重新剖析。傳入 --no-reuse 可強制對所有檔案進行冷啟動重新剖析;升級 Graft 本身後應使用此選項。
模型產生的部分則不同,而且最容易在沒有明顯跡象的情況下出錯。摘要與要點會快取。每個節點都會記錄其來源的內容雜湊,因此來源檔案變更時,節點會標記為過期,而不會繼續顯示為最新狀態。這個標記只有在有程序處理它時才有作用。使用 graft build --deep 重新整理,這會再次消耗模型 token。
讓過期狀態可見:
graft check .
echo $?結束狀態 0 表示圖譜與程式碼一致。結束狀態 1 表示兩者已產生偏差。可在 pre-push hook 中執行,或在 CI 中對分支執行,避免一份六個月前建立的映射,對 3 月改寫的程式碼仍然自信地提供答案。
仔細閱讀已發布的基準測試數據
Graft 的主要宣稱是「成本最多降低 4 倍、速度提升 3 倍,同時正確性更佳或不受影響」。這些數據來自該專案發布於 README 的自有基準測試。以下完整列出它回報的兩組執行結果。
The data behind this chart
[
{
"label": "Controlled sweep",
"run_count": 162,
"token_saving_pct": 42,
"tool_call_saving_pct": 46,
"correctness_pct": 93,
"baseline_correctness_pct": 93
},
{
"label": "SWE-bench Verified",
"run_count": 50,
"token_saving_pct": 23,
"tool_call_saving_pct": 25,
"correctness_pct": 66,
"baseline_correctness_pct": 54
}
]受控測試共執行 162 次,涵蓋 2 個儲存庫,其中一個是 Graft 自身的儲存庫;每項任務執行 3 次試驗。結果顯示,token 減少 42%,工具呼叫減少 46%。SWE-bench Verified 測試包含 50 個執行個體,兩組使用相同模型,回報的節省幅度較小:token 減少 23%,工具呼叫減少 25%。第三組測試重現了 5 個已合併的 PocketBase pull request,成本為 11.02 US dollars;基準組則為 13.91 US dollars。
請將這些結果一律視為供應商基準測試。兩項因素限制了這些結果的參考價值。受控測試包含 Graft 自身的儲存庫,也就是其作者調校工具時使用的程式碼庫。SWE-bench Verified 是由知名開放原始碼 Python 專案的問題組成的公開資料集;無論是否出於刻意,工具通常都會針對公開資料集進行最佳化。這兩項測試都不能代表你的私有 monorepo,因為你的程式庫有自己的命名習慣,也有自己的死碼。
正確性值得再次檢視。在受控測試中,正確性沒有變化:使用 map 時為 93%,未使用時為 93%。從 54% 提升至 66% 的結果只出現在 SWE-bench Verified。只要工具能降低 token 成本,而品質維持不變,這仍然是划算的交易。但不要把 SWE-bench 的正確性結果套用到受控測試的 token 結果,然後將兩者合併成單一宣稱。
在相信任何結論前,先測量你自己的 token 差異
唯一重要的數字,是來自你自己 repository 的數字。這個方法只需一個下午。
選擇一項可以完全重複的工作。問題比編輯更適合,因為編輯會變更 repository,第二次執行時就不再是相同的實驗。「哪個模組會對 login route 強制執行速率限制」就是適合的問題形式。
啟用 telemetry,並將資料傳送到自己的終端機:
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_METRICS_EXPORTER=console
claudeconsole exporter 會在收集 metric record 時逐筆輸出。你需要的是 claude_code.token.usage,其中包含 type attribute,值為 input、output、cacheRead 或 cacheCreation。Orientation 會出現在 input 與 cacheRead 中,因為檔案內容會寫入這兩個欄位。將兩者相加。
在 map 已接入的情況下,於每次全新的 session 中執行該工作 3 次。接著從 .mcp.json 移除 graft entry,再執行 3 次。比較中位數,不要比較單次結果,因為 agent 執行結果的差異很大,某次不走運的執行可能會得出與事實相反的結論。同時記錄 tool-call count:tool call 是機制,token 是結果;如果 token 減少但 tool call 沒有下降,表示變動發生在其他地方。
接著扣除 benchmark 未顯示的成本。graft build --deep 每次完整 refresh 都會使用 model token。6 個 tool schema 也會隨每個 request 一起傳送。如果 agent 執行於你租用的 server,為 agent 支出設定硬性上限,就能將這項成本從意外支出轉為預算;而coding agent 的 telemetry 實際回報哪些內容則說明啟用 exporter 後,哪些資料會離開這台機器。
程式碼庫對應圖何時不再有幫助?
- 儲存庫已經小到可放入上下文。 單一小型服務不需要對應圖,而且每次請求仍要支付 6 個工具結構描述的成本。如果代理程式今天只需呼叫工具 1 或 2 次就能找到任何檔案,請略過它。
- 使用的語言屬於廣泛支援層級。 一般呼叫邊可能讓
graft callers遺漏呼叫端,也可能因名稱衝突而產生錯誤的呼叫端。在信任影響範圍前,請使用graft grep確認。 - 圖已過時,但沒有人發現。
graft check發現差異時會以狀態碼 1 結束;這只有在有程序執行它時才有用。應使用 hook 或 CI 步驟,不要依賴人工習慣。 - monorepo 需要限定範圍。 單一 Git monorepo 會依據 workspace file、
go.mod、pyproject.toml或Cargo.toml自動分割,而graft ask "..." --in services/billing/可將查詢限制在單一子專案。促使你為每個套件建立 巢狀 AGENTS.md 檔案 的相同思維,也適用於對應圖。 - 代理程式忽略了連接方式。 先在實際工作階段中查看工具呼叫,再判斷對應圖是否有被使用。代理程式若仍在執行
grep,表示它從未讀取規則檔案。
FAQ
是否應將 graft/ 資料夾提交至 git?
不應該。graft build 會自動將 graft/ 加入您的 .gitignore,因為該圖譜是可重新產生的快取,類似 node_modules。它幾乎每次編輯都會變更,因此提交後會讓數百個產生的檔案掩蓋真正的差異。請提交用來告知代理程式地圖存在的設定,包括 AGENTS.md 和 .mcp.json,並讓每位協作者在本機執行 graft build。首次提交前,請使用 grep -n graft .gitignore 和 git status --short 驗證,因為 git 一旦開始追蹤某個檔案,就會持續追蹤;之後編輯 .gitignore 並不會停止追蹤該檔案。
執行 Graft 需要付費嗎?
結構分析部分不需要。graft build、graft ask、graft check 及 6 個 MCP 擷取工具都是 tree-sitter 操作,不會呼叫模型。graft build --deep 是付費部分:它透過 LLM 撰寫純英文摘要和各符號的核心說明,使用 GRAFT_PROVIDER、GRAFT_API_KEY 和 GRAFT_MODEL 設定,並可搭配 GRAFT_BASE_URL 連線至任何相容於 OpenAI 的端點。您可以只使用結構功能執行 Graft,完全不為圖譜本身消耗 token。
程式碼庫地圖實際能在我的儲存庫中節省多少成本?
不進行測量,沒有人能告訴您。該專案回報,在自身的 162 次執行測試中,使用地圖後 token 減少 42%;在 SWE-bench Verified 中則減少 23%,兩者都以未使用地圖的基準作比較。這些都是廠商基準測試,其中一項部分使用 Graft 自身的儲存庫執行,兩者都無法代表您的私有程式碼。請在設定 CLAUDE_CODE_ENABLE_TELEMETRY=1 和 OTEL_METRICS_EXPORTER=console 後,使用地圖重複執行同一個可重現問題 3 次,再不使用地圖執行 3 次,然後比較 input 和 cacheRead 類型中 claude_code.token.usage 的中位數。
重構時,圖譜會發生什麼變化?
結構會自行重新剖析。Graft 會檢查工作樹,只重新剖析變更過的檔案,因此重新命名會在下一次查詢時被擷取,額外開銷約為 3 ms;它讀取檔案而不是 git 歷史,因此也能看見尚未提交的變更。模型撰寫的摘要才會過時:每個節點都會儲存其來源的內容雜湊,來源變更時會將節點標記為過時,而不是改寫節點。執行 graft check . 查看偏差,再執行 graft build --deep 更新文字部分。
目前哪些程式碼代理程式可以使用 Graft?
截至 2026 年 8 月,graft init 已整合 Claude Code、Cursor、Codex、GitHub Copilot、Google Gemini、Kiro、Windsurf 和 AdaL。Claude Code 的整合最完整:在 .mcp.json 中加入 MCP server 項目、提供 statusline、編輯後 hooks,以及位於 .claude/ 下的 skill 檔案。Codex 取得 AGENTS.md 區段,以及位於 ~/.codex/ 下的全機 entries;graft init --no-global 不會處理這些項目。其他代理程式則會取得 rules 或 steering 檔案。任何其他 MCP client 都能直接使用該 server,只要註冊 npx -y @nanonets/graft@0.10.1 mcp 指令即可。