Appearance
Structured Output / JSON Mode
生产级 LLM 应用的第一道门槛:模型输出必须可解析、可验证、可类型化。Structured Output 不是"让模型输出 JSON"这么简单,而是一套从 schema 定义到运行时保证的完整工程体系。
Overview
Structured Output(结构化输出)指强制大语言模型的输出遵循预定义的数据格式(通常是 JSON,也可以是 YAML、XML 或自定义格式)。这是将 LLM 从"文本生成器"转变为"可靠系统组件"的关键技术。
与让模型"尽量输出 JSON"不同,现代 Structured Output 技术通过**约束解码(Constrained Decoding)**在生成阶段就保证输出合法,而非事后解析和重试。
The Problem
python
# ❌ 不可靠:纯文本提示
prompt = "分析这段评论的情感,输出 JSON:{\"sentiment\": \"...\"}"
response = model.generate(prompt)
# 风险:模型输出额外文字、JSON 格式错误、字段缺失、幻觉字段...
# ❌ 脆弱:事后正则提取
try:
data = json.loads(extract_json(response))
except:
data = fallback_parse(response) # 永远不知道会出什么问题生产环境中的真实问题:
- 模型在 JSON 前后添加解释文字
- 字符串未正确转义(含换行符、引号)
- 枚举值超出定义范围
- 数字字段输出字符串
- 必填字段缺失
- 幻觉出 schema 中不存在的字段
Technical Approaches
1. JSON Mode(软约束)
模型在训练时被教导输出合法 JSON,但无运行时保证。
| 提供商 | 实现 | 可靠性 |
|---|---|---|
| OpenAI JSON Mode | response_format: {type: "json_object"} | ~95% — 偶尔仍有格式问题 |
| OpenAI Structured Outputs | response_format: {type: "json_schema", schema: ...} | ~99% — 基于 constrained decoding |
| Anthropic | 通过 Prompt + 后处理 | ~90% — 依赖模型遵循指令 |
| Gemini | response_mime_type: "application/json" | ~95% |
2. Constrained Decoding(硬约束)⭐
在 token 采样阶段限制模型只能生成符合 schema 的 token,从原理上保证输出合法。
实现原理:
每步生成时:
1. 根据已生成的 token 和 schema,计算当前允许的下一个 token 集合
2. 将不允许的 token 的概率设为 -∞(或 0)
3. 从允许的 token 中采样关键优势:100% 保证输出符合 schema(只要实现正确),无需事后验证。
3. 主流 Constrained Decoding 实现
| 库/框架 | 原理 | 性能开销 |
|---|---|---|
| Outlines | 基于正则/EBNF 的 FSM 约束 | 低(预编译 FSM) |
| Guidance | Microsoft 的约束语言,上下文无关文法 | 中等 |
| JSON Schema → CFG | 将 JSON Schema 转为上下文无关文法 | 中等 |
| llama.cpp Grammar | GBNF 文法,原生集成在 C++ 推理中 | 极低 |
| vLLM Guided Decoding | 集成 Outlines,生产级性能 | 低 |
| XGrammar | 高性能 GPU 加速的 structured decoding | 极低 |
Schema-Driven Development
Structured Output 推动了 LLM 应用开发范式的转变:
python
# 先定义 schema(Pydantic / Zod / JSON Schema)
class SentimentAnalysis(BaseModel):
sentiment: Literal["positive", "neutral", "negative"]
confidence: float = Field(ge=0, le=1)
topics: list[str] = Field(max_length=5)
key_phrases: list[str]
# 然后让模型严格输出符合 schema 的数据
response = client.chat.completions.create(
model="gpt-4o",
messages=[...],
response_format={
"type": "json_schema",
"json_schema": {
"name": "sentiment_analysis",
"schema": SentimentAnalysis.model_json_schema()
}
}
)开发流程变化:
- 设计数据 schema(而非设计 Prompt 模板)
- 用 schema 驱动 Prompt("输出必须符合以下 JSON Schema...")
- 运行时自动验证和类型转换
- 下游系统直接消费结构化数据
Advanced Patterns
嵌套结构化输出
json
{
"entities": [
{
"name": "OpenAI",
"type": "organization",
"attributes": {"founded": 2015, "location": "San Francisco"}
}
],
"relations": [
{"from": "OpenAI", "to": "GPT-4", "type": "develops"}
]
}条件字段(OneOf / AnyOf)
根据某个字段的值,后续字段结构变化:
json
{
"action": "search",
"params": {"query": "..."} // action=search 时的参数
}
// 或
{
"action": "book",
"params": {"flight_id": "...", "passenger": "..."} // action=book 时的参数
}流式结构化输出
逐 token 解析部分 JSON,实现渐进式 UI 更新:
javascript
// 模型正在生成:{"sentiment": "pos...
// 已可解析部分:{sentiment: "positive"}Benchmark & Reliability
| 方法 | 格式合规率 | Schema 合规率 | 延迟影响 |
|---|---|---|---|
| 纯 Prompt | ~70% | ~50% | 无 |
| JSON Mode | ~95% | ~80% | 无 |
| Structured Outputs (OpenAI) | ~99% | ~95% | +5-10% |
| Outlines (constrained) | 100% | 100% | +10-20% |
| llama.cpp Grammar | 100% | 100% | +0-5% |
注:Schema 合规率指不仅 JSON 合法,且字段类型、枚举值、必填约束都满足。
Why It Matters
- 生产可靠性的底线:没有 Structured Output,LLM 应用就是"概率性正确"的——无法集成到确定性系统中
- 类型安全的终点:让动态生成的 LLM 输出进入静态类型的下游系统(TypeScript、Pydantic、Go struct)
- Agent 编排的基础:Function Calling 的参数生成、Agent 状态更新、工具结果解析都依赖 Structured Output
- 降低 Prompt 复杂度:当 schema 本身定义了输出结构,Prompt 可以大幅简化
Relationships
- 底层机制:Function Calling / Tool Use — Function Calling 的参数生成是 Structured Output 的最重要应用场景
- 约束工具:Outlines / llama.cpp — 开源 constrained decoding 的核心实现
- 应用基础:AI Agents — Agent 的状态管理和工具调用输出都依赖结构化数据
- 开发范式:Prompt Engineering — Structured Output 是 Prompt Engineering 在工程化方向的延伸
- 类型系统:与 Pydantic、Zod、JSON Schema 等类型系统深度集成
Open Questions
- Constrained Decoding 对模型生成质量有负面影响吗?限制 token 选择空间是否会降低输出多样性/创造性?
- 复杂 schema(深层嵌套、条件字段、递归结构)的约束解码性能如何?当前实现是否可扩展?
- 多模态结构化输出(图像+JSON、视频+JSON)的 schema 定义和约束方法尚不成熟
- 当模型需要"思考"后输出结构化结果时,如何分离 reasoning chain 和 final structured output?