跳到主要内容
SecondHome

语法掩码与约束解码:从 JSON Schema 到永不跑偏的采样

9 分钟前沿1bL2 p.26-35 ↗录像 ↗支撑材料 3 篇

模型偶发漏写一个右花括号,下游 json.loads 就全线崩溃,这是 agent 链路里最古老的一类失败。约束解码不给重试的机会,而是让格式错误在物理上不可能发生:把 JSON Schema 编译成文法与下推自动机,每步采样前算出允许 token 集合并施加掩码。本篇拆开这条从 schema 到 logits 的完整流水线,以及 XGrammar 把开销压到近零的工程手法。

模型偶发漏写一个 },下游的 json.loads 当场抛异常,整条流水线跟着停摆。

工程上先想到的都是补救:失败重试、报错回灌、few-shot 示范。但补救的共同底色是错误仍然可能发生,只是发生之后修。

约束解码换了一个思路:让格式错误在物理上不可能被采样出来。格式不是求出来的,是构造出来的。本篇把这条从 schema 到 logits 掩码的流水线逐层拆开。

四层约束,与它的边界

先看要保证的目标是什么。按 JSON Schema 规范(Draft 2020-12),一份 schema 能在四层把输出形式钉死(slides L2 p.26-28):

  1. 语法层:这是不是一段合法 JSON,括号配对、引号闭合;
  2. 形状层:键结构对不对,该有的键在不在、多余的键有没有(additionalProperties: false);
  3. 类型层:每个值的类型对不对,string、number、boolean 各就各位;
  4. 取值层:值本身合不合法,units 的枚举是 {C, F},那么 "F" 合法、"K" 出局。

传统的用法是事后校验:模型先自由生成,harness 拿这四层去检查,不合格再退回。约束解码把同一份 schema 挪到事前保证。但挪动之前,得先认清这四层约束的边界。

掩码保证形式,保证不了语义

四层约束全部满足,只说明输出形式合法,不说明它语义正确(slides L2 p.29)。units: "F" 完全合法,不代表模型把城市查对了。schema 只管词形,不管事实对错。掩码管不了的另一半,要靠第七节的评测栈去分层归因。

从文法到掩码:每步采样前发生什么

约束解码的路径,是把 JSON Schema 编译成上下文无关文法 CFG,再编译成下推自动机 PDA:状态记住文法走到了哪,栈记住嵌套有多深(slides L2 p.30-32)。

此后模型每生成一个 token,推理引擎先问 PDA「文法此刻允许哪些续写」,把词表里其余位置的 logits 全部 mask 成负无穷,采样便只能在允许集内进行。被 mask 掉的 token 概率为零,非法格式从一开始就没有出场机会。

这条路里藏着一个容易被低估的技术点:token 边界不等于文法边界(slides L2 p.32)。

BPE 词表里,一个 token 可能恰好等于 "F,开引号加值,横跨两个文法终结符;也可能等于 C",值加闭引号,截在字符串符号的半路。

一个 token 对一个文法符号的直觉在这里不成立。文法检查必须做到 tokenization 之下:逐 token 按已解码的字节推进状态机,别拿整只 token 去对照文法符号。

每采一个 token,把整棵 PDA 栈从头回放一遍再判定允许集。正确,但开销随上下文线性增长,批量推理下完全不可用。

把 token 判定按是否依赖栈内容分家:不依赖栈的预计算成 context-free 判定表,依赖栈的少数分子才跑全栈模拟,再用持久化的栈状态免去重复回放。绝大多数判定变成一次查表。

XGrammar:把开销压到近零的四步

上面右栏的思路,就是 XGrammar(MLSys 2025)给出的工程解法(slides L2 p.33-35):

  1. 前缀推进:每步生成后持久化 PDA 的栈状态,不做重复回放;
  2. 缓存判定:把不依赖栈内容的 token 预计算成 context-free 判定表,直接查表;
  3. 慢路径兜底:少数 context-dependent 的 token 才回退到全栈模拟;
  4. 合并掩码:把各来源的允许集合并成最终施加在采样上的掩码。

整套检查跑在 CPU 上,与 GPU 推理重叠进行,掩码计算不占用生成的关键路径。

论文报告结构化输出的开销被压到近零,大批量下还有数倍提速;这套引擎也已被 vLLM 等主流推理引擎收编。今天你在生产里开 response_format /structured output,底下跑的多半就是这类文法掩码。

约束解码已是结构化输出的默认地基。上一节各种聊天模板的差异,最后都统一到这张掩码上。

与本讲的连接

本篇下钻的是讲次页「约束解码」板块。伴读笔记把 JSON Schema 四层、PDA 与 XGrammar 四步各给了一段,本篇补的是它们串成流水线后的全貌:schema 编译、字节级推进、三路判定、掩码合并。重试修格式,从此可以从工程清单里划掉。

资料卡三件,各管一头:

掩码钉死形式。语义那一半,第七节的四层评测栈接着回答。这条保证的代价,是 CPU 上近零的开销。

本节论文与规范

本节资料

  • 工具参数描述的词汇表标准:type、properties、required、enum、additionalProperties 等关键字的语义与求值规则。读懂它才能立住本讲的关键论断——schema 约束保证「形式合法」但保证不了「语义正确」,也才能看懂各家推理引擎的约束解码到底支持到哪一档。

  • 中文系列长文:从 Function Calling 演进史讲到解码层实现技巧(特殊 token、语法掩码),再到 MCP 生态全貌。中文技术社区里少见的深度与准确度兼备的工具调用综述,适合作为英文材料的中转站——先读它建立框架,再啃英文原文。

  • 把上下文无关文法编译成下推自动机并做 token 掩码缓存,实现字节级约束解码,让 JSON 等结构化输出接近零开销——最高数倍提速并显著放宽大批量下的性能惩罚。已进入 vLLM 等主流推理引擎,是「语法掩码」从论文走向生产的代表作,本讲约束解码板块的核心文献。

AI 生成 · 仅供参考,与课程方无关。选中正文里的任何一段也能直接问我(解释一下 / 举个例子)。

本详解以 CMU 11-768 AI Agents (Fall 2026) 课程大纲为参考框架独立撰写,AI 辅助起草、人工抽审。 涉及的官方课件、论文与课程材料版权归原所有方,讲义为站内镜像、其余外链原文出处; 观点与文字(除标注引用外)为作者原创。