這篇文章面向需要把 GPT 輸出寫入資料庫、API、佇列或 Agent 執行器的後端開發者與資料工程師。內容以資料抽取、分類路由、工具參數、介面資料與生產管道等場景拆解設定方式,並補上拒絕、截斷、語意驗證及 Schema 版本管理。
一旦 GPT 回傳的 JSON 少一個欄位、混入額外鍵值,或在截斷後只剩半段內容,資料庫寫入與 Agent 執行就可能一起失敗。
最快的解法是:採用 Structured Outputs,在支援的情況下啟用 strict mode,再加上拒絕與截斷判斷、JSON Schema 驗證,以及資料庫層級的業務規則;嚴格模式只能保證結構合規,不能保證欄位內容一定真實。
這篇文章適合:
- 後端開發者:需要穩定解析 GPT API 回應,再寫入資料庫或下游 API。
- 資料工程師:需要把模型輸出送入批次處理、訊息佇列或資料管道。
- Agent 工程師:需要分別約束最終回答格式與工具參數,避免工具被錯誤資料觸發。
OpenAI Structured Outputs 指南的設定骨架
Structured Outputs 與普通 JSON 模式的差異,不在於「看起來像不像 JSON」,而在於模型輸出是否被限制在指定 Schema 的結構範圍內。OpenAI 的說明指出,JSON mode 主要改善合法 JSON 的產生;json_schema 搭配 strict: true,才是針對特定 JSON Schema 的結構約束方式。OpenAI Structured Outputs 官方公告
| 使用方式 | 主要保證 | 適合用途 | 仍需自行處理 |
|---|---|---|---|
| 提示詞要求輸出 JSON | 只是一項文字指示 | 原型、人工閱讀 | JSON 合法性、欄位漂移、額外內容 |
| JSON mode | 通常輸出合法 JSON | 不要求固定欄位的簡單回應 | 是否符合 Schema、欄位型別與業務邏輯 |
Structured Outputs + strict: true |
依支援範圍符合指定 Schema | 資料抽取、API、工具參數 | 拒絕、截斷、語意正確性、權限與資源狀態 |
對於非工具型的結構化回應,可使用 response_format 或對應 API 的 JSON Schema 格式;對於工具呼叫,則在函式定義中的參數 Schema 使用 strict: true。OpenAI 的 Function Calling 說明也明確指出,嚴格模式約束的是模型產生的函式引數,不是工具執行結果。Function Calling 官方說明
from openai import OpenAI
client = OpenAI()
schema = {
"type": "object",
"properties": {
"customer_id": {"type": "string"},
"issue_type": {
"type": "string",
"enum": ["billing", "technical", "unknown"]
},
"needs_human_review": {"type": "boolean"}
},
"required": ["customer_id", "issue_type", "needs_human_review"],
"additionalProperties": False
}
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "請從客服內容中抽取指定欄位。"},
{"role": "user", "content": "客戶反映帳單金額與預期不符。"}
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "support_ticket",
"strict": True,
"schema": schema
}
}
)
實際部署前,應先對照 Structured Outputs 官方指南 的支援範圍,因為 strict 並不是完整 JSON Schema 規格的無條件實作;不支援的關鍵字、過度複雜的結構或不同模型能力,都可能造成請求錯誤或需要改寫 Schema。
資料抽取的封閉 Schema
資料抽取最容易出現的錯誤,是只在提示詞中寫「請輸出 JSON」,卻沒有將資料契約寫進 Schema。例如,提示詞要求抽取姓名與日期,模型可能額外回傳說明文字、使用不同鍵名,或在沒有日期時自行猜測日期。
較穩定的設計方式是:
- 最外層固定為
object。 - 明確列出
properties。 - 將下游一定需要的欄位放進
required。 - 陣列使用
items約束每一個元素。 - 物件通常設定
additionalProperties: false,避免欄位悄悄擴張。 - 對「無法判斷」保留合法狀態,而不是逼迫模型填入猜測值。
JSON Schema 官方入門文件說明,Schema 不只是描述欄位名稱,也能表達型別與驗證條件;驗證器則會依 Schema 判斷輸入是否合規。
| 抽取需求 | 建議 Schema 設計 | 不建議做法 |
|---|---|---|
| 單一客戶資料 | 固定物件、固定鍵名、必要欄位 | 讓模型自行決定欄位名稱 |
| 多筆項目 | 陣列加上 items 物件定義 |
只寫「回傳一個清單」 |
| 缺少來源資料 | 使用 null 或明確未知狀態 |
讓模型依上下文猜數值 |
| 下游欄位固定 | additionalProperties: false |
接收任意額外鍵值後直接入庫 |
需要注意的是,Schema 通過只表示資料形狀正確。例如 customer_id 是字串,不代表它一定存在於資料庫;due_date 是日期字串,也不代表日期符合業務規則。
分類路由的枚舉與人工狀態
分類場景不應只要求模型輸出一段自由文字。客服分流、風險標籤、文件類型或工作流路由,都更適合使用 enum,讓下游程式只需處理有限狀態。
例如:
{
"type": "object",
"properties": {
"route": {
"type": "string",
"enum": ["billing", "technical", "sales", "unknown"]
},
"confidence_note": {
"type": "string"
},
"needs_human_review": {
"type": "boolean"
}
},
"required": ["route", "confidence_note", "needs_human_review"],
"additionalProperties": false
}
unknown 不是失敗欄位,而是生產系統的安全出口。當輸入同時涉及多個部門、內容不完整,或分類規則尚未涵蓋新案例時,讓模型回傳未知並交給人工複核,通常比讓它在錯誤分類下啟動自動流程更安全。
也不應把 confidence_note 誤解為可直接作為統計上的可信度分數。它只是輔助說明;若路由結果會觸發退款、帳戶異動或權限變更,應由固定規則、人工審批或其他獨立檢查決定是否執行。
工具參數的嚴格定義
Agent 的工具參數與最終回答格式是兩件事。前者描述「工具需要收到什麼引數」,後者描述「使用者最後看到什麼資料」。即使工具引數符合 JSON Schema,應用程式仍然必須檢查:
- 呼叫者是否有權操作指定資源。
- 資源目前是否存在、鎖定或已被其他工作修改。
- 金額、數量、日期區間是否符合業務上限。
- 工具是否允許在目前工作流階段執行。
- 是否需要二次確認或人工審批。
OpenAI API 參考文件中的函式定義指出,strict 只針對函式參數的 Schema 遵循;工具本身仍由應用程式執行與負責驗證。
因此,工具呼叫流程不應是「解析成功就執行」,而應是:
模型產生工具引數
→ JSON Schema 驗證
→ 身分與權限驗證
→ 資源狀態驗證
→ 業務規則驗證
→ 審批或執行
→ 保存工具輸入、結果與版本
若工具可能平行呼叫,還要確認目前模型與 API 設定是否允許這種行為。OpenAI 的 Structured Outputs 公告曾列出平行函式呼叫與嚴格 Schema 之間的限制,因此對有副作用的工具,應優先採用單一呼叫、逐步確認的設計。
介面資料的語意驗證
介面與下游 API 常見的陷阱,是把「型別正確」誤當成「資料正確」。以下幾類欄位尤其需要額外檢查:
- 日期:格式正確,但可能是過去日期、錯誤時區,或結束日期早於開始日期。
- 識別碼:符合字串型別,但可能不是目前租戶的資源。
- 金額:符合數字型別,但可能有錯誤幣別、小數位或負值。
- 關聯欄位:訂單與客戶 ID 各自存在,但兩者可能不屬於同一筆業務關係。
- 文字內容:通過長度限制,但可能含有不允許的個人資料或指令注入內容。
實作上可分成三層:
- Schema 驗證:確認 JSON 結構、必要欄位與型別。
- 業務驗證:查詢資料庫、權限系統與狀態機。
- 人工複核:處理高風險、低資訊量或規則未涵蓋的案例。
因此,「JSON Schema 驗證通過後還要做業務校驗嗎」的答案是肯定的。Schema 是資料契約的第一道門,不是事實查核器,也不是授權系統。
拒絕與截斷的錯誤分流
Structured Outputs 遇到拒絕時,不能把拒絕內容當作正常 JSON 解析。OpenAI 的官方公告說明,模型仍可能因安全政策拒絕請求,回應會提供 refusal 欄位;若輸出因長度限制或其他停止條件提前中斷,也不能假定結果已完整符合 Schema。
建議把結果先分成三類:
| 結果類型 | 判斷方式 | 處理策略 |
|---|---|---|
| 正常完成 | 沒有 refusal,完成原因正常,Schema 驗證通過 |
進入業務驗證 |
| 模型拒絕 | 存在 refusal 或拒絕型輸出內容 |
記錄原因,改走人工或安全替代流程 |
| 不完整回應 | finish_reason、Response 狀態或截斷資訊顯示未完成 |
保存原始回應,調整輸出上限或拆分任務 |
不要對所有失敗都盲目重試。拒絕通常不是增加重試次數就能解決;截斷可能需要縮短輸入、拆小 Schema 或提高輸出限制;業務驗證失敗則可能代表模型確實抽取到不可信資料。
每次失敗至少保存:
- 原始 API 回應。
- 模型名稱與請求模式。
- 錯誤類型及完成狀態。
- Schema 版本。
- 輸入內容的雜湊或可追溯識別碼。
- 最終是否重試、人工處理或丟入死信佇列。
OpenAI Responses API 參考文件可用來核對目前 API 的狀態欄位、拒絕資訊與回應結構;模型、SDK 或參數名稱更新後,應重新執行最小案例。
生產資料管道的回歸機制
GPT 結構化輸出為什麼仍然解析失敗,常見原因不只在模型本身,也可能是 Schema 不在支援子集、SDK 版本變更、程式誤讀回應欄位,或應用程式只做了 JSON 解析而沒有處理拒絕與截斷。
部署前可使用以下驗收清單:
- [ ] 以官方 SDK 執行最小物件 Schema。
- [ ] 測試巢狀物件與陣列,不只測單一字串欄位。
- [ ] 確認所有物件是否按要求設定
additionalProperties。 - [ ] 測試缺少資料、空陣列、未知分類與超長輸入。
- [ ] 測試安全拒絕,確認程式不會把拒絕文字送入資料庫。
- [ ] 測試輸出被截斷時,確認不會觸發工具或下游寫入。
- [ ] 以獨立驗證器再次檢查模型輸出。
- [ ] 對日期、金額、識別碼和關聯資料加入業務規則。
- [ ] 為正常、邊界、惡意及 Schema 升級案例建立固定樣本。
- [ ] 以
schema_version或等效欄位保存契約版本。 - [ ] SDK、模型或 Schema 支援子集變更後重新執行整套回歸測試。
Schema 變更也應像資料庫結構變更一樣管理。新增可選欄位通常比重新命名必要欄位安全;若欄位語意改變,應建立新版本並讓消費端逐步遷移,而不是直接覆寫舊契約。
對需要長時間執行批次驗證、持續整合或 Apple 開發流水線的團隊,還應把執行環境納入測試計畫。短期驗證可先使用現有工作站;若任務需要持續排程、固定 SDK、穩定連線與可重現的遠端環境,再比較臨時租用與常駐 Mac 環境。可先參考 nuvcloud 控制中心了解環境管理方式,並用 Mac mini 方案資訊核對是否符合任務週期。
從驗收表到執行環境
對單次資料遷移、Schema 升級或短期 Agent 測試而言,本地工作站通常較直接;但當團隊需要批量驗證、固定版本的 SDK、長時間執行回歸案例時,本地環境可能有三個實際缺點:開發者關機會中斷任務、多人共用環境容易產生版本差異,以及權限與原始回應保存規則不易集中管理。
Mac 方案也不是所有情況都適合。若工作負載長期滿載、需要特殊 PCIe 裝置或必須直接連接特定實體周邊,自購硬體或既有機房可能更合理;若只是臨時算力、短期驗證環境、Apple 平台相容性測試或持續執行資料管道,租用 Mac 可減少一次性採購、維護與閒置設備的負擔。
較穩妥的做法,是先整理團隊的 Schema 驗收表,再按任務週期評估本地執行、自購 Mac,或租用 nuvcloud 的遠端 Mac 環境;真正需要被比較的不是「模型能不能輸出 JSON」,而是整條管道能否在拒絕、截斷、版本升級與業務驗證失敗時仍然可追蹤、可恢復、可重現。
為 AI 開發流程打造穩定的遠端 Mac 環境
使用 nuvcloud 遠端 Mac,支援 JSON Schema、API 串接與 Agent 工作流程的開發與測試。
免受本機硬體限制,透過遠端連線即可執行資料處理、自動化任務及生產管道測試。