OpenAI Agents API + JSON Schema 實戰:如何讓 AI Agent 輸出穩定、可驗證、可執行的 JSON 資料?
通用 Schema 文已經寫過「為什麼要契約」。本篇只落到 Agents API:模型填的每一段 JSON,都要先過門,再執行。工具參數、MCP 輸入、你自己的帳簿,是三道門,不是一句「請輸出 JSON」。
閱讀前速覽
| 門 | 鎖什麼 | 誰執行 |
|---|---|---|
| Function parameters | order_id、布林、列舉 | additionalProperties: false |
| 宿主校验 | required_actions 裡的 arguments | 不過門就不跑函數 |
| 最终卡片 | 給使用者看的物件 | 另寫 Schema;不是 text.format |
| MCP | 另一張工具表的 inputSchema | subagent 可繼承 |
Schema 是門,不是禮貌。寬 object 等於多開一扇門。
穩定、可驗證、可執行,是三道手續,不是一個開關
在 Agents API 裡,模型仍然只填 JSON。官方 function 定義要帶一份 parameters Schema:required、additionalProperties: false、必要時 pattern / enum。這讓填充更穩。可驗證是你在執行前,用同一份 Schema 再跑一遍 arguments。可執行是過門之後,宿主才產生副作用。
- 穩定:把工具參數寫成嚴 Schema。寬
object等於請模型發明鍵。 - 可驗證:
required_actions裡的呼叫也要本地校驗。歷史裡的 function_call 不夠。 - 可執行:不過門就不跑
lookup_order。回寫success: false和原因,不要默默改庫。
工具怎麼調,見 OpenAI Agents API 完整解析。四個物件怎麼用 ID 相連,見 Agents API 的 JSON 資料結構。Schema / Function Calling / Structured Output 的通用分工,見 AI 為什麼需要 JSON Schema。本篇不再重複那三個詞的定義,只寫 Agents session 上怎麼落地。
第一道門:寫在 agent.tools 裡的 parameters
官方 function 條目是 name、description、一份 JSON Schema。建立 session 時寫進 agent.tools。虛構訂單,不要貼真實金鑰:
{
"type": "function",
"name": "lookup_order",
"description": "Look up an order by ID. Confirm before any refund.",
"parameters": {
"type": "object",
"properties": {
"order_id": { "type": "string", "pattern": "^[A-Z]-\\d{4}$" },
"include_lines": { "type": "boolean" }
},
"required": ["order_id", "include_lines"],
"additionalProperties": false
}
}
要點就這幾個:所有欄位進 required;物件關掉多餘鍵;單號用 pattern,不要只寫 string。函數預設會預載。要延後載入,設 defer_loading: true,並在工具表裡放 tool_search。門本身不變。
過不去的參數,不要執行
session 停在 agent.session.requires_action 時,required_actions 裡會出現 function_call。下面這一份看起來像在查單,其實已經越門:單號不合 pattern,還多了一個 refund:
{
"type": "function_call",
"turn_id": "turn_2",
"call_id": "call_3c91",
"name": "lookup_order",
"arguments": {
"order_id": "1042",
"include_lines": true,
"refund": true
}
}
同一對 turn_id / call_id,過門的形狀應當是這樣。多一個鍵、少一個 required、pattern 漂了,都不該進執行:
{
"type": "function_call",
"turn_id": "turn_2",
"call_id": "call_3c91",
"name": "lookup_order",
"arguments": {
"order_id": "A-1042",
"include_lines": true
}
}
第二道門:宿主用同一份 Schema 再跑
官方 Schema 提高填充品質,但不替你承擔副作用。執行前在本地再校驗一次。失敗寫成物件,便於複盤:
{
"check": "lookup_order",
"schema": "parameters",
"pass": ["order_id", "include_lines"],
"fail": [
{ "path": "/order_id", "reason": "pattern" },
{ "path": "/refund", "reason": "additionalProperties" }
],
"execute": false
}
失敗就向 events 端點回 agent.session.input.tool_result,success: false,error 寫人能讀的原因。成功才序列化 output 字串。不要新開 session「重試」。
{
"type": "agent.session.input.tool_result",
"turn_id": "turn_2",
"call_id": "call_3c91",
"success": false,
"error": "order_id failed schema: expected ^[A-Z]-\\d{4}$"
}
最終卡片是第三道門,不是 Responses 的 text.format
給使用者看的訂單卡片,和工具參數不是同一份 Schema。Agents API 的 session 不會自動套上 Responses 那種 text.format.json_schema。三件事要分開寫:
- 工具參數:鎖副作用入口。查單、改庫走這裡。
- 最終卡片:你從 items 抽出物件後,用另一份 Schema 校驗再展示。
- 若必須用 Structured Outputs 鎖最終形狀:那是 Responses 的層。不要混進同一條 session 資源。
循環該不該託管,見 Agents API vs Responses API。MCP 那一行只是另一張工具表,inputSchema 同樣要 additionalProperties: false。邊界見 MCP Tool Schema vs Function Calling。subagent 繼承 MCP Schema,不繼承你的 function。
五步把 JSON 收成可執行
不要從「請輸出 JSON」開始。按門走:
- 先寫工具 Schema。pattern、enum、required、additionalProperties: false 一次收緊。
- 同一份 Schema 放進宿主校驗器。和官方 parameters 不要各寫各的。
- required_actions 到了先校驗再執行。不過門就回 success: false。
- 最終卡片另寫 Schema。從 items 抽出物件後校驗,不要把散文當卡片。
- 帳簿欄位自己存。compact 會丟細節。金額、審批不要只活在對話裡。
工作流怎麼收成 Schema,見 JSON Schema 與 AI Agent 工作流。本篇只要求:Agents session 上,每道門都有物件。
複盤穩定輸出,先攤開兩段 JSON
漂了,多半是門沒對齊,不是模型變笨。
- 把 parameters 和 arguments 格式化,看多餘鍵和缺欄位。
- 用 JSONPath 提取 抽出
order_id、$.parameters.required。 - 過門失敗和成功的呼叫 JSON Diff。pattern 漂了先看 Schema,再怪模型。
- 最終卡片 Schema 和工具 Schema 分開審。鎖卡片不鎖參數,等於只裝修門面。
- 訂單、session、內網 MCP 不要貼進公共格式化站。JSONSort 在瀏覽器本地跑。
兩段軌跡都能按 Schema 重建,你才是在收 JSON,不是在收散文。
常見誤區
- 只在 instructions 裡寫「請輸出 JSON」。那不是門。門在 parameters 和宿主校驗器。
- required 只列一部分欄位。沒列的鍵,模型可以不填,下一跳就會空。
- additionalProperties 不寫或寫成 true。官方示例關掉多餘鍵。多一個 refund,副作用就偏了。
- 把 Responses 的 text.format 抄進 Agents session。兩套資源。最終卡片你自己校驗。
- 校驗失敗還是執行了函數。可執行的前提是過門。先回 error。
- 讓 subagent 跑你的 function。子代理不支援 function 工具。嚴 Schema 留在主 Agent。
FAQ
Agents API 有沒有 Structured Outputs?
工具參數靠 JSON Schema 收緊。給使用者看的最終物件,session 不會自動套 Responses 的 text.format。要鎖最終形狀,自己校驗,或把那一跳放到 Responses。
官方 Schema 還要我再校驗嗎?
要。官方門提高填充品質。副作用發生在你的行程。required_actions 裡的 arguments 過同一份 Schema,失敗就 success: false。
pattern 和 enum 有必要嗎?
有。string 太寬,模型會填 1042 而不是 A-1042。金額、狀態用 enum 或明確格式。寬類型是最常見的越門方式。
MCP 工具要不要同一套紀律?
要。inputSchema 同樣關掉多餘鍵、列全 required。subagent 繼承 MCP,不繼承 function。查自家訂單的門,留在主 Agent。
校驗失敗會不會把 session 卡死?
回同一對 turn_id / call_id 的 tool_result 即可。error 寫清楚,模型可以改參數再調。不要另開 session 當重試。
結論
Agents API 上的穩定 JSON,不是更長的提示。它是三道門:工具 parameters、宿主再校驗、最終卡片另寫 Schema。可執行發生在過門之後。
先寫清 A-1042 過不了哪一條規則。能指到路徑,資料才算可驗證。模型會換;門應該先穩定。
延伸阅读
- OpenAI Agents API 完整解析 Session、Tool Calling、Schema 怎麼接
- Agents API 的 JSON 数据结构 Agent / Tool Call / Tool Result / Handoff
- Agents API vs Responses API 循環該由誰跑
- 长时间运行的 Agents API Session、compact、Subagents
- AI 为什么需要 JSON Schema Schema / Function Calling / Structured Output
- JSONSort 本地工具箱 格式化、校驗、Diff
- 全部文章 更多 JSON 與 AI 開發筆記
更新紀錄: 首次發布