在前面的系列文章中,我們已經學會了如何使用 Anthropic Messages API 進行基本的文字對話。但 Claude 的能力遠不止於此——透過 Tool Use(工具使用,也稱為 Function Calling),你可以讓 Claude 呼叫你自定義的工具函數,實現查詢天氣、存取資料庫、呼叫第三方 API 等外部操作。這是建構 AI Agent 最關鍵的能力之一。
什麼是 Tool Use(Function Calling)?
Tool Use 讓你在 API 請求中定義一組工具(tools),每個工具描述了名稱、用途和輸入參數的 JSON Schema。當使用者的提問需要外部資訊時,Claude 會自動判斷應該呼叫哪個工具,並產生結構化的工具呼叫請求。你的應用程式負責實際執行該工具,再將結果回傳給 Claude,由 Claude 整合所有資訊後產生最終回覆。
整個流程可以用以下步驟概括:
- 你在 API 請求中定義可用的工具(tools)及其 JSON Schema
- Claude 分析使用者的問題,判斷是否需要呼叫工具
- Claude 回傳
stop_reason: "tool_use"與tool_use區塊 - 你的程式接收工具呼叫請求,執行對應的函數
- 將執行結果以
tool_result格式回傳給 Claude - Claude 根據工具結果產生最終的自然語言回覆
定義工具 Schema
每個工具定義包含三個核心欄位:name(工具名稱)、description(工具描述)和 input_schema(輸入參數的 JSON Schema)。好的工具描述是提升 Claude 判斷準確度的關鍵。
| 欄位 | 類型 | 說明 |
|---|---|---|
name | string | 工具名稱,需符合 ^[a-zA-Z0-9_-]{1,64}$ |
description | string | 詳細描述工具的功能、使用時機和行為特性 |
input_schema | object | 符合 JSON Schema 規範的參數定義 |
input_examples | array(選填) | 範例輸入,幫助 Claude 更理解工具的使用方式 |
以下是一個天氣查詢工具的定義範例:
{
"name": "get_weather",
"description": "取得指定地點的目前天氣資訊。地點需包含城市名稱,可選擇溫度單位。當使用者詢問天氣狀況時使用此工具。",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名稱,例如 Taipei, Taiwan"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "溫度單位,預設為 celsius"
}
},
"required": ["location"]
}
}
工具描述的最佳實踐
- 詳細描述:至少 3-4 句話說明工具的功能、使用時機和限制
- 參數說明:每個參數都應有清楚的 description,包含格式範例
- 合併相關操作:將相關功能合併為一個工具搭配 action 參數,減少選擇模糊性
- 命名空間:使用
github_list_prs、slack_send_message等有意義的前綴 - 精簡回傳:工具回傳結果只包含 Claude 需要的關鍵資訊
在 Messages API 中傳遞工具定義
工具定義透過 API 請求的 tools 頂層參數傳遞。以下是使用 Python SDK 的完整範例:
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=[
{
"name": "get_weather",
"description": "取得指定地點的目前天氣資訊",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名稱,例如 Taipei, Taiwan"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "溫度單位"
}
},
"required": ["location"]
}
}
],
messages=[
{"role": "user", "content": "台北現在的天氣如何?"}
]
)
print(response)
處理 tool_use 回應
當 Claude 判斷需要使用工具時,API 回應的 stop_reason 會是 "tool_use",並在 content 陣列中包含一個或多個 tool_use 區塊。每個區塊包含三個重要欄位:
| 欄位 | 說明 |
|---|---|
id | 此次工具呼叫的唯一識別碼,用於後續回傳結果時對應 |
name | 被呼叫的工具名稱 |
input | 符合工具 input_schema 的參數物件 |
以下是 Claude 回應的範例:
{
"id": "msg_01Aq9w938a90dw8q",
"model": "claude-sonnet-4-6",
"stop_reason": "tool_use",
"role": "assistant",
"content": [
{
"type": "text",
"text": "我來幫你查詢台北目前的天氣狀況。"
},
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "get_weather",
"input": { "location": "Taipei, Taiwan", "unit": "celsius" }
}
]
}
回傳工具執行結果(tool_result)
收到 tool_use 回應後,你需要在你的程式中執行對應的工具函數,然後將結果以 tool_result 格式回傳給 Claude。回傳時需包含 tool_use_id(對應原始呼叫的 id)和 content(執行結果)。
# 假設我們已經執行了天氣查詢函數
weather_data = get_weather_from_api("Taipei, Taiwan", "celsius")
# 將結果回傳給 Claude
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools, # 同樣的工具定義
messages=[
{"role": "user", "content": "台北現在的天氣如何?"},
# 包含 Claude 的完整回應(含 text 和 tool_use)
{"role": "assistant", "content": assistant_response.content},
# 回傳工具執行結果
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "台北目前天氣:晴天,氣溫 28°C,濕度 75%"
}
]
}
]
)
收到 tool_result 後,Claude 會將工具回傳的資料整合進自然語言回覆中,例如:「台北目前是晴天,氣溫 28°C,相對濕度為 75%。」
多輪工具對話流程
在實際應用中,一次對話可能需要多次工具呼叫。建構 Agentic Loop(代理迴圈)的標準做法是持續檢查 stop_reason,直到 Claude 的回應不再是 "tool_use" 為止:
import anthropic
import json
client = anthropic.Anthropic()
tools = [
{
"name": "get_weather",
"description": "取得指定地點的目前天氣資訊",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "城市名稱"}
},
"required": ["location"]
}
},
{
"name": "get_time",
"description": "取得指定地點的目前時間",
"input_schema": {
"type": "object",
"properties": {
"timezone": {"type": "string", "description": "時區,例如 Asia/Taipei"}
},
"required": ["timezone"]
}
}
]
# 工具執行函數
def execute_tool(name, input_data):
if name == "get_weather":
return json.dumps({"temp": "28°C", "condition": "晴天"})
elif name == "get_time":
return json.dumps({"time": "2026-06-02 14:30:00"})
return "未知工具"
# Agentic Loop
messages = [{"role": "user", "content": "台北現在幾點?天氣如何?"}]
while True:
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
messages=messages
)
# 將 assistant 回應加入對話
messages.append({"role": "assistant", "content": response.content})
# 如果不需要工具呼叫,結束迴圈
if response.stop_reason != "tool_use":
break
# 處理所有工具呼叫
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result
})
# 回傳工具結果
messages.append({"role": "user", "content": tool_results})
# 輸出最終回覆
for block in response.content:
if hasattr(block, 'text'):
print(block.text)
多工具同時使用(Parallel Tool Use)
Claude 可以在單一回應中同時呼叫多個工具。例如,使用者問「台北和東京的天氣分別如何?」時,Claude 可能在一次回應中發出兩個 tool_use 區塊。你需要執行所有工具呼叫,並將所有結果一次回傳:
# Claude 的回應可能包含多個 tool_use 區塊
# response.content = [
# TextBlock(text="我來同時查詢兩個城市的天氣。"),
# ToolUseBlock(id="toolu_01...", name="get_weather", input={"location": "Taipei"}),
# ToolUseBlock(id="toolu_02...", name="get_weather", input={"location": "Tokyo"})
# ]
# 回傳所有結果
tool_results_message = {
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01...",
"content": "台北:晴天,28°C"
},
{
"type": "tool_result",
"tool_use_id": "toolu_02...",
"content": "東京:多雲,22°C"
}
]
}
如果你不需要平行工具呼叫功能,可以在 API 請求中設定 "parallel_tool_use": false 來停用它,這樣 Claude 每次只會回傳一個工具呼叫。
強制使用特定工具(tool_choice)
透過 tool_choice 參數,你可以控制 Claude 是否以及如何使用工具。這在某些場景特別有用,例如強制 Claude 使用結構化輸出格式。
| 選項 | 行為 | 使用場景 |
|---|---|---|
auto | Claude 自行決定是否呼叫工具(預設值) | 一般對話場景 |
any | Claude 必須使用至少一個工具 | 確保一定會觸發工具呼叫 |
tool | 強制使用指定的工具 | 結構化輸出、特定流程控制 |
none | 禁止使用任何工具 | 純文字回覆場景 |
# 強制使用特定工具
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
tool_choice={"type": "tool", "name": "get_weather"},
messages=[{"role": "user", "content": "台北的狀況如何?"}]
)
# 必須使用任一工具
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
tool_choice={"type": "any"},
messages=[{"role": "user", "content": "查詢台北資訊"}]
)
錯誤處理
工具執行過程中難免會遇到錯誤,例如 API 逾時、參數無效等情況。你可以在 tool_result 中使用 is_error: true 來告知 Claude 工具執行失敗,Claude 會根據錯誤訊息產生適當的回覆:
# 工具執行失敗時的回傳格式
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "ConnectionError: 天氣服務 API 暫時無法使用(HTTP 500)",
"is_error": True
}
錯誤處理的最佳實踐:
- 提供具體錯誤訊息:避免使用「失敗」等籠統描述,應說明具體原因和建議的下一步
- 包含重試提示:例如「頻率限制超過,請在 60 秒後重試」
- 參數驗證:在執行工具前先驗證參數,提早回報無效輸入
- 使用 strict 模式:在工具定義中加入
strict: true,確保 Claude 的工具呼叫一定符合你的 Schema
Strict 模式:保證 Schema 一致性
在工具定義中加入 strict: true,可以確保 Claude 的工具呼叫輸入一定完全符合你的 JSON Schema,避免缺少必要參數或型別不匹配的問題:
{
"name": "get_weather",
"description": "取得指定地點的天氣資訊",
"strict": true,
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名稱"
}
},
"required": ["location"],
"additionalProperties": false
}
}
完整實戰範例
以下提供三個常見的 Tool Use 應用場景,幫助你理解如何在實際專案中運用。
範例一:天氣查詢 Agent
import anthropic
import json
import httpx
client = anthropic.Anthropic()
# 定義天氣工具
weather_tool = {
"name": "get_weather",
"description": "取得指定城市的即時天氣資訊,包含溫度、濕度、天氣狀況描述。",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名稱,例如 Taipei, Tokyo, New York"
}
},
"required": ["city"]
}
}
def fetch_weather(city: str) -> str:
"""模擬天氣 API 查詢"""
# 實際應用中替換為真實的天氣 API 呼叫
weather_data = {
"Taipei": {"temp": 28, "humidity": 75, "condition": "晴天"},
"Tokyo": {"temp": 22, "humidity": 60, "condition": "多雲"},
"New York": {"temp": 18, "humidity": 55, "condition": "陰天"}
}
data = weather_data.get(city, {"temp": 20, "humidity": 50, "condition": "未知"})
return json.dumps(data, ensure_ascii=False)
def chat(user_message: str):
messages = [{"role": "user", "content": user_message}]
while True:
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=[weather_tool],
messages=messages
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
break
tool_results = []
for block in response.content:
if block.type == "tool_use":
if block.name == "get_weather":
result = fetch_weather(block.input["city"])
else:
result = json.dumps({"error": "未知工具"})
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result
})
messages.append({"role": "user", "content": tool_results})
return response.content[-1].text
# 執行
print(chat("台北和東京今天天氣怎麼樣?"))
範例二:計算機工具
import anthropic
import json
client = anthropic.Anthropic()
calculator_tool = {
"name": "calculator",
"description": "執行數學運算。支援加減乘除、次方、平方根等基本運算。當使用者需要精確的數學計算時使用。",
"input_schema": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "數學表達式,例如 '(15 + 27) * 3' 或 '144 ** 0.5'"
}
},
"required": ["expression"]
}
}
def calculate(expression: str) -> str:
"""安全地執行數學運算"""
try:
# 限制只允許數學運算相關的字元
allowed = set("0123456789+-*/().** ")
if not all(c in allowed for c in expression):
return json.dumps({"error": "不支援的運算符"})
result = eval(expression)
return json.dumps({"result": result})
except Exception as e:
return json.dumps({"error": str(e)})
messages = [{"role": "user", "content": "幫我算一下 (125 + 375) * 2.5 是多少?"}]
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=[calculator_tool],
messages=messages
)
# 處理工具呼叫
if response.stop_reason == "tool_use":
messages.append({"role": "assistant", "content": response.content})
for block in response.content:
if block.type == "tool_use":
result = calculate(block.input["expression"])
messages.append({
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": block.id,
"content": result
}]
})
final = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=[calculator_tool],
messages=messages
)
print(final.content[0].text)
範例三:資料庫查詢工具
import anthropic
import json
import sqlite3
client = anthropic.Anthropic()
# 定義多個資料庫相關工具
db_tools = [
{
"name": "query_database",
"description": "對 SQLite 資料庫執行 SELECT 查詢。僅支援讀取操作,不允許修改資料。回傳查詢結果的 JSON 格式。",
"input_schema": {
"type": "object",
"properties": {
"sql": {
"type": "string",
"description": "SQL SELECT 查詢語句"
}
},
"required": ["sql"]
}
},
{
"name": "list_tables",
"description": "列出資料庫中所有表格名稱及其欄位結構。",
"input_schema": {
"type": "object",
"properties": {},
"required": []
}
}
]
def execute_query(sql: str) -> str:
"""執行 SQL 查詢"""
conn = sqlite3.connect("app.db")
try:
# 安全檢查:僅允許 SELECT
if not sql.strip().upper().startswith("SELECT"):
return json.dumps({"error": "僅允許 SELECT 查詢"})
cursor = conn.execute(sql)
columns = [desc[0] for desc in cursor.description]
rows = [dict(zip(columns, row)) for row in cursor.fetchall()]
return json.dumps({"columns": columns, "rows": rows, "count": len(rows)}, ensure_ascii=False)
except Exception as e:
return json.dumps({"error": str(e)})
finally:
conn.close()
def list_tables() -> str:
"""列出所有資料表"""
conn = sqlite3.connect("app.db")
try:
cursor = conn.execute(
"SELECT name FROM sqlite_master WHERE type='table'"
)
tables = []
for (name,) in cursor.fetchall():
cols = conn.execute(f"PRAGMA table_info({name})").fetchall()
tables.append({
"table": name,
"columns": [{"name": c[1], "type": c[2]} for c in cols]
})
return json.dumps(tables, ensure_ascii=False)
finally:
conn.close()
# 使用範例
messages = [{"role": "user", "content": "資料庫裡有哪些表格?最近的訂單有哪些?"}]
while True:
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=4096,
tools=db_tools,
messages=messages
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
break
tool_results = []
for block in response.content:
if block.type == "tool_use":
if block.name == "query_database":
result = execute_query(block.input["sql"])
elif block.name == "list_tables":
result = list_tables()
else:
result = json.dumps({"error": "未知工具"})
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result
})
messages.append({"role": "user", "content": tool_results})
# 輸出最終回覆
for block in response.content:
if hasattr(block, "text"):
print(block.text)
Server Tools 與 Client Tools 的差異
Anthropic API 支援兩種工具類型。Client Tools(客戶端工具)由你的應用程式執行,包含自定義工具和 Anthropic 定義的 Schema 工具(如 bash、text_editor)。Server Tools(伺服器端工具)由 Anthropic 的基礎設施執行,例如 web_search、code_execution 等,你不需要處理工具呼叫和結果回傳的流程。
| 特性 | Client Tools | Server Tools |
|---|---|---|
| 執行位置 | 你的應用程式 | Anthropic 基礎設施 |
| 需要處理 tool_result | 是 | 否 |
| 範例 | 自定義函數、bash、text_editor | web_search、code_execution |
| 額外計費 | 僅一般 Token 費用 | 可能有額外使用量計費 |
Token 計費注意事項
使用 Tool Use 時,額外的 Token 消耗來自三個部分:工具定義本身(tools 參數中的名稱、描述和 Schema)、tool_use 區塊(API 請求與回應中的工具呼叫內容)、以及 tool_result 區塊(工具執行結果)。此外,啟用工具時系統會自動注入一段特殊的系統提示,不同模型消耗的 Token 數略有不同,大約在 300-350 tokens 左右。
延伸閱讀
- Anthropic 官方 Tool Use 文件:完整的工具使用概覽與 API 參考
- Define Tools:工具定義的詳細說明與最佳實踐
- Handle Tool Calls:處理工具呼叫與回傳結果的完整指南
- Strict Tool Use:使用 strict 模式確保 Schema 一致性
- Parallel Tool Use:平行工具呼叫的進階用法
- Writing Tools for Agents:Anthropic 工程團隊的工具設計心得
總結
Tool Use 是讓 Claude 從「聊天機器人」進化為「AI Agent」的關鍵能力。透過定義工具 Schema、處理工具呼叫迴圈、回傳執行結果,你可以讓 Claude 與任何外部系統互動——從簡單的天氣查詢到複雜的資料庫操作,甚至是多系統的企業級整合。
掌握 Tool Use 的核心概念後,你已經具備了建構功能強大的 AI 應用的基礎。在下一篇文章中,我們將探討更進階的主題,繼續擴展你的 Claude API 開發技能。