Deferred Tools 與 ToolSearch
Claude Code 如何延遲工具 schema,並用 ToolSearch 按需載入 — 讓大型 MCP 工具集不再拖累每個上下文視窗。
你將學到什麼
在第 5 堂課中,你看過讓 skills 得以擴展的雙層技巧:名稱與一行描述放在系統提示中,完整定義只在 skill 被調用時才載入。Fable 5 時代把同樣的想法推廣到了工具本身。不再是每個工具 schema 都預先載入到上下文中 — 許多工具一開始只有名稱,由專門的 ToolSearch(工具搜尋)工具按需取得它們的完整 schema。
完成後,你將了解:
- 什麼是延遲工具(deferred tool),以及它在 session 中如何呈現
- ToolSearch 的三種查詢形式:
select:、關鍵字搜尋、+require - 先載入、再呼叫的流程 — 為什麼延遲工具在取得 schema 之前無法被呼叫
- 這個設計為何存在(劇透:上下文是一種預算)
- 它對大規模 MCP 改變了什麼,包括子代理與 workflow 代理
- 一個會咬到自動化的注意事項:無頭(headless)執行中需互動式驗證的 MCP 伺服器
問題是什麼
模型要呼叫一個工具,該工具的完整參數 schema — 每個欄位、型別、限制與描述 — 都必須在上下文中。對少數幾個內建工具來說,這沒問題。但真實 session 的工具面不會一直維持小規模:
- 檔案、shell、搜尋、任務、artifacts、排程等內建工具
- 你連接的每個 MCP 伺服器,各自貢獻一組自己的工具
- 這一切再乘上每個子代理,因為每個被啟動的代理都有自己的上下文視窗
痛點在這裡:在舊模式下,無論這些工具有沒有被呼叫過哪怕一次,你都得在每一輪為所有 schema 付費。為了查一張工單而連上大型 MCP 伺服器,它的整個工具目錄就會在 session 剩下的時間裡一路跟在上下文中 — 而且出現在你啟動的每個子代理裡。
這正是第 5 堂課中 skills 解決過的問題,在下一層重新浮現。Skill 的定義被延遲了;工具的 schema 卻沒有。解法的形狀是一樣的。
如何運作
延遲工具:只有名稱、沒有 schema
在目前的版本中,不是每個工具 schema 都會預先載入上下文。延遲工具只以名稱出現,列在 <system-reminder> 區塊裡(就是你看過用來承載被回憶起的記憶與模式變更的那種 harness 注入註記)。它們的參數 schema 不存在 — 也就是說它們還不能被呼叫。模型知道這個工具存在;但還不知道要怎麼調用它。
Context window
┌────────────────────────────────────────────────────┐
│ Loaded tools — full schemas, callable now │
│ • Read (full JSON schema) │
│ • Edit (full JSON schema) │
│ • Bash (full JSON schema) │
├────────────────────────────────────────────────────┤
│ Deferred tools — names only, NOT callable yet │
│ <system-reminder> │
│ The following deferred tools are available │
│ via ToolSearch: ... │
│ (schemas not loaded) │
│ </system-reminder> │
└────────────────────────────────────────────────────┘
拿這張圖和第 5 堂課的 skills 圖比較 — 這是同一套雙層架構。第 1 層是便宜的索引(名稱)。第 2 層是昂貴的酬載(完整 schema),按需載入。
ToolSearch:三種查詢形式
ToolSearch 是連接兩層的橋樑。它接收一個查詢,比對延遲工具清單,回傳符合項目的完整 schema 定義。三種查詢形式:
| 查詢 | 形式 | 作用 |
|---|---|---|
"select:Read,Edit,Grep" | 精確選取 | 依名稱取得這幾個確切的工具 |
"notebook jupyter" | 關鍵字搜尋 | 用關鍵字比對延遲清單,回傳最佳匹配 |
"+slack send" | 必含 + 排名 | 要求工具名稱包含 slack,再依其餘詞彙排名 |
當你已經知道工具名稱時(system-reminder 告訴過你),用 select:。當你知道自己想做什麼、但不知道工具叫什麼時,用關鍵字搜尋。當你知道工具屬於哪個伺服器或家族、想在其中縮小範圍時,用 +require。
先載入,再呼叫
一旦 ToolSearch 回傳了某個工具的 schema,該工具就和一開始就載入的任何工具一樣可以呼叫。流程永遠是同樣的兩步:
Deferred tool (name only)
│
│ ToolSearch("select:<name>")
▼
Schema now in context
│
│ normal tool call
▼
Tool executes
沒有第三種狀態,也沒有特殊的呼叫慣例。取得 schema 之後,延遲就隱形了。
為什麼存在:上下文預算
花在未使用工具 schema 上的每個 token,都是無法用在你的程式碼、你的對話、或你的代理推理上的 token。Skills 開創的延遲載入哲學 — 名稱與描述預先載入,完整定義按需載入 — 把固定稅金變成按使用付費的成本。你在準備 grep 的時候才為 Grep 的 schema 付費,而不是在一個從不搜尋任何東西的 session 的每一輪付費。
大規模 MCP
這對 MCP 影響最大。MCP 伺服器是工具面真正變大的地方,而延遲載入從兩方面改變了經濟學:
-
大型伺服器不再對每一輪課稅。 session 連接的 MCP 伺服器的工具以名稱形式待在延遲清單中。大型 MCP 工具集不再撐爆每個上下文視窗 — 你載入實際需要的那兩個工具,而不是整份目錄。
-
子代理與 workflow 代理各自按需載入 schema。 Workflow 與子代理的上下文可以透過 ToolSearch 存取所有 session 連接的 MCP 工具,schema 在需要它的那個代理中個別載入。扇出(fan-out)十個代理不代表每份 MCP schema 要複製十份 — 而是每個代理只取得自己任務所需的部分。
有一個值得在設計時繞開的注意事項:需互動式驗證的 MCP 伺服器 — 例如 claude.ai 的 connectors(連接器)— 在無頭或 cron 執行中可能不存在。 一個在互動 session 中因為你點過驗證流程而可用的工具,在同一個提示照排程執行時可能就是不存在。如果你的自動化依賴這樣的伺服器,請驗證它是否存在,而不是假設它在。
實作範例
我們走一遍完整循環:發現一個延遲工具、載入它、呼叫它。
第 1 步 — session 告訴你哪些被延遲了。 在 session 早期,一則 system-reminder 會列出延遲工具的名稱:
<system-reminder>
The following deferred tools are available via ToolSearch.
Their schemas are NOT loaded — use ToolSearch to load tool
schemas before calling them:
Grep, ... (names only)
</system-reminder>
此時 Grep 看得到但不能呼叫 — 上下文中沒有 schema 可以用來組出有效的呼叫。
第 2 步 — 用精確名稱載入。 既然提醒已經給了你名稱,就用 select: 形式:
ToolSearch("select:Grep")
你可以一次批量載入多個 — "select:Read,Edit,Grep" 一次呼叫就取得三個。當你知道等一下都會用到時,一起載入比三次往返便宜。
第 3 步 — 正常呼叫。 ToolSearch 回傳了完整 schema,所以這個工具現在的行為就跟任何始終載入的工具一樣。Claude 用一般參數呼叫 Grep,得到一般結果。完成。
探索式變體。 假設你不知道工具的名稱 — 你只知道你需要處理 Jupyter notebook。關鍵字搜尋幫你做比對:
ToolSearch("notebook jupyter")
最匹配的延遲工具會連同完整 schema 一起回來。而如果你知道工具在你的 Slack MCP 伺服器上、卻想不起確切名稱:
ToolSearch("+slack send")
這會要求工具名稱包含 slack,並依 send 為候選者排名 — 縮小到單一伺服器的工具,而不是在整個延遲清單裡比對出一堆雜訊。
實務上你很少需要手動觸發這些。當 Claude 判斷某個延遲工具正適合手上的工作時,它會自己完成 ToolSearch 這一步。但了解這套機制,能解釋一些原本看起來很怪的 session 行為 — 比如在使用某個工具之前先停頓一輪去「查」它,或一個在互動時能用、在 cron 執行中卻消失的 MCP 工具。
前後對比
| 之前(所有 schema 預先載入) | 之後(延遲 + ToolSearch) |
|---|---|
| 每個工具 schema 都在每個上下文視窗中 | 名稱預先載入;schema 按需取得 |
| 連接大型 MCP 伺服器對每一輪課稅 | 未使用的 MCP 工具在載入前幾乎零成本 |
| 每個子代理背著完整工具面 | 每個代理只載入其任務所需的 schema |
| 工具面的成長拖累每個 session | 工具面擴展不再有每輪稅金 |
| Skills 延遲、工具永遠即時載入 | 兩層共用同一套延遲載入哲學 |
關鍵洞見
上下文是一種預算,而工具變成了按使用付費。
這個更深層的模式值得點名,因為你在這門課裡已經見過它三次。Skills 預先載入名稱、按需載入內容(第 5 堂課)。記憶預先載入一份索引(MEMORY.md)、按需載入個別記憶檔案(第 29 堂課)。現在工具預先載入名稱、按需載入 schema。這套架構不斷押同一個注:每個上下文視窗放一份便宜的索引,昂貴的酬載只在使用的那一刻才取得。
這個押注正是讓生態系能成長而不劣化的原因。你可以連接更多 MCP 伺服器、安裝更多 skills、累積更多記憶,而不會讓每次新增都拖慢或排擠未來的每個 session。擁有一項能力的邊際成本趨近於零;你只在使用它時付費。
當你設計自己的 MCP 伺服器或代理配置時,把這個原則繼承下來:讓索引便宜且有描述力(好的工具名稱和清楚的一行描述,讓你的工具容易被找到、被選中),而且不必再像從前那樣擔心目錄的大小。
下一堂課
第 31 堂課涵蓋排程自主性:Loops、Wakeups 與 Cron — Claude Code 如何在你不在迴圈中時運作:/loop 與它的動態自我調速模式、ScheduleWakeup(排程喚醒)工具與它的提示快取經濟學、cron 任務與一次性喚醒的差別,以及為什麼一個知道何時該睡的代理,比一個從不睡覺的代理更便宜。