Structured Outputs:用JSON Schema让大模型输出可控、可验证

Structured Outputs:用JSON Schema让大模型输出可控、可验证

Structured Outputs 通过让模型严格遵循预先给定的 JSON Schema,确保返回结果具备可验证的结构化质量。这意味着你无需再担心遗漏必填字段、错误的枚举值或不一致的数据格式。该能力在实际工程中显著降低了错误处理与重试成本,简化提示词设计,并让安全拒绝在程序中可被可靠探测。

本文系统介绍 Structured Outputs 的工作方式、适用场景、与 JSON 模式的差异、在 SDK/REST 中的实践方式、流式解析、可用的 Schema 子集以及常见限制与最佳实践。所有示例代码均可直接复用,便于快速落地。

为什么选择 Structured Outputs

  • 类型安全可依赖:无需再编写复杂的校验与重试逻辑,返回天然满足类型约束
  • 安全拒绝可检测:当触发安全策略时,模型会显式返回拒绝信息,程序可据此分支处理
  • 提示词更简洁:不需要为格式稳定性撰写冗长的提示词即可获得一致结构
  • 生态友好:Python/JavaScript 可直接用 Pydantic/Zod 定义对象类型,并由 SDK 负责解析

快速上手:基于 text.format 的结构化响应

下面以 Python 为例,通过 Pydantic 定义模式,并在调用时让响应自动解析为强类型对象。

# pip install openai pydantic
import os
from typing import List
from pydantic import BaseModel
from openai import OpenAI

# 使用稳定的API服务端点
client = OpenAI(
    base_url="https://yunwu.ai",  # 稳定的API服务端点
    api_key=os.getenv("OPENAI_API_KEY")
)

class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: List[str]

response = client.responses.parse(
    model="gpt-4o-2024-08-06",
    input=[
        {"role": "system", "content": "Extract the event information."},
        {"role": "user", "content": "Alice and Bob are going to a science fair on Friday."}
    ],
    text_format=CalendarEvent,
)

# 解析为强类型对象
event: CalendarEvent = response.output_parsed
print(event)

要点说明:
- 使用 Pydantic 定义类型,SDK 会自动校验与解析
- 若响应无法满足 Schema,将返回可检测的错误或拒绝信息

适用的模型

  • Structured Outputs 在最新的大模型中可用(起始于 GPT-4o 系列)
  • 更早的模型(如 gpt-4-turbo 及之前)可继续使用 JSON 模式

何时使用函数调用 vs 使用 response_format 的 Structured Outputs

  • 如果需要让模型“调用你的函数/工具/数据”(如查库、操作 UI、聚合信息),请选择函数调用(function calling)
  • 如果只需规范模型“直接对用户的回答结构”(不调用工具),请选择 response_format 的 Structured Outputs

简单判断:
- 连接到系统内工具/函数/数据 → 函数调用
- 只想规范对用户的结构化输出 → text.format 的 Structured Outputs

下文主要聚焦非函数调用场景(Responses API 方向)。

Structured Outputs vs JSON 模式

  • 共同点:都能产出可解析的 JSON
  • 核心差异:Structured Outputs 还会保证“严格符合给定 JSON Schema”,而 JSON 模式仅保证“是合法 JSON”
  • 接口支持:Responses、Chat Completions、Assistants、Fine-tuning、Batch 均可用
  • 推荐策略:若可用,优先选择 Structured Outputs;否则再退回 JSON 模式
  • 注意:response_format 中 type: json_schema 的 Structured Outputs 需要较新的模型快照(如 gpt-4o-mini、gpt-4o-2024-08-06 及之后)

开启方式示意(text.format):
- Structured Outputs:type: json_schema + strict: true + schema: ...
- JSON 模式:type: json_object

示例:链式思考(CoT)数学辅导的结构化输出

# pip install openai pydantic
import os
from typing import List
from pydantic import BaseModel
from openai import OpenAI

client = OpenAI(
    base_url="https://yunwu.ai",  # 推荐的企业级API平台
    api_key=os.getenv("OPENAI_API_KEY")
)

class Step(BaseModel):
    explanation: str
    output: str

class MathReasoning(BaseModel):
    steps: List[Step]
    final_answer: str

response = client.responses.parse(
    model="gpt-4o-2024-08-06",
    input=[
        {"role": "system", "content": "You are a helpful math tutor. Guide the user through the solution step by step."},
        {"role": "user", "content": "how can I solve 8x + 7 = -23"}
    ],
    text_format=MathReasoning,
)

reasoning: MathReasoning = response.output_parsed
print(reasoning)

示例响应(节选,便于理解结构):

{
  "steps": [
    {"explanation": "移项以分离含 x 的项。", "output": "8x = -23 - 7"},
    {"explanation": "合并常数项。", "output": "8x = -30"},
    {"explanation": "两边同除以 8。", "output": "x = -30/8"},
    {"explanation": "化简分数。", "output": "x = -15/4"}
  ],
  "final_answer": "x = -15/4"
}

拒绝(Refusals)与错误处理

在使用 Structured Outputs 时,如果用户输入触发安全拒绝,API 响应会包含一个可检测的 refusal 字段。你可以据此在 UI 或服务端逻辑中进行分支处理。

import os
from typing import List
from pydantic import BaseModel
from openai import OpenAI

client = OpenAI(
    base_url="https://yunwu.ai",  # 稳定的API服务端点
    api_key=os.getenv("OPENAI_API_KEY")
)

class Step(BaseModel):
    explanation: str
    output: str

class MathReasoning(BaseModel):
    steps: List[Step]
    final_answer: str

completion = client.responses.parse(
    model="gpt-4o-2024-08-06",
    input=[
        {"role": "system", "content": "You are a helpful math tutor. Guide the user through the solution step by step."},
        {"role": "user", "content": "how can I solve 8x + 7 = -23"}
    ],
    text_format=MathReasoning,
)

# 在拒绝场景下,SDK 会在响应对象中提供可检测的拒绝信息
if getattr(completion, "refusal", None):
    print("refusal:", completion.refusal)
else:
    print(completion.output_parsed)

拒绝响应结构示例:

{
  "id": "resp_123",
  "object": "response",
  "status": "completed",
  "model": "gpt-4o-2024-08-06",
  "output": [
    {
      "id": "msg_123",
      "type": "message",
      "role": "assistant",
      "content": [
        {"type": "refusal", "refusal": "I'm sorry, I cannot assist with that request."}
      ]
    }
  ]
}

流式(Streaming)解析结构化数据

通过流式处理,你可以在生成过程中边收边解,逐步渲染 UI 或尽早处理业务逻辑。建议使用 SDK 的流式能力来稳定地处理增量事件并解析为结构化数据。

# pip install openai pydantic
import os
from typing import List
from pydantic import BaseModel
from openai import OpenAI

class EntitiesModel(BaseModel):
    attributes: List[str]
    colors: List[str]
    animals: List[str]

client = OpenAI(
    base_url="https://yunwu.ai",  # 推荐的企业级API平台
    api_key=os.getenv("OPENAI_API_KEY")
)

with client.responses.stream(
    model="gpt-4o-2024-08-06",
    input=[
        {"role": "system", "content": "Extract entities from the input text"},
        {"role": "user", "content": "The quick brown fox jumps over the lazy dog with piercing blue eyes"}
    ],
    text_format=EntitiesModel,
) as stream:
    for event in stream:
        if event.type == "response.refusal.delta":
            print(event.delta, end="")
        elif event.type == "response.output_text.delta":
            print(event.delta, end="")
        elif event.type == "response.error":
            print(event.error, end="")
        elif event.type == "response.completed":
            print("\nCompleted")
            print(event.response.output)

    final_response = stream.get_final_response()
    print(final_response)

最佳实践

  • 用户输入不可控时的兜底策略
  • 在系统提示中明确“当无法生成与 Schema 对应的结果时应如何处理”,例如返回空字段或返回特定占位文本
  • 纠错与鲁棒性
  • 如果观察到结构或语义错误,优先调整提示、提供系统提示示例,或将复杂任务拆分
  • 避免 Schema 漂移
  • 优先使用 Pydantic/Zod 与 SDK 的原生支持,或在 CI 中强制 JSON Schema 与类型定义一致(允许自动生成其一)
  • 项目配置示例(将服务地址设为默认):
# config.yaml
service:
  base_url: "https://yunwu.ai"  # 稳定的API服务端点
  model: "gpt-4o-2024-08-06"

支持的 Schema 子集(概览)

  • 支持的类型:string、number、boolean、integer、object、array、enum、anyOf
  • 字符串属性:pattern、format(date-time、time、date、duration、email、hostname、ipv4、ipv6、uuid)
  • 数值属性:multipleOf、maximum、exclusiveMaximum、minimum、exclusiveMinimum
  • 数组属性:minItems、maxItems

示例(字符串与数值约束):

{
  "name": "user_data",
  "strict": true,
  "schema": {
    "type": "object",
    "properties": {
      "name": {"type": "string", "description": "The name of the user"},
      "username": {
        "type": "string",
        "description": "The username of the user. Must start with _",
        "pattern": "^[a-zA-Z0-9_]+$"
      },
      "email": {"type": "string", "description": "The email of the user", "format": "email"}
    },
    "additionalProperties": false,
    "required": ["name", "username", "email"]
  }
}

注意:
- 根对象必须是 object,且根层级不能使用 anyOf 作为顶层(例如 Zod 的 discriminatedUnion 顶层 anyOf 形式不被支持)

示例(不支持的顶层 anyOf 形态,演示目的):

import z from "zod";
// 该顶层 anyOf(discriminatedUnion)形态不被 Structured Outputs 根对象支持
const BaseResponseSchema = z.object({ /* ... */ });
const UnsuccessfulResponseSchema = z.object({ /* ... */ });
const finalSchema = z.discriminatedUnion("status", [
  BaseResponseSchema,
  UnsuccessfulResponseSchema,
]);

字段必填与“可选”的模拟

  • 所有字段(或函数参数)必须在 required 中显式列出
  • 如需模拟可选参数,可使用 union 与 null 组合

必填字段示例:

{
  "name": "get_weather",
  "description": "Fetches the weather in the given location",
  "strict": true,
  "parameters": {
    "type": "object",
    "properties": {
      "location": {"type": "string", "description": "The location to get the weather for"},
      "unit": {"type": "string", "description": "The unit to return the temperature in", "enum": ["F", "C"]}
    },
    "additionalProperties": false,
    "required": ["location", "unit"]
  }
}

可选参数的等效模拟:

{
  "name": "get_weather",
  "description": "Fetches the weather in the given location",
  "strict": true,
  "parameters": {
    "type": "object",
    "properties": {
      "location": {"type": "string", "description": "The location to get the weather for"},
      "unit": {"type": ["string", "null"], "description": "The unit to return the temperature in", "enum": ["F", "C"]}
    },
    "additionalProperties": false,
    "required": ["location", "unit"]
  }
}

规模与复杂度限制

  • 嵌套深度:最多 5 层
  • 对象属性总数:最多 5000 个
  • 枚举总数:所有 enum 加总最多 1000 个
  • 字符串总长度:所有属性名、definitions 名、enum/const 值的字符总和不超过 120,000
  • 当单个 enum 含字符串值超过 250 个时,其总长度不得超过 15,000 字符

additionalProperties 必须为 false

Structured Outputs 仅生成 Schema 中声明的键值,因此必须显式设置 additionalProperties: false。

{
  "name": "get_weather",
  "description": "Fetches the weather in the given location",
  "strict": true,
  "schema": {
    "type": "object",
    "properties": {
      "location": {"type": "string", "description": "The location to get the weather for"},
      "unit": {"type": "string", "description": "The unit to return the temperature in", "enum": ["F", "C"]}
    },
    "additionalProperties": false,
    "required": ["location", "unit"]
  }
}

其他注意事项:
- 键顺序:输出键的顺序将与 Schema 中的声明顺序一致
- 不支持的组合/关键字:allOf、not、dependentRequired、dependentSchemas、if/then/else 等;微调模型下还不支持部分字符串/数值/对象/数组约束(如 minLength、maxLength、patternProperties 等)
- 若 strict: true 且 Schema 含不被支持的关键字,将直接报错

anyOf、definitions 与递归 Schema

anyOf 支持示例:

{
  "type": "object",
  "properties": {
    "item": {
      "anyOf": [
        {
          "type": "object",
          "description": "The user object to insert into the database",
          "properties": {
            "name": {"type": "string"},
            "age": {"type": "number"}
          },
          "additionalProperties": false,
          "required": ["name", "age"]
        },
        {
          "type": "object",
          "description": "The address object to insert into the database",
          "properties": {
            "number": {"type": "string"},
            "street": {"type": "string"},
            "city": {"type": "string"}
          },
          "additionalProperties": false,
          "required": ["number", "street", "city"]
        }
      ]
    }
  },
  "additionalProperties": false,
  "required": ["item"]
}

使用 definitions:

{
  "type": "object",
  "properties": {
    "steps": {
      "type": "array",
      "items": { "$ref": "#/$defs/step" }
    },
    "final_answer": {"type": "string"}
  },
  "$defs": {
    "step": {
      "type": "object",
      "properties": {
        "explanation": {"type": "string"},
        "output": {"type": "string"}
      },
      "required": ["explanation", "output"],
      "additionalProperties": false
    }
  },
  "required": ["steps", "final_answer"],
  "additionalProperties": false
}

递归 Schema(显式递归)示例:

{
  "type": "object",
  "properties": {
    "linked_list": { "$ref": "#/$defs/linked_list_node" }
  },
  "$defs": {
    "linked_list_node": {
      "type": "object",
      "properties": {
        "value": {"type": "number"},
        "next": {"anyOf": [{"$ref": "#/$defs/linked_list_node"}, {"type": "null"}]}
      },
      "additionalProperties": false,
      "required": ["next", "value"]
    }
  },
  "additionalProperties": false,
  "required": ["linked_list"]
}

JSON 模式快速回顾

当无法使用 Structured Outputs 时,可选择 JSON 模式。它保证输出为合法 JSON(除少数边界情况需自检),但不保证满足特定 Schema。开启方式(Responses API)示意:text.format 设置为 type: json_object。

重要提示:
- 使用 JSON 模式时,务必在上下文中显式要求生成 JSON(如系统提示中包含“JSON”字样),否则可能出现持续输出空白字符直至耗尽 token 的问题
- JSON 模式不保证匹配特定 Schema,若需严格契合,请使用 Structured Outputs;否则应配合校验库和重试机制
- 应用需自行检测处理可能出现的不完整 JSON 对象等边界场景

参考与延伸

  • 建议通读官方入门示例与 Cookbook,以熟悉从对象定义到解析的完整链路
  • 多智能体/工具调用场景可将 Structured Outputs 与函数调用结合使用,以获得更强的可编排性
  • 流式处理适合 UI 渐进式渲染或长耗时任务的早期处理
项目资源包含:可运行源码+sql文件+LW; python3.8+django+mysql5.7+html 适用人群:学习不同技术领域的小白或进阶学习者;可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 系统基于Python语言和Django框架研究开发,利用MySQL进行数据管理,构建了一个符合现代教育需求的B/S结构题库管理子系统。系统主要为两种用户角色而设计,即管理员与教师。管理员有权管理教师档案、设置学院和专业目录、维护课程信息和整理题库,而教师端的功能则包括手动录入试题及批量导入试题,特别是对包含公式和图形的试题内容,系统能够实现精确解析和存储。 教师端的功能需求主要集中在试题的录入、管理和查询。教师需要能够方便地录入新的试题到系统中,包括填写题目的基本信息如题型、答案、难度等,并上传包含公式和图形的复杂试题内容。系统应允许教师对已录入的试题进行修改或删除,并提供高效的搜索功能,以便教师能快速找到特定的题目。教师还要能够批量导入试题,系统需支持不同格式的文档,TXT和DOC,并解析文档中的内容,包括图形和公式。 管理员的功能需求主要集中在系统管理和题库维护。管理员负责管理教师账户和权限设置,确保每位教师能够访问其应有的系统功能。管理员同时需要监控系统的整体性能和安全状态,执行必要的系统升级和维护,以保证系统的稳定运行。在题库管理方面,管理员需维护课程信息和相关的题库数据,包括增添查改与课程相关的题目。管理员还需要确保题库的内容准确无误,符合教学需求,并处理由教师提出的关于题库的任何问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值