结构化输出实战:为什么你的 AI 总返回非法 JSON
调用 AI 拿结构化数据,最烦的就是偶发的非法 JSON。本文讲清约束解码、JSON Schema、重试与校验的完整方案,附可直接用的模板。
一个让人抓狂的日常
你用 AI 提取数据,提示词里明明写了”只返回 JSON”,但总有一两次它给你加一句”好的,这是结果:“,或者少个逗号、多个注释。解析一失败,整条流水线就崩。
很多人的做法是”再包一层 try/except,失败重试”。这是治标不治本——重试成本高,还会掩盖真正的问题。
第一步:搞清三种可靠性等级
结构化输出有三档,可靠性天差地别:
- 第一档:提示词约束。只在提示里写”返回 JSON”。最不可靠,模型有概率违反。
- 第二档:JSON 模式(JSON Mode)。API 保证输出是合法 JSON,但不保证字段符合你的 schema。
- 第三档:约束解码(Structured Outputs / Schema 约束)。API 保证输出严格符合你给的 JSON Schema。最可靠。
核心认知:只要能用第三档,就别用第一档。 现在主流厂商的旗舰模型都支持 schema 级约束,这是根治方案。
第二步:把 Schema 设计对
即便有约束解码,schema 设计不好照样出事。几个铁律:
1. 必填字段要少而稳
把所有字段都设成 required,模型没把握时只会硬编。让模型能说”我不知道”——加一个 uncertain 布尔字段或 null 类型。
2. 用 enum 替代自由文本
如果需要模型输出”高/中/低”这种有限取值,用 enum 而不是 string。约束解码会把输出限制在枚举里,从根上杜绝”比较高”这类越界值。
3. 拒绝深层嵌套
schema 越深,模型越容易在中间层出错。能拍平就拍平,用扁平结构 + 引用 ID 代替深层嵌套。
4. 数值加边界
minimum / maximum 参数,防止模型返回负价格、超过 100 的百分比。
第三步:校验层不能省
即使有约束解码,服务端必须再校验一遍。原因:
- 约束解码保证”符合 schema”,但不保证符合业务规则
- 比如格式对但逻辑错:数量为 0、日期为未来、ID 不存在
用 Zod、Pydantic 这类库做语义校验,把 format 之上的业务规则兜住。schema 管形状,校验管对错。
第四步:重试要”带上错误”
如果确实要重试,别傻重试。把校验失败的报错原文塞回下一轮提示:
上一次输出校验失败:字段
price必须大于 0,你返回了 -5。请修正后重新输出。
这叫错误反馈重试,成功率比无脑重试高得多。同时设上限(比如 2 次),避免死循环烧钱。
一个完整的容错流程
- 用约束解码请求,schema 明确、字段精简
- 服务端用 Pydantic/Zod 做形状 + 业务双重校验
- 失败则带错误信息重试,最多 2 次
- 仍失败则降级:落日志、转人工、返回默认值
- 所有失败进监控,看是偶发还是系统性问题
常见坑
- 把 JSON Mode 当 Structured Outputs:前者只保证合法,不保证 schema,别搞混
- schema 太复杂:字段超过 20 个就要考虑拆分任务
- 忽略降级路径:没有兜底,一次失败就炸穿
- 不做失败监控:失败率悄悄涨到 5% 你都发现不了
一句话总结
结构化输出要可靠,靠四层防线:约束解码保形状、schema 设计防越界、服务端校验保业务、错误反馈重试做兜底。别再用提示词求模型”乖一点”了——把可靠性交给机制,而不是祈祷。