结构化输出如何处理模型返回的枚举值错误
我在做工单自动分类时,最容易被“模型返回了一个不在枚举里的值”带偏:日志里看见 enum mismatch,第一反应往往是再试一次。更稳的处理顺序是先分三层:响应被拒答、输出被截断,还是确实拿到了 JSON 但字段值不在业务集合里。只有第三种才是枚举校验问题。
官方地址:https://developers.openai.com/api/docs/guides/structured-outputs
- 用 JSON Schema 的
enum写死机器可接受的值,不要只在提示词里描述“请使用这几个词”。 - 解析顺序是状态判断、结构解析、业务枚举校验;拒答和不完整不能伪装成默认分类。
- 重试必须改变可恢复条件,并保存脱敏原始响应、schema 版本、请求 ID 和错误类型。
枚举报错通常来自三种不同现场
我现在排查这类问题,会先看响应状态和原始载荷,再看字段值。一个严格的结构化输出接口,在成功时应匹配提供的 JSON Schema;但模型也可能因为安全原因拒答,或者因为达到输出上限而不完整。把这两类情况直接送进业务反序列化,最后看到的往往只是一个模糊的“枚举错误”。
| 现场 | 典型证据 | 正确动作 |
|---|---|---|
| 契约不匹配 | JSON 可解析,但 label 不在允许集合 | 检查 enum、模型能力和服务端适配层 |
| 拒答 | 响应带 refusal 或没有可用结构化内容 | 转人工或安全兜底,不当作分类结果 |
| 不完整 | 状态为 incomplete,原因可能是 max tokens | 增加预算或缩短输入后重试 |

先在 JSON Schema 中固定真正允许的值
提示词里的“只能返回 bug、question、feature”只是自然语言约定,不能代替机器契约。以支持结构化输出的 Responses API 为例,字段应声明为字符串并给出 enum。同时把必填字段写进 required,关闭额外字段,避免下游悄悄接收另一种形状。
{
"type": "object",
"properties": {
"label": {
"type": "string",
"enum": ["bug", "question", "feature"]
},
"confidence": {"type": "number"}
},
"required": ["label", "confidence"],
"additionalProperties": false
}
请求层再将这个 schema 作为 JSON Schema 格式传入,并启用严格模式(如果当前模型和接口支持)。这里要记住一个边界:严格模式约束的是成功生成的结构,不能保证拒答一定有 label,也不能替你定义“未知”是否是合法业务值。如果业务确实允许无法判断,就把 unknown 明确写进枚举,而不是收到异常后临时改写。
解析前先判断 refusal 和 incomplete
我更愿意把解析器写成一个小的状态机:先判断响应是否完成,再取结构化文本,最后才检查枚举。这样日志会告诉你“为什么没有结果”,而不是把所有失败都记成同一类。
import json
ALLOWED_LABELS = {"bug", "question", "feature"}
def parse_classification(response: dict) -> dict:
# 拒答不能转换为默认分类,否则会污染业务统计。
if response.get("status") == "incomplete":
reason = response.get("incomplete_details", {}).get("reason", "unknown")
raise RuntimeError(f"incomplete response: {reason}")
if response.get("refusal"):
raise RuntimeError("model refusal: keep for review")
raw = response.get("output_text", "")
try:
data = json.loads(raw)
except json.JSONDecodeError as exc:
# 原文只进入诊断记录,不直接进入业务表。
raise ValueError("structured output is not valid JSON") from exc
label = data.get("label")
if label not in ALLOWED_LABELS:
# 业务集合是最后一道防线,防止适配层放宽 schema 后漏检。
raise ValueError(f"enum mismatch: {label!r}")
return data
不同 SDK 对拒答和结构化内容的字段封装可能不同,所以不要照抄字段路径就上线;先在适配层把供应商响应归一成 status、refusal、incomplete_details 和 output_text 四个内部字段,再让业务只依赖这四个字段。

重试必须改变条件,原始响应要能追查
枚举不匹配时不要无限重试同一条请求。若是旧模型或非严格模式,先修正 schema 适配;若是输入含糊,补充分类定义和一个反例;若是 incomplete,缩短上下文或增加输出预算。拒答则应走安全兜底,不能用重试把拒答“洗”成业务结果。
每次失败至少记录 request_id、模型标识、schema_version、错误类型、响应状态和脱敏后的原始响应。原始响应里可能含用户输入、密钥回显或个人信息,落盘前要按字段脱敏,并设置访问权限和保留期限。重试成功后也保留第一次失败的分类,后续才能判断是契约修复有效,还是偶然采样成功。
| 错误类型 | 是否重试 | 重试改变什么 |
|---|---|---|
| enum mismatch | 有条件 | 修 schema、模型适配或输入分类说明 |
| incomplete | 可以 | 缩短上下文、提高预算或拆分任务 |
| refusal | 不按普通重试处理 | 执行安全兜底、人工审核或改写业务流程 |
上线前看五个指标,而不是只看成功率
我会把 enum_mismatch、refusal、incomplete、重试次数和人工回退分别统计,并按模型、schema 版本、业务场景切分。这样一次 schema 发布后,能看出是某个枚举被删除、某类输入变长,还是模型适配层没有把状态传出来。
- 枚举值是否来自同一份版本化契约,前后端和数据表没有各写一套。
- 严格模式不可用时,客户端是否仍保留本地 JSON 与枚举校验。
- 拒答和不完整是否有独立状态,不会写入默认标签。
- 原始响应是否脱敏、可按请求 ID 找回,并有保留期限。
- 重试是否有次数上限,最终失败是否进入人工或明确的业务兜底。
常见问题
JSON 合法为什么还会枚举错误?
JSON 语法正确只说明文本能被解析,不能说明字段值属于业务允许集合。还要检查 enum、本地类型定义和适配层是否使用了同一份契约。
把未知值加入 enum 就能解决吗?
只有当“无法判断”本身是产品认可的结果才可以加入。否则它会把模型不确定性藏起来,应该保留原始响应并进入人工或业务兜底。
为什么不建议看到枚举错误就重试?
相同输入、相同契约和相同模型可能反复得到同类结果,重试只增加延迟和成本。先确认错误层,再改变 schema、输入、预算或处理路径。
LiblibAI把一张草图做成成品要生成几轮?按探索、精修和放大估算
- 上一篇
- LiblibAI把一张草图做成成品要生成几轮?按探索、精修和放大估算
- 下一篇
- Go encoding/xml 如何映射重复子节点到切片
-
- 科技周边 · 人工智能 | 22小时前 |
- 本地模型量化时怎么比较 4-bit 与 8-bit 代价
- 481浏览 收藏
-
- 科技周边 · 人工智能 | 23小时前 |
- 语音转写带说话人分离时如何处理重叠发言
- 115浏览 收藏
-
- 科技周边 · 人工智能 | 1天前 |
- 扩散模型固定 seed 后为什么仍有细节差异
- 457浏览 收藏
-
- 科技周边 · 人工智能 | 1天前 | JSON · 人工智能 · schema · 工程实践 · 大模型 · Python LLM JSONSchema 结构化输出 JSON Schema 有限重试
- LLM 输出 JSON Schema 不稳定时怎么设计重试
- 480浏览 收藏
-
- 科技周边 · 人工智能 | 1天前 | 人工智能 · OCR · 文档理解 · OCR 表格识别 行列关系 Table Transformer
- OCR 识别表格时如何保留行列关系
- 147浏览 收藏
-
- 科技周边 · 人工智能 | 1天前 |
- RAG 文档切片的重叠长度怎么按检索目标调整
- 280浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 98次使用
-
- OpenCompass
- OpenCompass是上海AI实验室推出的开源大模型评测平台,提供CompassKit、CompassHub和CompassRank三大核心组件,支持LLM及多模态模型的一站式标准化评估与排行榜查询。
- 28次使用
-
- SuperCLUE
- SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
- 253次使用
-
- C-Eval
- 深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
- 180次使用
-
- AI Prompt Library
- 探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
- 115次使用
-
- Go Excelize API源码阅读SetSheetViewOptions示例解析
- 2022-12-24 485浏览
-
- Go快速开发一个RESTfulAPI服务
- 2023-01-01 493浏览
-
- etcd通信接口之客户端API核心方法实战
- 2023-01-07 433浏览
-
- golangAPI请求队列的实现
- 2023-01-24 489浏览
-
- Go 通过 Map/Filter/ForEach 等流式 API 高效处理数据的思路详解
- 2022-12-28 267浏览

