结构化输出与 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 的支持程度不一,接入前务必查官方文档:

服务/形态常见写法说明(以官方文档为准)
OpenAIjson_object / json_schema 均支持兼容规范的原始出处
DeepSeekjson_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 通向工具的每条路径上,都应设一道格式校验的闸门。

笔记加载中…