跳到正文
openrouter blog·· 2026-08-12精選AI 評分69

跨模型工具呼叫:一次編寫迴圈,隨意切換模型

Tool Calling Across Any Model: Write the Loop Once, Swap the Model String

AI 導讀

跨模型工具呼叫:一次編寫迴圈,隨意切換模型 - 工具定義:使用 OpenAI 相容的 JSON schema 定義 get_weather(location, unit),同一套工具可在所有模型上重用。

推薦理由

本指南示範單一程式碼即可在 OpenAI、Claude 與 Llama 等多個模型上執行工具呼叫,降低切換成本,並提供完整的迴圈流程與錯誤處理建議。

正文 · AI 翻譯

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”。

迴圈包含四個步驟:

  1. 傳送對話與工具定義。
  2. 模型回傳一個 tool_calls 請求,包含名稱與 JSON 引數。
  3. 您的程式執行工具並將結果加入對話。
  4. 再次傳送對話。模型讀取結果並回傳最終答案。

Diagram of the four-step tool-calling loop: your app sends messages and tool definitions to the model, receives a tool_calls request, executes get_weather locally, returns the result with its tool_call_id, and receives the final answer

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 格式。此規則適用於任何具備工具功能的模型。切換前請先確認工具支援。

Diagram titled one tool, three models: a single get_weather tool definition and loop on the left feed one model string that fans out to Claude, GPT, and Llama, all returning the same tool_calls shape

對開源模型執行

專有模型通常共享格式,因此開源權重模型是更強的測試。再次修改字串:

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,再將對話傳回。

參考資料

來源:openrouter blog · openrouter.ai