函式呼叫:Responses API 與 Chat Completions 比較
從傳輸協定層比較函式定義、呼叫 ID、結果訊息、串流參數、授權、冪等性與路由相容性。

Responses 與 Chat Completions 都能要求應用執行函式,但兩者表示工具呼叫流程的方式不同。Chat Completions 把定義放在 tools[].function,回傳 message.tool_calls,並以 role: "tool" 訊息接收結果;Responses 使用扁平的函式定義,回傳帶型別的 function_call 輸出項目,再透過 call_id 連結 function_call_output。
模型不會執行你的函式。應用必須驗證參數、確認授權、精確執行、回傳結果,並在請求重試時避免重複副作用。
工具呼叫流程有四個步驟
1. 宣告允許呼叫的函式及其參數結構描述
2. 接收模型提出的一次或多次函式呼叫
3. 在應用程式中驗證、授權並執行每次呼叫
4. 使用每次呼叫的關聯 ID 回傳對應結果
第四步完成後,模型才能依據工具結果產生最終答案。若模型再次要求工具,使用新的呼叫 ID 重複流程。OpenAI Function Calling 指南也將其定義為多步交換;把第一個工具呼叫當成最終答案是常見整合錯誤。
比較傳輸契約
| 項目 | Responses API | Chat Completions |
|---|---|---|
| 函式定義 | 扁平 tools[],包含 name、description、parameters、strict |
同欄位放在 tools[].function |
| 模型提出的呼叫 | function_call 輸出項目 |
助理訊息中的 tool_calls[] |
| 關聯識別 | call_id |
工具呼叫的 id,結果使用 tool_call_id |
| 函式名稱 | function_call.name |
tool_calls[].function.name |
| 參數 | function_call.arguments JSON 字串 |
tool_calls[].function.arguments JSON 字串 |
| 結果 | function_call_output 輸入項目 |
role: "tool" 訊息 |
| 串流參數 | 帶型別的參數增量事件 | delta.tool_calls[] 片段 |
| 最終文字 | 帶型別的輸出項目 / 輔助函式 | choices[0].message.content |
不要依陣列位置關聯呼叫。平行呼叫與串流片段的完成順序可能不同;明確的呼叫 ID 才是穩定的關聯鍵。
定義嚴格的函式參數結構
以下唯讀函式只允許查詢配送狀態:
{
"type": "object",
"properties": {
"order_id": {
"type": "string",
"pattern": "^ORDER-[0-9]{4}$"
}
},
"required": ["order_id"],
"additionalProperties": false
}
strict: true 會要求支援的模型遵循參數結構描述,但應用仍須解析與驗證。pattern 等 JSON Schema 功能可能依模型或供應商而異,因此函式參數也應通過 Structured Outputs 相容性測試。
使用 Responses API 實作
Responses 中的函式定義是扁平物件:
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.MODELFLARE_API_KEY,
baseURL: "https://modelflare.dev/v1",
});
const model = process.env.MODEL_ID;
if (!model) throw new Error("缺少必要的 MODEL_ID");
const orders = new Map([
["ORDER-1001", { status: "in_transit", eta: "2026-08-06" }],
]);
const tools = [
{
type: "function",
name: "get_delivery_status",
description: "回傳指定訂單目前的配送狀態。",
parameters: {
type: "object",
properties: {
order_id: { type: "string", pattern: "^ORDER-[0-9]{4}$" },
},
required: ["order_id"],
additionalProperties: false,
},
strict: true,
},
];
function executeTool(name, rawArguments) {
if (name !== "get_delivery_status") {
throw new Error(`不允許呼叫工具:${name}`);
}
const args = JSON.parse(rawArguments);
if (!/^ORDER-[0-9]{4}$/.test(args.order_id)) {
throw new Error("order_id 無效");
}
return orders.get(args.order_id) ?? { status: "not_found" };
}
const first = await client.responses.create({
model,
input: "ORDER-1001 配送到哪裡了?",
tools,
parallel_tool_calls: false,
});
const calls = first.output.filter((item) => item.type === "function_call");
if (calls.length === 0) {
console.log(first.output_text);
process.exit(0);
}
const toolOutputs = calls.map((call) => ({
type: "function_call_output",
call_id: call.call_id,
output: JSON.stringify(executeTool(call.name, call.arguments)),
}));
const final = await client.responses.create({
model,
input: [...first.output, ...toolOutputs],
tools,
parallel_tool_calls: false,
});
console.log(final.output_text);
關鍵是模型輸出的 call.call_id 必須與結果的 call_id 完全一致。無狀態整合應把前一次輸出項目與函式結果一起送回;若要延續有狀態的回應,則必須確認路由與儲存政策確實支援,不應假設每個 OpenAI 相容路由都會保存回應狀態。
使用 Chat Completions 實作
Chat Completions 在 function 下包住定義,並把結果加入訊息紀錄:
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.MODELFLARE_API_KEY,
baseURL: "https://modelflare.dev/v1",
});
const model = process.env.MODEL_ID;
if (!model) throw new Error("缺少必要的 MODEL_ID");
const orders = new Map([
["ORDER-1001", { status: "in_transit", eta: "2026-08-06" }],
]);
const tools = [
{
type: "function",
function: {
name: "get_delivery_status",
description: "回傳指定訂單目前的配送狀態。",
parameters: {
type: "object",
properties: {
order_id: { type: "string", pattern: "^ORDER-[0-9]{4}$" },
},
required: ["order_id"],
additionalProperties: false,
},
strict: true,
},
},
];
function executeTool(name, rawArguments) {
if (name !== "get_delivery_status") {
throw new Error(`不允許呼叫工具:${name}`);
}
const args = JSON.parse(rawArguments);
if (!/^ORDER-[0-9]{4}$/.test(args.order_id)) {
throw new Error("order_id 無效");
}
return orders.get(args.order_id) ?? { status: "not_found" };
}
const messages = [
{ role: "user", content: "ORDER-1001 配送到哪裡了?" },
];
const first = await client.chat.completions.create({
model,
messages,
tools,
parallel_tool_calls: false,
});
const assistant = first.choices[0].message;
const calls = assistant.tool_calls ?? [];
if (calls.length === 0) {
console.log(assistant.content);
process.exit(0);
}
messages.push(assistant);
for (const call of calls) {
const result = executeTool(call.function.name, call.function.arguments);
messages.push({
role: "tool",
tool_call_id: call.id,
content: JSON.stringify(result),
});
}
const final = await client.chat.completions.create({
model,
messages,
tools,
parallel_tool_calls: false,
});
console.log(final.choices[0].message.content);
必須先把包含工具呼叫的助理訊息加回 messages,再附加每個 role: "tool" 結果。缺少原始訊息或使用不相符的 tool_call_id 都會形成無效的對話紀錄。
將串流參數視為片段
串流中的 {"order 不是無效 JSON,而是不完整的 JSON。安全的解析器應:
- 依呼叫識別或索引位置建立獨立緩衝區;
- 把每個參數增量附加到正確緩衝區;
- 等待參數完成或回應完成訊號;
- 只對完整字串執行一次 JSON 解析;
- 完成驗證後才執行函式。
Responses 使用帶型別的事件;Chat Completions 使用 choices[].delta.tool_calls[],索引代表進行中的項目,最終工具呼叫才帶有穩定 ID。第一個片段到達時絕不能執行。更多時間與完成邊界見 AI API 串流指南。
讓工具執行安全且冪等
- 只允許已註冊的函式名稱;
- 在大小限制內解析參數,並於應用端驗證完整結構描述;
- 依目前使用者或工作負載授權資源操作;
- 分離唯讀工具與具有副作用的工具;
- 回傳模型前移除密鑰與敏感輸出;
- 設定截止時間,並限制下游重試次數;
- 保存安全的操作參照,而不是原始私密內容。
具有副作用的工具應由穩定的應用請求與呼叫識別產生冪等鍵,並在回傳模型前持久化完成結果。網路重試再次送出相同呼叫時,回傳已保存結果,而不是第二次退款、傳訊、部署或修改資料庫。
模型產生的函式名稱與參數都是不可信輸入。參數結構符合約束不代表有權限、擁有資源,也不能取代交易邊界。
決定如何處理多個呼叫
範例使用 parallel_tool_calls: false 讓狀態機容易檢查。開啟平行呼叫時:
- 一律依呼叫 ID 關聯結果,不依完成順序;
- 限制每次回應的呼叫數量與應用並行度;
- 定義單一失敗是否取消、阻擋或與成功結果共存;
- 每個已接受的呼叫都必須回傳一個結果;
- 順序重要的副作用必須序列化。
平行執行可縮短獨立讀取的時間,但會增加授權、順序、重試與部分失敗的複雜度。只有工作負載確實受益時才開啟。
理解 Modelflare 相容邊界
Modelflare 會在合格的 OpenAI/Codex 路由之間保留嚴格的函式定義,並在 Responses 與 Chat Completions 之間映射受支援的函式呼叫格式。這個範圍刻意小於 Responses 的完整工具能力。
應用定義的函式工具可在兩種格式中表示;由供應商執行的搜尋、程式碼執行或其他託管工具,並不等同於應用程式的函式呼叫,不能假設可轉成 Chat Completions。其他 OpenAI 相容模型家族在未單獨驗證前,採用原樣透傳的 Chat Completions;由上游決定是否支援 tools、strict、平行呼叫、串流參數與 tool_choice。
上線前應完成下列測試,而不是只確認第一個請求回傳 200:
| 測試 | 必要證據 |
|---|---|
| 唯讀呼叫 | 函式名稱、已解析參數、呼叫 ID、結果關聯、最終文字 |
| 無效參數 | 明確拒絕且未執行工具 |
| 未知函式 | 被應用允許清單拒絕 |
| 不需工具 | 正常最終文字,沒有虛構呼叫 |
| 串流呼叫 | 完整重組參數與正確呼叫 ID |
| 重複請求 | 一次副作用或一次快取結果 |
| 多重呼叫 | 不受完成順序影響的關聯 |
| 路由備援 | 保持模型、協定、結構描述與結果契約 |
路由切換應保持明確,可參考可靠 AI API 路由;工作負載與權限隔離可參考 AI API 金鑰安全。
依應用合約選擇格式
需要帶型別的輸出項目、Responses 事件模型、有狀態延續或其他已驗證能力時使用 Responses。已有穩定的訊息紀錄,且選定供應商的工具契約經過驗證時使用 Chat Completions。更完整比較見 Responses API 與 Chat Completions。
兩種格式都應遵守同一標準:明確的工具允許清單、嚴格的參數結構描述、應用端驗證、執行前授權、依呼叫 ID 關聯、對副作用做冪等,以及把完整結果送回模型。可靠性來自這些控制,而不是端點名稱。