结构化输出:JSON/YAML 与 Schema 约束

核心04-结构化输出与工具调用⏱ 25 分钟已审校
学习进度:

一句话定义

结构化输出让模型按预定义 Schema(JSON/YAML/表格)返回机器可解析的数据——用 Schema 描述、格式示例与(可选的)解码期约束把"自由文本"变成"接口"。

为什么重要

只要输出要被程序消费(入库、调接口、驱动 UI),自由文本就是事故源。结构化输出把提示工程的交付物从"一段话"升级为"一个有类型的接口",是 LLM 功能可测试、可监控、可回归的前提——校验-重试回路:让输出自修复 的校验回路与 回归测试:把提示当代码守护 的回归测试都建立在它之上。

前置知识

直观类比

自由文本像口头交代,结构化输出像填表:口头交代接收方要脑补(解析),填表每格有名称与类型,缺格就是缺——接口化让"理解成本"从每次消费转移到一次性的表设计。

图示

提示层 Schema(教学)  ─┐
格式示例(模仿)       ─┼→ 模型输出 → JSON.parse → 失败 → 重试回路(kp-021)
解码期约束(语法强制) ─┘                              ↓ 成功
                                          Schema 校验(字段/类型/枚举) → 业务

核心概念

  • Schema 描述:在提示中给出字段、类型、枚举值与缺省规则,通常以 JSON Schema 或简化伪 Schema 呈现。
  • 解码期约束(Constrained Decoding):部分 API 提供的原生能力(JSON Mode、结构化输出参数),在生成阶段直接限制合法 token——比纯提示约束更硬。
  • 枚举收紧:自由字符串字段尽量改为枚举("类型": "咨询|投诉|退款"),可枚举即可校验、可统计。
  • 缺省语义:明确定义缺失的表达(null vs 空串 vs 省略字段),并禁止模型自创。
  • 嵌套深度控制:层级越深出错率越高,能用扁平结构不要三层嵌套。

原理与机制

提示层约束的机制仍是模式模仿(示例 + 结尾锚点),其可靠性有天花板;解码期约束则把"模式"变成"语法"——生成器在每个位置只允许 Schema 合法的 token,从机制上杜绝非法 JSON。工程策略分层:

  1. 优先用平台原生约束(若可用):这是免费的正确性;
  2. 提示层 Schema 兜底:跨平台/无原生能力时,Schema 放系统提示 + 一个格式示例 + 用户消息结尾重申;
  3. 解析层防御:无论哪层约束,JSON.parse 仍可能失败(截断、尾随逗号),必须有重试回路(校验-重试回路:让输出自修复)。

Schema 设计本身的减错原则:字段名用业务语义;每个字段写"取值范围 + 缺失语义 + 示例值";拒绝 oneOf 深嵌套,用判别字段("type": "A"|"B" + 对应可选字段)替代。

公式或模型

本节不适用——Schema 合法性由解析器机械判定,无需建模;解析失败率用回归集统计。

实例或案例

案例:把闲聊式输出改造成入库接口。

before:

分析这条用户反馈,告诉我问题是什么、严重不严重。

after:

分析用户反馈,仅输出符合以下 Schema 的 JSON,不要其他文字:
{
  "类别": "物流|质量|服务|其他",
  "严重度": 1-5 的整数,      // 5=阻断使用,1=轻微
  "摘要": "≤30字的问题概述",
  "可复现": true|false
}
字段缺失时对应值为 null;禁止新增字段。
示例:
输入:包裹碎了半箱,客服三天没回复
输出:{"类别":"物流","严重度":4,"摘要":"商品破损且客服响应迟缓","可复现":true}
输入:{反馈文本}
仅输出 JSON。

操作步骤:① 定义 Schema(字段、类型、枚举、缺省);② 有原生约束则开启,无则按模板三件套;③ 写 Schema 校验器(类型/枚举/未知字段);④ 失败样本进 评估集构建:黄金样本与失败分类 评估集。

排错清单:

  • 症状:偶发非法 JSON → 逐层检查:原生约束开没开、结尾锚点在不在、输出是否被截断(token 上限);
  • 症状:模型新增字段或改字段名 → Schema 里显式写"禁止新增字段",校验器拒绝未知字段并重试;
  • 症状:嵌套结构频繁错位 → 扁平化或拆两次调用,一次只产一层。

常见误区

  • 以为开了 JSON Mode 就万事大吉:语法合法不等于语义正确(枚举外的值、逻辑矛盾),Schema 校验仍不可省;
  • Schema 过度设计:十几个可选字段、三层嵌套让模型与维护者都痛苦;从最小可用字段集起步;
  • 把提示层约束当语法保证:纯提示永远可能破格式,解析失败路径必须存在且被测试。

自测题

  1. 提示层约束与解码期约束的本质区别?

答:前者靠模式模仿(统计倾向,可破),后者在生成期限制合法 token(语法强制,硬保证),两者应叠加而非互替。

  1. 为什么字段尽量用枚举?

答:可枚举才可校验、可统计、可回归;自由字符串把不确定性转嫁给下游。

  1. JSON 语法合法后还要校验什么?

答:字段完整性、类型、枚举取值、未知字段、业务级一致性(如严重度 5 但摘要写"轻微")。

与其他知识点的关系

校验-重试回路:让输出自修复 消费本条的失败输出实现自修复;工具调用提示:函数描述、参数设计与选择引导 的工具参数本质是出向 Schema;示例设计参照 零样本、少样本与示例工程;失败样本归集到 评估集构建:黄金样本与失败分类。

延伸阅读

  • 综述:The Prompt Report(Schulhoff et al., 2024)——输出约束类技法的工程分类。

来源

  • The Prompt Report: A Systematic Survey of Prompting Techniques (Schulhoff et al.
  • 2024)