本系列前几篇分别讲了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 降级。