Structured Output

专栏:AI 全栈开发|20

一、当模型输出要进数据库,普通文本就不够用了

聊天回答多一句解释通常没关系,但当输出要写 CRM、触发退款流程或进入数据库,多一个键、少一个字段、把布尔值写成字符串,都可能变成真实业务故障。Structured Output 的目标不是让 JSON "更好看",而是把格式约束从提示词愿望升级成程序契约。

|------------------------------------------------------------------------------------------------|
| 上一篇 19《Streaming Model Output》解决的是"怎样把文本增量稳定送到浏览器"。这一篇换一个问题:当下游不再是人,而是代码,模型输出怎样才能成为程序敢直接消费的数据。 |

二、先把 JSON Mode 和 Structured Output 分清

|----------------------|---------------|---------------------|--------------|
| 方式 | 保证什么 | 不能保证什么 | 适合 |
| 提示词约定 | 什么都不硬保证 | 可能带 Markdown、废话、缺字段 | 低风险内部 Demo |
| JSON Mode | 输出是合法 JSON | 不保证字段、类型和枚举 | 只要求"能 parse" |
| JSON Schema + strict | 输出遵循给定 Schema | 不保证业务语义一定正确 | 提取、分类、路由、入库 |

本文继续沿用 F16~F19 的 Chat Completions 教学基线,所以示例使用 response_format。若项目使用 Responses API,同一概念已经移动到 text.format;两种 API 的字段位置不同,但"Schema + strict + 应用侧边缘状态处理"的思路不变。

三、Schema 管"形状",业务仍要管"意义"

结构化输出最容易被误解成"模型从此不会错"。它能显著提高结构可靠性,但不能替代业务判断:Schema 可以要求 category 只能是 billing / bug / advice,却无法保证模型把真实退款投诉一定分类成 billing。格式正确、值域合法、语义正确,是三个不同层次。

• 形状:字段是否存在、类型是否正确、是否允许多余键------由 Schema 约束。

• 值域:enum、数值上下限等可部分写进 Schema,同时仍应在应用侧校验。

• 语义:分类是否真的判断对、提取内容是否忠于原文------只能靠业务规则、评测与人工抽查。

四、最小请求:json_schema + strict 真正长什么样

Chat Completions 教学基线:response_format 里放 Schema

复制代码
schema = {
    "type": "object",
    "properties": {
        "customer_name": {"type": "string"},
        "lead_level": {
            "type": "string",
            "enum": ["high", "mid", "low"]
        }
    },
    "required": ["customer_name", "lead_level"],
    "additionalProperties": False
}

response_format = {
    "type": "json_schema",
    "json_schema": {
        "name": "lead_extract",
        "strict": True,
        "schema": schema
    }
}

strict 模式的关键不是多写一个布尔值,而是 Schema 本身必须落在 Provider 支持的子集里。以 OpenAI 当前规则为例:对象要显式 additionalProperties:false,字段都必须列入 required;逻辑上的"可选值"可以用包含 null 的联合类型表达。

五、三层保障缺一不可

图 2 请求侧、生成侧、响应侧各守一道关;strict 不是"跳过校验"的许可证

请求侧用 Pydantic/Zod/手写 JSON Schema 定义契约;生成侧由 Provider 做受限解码,尽量不让 Schema 外的 token 路径被生成;响应侧仍要显式处理拒绝、输出不完整与应用层校验。尤其是用户输入场景,安全拒绝不一定遵循你的业务 Schema,输出被 max token 截断时也不能拿半截 JSON 继续 parse。

|---------------------|-------------------------------|
| 响应状态 | 应该怎么处理 |
| 正常结构化结果 | parse → 强类型 validate → 业务规则校验 |
| refusal | 走拒绝分支,不把拒绝文本当 JSON 解析 |
| incomplete / length | 视为未完成,调整预算或重试,不消费半截对象 |
| Provider 不支持 strict | 显式降级并加强响应侧校验,不能假装仍有强约束 |

六、让代码模型成为单一事实来源

原稿采用 Pydantic 定义输出对象,这个方向应保留:业务代码面对的是 Lead 这样的强类型对象,Schema 由模型类导出,而不是再手写第二份字段表。这样字段名、枚举和值域修改时只改一处,避免"代码模型已经变了,发送给 Provider 的 Schema 还停留在旧版本"。

Pydantic:定义对象,再由 SDK/Helper 生成 Schema

复制代码
from typing import Literal
from pydantic import BaseModel, Field

class Lead(BaseModel):
    customer_name: str
    lead_level: Literal["high", "mid", "low"]
    category: Literal["billing", "bug", "advice", "other"]
    urgency: int = Field(ge=1, le=5)
    reply_suggestion: str

# 生产中优先使用 SDK 的 parse/helper,
# 或统一封装一个 schema adapter,避免业务代码散落手写 JSON Schema。

七、生产链路:能力探测、边缘状态、校验和有限重试

图 3 生产路径先判断 Provider 能力,再处理 refusal / incomplete,最后才把对象交给业务层

兼容网关的最大风险不是"请求直接失败",而是某些字段被忽略后悄悄退回普通文本模式。因此 Provider Adapter 应明确记录当前端点支持 json_schema、json_object 还是仅提示词约定;每次降级都要让响应侧的校验更严格,而不是把约束丢掉。

• Schema 设计尽量清晰、扁平;复杂嵌套不是绝对不能用,但兼容端点支持度与首次 Schema 处理延迟都应测试。

• 校验失败只做有限重试,并把"失败率"当指标;如果同一 Schema 经常失败,应优先修设计,不是无限加重试。

• 用户原文只放在数据位置,不要拼进 Schema description 或系统结构;结构正确并不能阻止提示注入操控字段含义。

八、最容易把 Structured Output 用错的 7 件事

• 只写"请输出 JSON"却把下游当强类型对象使用:格式可靠性仍然押在模型自觉上。

• 把 JSON Mode 当成 JSON Schema:合法 JSON 并不等于字段、类型和枚举满足业务契约。

• strict Schema 漏掉 required 或 additionalProperties:false:强约束请求可能直接被拒绝。

• 手写 Schema 与 Pydantic/Zod 各维护一份:长期一定漂移,单一事实来源更重要。

• 拿到响应就 json.loads:先判断 refusal / incomplete 等边缘状态,再谈解析。

• 校验失败无限重试:每次重试都在花钱;有限重试后应进入人工/待审或业务降级。

• 认为 Schema 能保证内容真实:它只能把脏数据装进"形状正确"的盒子,语义仍需要评测与规则。

九、总结与下一篇

Structured Output 最值得记住的不是某个参数名,而是三层保障:请求侧定义契约,生成侧按契约约束输出,响应侧处理边缘状态并把结果校验成真正的业务对象。三层中任何一层缺失,可靠性都会重新退回"相信模型会乖乖输出"。

下一篇 F21《Model Provider 抽象层》会把 F16~F20 逐渐散落的 base_url、模型名、请求差异和错误语义收进 Provider Adapter,让业务代码只面对统一的 ModelService。

相关推荐
2367.1 小时前
AI视频怎么统一片头、字幕、转场和品牌风格:后编辑包装全流程指南
人工智能
AI小百科1 小时前
MoE 架构:为什么大模型开始“分组干活“
人工智能
马剑威(威哥爱编程)1 小时前
【HarmonyOS 7 小艺智能体实战】06:VibeCoding 十分钟搓出一个 Skill(上)——从想法到生成
人工智能·华为·harmonyos
骇客野人1 小时前
从大模型对话聊天都人工智能智能体干活到如今的AI编程的思考以及未来发展趋势展望
人工智能·ai编程
FRing031 小时前
JavaScript入门到精通三十一(原型继承、原型链)
开发语言·前端·javascript
2601_965449081 小时前
AI视频制作:做系列考古视频音色不统一,AI 音色克隆的完整流程
人工智能·机器学习·音视频
4SAPI1 小时前
会议智能处理大模型 API 生产选型指南:4SAPI、硅基流动与 OpenRouter 的架构权衡
人工智能·架构·自动化·gateway·php
成旭先生1 小时前
车辆、能源与地理数据 API 实战:把定价、油价、行政区划串成一条链路
人工智能·windows·能源·api集成·油价查询·车辆定价·地址清洗
北方的银狐-Zero1 小时前
OntoL 7.0|面向工业多智能体的语义本体套件,让异构系统真正 “看懂彼此”——OntoL的本体产品矩阵
人工智能·mysql·sqlite·1024程序员节·本体论·本体产品