本系列前幾篇分別講了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或 SDKresponse_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 以各廠商當前檔案為準):
| 維度 | OpenAI | Gemini |
|---|---|---|
| 僅 JSON Mode | response_format: { "type": "json_object" } | responseMimeType: "application/json"(無 Schema) |
| Structured + Schema | response_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 有 parsed | response.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: false | strict 下所有 object 建議顯式 false | ✓ 推薦,防模型發明欄位 |
anyOf / oneOf | strict 下受限,宜簡化 | 支援有限,複雜 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}
跨廠商適配三步:
- 取交集關鍵字:只用
type、properties、required、enum、基本minimum/maximum;避免複雜oneOf。 - OpenAI strict 預處理:指令碼為每個 object 補
additionalProperties: false與完整required。 - 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 降級。