OpenAI API 結構化輸出(Structured Outputs)怎麼寫:json_schema、Pydantic / Zod、refusal 和 incomplete 處理

想讓 OpenAI 模型的回答嚴格符合你定義的 JSON 結構?在 Responses 請求的 text.format 裏寫 json_schema 並開啟 strict,或用 Pydantic、Zod 加 parse。本文講官方寫法、schema 子集與上限,以及 refusal 與 incomplete 的處理。

NNathaniel bigo··原創首發·AI 輔助撰寫
9 分鐘讀完
資料核對於 2026-11-05 · 依據官方文檔與公開資料整理 其他 帳號
本文根據 OpenAI 官方《Structured model outputs》整理,資料核對於 2026-11-05。配圖為本站整理的示意圖,不是介面截圖。代碼裏的模型名取自官方當天示例,請以官方模型頁為準。本站(bigolab.com)不是 OpenAI 官方網站。API 是按用量計費的獨立服務,與 ChatGPT 訂閱分開計算,Key 的獲取見 /guides/openai-api-key。

適用於誰

  • 要把模型回答直接交給程序處理的開發者:資訊抽取、分類打標、生成表單或介面描述;
  • 以前靠提示詞“請只輸出 JSON”、總是偶爾多一句廢話或少一個字段的人;
  • 想知道 Structured Outputs 和 JSON 模式有什麼區別的人。

如果你想了解跨廠商的思路(只靠提示詞、JSON 模式、結構化輸出三種做法對比),先看 /guides/llm-json-structured-output-reliable。本文只寫 OpenAI 的具體寫法。

結論先說

  1. Structured Outputs 保證輸出符合你給的 JSON Schema:不會漏掉必填字段,也不會冒出不存在的枚舉值。JSON 模式只保證是合法 JSON,不保證符合 schema。
  2. 官方建議能用就用 Structured Outputs,不用 JSON 模式。
  3. Responses API 裏的寫法:text.format 設成 type: "json_schema",並設 strict: true。
  4. 有現成輔助方法:Python 用 Pydantic 加 responses.parse,Node 用 Zod 加 responses.parse,結果在 output_parsed。
  5. 仍然要處理兩種意外:模型拒絕回答(refusal),以及輸出被 token 上限截斷(incomplete)。這兩種情況輸出不一定符合 schema。

Structured Outputs 與 JSON 模式的區別

圖:本站根據 OpenAI 官方《Structured model outputs》 整理的示意圖

一、和函數調用怎麼選

官方說 Structured Outputs 有兩種形式:一是用在函數調用上,二是用 json_schema 響應格式。區分很簡單:

  • 模型要連接你的工具、函數、數據:用函數調用,見 /guides/openai-api-function-calling;
  • 你想規定模型回答用戶時的結構:用 text.format,比如數學輔導應用想讓答案分成“步驟”和“最終答案”,好在介面上分別展示。

二、最省事的寫法:Pydantic 和 Zod

Python(需要先 pip install openai pydantic)。下面按官方“抽取日程”例子改寫:

python
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI()

class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]

response = client.responses.parse(
    model="gpt-6-astra",  # 以官方模型頁當天為準
    input=[
        {"role": "system", "content": "從用戶的話裏提取日程資訊。"},
        {"role": "user", "content": "小明和小紅週五要去參加科學展。"},
    ],
    text_format=CalendarEvent,
)

event = response.output_parsed
print(event.name, event.date, event.participants)

Node.js(需要 npm install openai zod,儲存為 .mjs 運行):

javascript
import OpenAI from "openai";
import { zodTextFormat } from "openai/helpers/zod";
import { z } from "zod";

const openai = new OpenAI();

const CalendarEvent = z.object({
  name: z.string(),
  date: z.string(),
  participants: z.array(z.string()),
});

const response = await openai.responses.parse({
  model: "gpt-6-astra", // 以官方模型頁當天為準
  input: [
    { role: "system", content: "從用戶的話裏提取日程資訊。" },
    { role: "user", content: "小明和小紅週五要去參加科學展。" },
  ],
  text: {
    format: zodTextFormat(CalendarEvent, "event"),
  },
});

console.log(response.output_parsed);

官方推薦盡量用 SDK 的這類輔助方法,理由是避免“JSON schema 和代碼裏的類型逐漸不一致”。如果你堅持手寫 schema,官方建議在 CI 裏加規則:只要 schema 或對應的數據類被改了就報警,或者讓 CI 從類型定義自動生成 schema。

三、自己寫 JSON Schema

不用輔助方法時,直接在 text.format 裏寫。下面是官方“數學輔導”例子的 Python 結構:

python
response = client.responses.create(
    model="gpt-6-astra",  # 以官方模型頁當天為準
    input=[
        {"role": "system", "content": "你是數學老師,一步一步引導用戶解題。"},
        {"role": "user", "content": "8x + 7 = -23 怎麼解?"},
    ],
    text={
        "format": {
            "type": "json_schema",
            "name": "math_response",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "steps": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "explanation": {"type": "string"},
                                "output": {"type": "string"},
                            },
                            "required": ["explanation", "output"],
                            "additionalProperties": False,
                        },
                    },
                    "final_answer": {"type": "string"},
                },
                "required": ["steps", "final_answer"],
                "additionalProperties": False,
            },
        },
    },
)

print(response.output_text)

注意 JSON 文本在 output_text 裏,是字符串,需要自己 json.loads。官方還說:第一次使用某個 schema 會有額外延遲,之後相同 schema 的請求沒有額外延遲。

官方給 schema 的三條建議:鍵名起得清楚直觀;給重要的鍵寫清楚標題和描述;用評測(evals)來決定哪種結構最適合你的場景。

四、schema 的限制

官方說 Structured Outputs 只支持 JSON Schema 的一個子集,要點:

  • 支持的類型:字符串、數字、布爾、整數、對象、數組、枚舉、anyOf;
  • 根必須是對象,且不能是 anyOf。官方特別提醒,用 Zod 的判別聯合(discriminated union)會在頂層產生 anyOf,這樣寫不行;
  • 所有字段必須 required。想表示可選,把類型寫成 ["string", "null"] 這樣的聯合,模型會對每個字段都給值,沒有就給 null;
  • 每個對象必須 additionalProperties: false;
  • 規模上限:最多 5000 個對象屬性、最多 10 層嵌套;所有屬性名、定義名、枚舉值的字符總長不超過 120,000;枚舉值總數最多 1000;
  • 不支持的關鍵字:allOf、not、dependentRequired、dependentSchemas、if、then、else;
  • 輸出裏的鍵順序和 schema 裏的順序一致;
  • 支持 $defs 定義和遞歸 schema。

如果開了 strict: true 卻傳了不支持的 schema,你會收到錯誤。字符串還能用 pattern 和部分 format(例如 email、date-time、uuid),數字能用 minimum、maximum 等,數組能用 minItems、maxItems,但官方說這些約束對微調模型暫不支持。

五、兩種邊界情況必須處理

結構化輸出拿不到結果怎麼辦:schema 不支持、缺少 required、可選字段、incomplete、refusal

圖:本站根據 OpenAI 官方《Structured model outputs》 整理的示意圖,處理辦法一欄為本站補充

官方強調:模型有時不會生成符合 schema 的有效響應,原因有兩個:拒絕和不完整。官方示例的判斷結構,改寫成 Python 如下:

python
response = client.responses.create(
    model="gpt-6-astra",  # 以官方模型頁當天為準
    input=[{"role": "user", "content": "8x + 7 = -23 怎麼解?"}],
    max_output_tokens=1024,
    text={"format": {"type": "json_schema", "name": "math_response",
                     "strict": True, "schema": schema}},  # schema 同上
)

if (response.status == "incomplete"
        and response.incomplete_details.reason == "max_output_tokens"):
    raise RuntimeError("輸出被截斷,結果不完整")

message = next(i for i in response.output if i.type == "message")
part = message.content[0]
if part.type == "refusal":
    print("模型拒絕:", part.refusal)
elif part.type == "output_text":
    print(part.text)
  • 達到 max_output_tokens 時,status 是 incomplete,JSON 可能是半截的;
  • 安全拒絕時,內容項的 type 是 refusal 而不是 output_text,官方把它稱為“可以用程序檢測的拒絕”。

另外官方還提醒兩件事:一是用戶輸入與 schema 完全不相關時,模型仍會努力湊出符合 schema 的內容,可能編造,所以提示詞裏要寫明“輸入不適用時返回空值或一句固定話”;二是 Structured Outputs 仍然可能出錯,內容錯了就調整指令、在系統指令裏加示例,或把任務拆成更簡單的子任務。

常見問題

Q:必須用 gpt-6-astra 嗎?

不是。官方說 Structured Outputs 在 gpt-4o 起的最新模型上可用,新項目建議從當天官方推薦的模型起步;json_schema 格式需要 gpt-4o-mini、gpt-4o-2024-08-06 及之後的快照。

Q:可以和流式一起用嗎?

可以。官方頁面有 SDK 的 stream 方法示例,會收到文字增量和拒絕增量事件,基礎流式寫法見 /guides/openai-api-streaming。

Q:它會讓請求更貴嗎?

官方這一頁沒有寫額外費用,價格以官方價格頁當天為準,見 /guides/openai-api-price-official-table。schema 本身會佔輸入 token 這一點,是本站的推斷,實際用量請看帳單。

Q:第一個請求很慢?

正常,官方說明首次使用新 schema 需要處理時間。

參考資料

  • OpenAI 官方:Structured model outputs — https://developers.openai.com/api/docs/guides/structured-outputs

Nathaniel 的更多內容

  1. 01

    Claude Code 在 Windows 上的常見坑:Git Bash 還是 PowerShell、路徑反斜槓、假報錯、WSL 怎麼選

    Windows 上用 Claude Code,裝好之後最容易踩的是:用哪個 shell、路徑寫法不一致、grep 沒匹配就報錯、鉤子沒生效、WSL 和原生怎麼選。本文逐條按官方文檔整理原因和處理辦法,附三段提示詞和一張對照表。

    Claude0
  2. 02

    Claude Code Hooks 實戰「提交前自動跑測試」:PreToolUse 攔住 git commit,測試不過就退出碼 2,附 Windows 注意點

    只寫一個完整例子:讓 Claude Code 每次要執行 git commit 之前,先自動跑項目測試,沒通過就攔下並把輸出交給它修。本文按官方 Hooks 文檔給出腳本、settings.json 配置、測試方法、排錯和 Windows 的兩處坑。

    Claude0
  3. 03

    Claude Code 自定義斜槓命令與 Skill 實戰:從零寫一個「生成更新日誌」的 /release-notes,含傳參、預執行命令和調試

    概念看過了,還是不知道從哪下手?本文按 Claude Code 官方「Extend Claude with skills」寫一個完整可用的例子:用 /release-notes 根據 git 記錄生成更新日誌,覆蓋目錄、frontmatter、傳參、預執行命令、預授權和調試。

    Claude0
  4. 04

    Claude Code 部署網站:先寫 Dockerfile 和 compose 在本機跑通,再按清單上雲伺服器,別讓它直接改線上

    想讓 Claude Code 幫你把網站部署到雲伺服器。本文只寫通用做法,按 Docker 官方文檔講:讓它寫 Dockerfile、.dockerignore 和 compose,在本機跑通,端口和數據卷怎麼設,再給出上線清單,並說明為什麼不要讓它直接動線上機器。

    Claude0
  5. 05

    Claude Code 寫爬蟲與數據清洗:先查 robots.txt 和網站條款,控制頻率,原始數據留一份再清洗

    想讓 Claude Code 寫爬蟲抓數據再清洗。本文先講合規:robots.txt 不是授權也不是安全措施、網站條款要自己讀;再講怎麼用 Python 標準庫檢查規則、小樣本先試、儲存原始數據,以及用 pandas 清洗,附四段提示詞。

    Claude0
  6. 06

    Claude Code 寫 Chrome 擴展:按 Manifest V3 起步,加載未打包的擴展,改了哪些文件要重新加載

    想讓 Claude Code 幫你寫一個 Chrome 擴展。本文按 Chrome 官方擴展文檔講:manifest.json 的最小寫法、在 chrome://extensions 加載未打包的擴展、哪些改動要刷新、怎麼看控制檯和報錯,再給四段提示詞,以及權限要盡量少。

    Claude0

同產品的其他教學

  1. 01

    OpenAI API 的 token 怎麼算、怎麼估算費用:input_tokens 計數接口、tiktoken 和隱藏的輸出 token

    發請求前想知道會用多少 token?官方有 Responses 的 input_tokens 計數接口,能把文字、圖片、文件、工具定義一起算準;本地 tiktoken 只適合純文本。本文給 Python 和 Node 示例,講輸出 token 為什麼可能比看到的多,以及怎麼估算費用。

    ChatGPT0
  2. 02

    OpenAI API Node.js / TypeScript 快速上手:npm install openai、example.mjs 第一個請求,到流式、函數調用、聯網搜尋

    用 Node.js 調 OpenAI API:設定 OPENAI_API_KEY 環境變量,npm install openai,新建 example.mjs,用 client.responses.create 發出第一個請求。並列出圖片、文件、流式、函數調用、聯網搜尋和錯誤捕獲的 Node 寫法。

    ChatGPT0
  3. 03

    OpenAI API 用量層級(usage tiers)與速率限制怎麼提升:Free、Build、Launch、Grow 和後台 Limits 頁

    OpenAI API 的限速不是固定的:官方按累計購買額度把組織分成 Free、Build、Launch、Grow 四個層級,達到門檻自動升級。本文講 RPM、TPM 等指標、怎麼在後台查自己的限額、響應頭怎麼讀、花費上限和限速的區別,以及怎麼讓限額變得夠用。

    ChatGPT0
  4. 04

    OpenAI API 報錯 400、401、403、404、429、500、503 怎麼排查:按官方錯誤碼頁逐個看原因和解決辦法

    調用 OpenAI API 返回 400、401、403、404、429、500、503,各自什麼意思?本文按官方 Error codes 頁,每個狀態碼一節,寫清官方給的原因和解決辦法,並說明 404 在官方頁面裏的位置、429 的六種情形,附 Python 和 Node 的錯誤捕獲寫法。

    ChatGPT0
  5. 05

    OpenAI Realtime API 是什麼:語音到語音會話、WebRTC 與 WebSocket、臨時密鑰和事件機制

    Realtime API 讓模型直接處理音頻,做可以隨時打斷的語音助手。本文按官方文檔講它是什麼、什麼時候用、瀏覽器接入的流程、session 與事件、可用聲音、60 分鐘會話上限、安全標識,以及它和轉寫、文字轉語音的分工。

    ChatGPT0
  6. 06

    OpenAI 圖像生成 API 怎麼用:gpt-image 系列、images.generate 與 Responses 工具、尺寸質素和遮罩編輯

    用 OpenAI API 生成或編輯圖片,有 Image API 和 Responses 裏的 image_generation 工具兩條路。本文按官方文檔給 Python 和 Node 示例,講 gpt-image 模型選擇、尺寸質素格式參數、遮罩編輯、被攔截時的錯誤處理,以及需要組織驗證的提醒。

    ChatGPT0

0 條評論

登入 後參與評論

還沒有評論,來搶沙發~