Structured Outputs让模型结果贴合 JSON Schema的实现方法
来源:17golang原创
时间:2026-09-15 19:20:20 191浏览 收藏
我在把客服文本接入工单系统时,最容易踩的坑不是模型不会回答,而是回答“看起来像 JSON”,业务却无法稳定使用:字段偶尔缺失,intent 写成了未约定的值,安全拒答还被误判成空结果。解决这类问题,重点是让模型输出直接服从 JSON Schema,并把正常对象、明确拒答和其他失败分开处理。
官方地址:https://developers.openai.com/api/docs/guides/structured-outputs
- Structured Outputs 约束的是结构,不替业务判断事实真伪。
- Pydantic 模型适合把字段、类型和枚举集中成一个契约。
refusal、parsed和传输/截断异常必须分别记录和兜底。
先把自由文本和目标对象分开
用户输入可以是不规则的自然语言,输出却应该是稳定的业务对象。先写清楚“需要哪些字段”,再决定哪些字段必填、哪些值只能来自枚举,别一开始就要求模型输出一大段包含说明文字的 JSON。
例如工单分类只需要意图、订单号和原因。intent 可以限定为退款、换货、查询三种;订单号缺失时不要让模型编造,应该允许业务层把它送回补充信息队列。这样 Schema 负责形状,业务代码负责是否足够办理。
用 strict Schema 固定字段和枚举
Python SDK 可以用 Pydantic 模型声明结构,再通过 chat.completions.parse 请求解析结果。下面的模型名从环境变量读取,避免把某个模型版本硬编码到业务逻辑中:
import os
from typing import Literal
from openai import OpenAI
from pydantic import BaseModel
class TicketIntent(BaseModel):
# 枚举限制业务分支,避免模型临时创造新的意图名
intent: Literal["refund", "exchange", "status"]
# 订单号可以为空字符串,但不能让模型虚构一个编号
order_id: str
# 保留用户原始诉求,便于人工复核和二次分流
reason: str
client = OpenAI()
response = client.chat.completions.parse(
model=os.environ.get("OPENAI_MODEL", "gpt-6-astra"),
messages=[
{"role": "system", "content": "Extract a support ticket intent."},
{"role": "user", "content": "我想退掉订单 A-2048,尺寸选错了。"},
],
response_format=TicketIntent,
)
message = response.choices[0].message
if message.parsed is not None:
# 解析成功后使用类型对象,不再手工 split 或猜字段位置
ticket = message.parsed
print(ticket.intent, ticket.order_id)
这里的关键不是提示词里反复强调“必须是 JSON”,而是把结构交给 response_format。OpenAI 官方文档说明,Structured Outputs 会让响应遵守提供的 JSON Schema;Python 库也支持用 Pydantic 定义对象。它比旧式 JSON mode 更适合字段契约,但不会替你判断订单号是否真实存在。

把 parsed、拒答与解析失败分开处理
结构化输出最容易被忽略的边界是拒答。拒答不是“字段为空”,也不是可以继续写入工单的正常对象。读取消息时先判断拒答,再判断解析结果;同时保留完成原因,便于区分模型主动拒绝和响应被截断。
message = response.choices[0].message
finish_reason = response.choices[0].finish_reason
if message.refusal:
# 拒答只进入安全记录或人工队列,不伪装成 TicketIntent
result = {"kind": "refusal", "detail": message.refusal}
elif message.parsed is not None and finish_reason == "stop":
# 只有完整对象才进入后续业务判断
result = {"kind": "ok", "ticket": message.parsed}
else:
# 截断、服务异常或解析缺失都需要独立的降级策略
result = {"kind": "unusable", "finish_reason": finish_reason}
“贴合 Schema”只解决格式一致性,不保证每次都能拿到可办理结果。拒答可能来自安全策略,unusable 可能来自截断、超时或上游异常,两者的日志、重试次数和人工处理路径都不应混在一起。

让业务兜底接住解析异常
建议在 API 调用外再包一层有限的运行时处理:网络错误可以短暂重试,响应不完整可以进入待补充队列,明确拒答则记录原因并停止自动办理。不要对所有失败都无限重试,否则同一请求可能被重复消费。
| 现象 | 应该确认 | 处理方向 |
|---|---|---|
| parsed 为空且有 refusal | 是否为明确安全拒答 | 记录拒答,转人工或安全流程 |
| finish_reason 不是 stop | 响应是否被截断或中断 | 有限重试,失败后保留原文 |
| 对象完整但订单号无效 | 业务系统能否查到订单 | 交给业务校验,不回头改 Schema |
用边界样例验证契约
上线前至少准备五类样例:正常退款、缺少订单号、同时提到退款和换货、需要拒答的请求、长文本或中途截断。每个样例都记录最终分支和是否产生业务副作用。这样出现问题时,能快速回答“是 Schema 没覆盖、模型拒答,还是业务数据校验失败”。
最后再检查两件事:Schema 字段是否真的是业务所需的最小集合;降级路径是否有幂等键和人工回收点。Structured Outputs 让模型结果更像可靠接口,但可靠的业务接口仍需要超时、日志、重试上限和数据校验共同完成。
常见问题
Structured Outputs 和 JSON mode 有什么区别?
JSON mode 主要保证返回是合法 JSON;Structured Outputs 还要求它遵守提供的 JSON Schema,适合固定字段和枚举的业务对象。
Schema 严格了,还需要业务校验吗?
需要。Schema 能约束类型和结构,不能确认订单存在、权限正确或业务状态允许退款。
拒答能不能当成空对象继续处理?
不能。拒答应通过 message.refusal 单独记录,避免把安全边界误写成正常工单。
什么时候应该重试?
网络抖动或可识别的截断可以有限重试;明确拒答和业务数据无效不应靠重复请求绕过。
-
225 收藏
-
394 收藏
-
202 收藏
-
195 收藏
-
299 收藏
-
科技周边 · 人工智能 | 3小时前 | 人工智能 · 结构化输出 · JSON解析 · 模型输出 · 接口容错 · 大模型结构化输出 模型输出JSON缺字段 JSON兜底解析 JSON Schema校验 AI接口异常处理272 收藏
-
251 收藏
-
357 收藏
-
473 收藏
-
381 收藏
-
195 收藏
-
162 收藏
-
246 收藏
-
145 收藏
-
科技周边 · 人工智能 | 15小时前 | 人工智能 · rag · 向量检索 · 检索增强生成 · rerank · 向量数据库 metadata filter 向量检索过滤条件 rerank顺序 RAG检索409 收藏
-
科技周边 · 人工智能 | 16小时前 | 上下文管理 · 向量检索 · AI工程 · RAG实践 · 文档切片 · chunk overlap RAG文档切片 RAG重叠窗口 上下文膨胀 向量检索召回238 收藏
-
科技周边 · 人工智能 | 17小时前 | API · 人工智能 · 结构化输出 · 函数调用 · Responses API Structured Outputs function_call_output111 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习