上週我在幫公司的 AI Agent 接上第 47 個 MCP server 時,API 帳單跳了一個數字讓我差點從椅子上摔下來。

不是因為用量暴增。是因為每一次 API 呼叫,光是把 36 個 MCP server 的工具定義塞進 context,就吃掉了將近 40,000 tokens。模型還沒開始思考,錢已經燒了一半。

3 月 5 日 OpenAI 發布 GPT-5.4 時,benchmark 數字和 Computer Use 搶走了所有目光。但對我來說,最值得注意的功能只有一個——Tool Search。

工具爆炸問題:你可能已經踩到了

先說個數字。一個標準的 function calling 工具定義,包含名稱、描述、參數 schema,平均佔 200-500 tokens。聽起來不多?

算一下:

  • 10 個工具 → ~3,000 tokens(還好)
  • 50 個工具 → ~15,000 tokens(開始痛)
  • 200 個工具 → ~60,000 tokens(每次呼叫都在燒錢)
  • 500 個工具 → ~150,000 tokens(恭喜,光工具定義就用掉一般模型 context 的一半)

問題不只是成本。工具定義佔的 context 越多,留給實際對話和推理的空間就越少。我在實測中觀察到,當工具定義超過 context 的 30%,模型選錯工具的機率明顯上升。它會開始「分心」——在幾百個工具的描述裡迷路。

你可能會說:「我的 Agent 哪需要 500 個工具?」

如果你在做企業級的 Agent 系統,接了 CRM、ERP、專案管理、程式碼倉庫、監控系統、文件平台⋯⋯每個系統暴露 10-20 個 API endpoint,加起來就是幾百個。這不是假設性場景,這是我現在每天面對的現實。

OpenAI 的做法直覺到讓人想問「為什麼之前沒有」。

傳統方式:每次 API 呼叫,把所有工具定義塞進 request。

1
2
3
4
5
6
7
8
9
10
11
12
// 傳統:一次全塞
{
"model": "gpt-5.2",
"messages": [...],
"tools": [
// 工具 1 定義(~300 tokens)
// 工具 2 定義(~250 tokens)
// ...
// 工具 200 定義(~400 tokens)
// 總共:~60,000 tokens,還沒開始對話
]
}

Tool Search 方式:只傳一份輕量的工具清單(名稱 + 一句描述),模型需要時才查詢完整定義。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Tool Search:按需載入
{
"model": "gpt-5.4",
"messages": [...],
"tools": [
{
"type": "tool_search",
"tool_search": {
"tools": [
{"name": "create_jira_ticket", "description": "建立 Jira 工單"},
{"name": "query_database", "description": "查詢 PostgreSQL 資料庫"},
{"name": "send_slack_message", "description": "發送 Slack 訊息"}
// ...還有 197 個,但每個只佔 ~20 tokens
]
}
}
]
}

模型看到使用者的需求後,先在輕量清單中搜尋可能需要的工具,再呼叫 tool search 取得完整的參數 schema。完整定義會被注入到 context 尾端,不會破壞前面的 cache。

OpenAI 用 Scale 的 MCP Atlas benchmark 測試了這個方法:36 個 MCP server、250 個任務,tool search 配置減少了 47% 的 token 用量,準確率不變。

47%。將近一半的 token 省下來了。

這不是新概念:RAG for Tools

如果你做過 RAG(Retrieval Augmented Generation),Tool Search 的思路會讓你有似曾相識的感覺。

RAG 解決的問題:模型不可能把整個知識庫塞進 context,所以先用向量搜尋找到相關文件,再把文件塞進 context。

Tool Search 解決的問題:模型不可能把所有工具定義塞進 context,所以先用搜尋找到相關工具,再把工具定義塞進 context。

一模一樣的模式。區別在於 OpenAI 把這個流程內建到模型裡,不需要開發者自己搭 pipeline。

但如果你用的不是 GPT-5.4 呢?或者你需要更細粒度的控制?

自己做 Tool Search:三種實作方式

方式一:語意搜尋(最通用)

把工具描述做成 embedding,使用者輸入進來時,先跑向量搜尋找到最相關的 5-10 個工具,只把這些工具的完整定義傳給模型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import numpy as np
from openai import OpenAI

client = OpenAI()

# 事先建好工具的 embedding 索引
tool_embeddings = {}
for tool in all_tools:
response = client.embeddings.create(
model="text-embedding-3-small",
input=f"{tool['name']}: {tool['description']}"
)
tool_embeddings[tool["name"]] = {
"embedding": response.data[0].embedding,
"definition": tool
}

def search_tools(user_message: str, top_k: int = 8) -> list:
"""根據使用者輸入,找出最相關的工具"""
query_embedding = client.embeddings.create(
model="text-embedding-3-small",
input=user_message
).data[0].embedding

scores = []
for name, data in tool_embeddings.items():
# cosine similarity
score = np.dot(query_embedding, data["embedding"])
scores.append((name, score, data["definition"]))

scores.sort(key=lambda x: x[1], reverse=True)
return [item[2] for item in scores[:top_k]]

# 使用
relevant_tools = search_tools("幫我建一張 Jira ticket 追蹤這個 bug")
response = client.chat.completions.create(
model="claude-opus-4-6", # 任何模型都能用
messages=[{"role": "user", "content": "幫我建一張 Jira ticket 追蹤這個 bug"}],
tools=relevant_tools # 只傳 8 個工具,不是 200 個
)

優點:模型無關,Claude、Gemini、開源模型都能用。
缺點:語意搜尋可能漏掉邊緣案例的工具。

方式二:分類路由(最快)

如果你的工具可以按領域分組,先判斷使用者意圖屬於哪個分類,再載入那個分類的工具。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
TOOL_CATEGORIES = {
"project_management": ["create_jira_ticket", "update_ticket", "list_sprints", ...],
"database": ["query_database", "create_table", "run_migration", ...],
"communication": ["send_slack_message", "create_channel", "send_email", ...],
"monitoring": ["get_metrics", "create_alert", "query_logs", ...],
}

def route_to_category(user_message: str) -> str:
"""用小模型快速分類意圖"""
response = client.chat.completions.create(
model="gpt-5.4-mini", # 用最便宜的模型做路由
messages=[{
"role": "system",
"content": f"分類使用者意圖。只回覆分類名稱:{list(TOOL_CATEGORIES.keys())}"
}, {
"role": "user",
"content": user_message
}],
max_tokens=20
)
return response.choices[0].message.content.strip()

優點:延遲最低,token 消耗最小。
缺點:跨領域的需求(「查資料庫然後發 Slack 通知」)需要額外處理。

方式三:兩階段呼叫(最精確)

第一階段讓模型看工具名稱和一句描述,選出它需要的工具。第二階段再傳入完整定義。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def two_stage_tool_call(user_message: str) -> dict:
# 階段一:選工具
tool_index = [
{"name": t["name"], "description": t["description"][:80]}
for t in all_tools
]

selection = client.chat.completions.create(
model="claude-opus-4-6",
messages=[{
"role": "system",
"content": (
"根據使用者需求,從工具清單中選出需要的工具名稱。"
"回覆 JSON 陣列,例如:[\"tool_a\", \"tool_b\"]"
)
}, {
"role": "user",
"content": f"需求:{user_message}\n\n可用工具:{tool_index}"
}]
)

selected_names = json.loads(selection.choices[0].message.content)

# 階段二:用完整定義執行
selected_tools = [t for t in all_tools if t["name"] in selected_names]

return client.chat.completions.create(
model="claude-opus-4-6",
messages=[{"role": "user", "content": user_message}],
tools=selected_tools
)

這本質上就是 GPT-5.4 在模型內部做的事,只是我們手動拆成兩步。

實戰考量:哪些坑要注意

Cache 友好性。 GPT-5.4 的 tool search 把新工具定義注入到 context 尾端,這保留了前面訊息的 cache。如果你自己做,記得也要這樣處理——別把動態載入的工具定義插到 system prompt 中間,那會讓整個 cache 失效。

Fallback 機制。 語意搜尋不是 100% 準確的。我的做法是:如果模型在第一輪沒有呼叫任何工具(可能是搜尋漏掉了),自動觸發一次全量工具載入重試。一次重試的成本比每次都傳全量低得多。

工具描述的品質決定一切。 Tool search 是靠描述來匹配的。如果你的工具描述是「執行操作 A」這種廢話,搜尋準確率會很低。每個工具描述應該包含:它做什麼、在什麼場景用、輸入輸出是什麼。

1
2
3
4
5
# ❌ 爛描述
{"name": "process_data", "description": "處理資料"}

# ✅ 好描述
{"name": "process_data", "description": "從 PostgreSQL 查詢銷售資料,按月份彙總,回傳 CSV 格式。用於產生月報表。"}

監控工具選擇的準確率。 上線之後,記錄每次 tool search 選了哪些工具、最終用了哪些。如果「選了但沒用」的比例超過 50%,說明你的搜尋太寬鬆;如果使用者經常需要手動指定工具,說明搜尋太嚴格。

那我該用 GPT-5.4 內建的,還是自己做?

簡單的判斷框架:

情境 建議
工具 < 30 個 不需要 tool search,全部傳就好
工具 30-100 個,用 GPT-5.4 用內建 tool search,省事
工具 30-100 個,用其他模型 自己做語意搜尋或分類路由
工具 > 100 個 不管用什麼模型,都應該自己做,加上 fallback
需要跨模型切換 自己做,保持模型無關性

我目前的做法是自己做語意搜尋 + 分類路由的混合方案,因為我們的 Agent 需要在不同模型之間切換(成本考量,簡單任務用便宜模型,複雜任務用 Opus 或 GPT-5.4)。

往前看:工具管理會變成基礎設施

MCP(Model Context Protocol)讓工具的定義和發現有了標準協議。Tool Search 讓工具的載入變成按需。下一步很可能是工具的版本管理和權限控制。

當 AI Agent 接的工具數量從幾十個變成幾百個,「工具管理」本身就會變成一個需要專門設計的子系統——類似微服務架構裡的服務發現(service discovery)。

你不會讓一個微服務在啟動時把所有其他服務的 API schema 載入記憶體。同樣的道理,你不應該讓 AI Agent 在每次對話時載入所有工具定義。

Tool Search 不是什麼革命性突破。它只是把我們在分散式系統裡用了十年的模式,搬到了 LLM 的世界。但有時候,把已知的好模式用在新場景,就是最好的工程決策。