引言
2024 年 8 月 6 日,OpenAI 发布了一个看起来不起眼的 API 更新:Structured Outputs。
没有发布会,没有直播,只有一篇技术博客。但这个更新解决了一个困扰开发者一年多的痛点——终于可以 100% 保证模型输出符合指定的 JSON Schema。
为了让你理解这意味着什么,看看 OpenAI 官方的数据:
在之前,即使使用 JSON Mode + 详细的 prompt,模型输出与开发者指定 schema 的匹配率也只有约 85%。使用 Structured Outputs 后,匹配率达到 100%。
从 85% 到 100%,这 15 个百分点意味着什么?意味着你可以省掉所有解析错误处理代码、所有重试逻辑、所有正则表达式修复。
2022GPT-4 发布,输出纯文本,格式不可控
2023.6JSON Mode 上线,保证合法 JSON 语法
2023-11Function Calling 引入 JSON Schema 约束(部分)
2024.8Structured Outputs 发布,100% schema 匹配
2024.8Claude 推出 tool_use 结构化输出方案
2024.10Gemini 支持 JSON Schema 约束输出
Structured Outputs 是什么?
一句话解释
你给模型一个 JSON Schema,模型保证输出的 JSON 严格符合这个 Schema——正确的字段名、正确的类型、正确的嵌套结构、正确的枚举值。
完整代码示例
python
from openai import OpenAI
from pydantic import BaseModel
client = OpenAI()
# 方式 1:使用 Pydantic 模型定义 schema
class MovieReview(BaseModel):
title: str
year: int
rating: float
sentiment: str
summary: str
response = client.beta.chat.completions.parse(
model="gpt-4o",
messages=[
{"role": "user", "content": "评论电影《星际穿越》"}
],
response_format=MovieReview, # 直接传 Pydantic 模型
)
review = response.choices[0].message.parsed
print(f"电影: {review.title}")
print(f"年份: {review.year}")
print(f"评分: {review.rating}")
print(f"情感: {review.sentiment}")方式 2:直接使用 JSON Schema
python
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "user", "content": "分析以下评论的情感"}
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "sentiment_analysis",
"strict": True,
"schema": {
"type": "object",
"properties": {
"sentiment": {
"type": "string",
"enum": ["positive", "negative", "neutral"]
},
"confidence": {
"type": "number"
},
"keywords": {
"type": "array",
"items": {"type": "string"}
}
},
"required": ["sentiment", "confidence", "keywords"],
"additionalProperties": False
}
}
}
)关键参数解释
Schema 中的关键字段:
type: "json_schema" ← 使用 schema 约束模式
strict: true ← 严格模式,100% 保证合规
additionalProperties: false ← 不允许额外字段
JSON Schema 的能力:
├── 字段名约束:必须叫 "sentiment",不能叫 "情感"
├── 类型约束:confidence 必须是 number
├── 枚举约束:sentiment 只能是三个值之一
├── 数组约束:keywords 是 string 数组
├── 嵌套约束:支持多层嵌套对象
└── 必填约束:所有 required 字段必须存在Constrained Decoding:背后的原理
为什么 Structured Outputs 能 100% 保证格式?
普通生成过程:
每一步从所有 token 中选一个
→ 模型自由发挥 → 可能输出任何格式
Constrained Decoding:
每一步只从「符合 schema 的 token」中选
→ 格式被硬约束 → 输出一定符合 schema
具体来说:
要生成 {"sentiment": "positive"}
Step 1: 只能选 {(其他 token 被屏蔽)
Step 2: 只能选 "(JSON key 必须是字符串)
Step 3-12: sentiment(schema 指定的 key)
Step 13: 只能选 ":
Step 14: 只能选 "(value 必须是字符串)
Step 15-22: positive(enum 中的一项)
...以此类推
每一步的可选 token 被语法树约束,不可能输出非法 JSON。和 JSON Mode 的区别
| 维度 | JSON Mode | Structured Outputs |
|---|---|---|
| 语法保证 | 合法 JSON ✅ | 合法 JSON ✅ |
| Schema 保证 | ❌ 不保证 | ✅ 100% 保证 |
| 字段名 | 可能变 | 必须正确 |
| 值类型 | 可能错 | 必须正确 |
| 枚举约束 | ❌ | ✅ |
| 额外字段 | 可能有 | 不允许 |
| 输出可靠性 | ~85% | 100% |
其他提供商的方案
Claude 的结构化输出
python
# Claude 通过 tool_use 实现结构化输出
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
tools=[{
"name": "sentiment_analysis",
"description": "分析文本情感",
"input_schema": { # ← JSON Schema
"type": "object",
"properties": {
"sentiment": {
"type": "string",
"enum": ["positive", "negative", "neutral"]
},
"confidence": {"type": "number"}
},
"required": ["sentiment", "confidence"]
}
}],
messages=[{"role": "user", "content": "分析:这家餐厅太好吃了!"}]
)
# 模型会生成一个 tool_use block,其中 input 就是结构化输出
for block in response.content:
if block.type == "tool_use":
result = block.input
print(result) # {"sentiment": "positive", "confidence": 0.9}Gemini 的结构化输出
python
# Gemini 通过 response_mime_type 和 response_schema 实现
import google.generativeai as genai
# 定义 schema
response_schema = {
"type": "object",
"properties": {
"sentiment": {"type": "string", "enum": ["positive", "negative", "neutral"]},
"confidence": {"type": "number"}
},
"required": ["sentiment", "confidence"]
}
# 调用
model = genai.GenerativeModel(
'gemini-2.5-pro',
generation_config={
"response_mime_type": "application/json",
"response_schema": response_schema
}
)趋势:JSON Schema 成为通用标准
共同特点:
OpenAI → JSON Schema(response_format)
Claude → JSON Schema(tool_use 的 input_schema)
Gemini → JSON Schema(response_schema)
JSON Schema 正在成为大模型结构化输出的通用语言。
这意味着:
✓ 你写的 schema 可以跨模型复用
✓ 切换模型不需要重写格式定义
✓ 生态工具(如 Pydantic)可以通用本节小结
| 概念 | 要点 |
|---|---|
| Structured Outputs | 2024.8 发布,100% 保证输出符合 JSON Schema |
| 核心原理 | Constrained Decoding——在 token 生成时约束可选项 |
| vs JSON Mode | JSON Mode 保证语法,Structured Outputs 保证结构 |
| 通用标准 | JSON Schema 被所有主流 API 提供商采用 |
| 工程意义 | 省掉所有解析错误处理代码,极大简化开发 |
思考题
- Structured Outputs 使用 Constrained Decoding 约束输出格式。这种约束会不会影响模型回答的准确性?为什么?
- 为什么 JSON Schema 成为了所有 API 提供商的共同选择,而不是各家定义自己的格式语言?
- 如果你想让模型输出一个「包含任意 key-value 的动态对象」,JSON Schema 还能约束吗?这会带来什么问题?