降低工具密集型工作流程的成本
使用工具的代理(agent)會在每次請求時再次傳送所有內容。程式化工具呼叫(Programmatic tool calling)與提示快取各自削減其成本的不同部分。
本影片中的示範錄製於 2026 年 9 月。它在 Claude Sonnet 5(claude-sonnet-5)上執行。結果在較新的模型上可能有所不同。
使用工具的代理會在每次請求時再次傳送所有內容,因此其成本會隨著每次工具呼叫而增加。 每個請求都會攜帶工具定義、系統提示、問題,以及到目前為止的每個工具結果。這些都是它的輸入 token,而每一個都會被計費。
程式化工具呼叫與提示快取都能削減這項成本。
Meridian 是一家擁有 40 名工程師的虛構工程顧問公司。它的代理收到了一個問題:2026 年 6 月哪些工程師的使用率低於 70%?該代理有兩個工具。list_employees 會回傳名冊,而 get_employee_hours 會回傳一名工程師的工時表。
回答這個問題花了代理 42 次請求與 1,124,799 個輸入 token。整次執行的 token 成本為 2.29 美元。
測量每個請求中的輸入 token
每次回應傳回時都印出 usage.input_tokens,因為這些計數會告訴您成本落在何處。
resp = client.messages.create(...)
print(resp.usage.input_tokens, "input tokens")Meridian 的代理執行一個典型的迴圈,其中每個回應都要求一次工具呼叫。以下是其中兩次的計數。
| 請求 | 它攜帶的內容 | 輸入 token |
|---|---|---|
| 請求 1 | 工具定義、系統提示與問題 | 606 |
| 請求 37 | 相同內容,再加上 36 個先前的工具結果 | 47,905 |
請求 1 只攜帶每個請求都會傳送的內容。後續的請求則增加了工具呼叫及其結果。比較這兩個計數,就能看出哪個部分增長了。
讓工具結果不被 Claude 讀取
透過程式化工具呼叫(在新分頁中開啟),Claude 會撰寫一個呼叫您工具的腳本,而程式碼執行工具會在容器中執行它。只有腳本印出的內容會傳達給 Claude。 Meridian 的 40 份工時表留在腳本那端,而 Claude 只會收到它簡短的列印輸出。
請先檢查您的工具與模型。 截至 2026 年 10 月,程式化工具呼叫不支援標記為 strict: true 的工具(在新分頁中開啟)。它也不支援 disable_parallel_tool_use,這個 tool_choice 設定會限制 Claude 在每個回應中只能呼叫一個工具。tool_choice 無法強制 Claude 從腳本中呼叫某個工具。
Claude Haiku 4.5 不支援此功能。此功能可在 Claude API、AWS 上的 Claude Platform,以及託管於 Anthropic 的 Microsoft Foundry 部署上執行。
啟用程式化工具呼叫需要四項變更。
- 將程式碼執行工具加入您的
tools清單。 - 標記腳本可以呼叫的工具。 為每一個工具提供一個
allowed_callers欄位,指名版本code_execution_20260120或更新版本(在新分頁中開啟)的程式碼執行工具。 - 將容器的 ID 傳回。 從每個因工具呼叫而停止的回應中讀取它,並在傳回工具結果的請求中一併傳送。
- 描述每個工具的結果。 在描述中說明其形式,因為腳本必須解析它。以下是 Meridian 對
get_employee_hours的描述。
Returns one engineer's hours for the month as JSON: {"name": str, "billable_hours": float, "utilization": float 0-1}
以下是 Meridian 在這些變更之後的迴圈。
CODE_EXECUTION = "code_execution_20260120"
TOOLS = [
{"type": CODE_EXECUTION, "name": "code_execution"},
{**LIST_EMPLOYEES, "allowed_callers": [CODE_EXECUTION]},
{**GET_EMPLOYEE_HOURS, "allowed_callers": [CODE_EXECUTION]},
]
messages = [{"role": "user", "content": QUESTION}]
container = None
while True:
resp = client.messages.create(
model="claude-sonnet-5", max_tokens=4096,
tools=TOOLS, messages=messages, container=container,
)
print(resp.usage.input_tokens, "input tokens")
if resp.stop_reason not in ("tool_use", "pause_turn"):
break
container = resp.container.id
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason == "tool_use":
messages.append({"role": "user", "content": run_tools(resp)})
print("".join(b.text for b in resp.content if b.type == "text"))LIST_EMPLOYEES與GET_EMPLOYEE_HOURS是 Meridian 的工具定義。- 您自己的
run_tools會執行每個工具呼叫並回傳結果。 - 在
pause_turn時,迴圈會將回應傳回並繼續。
當腳本呼叫工具時,回應的 stop_reason 會是 "tool_use"。您的程式碼執行該工具,而結果會傳給腳本,而不是 Claude。腳本可以平行執行工具,因此一個回應可以容納不止一個工具呼叫。
以下是同一個問題以兩種方式回答的結果。
| 執行方式 | 請求數 | 工具呼叫數 | 輸入 token | Token 成本 |
|---|---|---|---|---|
| 傳統迴圈 | 42 | 41 | 1,124,799 | 2.29 美元 |
| 程式化工具呼叫 | 3 | 41 | 28,486 | 0.0667 美元 |
兩次執行都使用相同的問題、相同的工具,以及 claude-sonnet-5。成本採用截至 2026 年 9 月的價格(在新分頁中開啟),而程式碼執行時間則與 token 分開計費。
程式化工具呼叫適合彼此不相依的查詢。當 Claude 必須在每個結果上進行推理後才能做出下一次工具呼叫時,它的幫助就很有限。
快取會重複出現的工具定義
透過提示快取,API 會儲存您請求的前綴,也就是到您標記的快取點為止的部分。再次讀取該前綴的成本會低得多。
第二個 Meridian 代理有 14 個工具,其定義總共有 3,305 個 token,每個請求都會再次傳送。
將 cache_control 放在最後一個工具上,即可標記快取點,整個清單就會被快取。 只需一行程式碼:
TOOLS[-1]["cache_control"] = {"type": "ephemeral"}在 Claude Sonnet 5 上,讀取前綴的成本為正常輸入價格的十分之一(在新分頁中開啟)。寫入的成本則為正常輸入價格的 1.25 倍。
快取的前綴預設會持續五分鐘,而每次使用都會更新它。對於間隔較長的請求,可在 cache_control 中加入 "ttl": "1h",讓它保留一小時。寫入一小時的快取成本為正常輸入價格的兩倍(在新分頁中開啟)。
Meridian 以有快取與沒有快取的方式執行了相同的八個問題。其總輸入成本從 0.0554 美元降至 0.0154 美元。
透過自動快取(在新分頁中開啟),您只需在請求的頂層放置一個 cache_control 欄位,API 就會為您放置快取點。
啟用快取後,請留意以下三件事:
- 更動任何工具定義,您就會失去整個快取。
- 在 Sonnet 5 上,快取的前綴至少要有 1,024 個 token。當前綴較短時,不會有任何內容被快取,也不會出現任何錯誤訊息。
- 啟用快取後,
input_tokens不包含已快取的前綴,因此一個請求的真實總量是input_tokens、cache_read_input_tokens與cache_creation_input_tokens三者相加。
在適合的地方結合這些功能
按以下順序加入它們。
- 先從快取開始。 只需一行程式碼。
- 測試程式化工具呼叫。 以有無
allowed_callers的方式執行一些您平常的流量,然後比較每次執行所計費的輸入 token。
針對下方兩個 Meridian 請求,找出佔輸入 token 大部分的部分,再挑選能削減它的功能。
重點整理
- 每個請求都會再次傳送所有內容,因此您會一再為舊的工具結果付費。
- 每次回應傳回時都印出
usage.input_tokens,以便了解請求中哪個部分的成本最高。 - 先從快取開始。在清單中最後一個工具上加入
cache_control。 - 當您的工具呼叫彼此不相依時,請搭配程式碼執行工具與
allowed_callers設定程式化工具呼叫。並在每個後續請求中傳送容器的 ID。