Function Calling:Responses API 與 Chat Completions 比較

從 Wire Contract 比較函式定義、Call ID、結果訊息、串流參數、授權、冪等與路由相容性。

Responses 與 Chat Completions 都能要求應用執行函式,但兩者表示 Tool Loop 的方式不同。Chat Completions 把定義放在 tools[].function,回傳 message.tool_calls,並以 role: "tool" 訊息接收結果;Responses 使用扁平 Function Definition、回傳帶型別的 function_call Output Item,再透過 call_id 連結 function_call_output

模型不會執行你的函式。應用必須驗證參數、確認授權、精確執行、回傳結果,並在請求重試時避免重複副作用。

Tool Loop 有四個步驟

1. 宣告允許的函式與參數 Schema
2. 接收模型提出的一個或多個 Function Call
3. 在應用程式中驗證、授權並執行
4. 使用每次呼叫的 Correlation ID 回傳結果

第四步完成後,模型才能依據工具結果產生最終答案。若模型再次要求工具,使用新的 Call ID 重複流程。OpenAI Function Calling 指南也將其定義為多步交換;把第一個 Tool Call 當成最終答案是常見整合錯誤。

比較 Wire Contract

項目 Responses API Chat Completions
Function Definition 扁平 tools[],包含 namedescriptionparametersstrict 同欄位放在 tools[].function
模型提出的呼叫 function_call Output Item Assistant tool_calls[]
關聯識別 call_id Tool Call id,結果使用 tool_call_id
函式名稱 function_call.name tool_calls[].function.name
參數 function_call.arguments JSON 字串 tool_calls[].function.arguments JSON 字串
結果 function_call_output Input Item role: "tool" Message
串流參數 Typed Argument Delta Events delta.tool_calls[] Fragments
最終文字 Typed Output Items / Helper choices[0].message.content

不要依 Array Position 關聯呼叫。平行呼叫與串流 Chunk 的完成順序可能不同;明確的 Call ID 才是穩定 Join Key。

定義嚴格的 Function Schema

以下唯讀函式只允許查詢配送狀態:

{
  "type": "object",
  "properties": {
    "order_id": {
      "type": "string",
      "pattern": "^ORDER-[0-9]{4}$"
    }
  },
  "required": ["order_id"],
  "additionalProperties": false
}

strict: true 會要求支援的模型遵循參數 Schema,但應用仍須解析與驗證。Pattern 等 JSON Schema 功能可能依模型或供應商而異,因此 Function Arguments 也應通過 Structured Outputs 相容性測試

使用 Responses API 實作

Responses 中的 Function Definition 是扁平物件:

const tools = [{
  type: "function",
  name: "get_delivery_status",
  description: "Return the current delivery status for one order.",
  parameters: {
    type: "object",
    properties: {
      order_id: { type: "string", pattern: "^ORDER-[0-9]{4}$" },
    },
    required: ["order_id"],
    additionalProperties: false,
  },
  strict: true,
}];

const first = await client.responses.create({
  model,
  input: "Where is ORDER-1001?",
  tools,
  parallel_tool_calls: false,
});

const calls = first.output.filter(item => item.type === "function_call");
const outputs = 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, ...outputs],
  tools,
  parallel_tool_calls: false,
});

關鍵是模型 Output 的 call.call_id 必須與結果的 call_id 完全一致。Stateless 整合應把前一次 Output Item 與 Function Result 一起送回;Stateful Continuation 則必須確認路由與儲存政策支援,不應假設每個 OpenAI-compatible 路由都保存 Response State。

使用 Chat Completions 實作

Chat Completions 在 function 下包住定義,並把結果加入 Message Transcript:

const tools = [{
  type: "function",
  function: {
    name: "get_delivery_status",
    description: "Return the current delivery status for one order.",
    parameters: {
      type: "object",
      properties: {
        order_id: { type: "string", pattern: "^ORDER-[0-9]{4}$" },
      },
      required: ["order_id"],
      additionalProperties: false,
    },
    strict: true,
  },
}];

const first = await client.chat.completions.create({ model, messages, tools });
const assistant = first.choices[0].message;
messages.push(assistant);

for (const call of assistant.tool_calls ?? []) {
  messages.push({
    role: "tool",
    tool_call_id: call.id,
    content: JSON.stringify(executeTool(
      call.function.name,
      call.function.arguments,
    )),
  });
}

必須先把包含 Tool Call 的 Assistant Message 加回 messages,再附加每個 role: "tool" 結果。缺少原始訊息或使用不相符的 tool_call_id 都會形成無效對話歷史。

將串流參數視為 Fragment

串流中的 {"order 不是無效 JSON,而是不完整 JSON。安全 Parser 應:

  1. 依 Call Identity 或 Indexed Slot 建立獨立 Buffer;
  2. 把每個 Argument Delta 附加到正確 Buffer;
  3. 等待 Arguments Done 或 Completed Signal;
  4. 只對完整字串執行一次 JSON Parse;
  5. 完成驗證後才執行函式。

Responses 使用 Typed Events;Chat Completions 使用 choices[].delta.tool_calls[],Index 代表進行中的項目,最終 Tool Call 才帶有穩定 ID。第一個 Fragment 到達時絕不能執行。更多時間與完成邊界見 AI API Streaming 指南

讓工具執行安全且冪等

  • 只允許已註冊的 Function Name;
  • 在大小限制內解析參數,並於應用端驗證完整 Schema;
  • 依目前 User 或 Workload 授權資源操作;
  • 分離唯讀工具與具有副作用的工具;
  • 回傳模型前移除 Secret 與敏感輸出;
  • 使用 Deadline 與有限的下游 Retry;
  • 保存安全 Operation Reference,而不是原始私密內容。

具有副作用的工具應由穩定 Application Request 與 Call Identity 產生 Idempotency Key,並在回傳模型前持久化完成結果。網路重試再次送出相同呼叫時,回傳已保存結果,而不是第二次退款、傳訊、部署或修改資料庫。

模型產生的 Function Name 與 Arguments 都是不可信輸入。Schema 合法不代表有權限、擁有資源或能取代 Transaction Boundary。

決定如何處理多個呼叫

範例使用 parallel_tool_calls: false 讓 State Machine 容易檢查。開啟平行呼叫時:

  • 一律依 Call ID 關聯結果,不依完成順序;
  • 限制每次 Response 的 Call 數量與應用 Concurrency;
  • 定義單一失敗是否取消、阻擋或與成功結果共存;
  • 每個已接受 Call 都必須回傳一個結果;
  • 順序重要的副作用必須序列化。

平行執行可縮短獨立讀取的時間,但會增加授權、順序、Retry 與部分失敗複雜度。只有工作負載確實受益時才開啟。

理解 Modelflare 相容邊界

Modelflare 會在合格 OpenAI/Codex 路由之間保留 Strict Function Definition,並對支援的 Function Call Shape 進行 Responses 與 Chat Completions 映射。這個範圍刻意小於完整 Responses Tool Surface。

應用定義的 Function Tool 可在兩種格式中表示;由供應商執行的 Search、Code Execution 或其他 Hosted Tool 並不等同於 Application Function Calling,不能假設可轉成 Chat Completions。其他 OpenAI-compatible 模型家族在未單獨驗證前採 Raw Chat Completions Pass-through,由上游決定 toolsstrict、Parallel Calls、Streaming Arguments 與 tool_choice 支援。

上線前應完成下列測試,而不是只確認第一個 Request 回傳 200

測試 必要證據
唯讀呼叫 Function Name、Parsed Arguments、Call ID、Result Linkage、Final Text
無效參數 明確拒絕且未執行工具
未知函式 被應用 Allowlist 拒絕
不需工具 正常 Final Text,沒有虛構呼叫
串流呼叫 完整重組參數與正確 Call ID
重複請求 一次副作用或一次快取結果
多重呼叫 不受完成順序影響的關聯
Route Fallback 保持模型、協定、Schema 與結果合約

路由切換應保持明確,可參考可靠 AI API 路由;工作負載與權限隔離可參考 AI API Key 安全

依應用合約選擇格式

需要 Typed Output Items、Responses Event Model、State Continuation 或其他已驗證能力時使用 Responses。已有穩定 Message Transcript,且選定 Provider 的 Tool Contract 經過驗證時使用 Chat Completions。更完整比較見 Responses API 與 Chat Completions

兩種格式都應遵守同一標準:明確 Tool Allowlist、Strict Argument Schema、應用端驗證、執行前授權、依 Call ID 關聯、對副作用做冪等,以及把完整結果送回模型。可靠性來自這些控制,而不是 Endpoint 名稱。