当前位置:首页 > 文章列表 > 科技周边 > 人工智能 > Structured Outputs让模型结果贴合 JSON Schema的实现方法

Structured Outputs让模型结果贴合 JSON Schema的实现方法

来源:17golang原创 2026-09-15 19:20:20 0浏览 收藏

我在把客服文本接入工单系统时,最容易踩的坑不是模型不会回答,而是回答“看起来像 JSON”,业务却无法稳定使用:字段偶尔缺失,intent 写成了未约定的值,安全拒答还被误判成空结果。解决这类问题,重点是让模型输出直接服从 JSON Schema,并把正常对象、明确拒答和其他失败分开处理。

官方地址:https://developers.openai.com/api/docs/guides/structured-outputs

要点速览
  • Structured Outputs 约束的是结构,不替业务判断事实真伪。
  • Pydantic 模型适合把字段、类型和枚举集中成一个契约。
  • refusalparsed 和传输/截断异常必须分别记录和兜底。

先把自由文本和目标对象分开

用户输入可以是不规则的自然语言,输出却应该是稳定的业务对象。先写清楚“需要哪些字段”,再决定哪些字段必填、哪些值只能来自枚举,别一开始就要求模型输出一大段包含说明文字的 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 更适合字段契约,但不会替你判断订单号是否真实存在。

Structured Outputs 将自然语言输入约束为 TicketIntent JSON Schema 和 Pydantic 类型对象的静态关系说明图
图1:结构说明图,查看自然语言、JSON Schema、TicketIntent 与业务字段之间的静态关系;不是运行截图。

把 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 可能来自截断、超时或上游异常,两者的日志、重试次数和人工处理路径都不应混在一起。

Structured Outputs 中 parsed 正常对象、refusal 明确拒答与 unusable 解析失败的边界关系说明图
图2:边界说明图,区分 parsed、refusal 与 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 单独记录,避免把安全边界误写成正常工单。

什么时候应该重试?

网络抖动或可识别的截断可以有限重试;明确拒答和业务数据无效不应靠重复请求绕过。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
OpenTelemetry 毕业后如何把 Trace、Metrics、Logs 统一到同一条证据链OpenTelemetry 毕业后如何把 Trace、Metrics、Logs 统一到同一条证据链
上一篇
OpenTelemetry 毕业后如何把 Trace、Metrics、Logs 统一到同一条证据链
墨刀AI适合产品经理做需求澄清会吗?先测试问题清单、决策记录和责任跟踪
下一篇
墨刀AI适合产品经理做需求澄清会吗?先测试问题清单、决策记录和责任跟踪
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    43次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    138次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    74次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    39次使用
  • CMMLU中文大模型评估基准:功能、使用教程与应用场景解析
    CMMLU
    深入了解CMMLU中文评估基准,涵盖67个学科主题,提供数据集下载、Zero-shot/Five-shot评估方法及排行榜,助力优化中文语言模型性能。
    26次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码