Tools 與 Function Calling
讓模型呼叫你定義的函式:工具定義、tool_choice、完整三步流程與串流處理。
Tools 與 Function Calling
Tool calling 讓模型能使用外部能力。模型本身不會執行工具,它只會告訴你「請用這些參數呼叫這個函式」;你執行後把結果送回去,模型再據此產生最終回覆。
Inference 把各家供應商的工具介面統一成 OpenAI 格式,所以同一份工具定義可以直接套用在 OpenAI、Anthropic 與 Gemini 模型上。
工具定義
{
"type": "function",
"function": {
"name": "get_weather",
"description": "取得指定城市的目前天氣。",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "城市名稱,例如「台北」" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["city"]
}
}
}
description 寫得越清楚,模型判斷得越準確。parameters 使用 JSON Schema。
tool_choice
| 值 | 行為 |
|---|---|
"auto" | 模型自行決定要不要呼叫工具。有 tools 時的預設值。 |
"none" | 禁止呼叫工具,只產生文字。 |
"required" | 強制至少呼叫一個工具。 |
{"type": "function", "function": {"name": "get_weather"}} | 強制呼叫指定工具。 |
三步流程
第一步:帶著工具送出請求
curl https://api.alphacurve.io/v1/chat/completions \
-H "Authorization: Bearer $INFERENCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o",
"messages": [{ "role": "user", "content": "台北現在天氣如何?" }],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "取得指定城市的目前天氣。",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
}],
"tool_choice": "auto"
}'
第二步:模型回傳 tool_calls
{
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_a1b2c3",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"台北\"}"
}
}
]
},
"finish_reason": "tool_calls"
}
]
}
注意 arguments 是 JSON 字串,不是物件,需要自行 parse。
第三步:把執行結果送回去
tool 訊息必須帶 tool_call_id,且要對應到剛才那一筆 id。
{
"messages": [
{ "role": "user", "content": "台北現在天氣如何?" },
{
"role": "assistant",
"content": null,
"tool_calls": [{ "id": "call_a1b2c3", "type": "function",
"function": { "name": "get_weather", "arguments": "{\"city\":\"台北\"}" } }]
},
{
"role": "tool",
"tool_call_id": "call_a1b2c3",
"content": "{\"temp_c\": 31, \"condition\": \"多雲\"}"
}
]
}
完整範例(Python)
import json
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.alphacurve.io/v1",
api_key=os.environ["INFERENCE_API_KEY"],
)
TOOLS = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "取得指定城市的目前天氣。",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}]
def get_weather(city: str) -> dict:
return {"city": city, "temp_c": 31, "condition": "多雲"}
messages = [{"role": "user", "content": "台北現在天氣如何?"}]
while True:
resp = client.chat.completions.create(
model="openai/gpt-4o",
messages=messages,
tools=TOOLS,
tool_choice="auto",
)
msg = resp.choices[0].message
messages.append(msg.model_dump(exclude_none=True))
if not msg.tool_calls:
print(msg.content)
break
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = get_weather(**args)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result, ensure_ascii=False),
})
完整範例(Node / TypeScript)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.alphacurve.io/v1",
apiKey: process.env.INFERENCE_API_KEY!,
});
const tools: OpenAI.Chat.ChatCompletionTool[] = [{
type: "function",
function: {
name: "get_weather",
description: "取得指定城市的目前天氣。",
parameters: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"],
},
},
}];
function getWeather(city: string) {
return { city, temp_c: 31, condition: "多雲" };
}
const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: "user", content: "台北現在天氣如何?" },
];
for (;;) {
const resp = await client.chat.completions.create({
model: "openai/gpt-4o",
messages,
tools,
tool_choice: "auto",
});
const msg = resp.choices[0].message;
messages.push(msg);
if (!msg.tool_calls?.length) {
console.log(msg.content);
break;
}
for (const call of msg.tool_calls) {
const args = JSON.parse(call.function.arguments);
messages.push({
role: "tool",
tool_call_id: call.id,
content: JSON.stringify(getWeather(args.city)),
});
}
}
串流下的 tool calls
串流時 arguments 會被拆成多段,需依 index 累積:
acc = {}
for chunk in client.chat.completions.create(
model="openai/gpt-4o",
messages=[{"role": "user", "content": "台北天氣?"}],
tools=TOOLS,
stream=True,
):
for tc in (chunk.choices[0].delta.tool_calls or []):
slot = acc.setdefault(tc.index, {"id": "", "name": "", "arguments": ""})
if tc.id:
slot["id"] = tc.id
if tc.function and tc.function.name:
slot["name"] = tc.function.name
if tc.function and tc.function.arguments:
slot["arguments"] += tc.function.arguments
print(acc) # arguments 完整後才可 json.loads
實務建議
- 一定要驗證參數。 模型產生的
arguments是不可信輸入,執行前務必以 schema 驗證。 - 控制迴圈次數。 設定最大回合數(例如 8 回合),避免無限工具迴圈把餘額燒光。
- 工具數量保持精簡。 工具定義佔用輸入 token,而且太多選項會降低選擇準確度。
- 錯誤也要回傳。 工具執行失敗時,把錯誤訊息當成
tool訊息內容送回去,模型通常能自行修正。 - 確認模型支援。 呼叫
GET /v1/models,檢查capabilities.tools。