结构化输出与约束解码

LLM 与 Prompt 基础进阶8讲解 3

约束解码把 JSON Schema 编译成语法,在采样时屏蔽不合法 token,保证的是语法而不是语义。语法表达不了的约束(数值范围、跨字段一致性、业务规则)必须靠校验层;四条路线(提示词要求 / 预填 / 函数调用 / 原生结构化输出)保证的东西不同,选型看端点支持与失败代价。

也叫:结构化输出 · structured outputs · 约束解码 · constrained decoding · JSON Schema · JSON 模式

原理拆解出自 T1-2

语法层管形状,值和权限归代码
schema 编译成语法,采样时封死违规 token —— 表达不了的那部分只能活在代码里

语法层 · 约束解码每生成一步,把不符合语法的 token 概率置零,只在合法候选里采样保证:能 parse、字段齐、类型对。编译产物缓存 24 小时,首次调用有额外延迟
值域层 · 代码校验枚举是否越界、数值范围、日期是否真实存在、格式正则"2026-02-30" 死在这一层 —— minimum / maximum 严格 schema 根本不支持
业务层 · 代码校验是否在该用户权限内、是否与来源一致、是否超过审批阈值refund_amount: 99999 死在这一层;高危动作的判断权必须收回代码

语法表达不了的,就一定管不住。截至 2026-08 官方明确列出的不支持项:递归 schema、外部 $refminimum / maximumminLength / maxLength、其他数组长度约束;additionalProperties 只能是 false

复杂度也有上限:所有严格 schema 合起来算,超了直接报编译错误。官方给的修法顺序是 —— 只给关键工具加 strict → 把可选参数改成必填 → 拍平嵌套 → 拆到多个请求。每个可选参数都是状态空间大户。

结构化输出不降低幻觉率,只是把幻觉变成格式合法的幻觉。副作用很隐蔽:parse 不再报错,你反而丢了一道天然告警 —— 第 2、3 层因此更重要,不是更不重要。
原文示意
路线一:提示词请求
  "请只返回 JSON,不要有其他文字"
      → 模型「尽量」照做 → 大概率对,偶尔加寒暄 / 缺字段 / 类型飘

路线二:JSON mode
  → 保证能 parse,但 {"随便什么": 1} 也算合法

路线三:约束解码(当前默认选择)
  你的 JSON Schema
      │ 编译
      ▼
   语法(grammar)
      │
      ▼
  每生成一步:把不符合语法的 token 概率置零 → 只在合法候选里采样
      → 结构上不可能违规
# 约束解码在做什么(示意)
for step in range(max_tokens):
    logits = model.next_token_logits(state)
    allowed = grammar.allowed_tokens(parse_state)   # 语法告诉你此刻哪些 token 合法
    logits[~allowed] = -inf                          # 其余全部封死
    tok = sample(logits)
    parse_state = grammar.advance(parse_state, tok)

关键限制(这是本篇最值钱的一段)

既然 schema 要被编译成语法,那语法表达不了的约束,就一定管不住。截至 2026-08,官方文档明确列出的不支持项包括:递归 schema、外部 $refminimum / maximumminLength / maxLength、其他数组长度约束;additionalProperties 只能是 false。此外还有整体复杂度上限——所有严格 schema 合起来算,超了会直接报编译错误,官方给的修法顺序是:只给关键工具加 strict → 把可选参数改成必填 → 拍平嵌套 → 拆到多个请求。(每个可选参数都会显著放大语法的状态空间,是复杂度大户。)编译产物会缓存 24 小时,所以首次调用有额外延迟,高频调用可以忽略。

把这些串起来就是本篇的结论:

数值范围、字符串长度、日期是否真实存在、金额是否在权限内、引用是否与来源一致——这些全部属于语义,语法层根本表达不了。它们必须活在你的代码里。

回头看开头的故障:"2026-02-30" 是合法字符串;99999 是合法整数。约束解码从来没打算管这两件事,是团队误以为它管。

以上节选自T1-2 结构化输出:约束解码保证语法,保证不了语义,读全文能看到前后语境。

延伸阅读

考这个知识点的题1

会连带问到7