跨模型工具呼叫:一次編寫迴圈,隨意切換模型
Tool Calling Across Any Model: Write the Loop Once, Swap the Model String
跨模型工具呼叫:一次編寫迴圈,隨意切換模型 - 工具定義:使用 OpenAI 相容的 JSON schema 定義 get_weather(location, unit),同一套工具可在所有模型上重用。
本指南示範單一程式碼即可在 OpenAI、Claude 與 Llama 等多個模型上執行工具呼叫,降低切換成本,並提供完整的迴圈流程與錯誤處理建議。
Tool calling,亦稱 function calling,允許模型以結構化 JSON 請求函式。您的程式執行該函式並回傳結果,模型再利用此結果完成回答。每個主要供應商皆支援此功能,但大多數教學只針對單一供應商,導致以 OpenAI 指南撰寫的程式在切換到 Claude 時須重寫。使用我們的 API,您不必重寫任何程式。只需寫一次迴圈,改變模型字串,其餘工具程式碼保持不變。
本指南涵蓋完整流程:定義工具、傳送請求、讀取 tool_calls 回應、執行函式、回傳其結果,並取得最終答案。接著,您只需改變一個字串,即可對三個供應商執行相同程式碼。
我們的 tool calling docs 列出了每個欄位。此指南展示了從開始到結束的完整流程。
總結
- 一個工具,
get_weather(location, unit),僅一次以 OpenAI 相容的 JSON schema 定義。 - 一個迴圈,示範於 cURL、Python 與 JavaScript/TypeScript。傳送
tools、讀取tool_calls、在本機執行、回傳結果,取得最終答案。 - 在三個模型上進行一次測試。相同迴圈可在 Claude、GPT 與開源權重模型上執行,僅需改變模型字串。任何支援工具的模型皆可使用。支援度因模型而異,切換前請先確認。
tool_calls為 JSON 字串引數陣列,請逐一解析每個引數字串,切勿假設為單一次呼叫。
什麼是 tool calling,為何 “function calling” 為同一件事?
Tool calling 與 function calling 只是同一機制的兩種稱呼。您以 JSON schema(包含名稱與輸入)描述函式給模型。模型隨後會請您的程式以特定引數呼叫它。您的程式執行該函式、回傳結果,模型則以此結果完成回答。
OpenAI 推廣了舊稱 “function calling”。大多數 API 現已改稱 “tool calling”。兩者含義相同。您傳送 tools 欄位,模型回傳 tool_calls,我們接受一種 OpenAI 相容的 schema,適用於 Claude、GPT 與 Llama,因此命名差異不會影響程式碼。本指南全程使用 “tool calling”,因為 API 欄位名稱如此。您仍會在舊版檔案與 SDK 中看到 “function calling”。
迴圈包含四個步驟:
- 傳送對話與工具定義。
- 模型回傳一個
tool_calls請求,包含名稱與 JSON 引數。 - 您的程式執行工具並將結果加入對話。
- 再次傳送對話。模型讀取結果並回傳最終答案。

Tool calling 與模型「執行」工具的區別
模型不會自行執行工具。它回傳 tool_calls 請求,您的應用程式負責執行。
它不會呼叫您的天氣 API、查詢資料庫或執行程式碼。它只傳送結構化請求,例如 get_weather(location="Paris"),並停止。您的應用程式執行函式並決定回傳內容。您保持對金鑰、副作用與驗證的控制。模型僅決定何時發問。
定義一個工具
在開始之前,您需要一個 OpenRouter 帳戶和 API 金鑰,您可以在 dashboard 中建立。將其匯出為 OPENROUTER_API_KEY,以便以下範例能從環境讀取。
您還需要一個 SDK:Python 3.10 或更新版本搭配 pip install openai,或 Node 22 或更新版本搭配 npm install openai。其餘不需。天氣函式回傳固定值而非實際呼叫 API,因此 OpenRouter 金鑰即為唯一所需金鑰。
本指南在每個範例中使用同一工具 get_weather(location, unit)。以 OpenAI 相容的 JSON schema 定義它:
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a location.",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name, e.g. 'Paris'",
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
},
},
"required": ["location"],
},
},
}
]接下來,寫下模型可呼叫的函式。實際應用會呼叫天氣 API,這個範例回傳固定值,故你不需要第二個金鑰來跟進。
import json
def get_weather(location, unit="celsius"):
# Real code would call a weather API here.
return {"location": location, "temperature": 18, "unit": unit, "sky": "clear"}現在把 SDK 指向我們的端點。我們接受 OpenAI API 格式,因此如果你已經在使用 OpenAI SDK,只需改變 base_url 與金鑰即可:
import os
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
)若想在接線 SDK 前先檢視原始請求,第一個 cURL 呼叫如下:
curl https://openrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-opus-4.8",
"messages": [{"role": "user", "content": "What'\''s the weather in Paris?"}],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a location.",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name, e.g. Paris"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["location"]
}
}
}]
}'程式碼中的請求/回應迴圈
此函式即整個迴圈,且在本指南中你不會再次修改它。它會傳送訊息與工具,檢查 tool_calls,執行每個工具,追加結果,並請模型給出最終答案:
def run_tool_loop(model, user_message):
messages = [{"role": "user", "content": user_message}]
# First call: the model may ask for a tool.
response = client.chat.completions.create(
model=model, messages=messages, tools=tools,
)
msg = response.choices[0].message
# No tool call? The model answered directly.
if not msg.tool_calls:
return msg.content
# Append the assistant's tool-call turn verbatim, then execute each call.
messages.append(msg)
for call in msg.tool_calls:
args = json.loads(call.function.arguments) # arguments arrive as a JSON string
result = get_weather(**args)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
})
# Second call: the model reads the tool result and writes the final answer.
final = client.chat.completions.create(
model=model, messages=messages, tools=tools,
)
return final.choices[0].message.content以下是在 Node 22+ 上使用指向我們端點的 openai 套件所寫的相同迴圈(JavaScript/TypeScript):
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://openrouter.ai/api/v1",
apiKey: process.env.OPENROUTER_API_KEY,
});
// Same schema as the Python `tools` array above.
const tools = [
{
type: "function",
function: {
name: "get_weather",
description: "Get the current weather for a location.",
parameters: {
type: "object",
properties: {
location: { type: "string", description: "City name, e.g. 'Paris'" },
unit: { type: "string", enum: ["celsius", "fahrenheit"] },
},
required: ["location"],
},
},
},
];
function getWeather(location, unit = "celsius") {
return { location, temperature: 18, unit, sky: "clear" };
}
async function runToolLoop(model, userMessage) {
const messages = [{ role: "user", content: userMessage }];
const response = await client.chat.completions.create({ model, messages, tools });
const msg = response.choices[0].message;
if (!msg.tool_calls) return msg.content;
messages.push(msg);
for (const call of msg.tool_calls) {
const args = JSON.parse(call.function.arguments);
const result = getWeather(args.location, args.unit);
messages.push({
role: "tool",
tool_call_id: call.id,
content: JSON.stringify(result),
});
}
const final = await client.chat.completions.create({ model, messages, tools });
return final.choices[0].message.content;
}在此程式上線前,請先加一件事:arguments 是模型產生的字串,而非已驗證的 payload。模型有時會回傳無效 JSON 或捏造你的 schema 未宣告的引數。請將解析包裝於錯誤處理中,並在傳遞給函式前檢查鍵值是否符合你的 schema。此範例為簡化程式碼,故略過此步驟。
以下所有範例均以不同的模型字串呼叫 run_tool_loop 或 runToolLoop。
對 Claude 執行
第一次執行只需提供模型名稱。傳入 Anthropic 模型,四個步驟即可在一次呼叫 run_tool_loop 中完成:
answer = run_tool_loop(
"anthropic/claude-opus-4.8",
"What's the weather in Paris?",
)
print(answer)
# → "It's currently 18°C and clear in Paris."那一次呼叫 run_tool_loop 執行了所有四個步驟並產生兩次 API 請求。第一次請求回傳一個 tool_calls 請求。你的程式執行了 get_weather 並追加結果。第二次請求回傳最終答案。
模型代稱在不同版本間會變動,請在依賴前確認我們的 模型目錄 上的正確字串。
閱讀回應
函式名稱與引數位於 tool_calls 陣列中。在第一個回應中,choices[0].message 如下:
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\": \"Paris\", \"unit\": \"celsius\"}"
}
}
]
}這裡有兩點需要注意。首先,arguments 是 JSON 編碼的字串,而非物件,必須用 json.loads 或 JSON.parse 解析。其次,tool_calls 是陣列,故你的程式必須處理每個回應中的多個呼叫。
對 GPT 執行
Claude 執行證明迴圈有效。下一次執行顯示相同程式碼也能在另一個供應商上運作。唯一的修改是字串:
answer = run_tool_loop(
"openai/gpt-4o",
"What's the weather in Paris?",
)
print(answer)
# → "The weather in Paris is 18°C and clear right now."與 Claude 範例唯一不同的是 anthropic/claude-opus-4.8 → openai/gpt-4o。工具 schema、迴圈、解析與結果訊息皆保持不變,因為我們對兩者回傳相同的 tool_calls 格式。此規則適用於任何具備工具功能的模型。切換前請先確認工具支援。

對開源模型執行
專有模型通常共享格式,因此開源權重模型是更強的測試。再次修改字串:
answer = run_tool_loop(
"meta-llama/llama-3.3-70b-instruct",
"What's the weather in Paris?",
)
print(answer)
# → "Right now in Paris it's 18°C with clear skies."這樣就能在同一程式碼基底上支援三個供應商,無需重寫。開源權重模型回傳與 Claude 和 GPT 相同的 tool_calls 結構。你可以將模型名稱存於設定檔,並在路由器、測試或備援中改動,而不必觸碰工具程式碼。
邊界情況與注意事項
上述迴圈涵蓋常見情況。實作中,四項因素可能導致失效:平行工具呼叫、串流、缺乏工具支援的模型,以及控制模型何時呼叫工具。
平行工具呼叫
部分模型會在單一回應中回傳多個工具呼叫。若詢問兩個城市,模型可能同時回傳 get_weather("Paris") 與 get_weather("Tokyo")。因此迴圈會以 msg.tool_calls 迭代,而非讀取 [0]。你的程式必須為每個呼叫追加一個工具結果,每個結果攜帶其自身的 tool_call_id,再送出下一個請求。範例迴圈已完成此操作。你亦可在請求中設定 parallel_tool_calls: false,讓模型一次只請求一個工具。
串流工具呼叫
當你串流回應時,工具呼叫會以稱為 delta 的片段到達。模型可能會將引數字串拆分到多個 chunk,並逐一建立 tool_calls 陣列。請勿在串流結束前執行工具。按索引收集 delta,等待完成訊號,解析完整的引數字串,然後執行。
不支援工具的模型
並非所有模型都支援工具。若你將 tools 傳送至沒有工具端點的模型,我們會回傳 404 錯誤,說明沒有端點支援工具使用。在備援路徑中,非工具模型可以忽略該欄位並回傳純文字,且不包含任何 tool_calls。此失敗不會產生錯誤,因此在將模型加入迴圈前請先確認支援度。瀏覽我們的 tool‑calling collection,或開啟任一模型的頁面於 catalog,並確認 supported_parameters 包含 tools。
強制或停用工具呼叫
使用 tool_choice 來控制模型是否呼叫工具:
tool_choice 值 | 行為 |
|---|---|
"auto" | 模型決定是否呼叫工具。這是預設行為,只要請求中存在 tools。 |
{"type": "function", "function": {"name": "get_weather"}} | 模型必須呼叫該特定工具。 |
"none" | 此請求已阻止工具呼叫。 |
OpenAI 相容的 schema 亦定義 "required",要求模型至少執行一次工具呼叫。對於更嚴格值的支援因模型而異,請於我們的 tool‑calling docs 確認行為,並於正式環境使用前對目標模型進行測試。
結論
你定義了一個工具,寫了一個工具呼叫迴圈,並透過改變單一字串,將相同程式碼執行於 Claude、GPT 與開源模型。工具定義與迴圈在模型變動時不會改變,因此更換供應商只是設定編輯,而非重寫。
三件事須記得:
- 工具定義與迴圈可跨模型使用。只需寫一次。所有具備工具功能的模型皆使用相同的
tools與tool_calls格式,因而更換供應商僅需一行編輯, tool_calls為 JSON 字串引數陣列。迴圈遍歷該陣列並解析每個引數字串。永遠不要假設回應僅包含一次呼叫,- 工具支援因模型而異。在上線前請先確認模型支援工具,
欲使用自訂工具,請從我們的 tool‑calling docs 之欄位參考開始,並挑選任一模型於 tool‑calling collection。
常見問題
什麼是 LLM 的工具呼叫?
工具呼叫允許模型請求你的程式在其代表下執行某些操作,最常見的是函式。模型會回傳一個結構化的 JSON 請求,列出工具與其引數;你的應用程式執行它,並將結果回傳,讓模型完成答案。模型本身不會執行任何動作。
工具呼叫與函式呼叫有何差異?
兩者功能上無差異,因為兩個名稱指向同一機制。「函式呼叫」是較舊的術語,由 OpenAI 推廣;「工具呼叫」則是目前大多數 API 採用的術語。在我們的 API 中,它們對應同一請求/回應結構,即 tools 欄位與 tool_calls 欄位,因而為一者所寫的程式碼亦適用於另一者。
如何使用 API 實作函式呼叫?
將你的函式描述為 JSON schema,並將其放入 tools 欄位與訊息一起傳送。若回應包含 tool_calls,解析引數字串並執行函式。接著將結果作為帶有相符 tool_call_id 的訊息附加,並再次傳送對話以取得最終答案。
工具呼叫在不同模型間是否相同?
是的,適用於具備工具功能的模型。我們接受一種 OpenAI 相容的 schema,並為 Claude、GPT 與開源權重模型回傳一個 tool_calls 格式,因此同一套工具定義與流程在所有模型上皆可直接使用。唯一變動的是模型字串。工具支援因模型而異,請逐一確認。
哪些模型支援工具呼叫?
工具支援是按模型而非統一。請瀏覽我們的 工具呼叫集合,查閱經精選的具備工具功能的模型,或在我們的 目錄中開啟任一模型頁面,檢查 supported_parameters 是否包含 tools。若模型沒有工具功能端點,我們會回傳 404 錯誤,說明沒有任何端點支援工具使用;在備援路徑中,非工具模型可能會忽略該欄位並以純文字回覆。
開源模型能做函式呼叫嗎?
可以。具備工具功能的開源權重模型,例如 Llama 3.3 70B Instruct,回傳與專有模型相同的 tool_calls 結構,因此相同的流程即可直接使用。請先在模型頁面查閱 supported_parameters 中的 tools,因為支援度會因開源權重系列與微調版而異。
什麼是平行工具呼叫?
平行工具呼叫是指在單一模型回應中回傳多個工具請求,例如針對不同城市的兩個 get_weather 呼叫。您的程式碼必須遍歷 tool_calls 陣列,並為每一次呼叫附加一條結果訊息,每條訊息皆攜帶其自己的 tool_call_id,再將對話傳回。
參考資料
- 工具呼叫檔案,我們的標準參考,說明
tools與tool_calls欄位。 - 工具呼叫模型集合,精選具備工具功能的模型。
- 模型目錄,完整清單,列出每個模型的
supported_parameters。 - OpenAI Python SDK,提供
tool_choice的預設行為與上述提及的arguments欄位型別的原始碼。
來源:openrouter blog · openrouter.ai