方案选型对比
技术研究 LLM 方案对比
输出控制方案的深度对比:JSON Mode vs Structured Outputs vs Instructor vs Tool Use
替代方案概述
针对 LLM 指令遵循问题,业界主流有四种输出控制方案:
| 方案 | 提供商 | 核心机制 | 保证级别 |
|---|---|---|---|
| 方案 A: JSON Mode | OpenAI / Anthropic | 系统提示强制 JSON 格式 | 最佳努力(~95%) |
| 方案 B: Structured Outputs | OpenAI / Anthropic / Google | API 级 schema 约束 + 受限解码 | 强制约束(99-100%) |
| 方案 C: Instructor Library | 第三方(开源) | Pydantic 验证 + 自动重试 | 依赖重试(~98%) |
| 方案 D: Tool Use / Function Calling | OpenAI / Anthropic | 将输出约束为函数调用 | 强制约束(~99%) |
对比维度
1. Schema 合规率
xychart-beta
title "各方案 Schema 合规率对比(%)"
x-axis ["JSON Mode", "Structured Outputs", "Instructor", "Tool Use"]
y-axis "合规率" 90 --> 100
bar [95, 99.9, 98, 99]
测试条件:
- 复杂 schema(10+ 字段,嵌套对象)
- 1000 次请求统计
- 相同 prompt,相同模型(GPT-4o)
数据来源:Vellum AI Function Calling vs Structured Outputs
2. 实现复杂度
| 方案 | 代码行数 | 依赖库 | 学习曲线 | 调试难度 |
|---|---|---|---|---|
| JSON Mode | 5-10 行 | 无 | 低 | 中(需手动解析) |
| Structured Outputs | 10-20 行 | Pydantic/Zod | 中 | 低(类型安全) |
| Instructor | 10-15 行 | Instructor 库 | 中 | 低(自动重试) |
| Tool Use | 20-30 行 | 无 | 高 | 中(需定义函数) |
3. 性能影响
| 方案 | 延迟影响 | Token 效率 | 成本影响 |
|---|---|---|---|
| JSON Mode | 基准 | 基准 | 基准 |
| Structured Outputs | +5-15% | 节省 30-60% | 略低(token 少) |
| Instructor | +10-30%(重试) | 与 JSON Mode 相同 | 略高(重试消耗) |
| Tool Use | +5-10% | 与 JSON Mode 相同 | 相同 |
说明:
- Structured Outputs 使用受限解码(constrained decoding),在 token 生成阶段就过滤不合规选项,反而可能减少 trial-and-error
- Instructor 重试会增加延迟,但仅在验证失败时触发(发生率<5%)
4. 错误恢复能力
flowchart LR
subgraph A["JSON Mode"]
A1[解析失败] --> A2[手动捕获]
A2 --> A3[手动重试]
A3 --> A4[需自定义逻辑]
end
subgraph B["Structured Outputs"]
B1[Schema 违规] --> B2[API 拒绝]
B2 --> B3[自动重试]
B3 --> B4[保证最终合规]
end
subgraph C["Instructor"]
C1[验证失败] --> C2[库捕获]
C2 --> C3[自动重试 + 反馈]
C3 --> C4[最多 max_retries]
end
subgraph D["Tool Use"]
D1[参数无效] --> D2[API 拒绝]
D2 --> D3[模型重试]
D3 --> D4[返回 function_call]
end
style A fill:#ffcdd2
style B fill:#c8e6c9
style C fill:#fff9c4
style D fill:#bbdefb
5. 可移植性
| 方案 | OpenAI | Anthropic | 本地模型 | |
|---|---|---|---|---|
| JSON Mode | ✅ | ✅ | ✅ | ✅ |
| Structured Outputs | ✅ (完整) | ✅ (beta) | ⚠️ (有限) | ❌ |
| Instructor | ✅ | ✅ | ✅ | ✅ |
| Tool Use | ✅ | ✅ | ⚠️ | ⚠️ |
关键洞察:Instructor 库提供最佳可移植性,支持所有主流提供商和本地模型(通过 LiteLLM、Ollama 等)。
决策矩阵
综合对比表
| 维度 | JSON Mode | Structured Outputs | Instructor | Tool Use |
|---|---|---|---|---|
| Schema 合规率 | ⭐⭐⭐ (95%) | ⭐⭐⭐⭐⭐ (99.9%) | ⭐⭐⭐⭐ (98%) | ⭐⭐⭐⭐⭐ (99%) |
| 实现简单度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| 错误恢复 | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 可移植性 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| Token 效率 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
| 类型安全 | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 维护成本 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
推荐使用场景
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 生产环境关键路径 | Structured Outputs | 100% 合规保证,类型安全 |
| 快速原型/POC | JSON Mode | 最快实现,无需 schema 定义 |
| 多供应商支持 | Instructor | 统一接口,自动切换 |
| 复杂业务逻辑验证 | Instructor + Pydantic | 自定义验证器,错误反馈 |
| Agent/工具调用 | Tool Use | 原生支持,语义清晰 |
| 成本敏感型应用 | Structured Outputs | Token 用量减少 30-60% |
| 本地模型部署 | Instructor | 支持 Ollama、vLLM 等后端 |
选择理由
推荐方案:Structured Outputs + Instructor 组合
基于本研究的目标(生产级指令遵循稳定性),我们推荐:
Primary(首选): Structured Outputs(OpenAI 或 Anthropic)
理由:
- 最高合规率:OpenAI 官方保证 100% schema 合规,消除解析错误
- 类型安全:Pydantic/Zod 模型提供编译时检查
- Token 效率:精简 schema 节省 30-60% 输出 token
- 开发体验:直接使用 Python/TypeScript 类型,无需手动验证
Fallback(备选): Instructor Library
理由:
- 多供应商支持:无缝切换 OpenAI、Anthropic、Google、本地模型
- 自动重试:验证失败自动反馈给模型,无需手动实现
- 复杂验证:支持自定义 Pydantic 验证器(email 格式、业务规则等)
- 向后兼容:可逐步迁移到原生 Structured Outputs
为什么不推荐纯 JSON Mode
| 问题 | 影响 | 缓解方案 |
|---|---|---|
| 无法保证合规 | 5-10% 请求解析失败 | 需要手动重试逻辑 |
| 无类型安全 | 运行时错误风险 | 需手动编写验证器 |
| ** verbose schema** | 增加输入 token | 无法优化 |
| 错误反馈困难 | 需手动解析错误 | 增加代码复杂度 |
结论:JSON Mode 仅适用于原型阶段,生产环境应升级到 Structured Outputs。
为什么不推荐纯 Tool Use
Tool Use 的优势在于语义清晰(明确区分指令与工具调用),但在以下场景不如 Structured Outputs:
- 简单数据提取:不需要工具调用的语义开销
- 嵌套复杂结构:Tool Use 的 parameter schema 不如 Pydantic 灵活
- 流式输出:Structured Outputs 支持 stream,Tool Use 通常不支持
结论:Tool Use 适用于 Agent 场景,数据提取优先选择 Structured Outputs。
迁移路径
对于已使用 JSON Mode 的项目,建议按以下路径迁移:
flowchart LR
A[当前:JSON Mode] --> B[阶段 1: 添加 Pydantic 模型]
B --> C[阶段 2: 集成 Instructor]
C --> D[阶段 3: 迁移到原生 Structured Outputs]
A --> A1["可靠性:~95%"]
D --> D1["可靠性:~99.9%"]
style A fill:#ffcdd2
style D1 fill:#c8e6c9
阶段 1:定义 Pydantic 模型,手动验证
from pydantic import BaseModel
class Event(BaseModel):
name: str
date: str
response = client.chat.completions.create(
model="gpt-4o",
messages=[...],
response_format={"type": "json_object"} # JSON Mode
)
data = json.loads(response.choices[0].message.content)
event = Event(**data) # 手动验证
阶段 2:集成 Instructor,自动重试
import instructor
client = instructor.patch(OpenAI())
event = client.chat.completions.create(
model="gpt-4o",
messages=[...],
response_model=Event, # 自动验证 + 重试
max_retries=3
)
阶段 3:迁移到原生 Structured Outputs
response = client.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=[...],
response_format=Event # 原生支持
)
event = response.choices[0].message.parsed
参考资料
- Vellum AI: Function Calling vs Structured Outputs vs JSON Mode - 详细基准测试
- Instructor Library Patterns - Instructor 使用模式
- OpenAI Structured Outputs - OpenAI 官方文档
- Anthropic Structured Outputs - Anthropic 官方文档