Skip to content

结构化输出详解

让大模型稳定输出程序能解析的格式(JSON、枚举、特定 Schema)是 AI 工程落地的刚需——Agent 工具调用、数据抽取、前后端对接都依赖它。「怎么保证模型一定输出合法 JSON」是高频工程题,能讲清约束解码原理和副作用的人不多。

为什么结构化输出是难点?

LLM 本质是「文字接龙」,天生输出自由文本。直接要求输出 JSON 的常见翻车:

  • 多了解释文字(「好的,这是你要的 JSON:……」);
  • 用 Markdown 代码块包裹;
  • 引号/逗号/括号不匹配,JSON 非法;
  • 字段缺失、类型不对、枚举值乱编。

按调用量放大:99% 成功率 = 每天 1 万次调用就有 100 次解析失败,必须工程化根治。

实现手段:从弱到强的谱系

手段原理可靠性
Prompt 约束提示里要求 JSON + 给示例最弱,模型可能不守规矩
Function Calling借工具参数机制输出结构(模型被专门训练过)较强
JSON ModeAPI 保证输出合法 JSON(不保证符合你的 schema)
Schema 约束 / 约束解码解码层强制每个 token 符合结构最强,语法层面 100% 合规
校验 + 重试/修复输出后 schema 校验,失败回喂重试或 json-repair 修复兜底层

区别记牢:JSON Mode 保证「是 JSON」,Schema 约束保证「是你要的那个 JSON」

约束解码(Constrained Decoding)原理

核心思想:生成每个 token 前,根据目标格式屏蔽所有不合法候选,只从合法集合采样:

已生成: {"age":
合法下一步: 数字、空格            ──► 其余 15 万 token 的 logits 置 -inf
已生成: {"age": 25, "name":
合法下一步: 字符串开始的引号       ──► 同上

实现机制:把 JSON Schema/正则/语法编译成有限状态机(FSM)或下推自动机,解码时跟踪当前状态,每步生成该状态的 token 掩码。工程难点在「快」:词表 15 万,每步都算掩码开销不小——Outlines 把状态-掩码预编译成查找表,XGrammar 做掩码缓存与并行优化,把开销压到接近零。

代表实现:OpenAI Structured OutputsOutlinesXGrammar、vLLM/TGI 的 guided decoding、llama.cpp 的 GBNF 语法。

约束解码的副作用(高分考点)

  1. 「合法 ≠ 正确」:约束只管语法,字段值照样可能是编的。schema 要给「不知道」留出口(nullable/"unknown" 枚举),否则模型被迫编造。
  2. 思考空间被压缩:被迫立刻输出 { 的模型没有「打草稿」的余地,复杂任务准确率可能下降。对策:先想后填——第一步自由文本推理(或在 schema 里加 reasoning 字段放最前),第二步再约束输出结构。
  3. 格式与训练分布的偏离:过于奇怪的 schema(深层嵌套、罕见键名)会把模型推出舒适区,错误率上升——schema 本身也是 prompt 的一部分。

工程最佳实践

  • 优先用原生能力:API 的 Structured Outputs / FC 优先于裸 prompt;
  • Schema 简单扁平:嵌套 ≤2 层、字段适量,复杂结构拆多次调用;
  • 善用枚举与描述:固定取值用 enum;每个字段写 description(它们会被模型读到);
  • 字段顺序有讲究:让 reasoning 在前、结论字段在后,利用自回归「先想后答」;
  • 流式解析:长结构用增量 JSON 解析器边收边处理(partial JSON parsing),前端体验关键;
  • 兜底链:约束解码 → schema 校验 → json-repair 自动修复 → 带错误信息重试 → 降级/人工;
  • 降温度:结构化任务用低温度;
  • 评估:抽取类任务建评估集量化「字段级准确率」,不要只看 JSON 合法率。

与 Function Calling 的关系

Function Calling 的「参数生成」本质就是一次结构化输出——模型按工具参数 schema 产出合法 JSON,许多推理引擎用同一套约束解码机制支撑两者。所以结构化输出能力是 Function Calling 与 MCP 可靠性的地基。

高频追问

Q:怎么保证大模型一定输出合法且符合结构的 JSON? 分层答:首选约束解码/Structured Outputs(语法层 100% 合规)→ JSON Mode(仅保证合法 JSON)→ prompt+示例(最弱);无论用哪层都保留「校验 → 修复 → 重试」兜底链。生产 = 约束解码 + 兜底。

Q:JSON Mode 和 Structured Outputs 的区别? JSON Mode 只保证输出可被 JSON 解析;Structured Outputs 进一步保证严格符合给定 Schema(字段、类型、枚举全对),可直接反序列化成强类型对象,省去防御性校验。

Q:约束解码的原理?为什么几乎不增加延迟? Schema 编译成 FSM,每步按状态生成合法 token 掩码、非法 logits 置 -inf。掩码可离线预编译 + 缓存(Outlines/XGrammar 的核心优化),每步只是查表,开销接近零;某些实现甚至更快——结构性 token(引号、冒号)可以直接「快进」不用模型采样。

Q:约束解码有什么副作用?怎么缓解? ①强迫输出掩盖「不知道」→ schema 留 null/unknown;②压缩思考空间 → 先自由推理再约束输出(或 reasoning 字段前置);③怪异 schema 推高错误率 → schema 保持自然简单。核心认知:约束管格式,不管事实

Q:抽取任务里模型总编造不存在的字段值怎么办? schema 允许 null 并在描述中明确「原文没有就填 null」;字段加 evidence/quote 要求附原文依据;低温度;评估集按字段统计准确率定位高发字段,针对性改描述或拆步骤。

Q:流式场景怎么处理结构化输出? 用增量 JSON 解析(容忍未闭合的部分 JSON),字段一旦完整就先消费(如先渲染 title 再等 body);FC 参数在流式下是分片到达的 delta,要拼完再解析执行。

基于 MIT 许可发布