OpenAI Structured Outputs vs Gemini Structured Output:2026 JSON Schema API 完整對比

OpenAI Structured Outputs 與 Gemini Structured Output 並排對照:2026 API 欄位、JSON Schema 子集、strict 模式、程式範例與跨廠商 Schema 復用。

本系列前幾篇分別講了Structured Output 通用落地、Gemini 一家的設定,以及Tool Calling 引數校驗。如果你同時接 OpenAI 與 Google Gemini,最常被問的是:兩家 Structured Output 到底差在哪?同一份 JSON Schema 能不能原樣複用?

2026 年兩家都已把 JSON Schema 約束解碼(constrained decoding)做成一等公民,但請求欄位名、Schema 子集、strict 語義、SDK 形態並不相同。本文按「概念對齊 → API 對照 → Schema 相容性 → 程式碼示例 → 選型與遷移」給出完整對比,方便你在多模型 Agent 裡寫一份 Schema、兩處呼叫。

概念對齊:Structured Outputs 與 Structured Output

名稱上只差一個 s,含義卻常被混用:

  • OpenAI Structured Outputs(複數):Chat Completions / Responses API 裡 response_format: { type: "json_schema", ... } 的能力品牌名;2024 年 8 月起在 gpt-4o-2024-08-06 及之後模型上 GA,2026 年仍是 OpenAI 生產抽取的主路徑。
  • Gemini Structured Output(單數):Google 檔案對「MIME + Schema 約束生成」的統稱;對應 responseMimeType: application/json 加 responseJsonSchema 或 SDK response_schema。

兩者共同目標:讓模型最終答覆(不是 tool arguments)符合 JSON Schema,在 token 生成階段就排除非法 JSON 路徑。與 JSON Mode(只保證合法 JSON、不保證形狀)相比,都是 L3 級硬約束——詳見《從 Prompt 到 Structured Output》的四層模型。

都不替代 Tool Calling:Structured Output 管「給使用者/下游程式的 JSON」;Tool Calling 管「工具引數 JSON」——見《資料流》。

2026 API 欄位對照表

下面以「抽取一張發票 object」為例,對比最常用設定位(REST / SDK 概念層,具體 path 以各廠商當前檔案為準):

維度OpenAIGemini
僅 JSON Moderesponse_format: { "type": "json_object" }responseMimeType: "application/json"(無 Schema)
Structured + Schemaresponse_format: { "type": "json_schema", "json_schema": { "name", "schema", "strict": true } }MIME + responseJsonSchema(REST)或 response_json_schema / response_schema(SDK)
Schema 來源標準 JSON Schema 字典;strict: true 時子集更嚴標準 JSON Schema(responseJsonSchema)或 OpenAPI 3.0 子集(舊 responseSchema)
解析入口message.content 字串 → JSON.parse;部分 SDK 有 parsedresponse.text;Python google-genai 可 response.parsed(Pydantic)
推薦模型(2026)gpt-4o、gpt-4.1 系列gemini-2.5-flash、gemini-3.7-flash 等 2.5+ / 3.x
列舉分類旁路Schema 內 enum另支援 responseMimeType: text/x.enum(只輸出列舉字串)

遷移時不要交叉貼上 OpenAPI 3.0 大寫型別與 JSON Schema 小寫型別:Gemini 舊 responseSchema 用 OBJECT / STRING;OpenAI 與 Gemini 新 JSON Schema 通道都用 object / string。

JSON Schema 子集與 strict 差異

兩家都支援約束解碼,但接受的 Schema 關鍵字集合不同。寫跨廠商 Schema 時應取交集:

關鍵字 / 行為OpenAI(strict: true)Gemini(responseJsonSchema)
type / properties / required✓ 必需模式✓
enum、minimum / maximum✓✓(以檔案當前列表為準)
additionalProperties: falsestrict 下所有 object 建議顯式 false✓ 推薦,防模型發明欄位
anyOf / oneOfstrict 下受限,宜簡化支援有限,複雜 union 易失敗
$ref 深度strict 要求可 inline 展開過深巢狀可能被拒,宜寫扁
欄位順序不保證與 Schema 一致2.5+ 儘量保持 Schema 欄位順序
語義保證結構不保證事實正確同左;均需二次校驗

OpenAI strict: true 的額外含義:Schema 必須滿足更嚴格的子集(例如每個 object 宣告 additionalProperties: false,required 覆蓋全部 properties 等),否則 API 可能直接 400。Gemini 沒有同名開關,但實踐中同樣推薦「扁平 object + 禁額外欄位」——與《JSON Schema Contract》裡的 Agent 最佳實踐一致。

無論哪家,生產環境都要用同一份 Schema 再跑 ajv / jsonschema / Pydantic——約束解碼降語法錯誤,不替你做業務斷言。

OpenAI Structured Outputs 完整示例

用 Pydantic 匯出 Schema,strict: true 開啟 Structured Outputs(2026 主流寫法):

from openai import OpenAI
from pydantic import BaseModel, Field

client = OpenAI()

class LineItem(BaseModel):
    name: str
    qty: int = Field(ge=1)
    unit_price_cents: int = Field(ge=0)

class Invoice(BaseModel):
    vendor: str
    currency: str = Field(description="ISO 4217, e.g. CNY")
    items: list[LineItem]
    total_cents: int

schema = Invoice.model_json_schema()
# strict 建議:根與巢狀 object 均 additionalProperties: false
schema["additionalProperties"] = False
for prop in schema.get("properties", {}).values():
    if isinstance(prop, dict) and prop.get("type") == "object":
        prop["additionalProperties"] = False

response = client.chat.completions.create(
    model="gpt-4o-2024-08-06",
    messages=[{
        "role": "user",
        "content": "從文字抽取發票:Acme 售出 2 個鍵盤,共 39800 分。",
    }],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "invoice",
            "strict": True,
            "schema": schema,
        },
    },
)

import json
invoice = json.loads(response.choices[0].message.content)

要點:json_object 只開 JSON Mode;要帶 Schema 必須 type: json_schema。name 用於日誌與多 Schema 場景。若 400,先檢查 strict 子集(缺 additionalProperties、required 不完整等)。

Gemini Structured Output 完整示例

同一 Invoice 模型,用 google-genai SDK(2026 推薦):

from google import genai
from google.genai import types
from pydantic import BaseModel, Field

client = genai.Client()

class LineItem(BaseModel):
    name: str
    qty: int = Field(ge=1)
    unit_price_cents: int = Field(ge=0)

class Invoice(BaseModel):
    vendor: str
    currency: str
    items: list[LineItem]
    total_cents: int

response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="從文字抽取發票:Acme 售出 2 個鍵盤,共 39800 分。",
    config=types.GenerateContentConfig(
        response_mime_type="application/json",
        response_schema=Invoice,  # 或 response_json_schema=Invoice.model_json_schema()
    ),
)

invoice = response.parsed  # Pydantic 例項
# 無 parsed 時:json.loads(response.text)

REST 等價體在 generationConfig 裡設 responseMimeType + responseJsonSchema。需要完整 Draft 關鍵字時用 response_json_schema;簡單物件用 Pydantic response_schema 更省事。Gemini 3.7 Flash 等新模型欄位相同,只換 model——見《3.7 Flash 介紹》。

同一份 Schema 如何跨廠商複用

推薦倉庫結構:

schemas/
  invoice.v1.json          # 單一 JSON Schema 源
  samples/
    invoice.valid.json
    invoice.missing_vendor.json

# Python 共享層
from pathlib import Path
import json
schema = json.loads(Path("schemas/invoice.v1.json").read_text())

# OpenAI 包裝
openai_body = {"type": "json_schema", "json_schema": {"name": "invoice", "strict": True, "schema": schema}}

# Gemini 包裝
gemini_config = {"response_mime_type": "application/json", "response_json_schema": schema}

跨廠商適配三步:

  1. 取交集關鍵字:只用 type、properties、required、enum、基本 minimum/maximum;避免複雜 oneOf。
  2. OpenAI strict 預處理:指令碼為每個 object 補 additionalProperties: false 與完整 required。
  3. CI 雙端取樣:同一樣本對 OpenAI 與 Gemini 各跑一次(或 mock),用同一 ajv 校驗輸出——與 Tool Schema 的樣例驅動測試相同思路。

本機開發:把 Schema 與模型輸出貼進 JSON 工具箱做 Diff,不上傳伺服器。

選型:什麼時候用 OpenAI、什麼時候用 Gemini

場景更常見選擇原因
已有 OpenAI Agent 棧(Assistants / Responses)OpenAI Structured Outputs與 tools、evals、現有 SDK 一體
多模態長檔案 + JSON 抽取Gemini 2.5+ / 3.x百萬 token 上下文、PDF/影片輸入與 Structured Output 同請求
欄位順序敏感(CSV/表格對齊)Gemini 2.5+官方強調 Schema 欄位順序保留
強 strict 契約、禁額外欄位OpenAI strict: true子集明確,違規直接拒請求
純列舉分類(無 JSON 物件)Gemini text/x.enum比包一層單欄位 object 更省 token
雙雲 / 降級共享 Schema + 雙介面卡一家限流切另一家,Schema 版本不變

2026 年許多團隊並非二選一,而是同 Schema、雙 Provider:抽取層抽象成 generate_structured(prompt, schema) -> dict,內部按設定路由 OpenAI 或 Gemini。

從單廠商遷移到雙廠商的檢查清單

  • Schema 是否已從 OpenAPI 3.0 大寫型別遷到 JSON Schema 小寫?
  • OpenAI 側是否滿足 strict: true 子集(additionalProperties、required)?
  • Gemini 是否使用 responseJsonSchema 而非僅 application/json?
  • 是否區分 Structured Output(最終 JSON)與 Tool Calling(arguments)兩套 Schema?
  • 執行時是否對兩家輸出做同一份 jsonschema 二次校驗?
  • Prompt 是否仍要求 markdown 圍欄?(應刪掉——Structured Output 不需要 ```json)
  • 日誌是否記錄 schema 版本號,便於 A/B 與回滾?

常見問題 FAQ

OpenAI Structured Outputs 和 Gemini Structured Output 是同一套 API 嗎?

不是。概念都是「JSON Schema 約束的最終答覆」,但 OpenAI 用 response_format.json_schema + strict,Gemini 用 responseMimeType + responseJsonSchema(或 SDK response_schema)。Schema 本體可共享,請求包裝層需分別寫。

同一份 Pydantic 模型能直接給兩家嗎?

可以。model_json_schema() 匯出後,OpenAI 需按 strict 規則補 additionalProperties 與 required;Gemini 可直接 response_json_schema 或 response_schema=Model。先在 CI 用樣例跑通兩家再上線。

哪家 Schema 支援更「全」?

都不保證完整 JSON Schema Draft。OpenAI strict 子集檔案化最細;Gemini 對 JSON Schema 的支援在 2.5 後明顯增強但仍宜寫扁。跨廠商應取交集,而不是用滿 Draft 2020-12。

JSON Mode 和 Structured Output 在兩家分別怎麼開?

OpenAI:json_object vs json_schema。Gemini:僅 application/json vs JSON + Schema。只開 JSON Mode 時兩家都只保證語法 JSON,不保證欄位形狀。

還需要自己做 JSON 校驗嗎?

需要。兩家約束解碼都主要保證結構與型別,不保證業務正確或事實真實。應用層應用同一份 Schema 再校驗,失敗則重試或人工。

和 Tool Calling 的 Schema 能混用嗎?

定義層可共用 Pydantic/Zod 生成器,但呼叫層分開:Structured Output 綁在 response_format / generationConfig;Tool Calling 綁在 tools[].parameters 或 MCP inputSchema。勿用 Structured Output 假裝已執行工具。

總結

2026 年 OpenAI Structured Outputs 與 Gemini Structured Output 已是同一問題的兩種廠商實現:都用 JSON Schema 在解碼階段鎖形狀,都比 JSON Mode 硬一層。差異在 API 欄位、strict 子集、SDK 解析入口與多模態/上下文等工程細節。

實踐路徑:一份 Schema 源 → OpenAI strict 預處理 → Gemini responseJsonSchema → 統一執行時校驗。先用 JSON 工具箱本機對照 Schema 與樣例輸出,再接入雙 Provider 降級。