让 AI 稳定输出 JSON 的方案阶梯

“让模型输出 JSON”看起来是一个很小的需求,但它往往是 AI 应用从 demo 进入工程系统的第一道门槛。

在 demo 里,模型偶尔多说一句“下面是结果:”没有关系,人可以读懂。在生产系统里,这句话可能让 JSON parser 直接失败;字段少一个,工具调用就缺参数;类型错一个,工作流分支就走偏。

这篇文章不从语法技巧开始,而是从业务场景出发,按成本从低到高梳理一条方案阶梯:prompt、JSON mode、schema、修复、重试。

场景

假设一个销售 CRM 想用 AI 从客户来信中提取线索信息。

输入来自邮件、官网表单和客服转交的聊天记录。销售经理希望系统自动生成一个线索对象:

{
  "companyName": "Acme Robotics",
  "contactName": "Lina Chen",
  "need": "希望了解企业版 API 调用额度和私有化部署",
  "budgetRange": "unknown",
  "priority": "high",
  "nextAction": "schedule_demo",
  "missingFields": ["phone"]
}

这个对象会被 CRM 消费:

  • companyNamecontactName 写入线索。
  • priority 决定销售跟进顺序。
  • nextAction 决定自动任务。
  • missingFields 决定是否发补充信息邮件。

常见失败样本包括:

  • 模型输出 Markdown 代码块,解析器没有剥离。
  • priority 输出成 "紧急",但系统只接受 low | medium | high
  • budgetRange 不知道时编造 "50k-100k"
  • missingFields 本应是数组,却输出成字符串 "phone"
  • 邮件里没有公司名,模型为了完整性猜了一个。

这些问题不是“模型不会 JSON”这么简单,而是输出契约、未知值策略、业务容错和失败处理没有设计清楚。

问题类型

JSON 稳定性至少包含三层。

第一层是语法合法:输出能被 JSON parser 解析。

第二层是结构符合预期:字段、类型、枚举、嵌套层级符合 schema。

第三层是业务可用:字段值来自输入或可信上下文,能通过业务规则校验。

层级典型错误解决重点
JSON 语法多余文本、漏引号、未转义换行、尾随逗号JSON mode、严格解析、修复
结构契约缺字段、类型错、枚举漂移、嵌套错schema、示例、程序校验
业务语义编造缺失字段、工具参数无权限、金额范围异常业务校验、拒绝猜测、人工确认

适合交给模型的是从非结构化输入里识别和抽取候选信息。应该由程序完成的是解析、校验、默认值处理和业务规则判断。

这里要特别注意“未知值”。很多 JSON 输出失败不是因为模型不会填字段,而是因为团队没有告诉模型:信息缺失时应该返回 null"unknown"、空数组,还是把字段放进 missingFields

风险/收益

稳定 JSON 的收益是让模型输出能进入自动化链路。

主要收益包括:

  • 降低解析失败率。
  • 减少下游字段兼容代码。
  • 让错误能按字段归因。
  • 让工具调用参数更稳定。
  • 让线上监控可以量化。

成本也很现实:

  • prompt 需要维护示例和字段规则。
  • schema 需要和业务代码同步。
  • 修复和重试会增加延迟与 token 成本。
  • 复杂字段会增加模型出错概率。
  • 失败处理需要产品和运营流程配合。

什么时候简单 prompt 足够?

如果字段少、低风险、输出主要给人工看、失败后用户可以重新生成或编辑,简单 prompt 往往够用。比如让模型输出一个包含 titlesummarytags 的内容草稿,用户会人工发布。

什么时候需要工程方案?

只要 JSON 会入库、触发工具、决定权限、影响 SLA、产生对外动作或进入无人值守工作流,就需要 JSON mode、schema、程序校验、重试和降级。

方案阶梯

第一层:Prompt

prompt 是最低成本的起点。

一个可用的 JSON prompt 至少要写清楚:

  • 只输出 JSON,不要 Markdown,不要解释。
  • 每个字段的含义。
  • 必填字段和可选字段。
  • 枚举值只能从给定集合选择。
  • 未知信息如何表示。
  • 不要猜测输入里没有的信息。
  • 给出一两个正反示例。

示例:

你要从客户消息中抽取销售线索,只输出合法 JSON。
不要使用 Markdown 代码块,不要输出解释。

字段:
- companyName: string | null。只能来自原文,无法确认时为 null。
- priority: "low" | "medium" | "high"。根据紧急程度判断。
- missingFields: string[]。列出无法从原文确认但销售跟进需要的信息。

如果信息缺失,不要猜测,把字段设为 null 或加入 missingFields。

prompt 的优点是便宜、灵活、上线快。缺点是它是软约束,遇到长输入、复杂 schema、强诱导或模型切换时会不稳定。

第二层:JSON Mode

JSON mode 主要解决“输出必须是合法 JSON”的问题。

它适合已经确定要程序解析,但字段结构还不复杂的场景。相比普通 prompt,它可以显著减少多余解释、Markdown 包裹和语法错误。

但 JSON mode 不是 schema 校验。它可能输出合法 JSON:

{
  "priority": "very_high",
  "missingFields": "phone"
}

这段 JSON 语法合法,但业务结构仍然错误。

所以 JSON mode 通常不是终点,而是 schema 和程序校验前的一层语法保障。

第三层:Schema

当字段会进入下游系统时,需要明确 schema。

schema 的价值是把“你大概输出这些字段”变成“系统只接受这个契约”。

设计 schema 时要克制:

  • 字段越少越稳定。
  • 枚举越明确越稳定。
  • 嵌套越浅越稳定。
  • 未知值策略越清楚越稳定。
  • 每个字段最好能对应明确业务用途。

不建议为了“以后可能用到”让模型一次输出大量字段。没有下游消费的字段会增加失败概率,也会让评估更困难。

第四层:解析与校验

无论用了 prompt、JSON mode 还是 schema,后端都必须校验。

校验至少包括:

  • JSON parse 是否成功。
  • 必填字段是否存在。
  • 字段类型是否正确。
  • 枚举值是否在范围内。
  • 字符串长度、数组长度、数字范围是否合理。
  • 业务规则是否通过。

解析失败和校验失败要分开记录。前者说明输出不是合法 JSON,后者说明 JSON 合法但不符合契约。

第五层:修复

修复适合模型输出里信息基本可用,但格式或局部字段错误的情况。

例如第一次输出:

{
  "companyName": "Acme Robotics",
  "priority": "紧急",
  "missingFields": "phone"
}

校验错误可以反馈给模型:

上一次输出未通过校验:
- priority 必须是 "low" | "medium" | "high"
- missingFields 必须是 string[]

请只修复 JSON,不要新增原文没有的信息。

修复的收益是保留已有抽取结果,成本通常低于完全重试。但如果原始输出已经明显偏离任务,修复会在错误基础上继续修补,不如重新生成。

第六层:重试

重试适合解析失败、模型跑偏或修复失败的情况。

重试必须有边界:

  • 最多重试次数。
  • 总超时时间。
  • 最大 token 成本。
  • 是否换模型或降低复杂度。
  • 是否切换到更保守 prompt。
  • 失败后进入什么降级路径。

一个常见策略是:

  1. 第一次使用正常 prompt 和 schema。
  2. 解析失败后尝试一次修复。
  3. schema 失败后带错误重试一次。
  4. 仍失败则转人工或要求用户补充信息。

不要无限重试,也不要把所有失败都吞掉。

第七层:降级和人工处理

当 JSON 仍然不稳定时,系统必须知道如何失败。

低风险场景可以提示用户重新生成。中风险场景可以创建待处理任务。高风险场景必须阻止自动执行,并把原始输入、模型输出、校验错误展示给人工。

稳定性工程的目标不是让失败永远不发生,而是让失败不会悄悄流进业务系统。

方案选型表

场景条件推荐方案不推荐方案判断理由额外成本
字段少、输出给人工看Prompt + 示例复杂 schema人工能发现和修正格式问题prompt 维护
需要机器解析但风险低JSON mode + 解析失败提示自由文本 + 正则先保证 JSON 语法合法解析错误处理
输出要入库JSON mode + schema + 后端校验只相信模型输出入库需要稳定字段和版本契约schema 版本管理
输出作为工具参数schema + 业务校验 + 重试解析后直接调用工具参数错会影响业务系统权限、范围和归属校验
输入复杂、缺失多分步骤抽取 + missingFields一次输出大对象拆小任务能降低失败率多次模型调用
高风险动作结构化输出 + 人工确认自动执行JSON 正确不代表动作应该执行审核和审计流程

反模式

第一种反模式:把 JSON 包在 Markdown 代码块里再让后端剥离。

这在早期调试方便,但生产链路里会增加不必要的解析分支。既然下游只要 JSON,就要求模型只输出 JSON。

第二种反模式:字段未知时让模型“尽量补全”。

补全看起来让数据更完整,实际上会引入编造字段。对工具调用和 CRM 入库来说,null 通常比猜测更安全。

第三种反模式:用自然语言字段名。

例如 priority 允许 "低" | "中" | "高" | "比较急" | "马上处理",会让统计和路由变复杂。工程系统更适合稳定枚举,展示层再做本地化。

第四种反模式:一次抽取所有信息。

复杂销售线索、保险理赔、医疗问卷、合同审查都可能包含很多字段。一次输出大 JSON 容易失败,应该把分类、抽取、校验和补充提问拆开。

第五种反模式:校验失败后自动填默认值。

默认值会掩盖模型错误。特别是优先级、金额、权限、时间范围这类字段,错误默认值可能比失败更危险。

第六种反模式:只统计最终成功率。

如果一个请求经过三次重试才成功,最终成功率看起来很好,但成本和延迟已经恶化。应该同时看首轮通过率、修复成功率、重试次数和失败类型。

工程清单

  • 场景边界:JSON 是给人看、入库、驱动 UI,还是调用工具。
  • 字段设计:每个字段是否有明确下游用途。
  • 未知值策略:缺失信息用 nullunknown、空数组还是 missingFields
  • Prompt:是否禁止 Markdown、解释文本和猜测。
  • JSON mode:是否用于需要机器解析的链路。
  • Schema:是否定义必填字段、枚举、类型、长度和范围。
  • 校验:是否区分 parse error、schema error 和 business error。
  • 修复:是否把校验错误反馈给模型,并限制只修复结构。
  • 重试:是否设置次数、超时、成本和模型策略。
  • 降级:失败后是否转人工、提示补充信息或停止自动流程。
  • 监控:是否记录首轮通过率、重试率、字段错误率和平均延迟。
  • 评估:是否用真实失败样本做回归测试。

小结

稳定 JSON 输出不是一个 prompt 小技巧,而是一条从软约束到硬约束的工程链路。

简单 prompt 适合低风险、字段少、人工可复核的场景。JSON mode 解决语法稳定,schema 解决结构契约,程序校验负责真正把关,修复和重试负责恢复,降级和人工确认负责保护业务流程。

判断是否升级方案时,可以问一句最朴素的问题:如果这个 JSON 错了,下游会不会自动做错事?如果答案是会,就不要只靠 prompt。