结构化输出与 JSON Schema
Agent 的代码要依据模型结果做分支判断、写数据库、调工具,靠自然语言「猜意思」不可靠。结构化输出就是让模型按约定格式(通常是 JSON)返回,并用 JSON Schema 校验——本章讲清这套机制与落地写法。
为什么 Agent 需要结构化输出
- 程序可解析:JSON 能直接变成 dict/对象,省去写正则抽字段。
- 契约清晰:字段名、类型定死,下游代码不会拿到意外结构。
- 校验前置:结构不对当场报错,而不是带着脏数据跑完全程。
- 工具需要:函数调用的参数本身就是结构化 JSON。
一句话:模型输出越规矩,Agent 越不容易「带病运行」。
JSON Schema 约定
JSON Schema 是描述「JSON 应该长什么样」的标准。用它约束一个天气查询结果:
{
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名"},
"condition": {"type": "string", "enum": ["晴", "阴", "雨", "雪"]},
"celsius": {"type": "number"}
},
"required": ["city", "condition", "celsius"],
"additionalProperties": false
}
声明了字段类型、枚举与必填项,模型就按这个模板填内容,而不是自由发挥。
response_format:让输出守格式
OpenAI 兼容接口常用 response_format 参数约束输出,常见两种形态:
{"type": "json_object"}
要求返回合法 JSON 对象;部分服务要求提示词里出现「json」字样才生效。
{"type": "json_schema", "json_schema": {"name": "weather", "schema": {"type": "object"}}}
按给定 Schema 严格生成,比 json_object 更可靠。Python 里的写法:
import json
import os
from openai import OpenAI
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
resp = client.chat.completions.create(
model=os.getenv("MODEL"),
messages=[{"role": "user", "content": "北京今天天气如何?请输出 json"}],
response_format={"type": "json_object"},
)
data = json.loads(resp.choices[0].message.content)
print(data) # 输出:{'city': '北京', 'condition': '晴', 'celsius': 24}
各家支持情况
各家对 json_object / json_schema 的支持程度不一,接入前务必查官方文档:
| 服务/形态 | 常见写法 | 说明(以官方文档为准) |
|---|---|---|
| OpenAI | json_object / json_schema 均支持 | 兼容规范的原始出处 |
| DeepSeek | json_object 常见 | 提示词需含 json 字样 |
| 通义百炼兼容模式 | 兼容 OpenAI 写法 | 原生接口另有参数 |
| Anthropic Claude | 通过 tool use 约束 | 无 response_format 参数 |
不要在代码里假设各家「全兼容」,兼容模式与原生接口的行为可能不同。
解析失败兜底策略
再严格的约束也可能偶尔失手,Agent 代码必须能兜底:
# 承接上文示例:json 已导入、resp 已取得
content = resp.choices[0].message.content or ""
try:
data = json.loads(content)
except json.JSONDecodeError:
start, end = content.find("{"), content.rfind("}")
data = json.loads(content[start:end + 1]) if start != -1 else None
if data is None:
print("解析失败,记录日志等待重试") # 输出:解析失败,记录日志等待重试
更稳的顺序是:先重试一次并附上错误信息让模型修正,再尝试截取 JSON 片段,仍失败就放弃本次并记录日志,由上层决定重来或降级,避免无限循环烧钱。
小结
结构化输出让模型从「会说话」升级为「守契约」:用 JSON Schema 定义字段,用 response_format 约束生成,用 json.loads 解析,用重试与截取兜底。Agent 通向工具的每条路径上,都应设一道格式校验的闸门。