JSONSchemaBench:真实世界的模式复杂度打破了大语言模型结构化输出的保证
大多数团队将约束解码视为一个已解决的问题——添加一个 JSON 模式,即可获得有效的 JSON。JSONSchemaBench (arXiv:2501.10868) 是首次针对 9,558 个真实世界模式测试该假设的系统性尝试,结果并不像营销宣传的那样令人宽慰。
论文内容
Saibo Geng、Hudson Cooper、Michał Moskal 及其微软研究院的同事介绍了 JSONSchemaBench,这是一个包含 9,558 个源自真实生产环境的模式基准测试:包括 GlaiveAI 函数调用签名、按复杂度(从简单到极难)分层的 GitHub 仓库、Kubernetes API 配置、Snowplow 事件分析模式以及 JSONSchemaStore 集合。他们评估了六种约束解码框架——Guidance、Outlines、Llamacpp、XGrammar、OpenAI Structured Outputs 和 Gemini——涵盖三个维度:覆盖率(框架能处理多少比例的模式)、效率(与无约束生成相比 的每秒 Token 开销)和质量(下游任务的准确性)。评估矩阵还包括官方 JSON Schema 测试套件,该套件记录了任何合规引擎都应支持的 45 个特征类别。
其核心观点是,模式复杂度是区分强大框架与脆弱框架的关键变量,且没有任何单一框架能在所有三个维度上都占据主导地位。
核心观点
- 覆盖率在模式复杂度面前崩溃。 在简单的 GlaiveAI 模式上,所有框架的得分都在 86% 以上。但在 GitHub-Hard 模式(多层嵌套、递归定义、复杂模式约束)上,Guidance 降至 41%,Llamacpp 降至 39%,XGrammar 降至 28%,而 Outlines 则跌至惨不忍睹的 3%。OpenAI 在 GitHub-Hard 上的覆盖率仅为 9%,而 Gemini 在中等复杂度或更高复杂度的模式上完全无法产生有效输出。
- Kubernetes 暴露了 XGrammar 的特定弱点。 尽管 XGrammar 声称速度极快,但在 Kubernetes 模式上的覆盖率仅为 7%,这可能是因为这些模式依赖于上下文相关的模式,而 XGrammar 的上下文无关预计算无法处理。对于生产级智能体,能够处理包含 Kubernetes 配置在内的基准测试覆盖率并非可选项。
- 欠约束比编译失败更危险。 XGrammar 在 JSON Schema 测试套件中出现了 38 次欠约束失败——这意味着它输出了违反声明模式的 JSON,却静默报告成功。Guidance 只有 1 次此类失败。对于写回型智能体,编译错误可以在设计阶段被捕获;而欠约束失败会在运行时损坏数据且没有任何信号。
- Guidance 的快进功能带来了真实的 50% 提速。 当存在长确定性序列(例如固定对象结构 中的字段名称)时,Guidance 可以在每个解码步骤中推进多个 Token。在 A100 上的 Llama-3.1-8B 上,Guidance 的每个输出 Token 运行时间为 6-9 毫秒,而无约束生成为 15-16 毫秒。Outlines 比无约束生成更慢,为 30-46 毫秒,主要原因是其前期自动机编译在每个模式上需要花费 3-8 秒。
- 约束解码适度提升了推理准确性。 在 GSM8K(数学)上,Guidance 将准确性从 80.1%(无约束)提升到 83.8%。在 Last Letter 和 Shuffle Objects 任务中,增益在 1-3 个百分点之间。这反驳了广泛引用的担忧,即强迫使用 JSON 格式会降低回答质量——但其影响程度较小,格式选择不应成为框架选型的驱动因素。
- 没有任何框架能涵盖所有 45 个 JSON Schema 特征类别。 Guidance 涵盖了 13 个,Llamacpp 和 XGrammar 各涵盖 1 个,Outlines 则为 0。实际意义在于,任何使用
if/then/else、unevaluatedProperties或递归$ref定义的模式,其行为都将取决于底层使用的引擎,且具有不可预测性。
哪些结论站得住脚,哪些站不住
该基准测试最显著的贡献在于模式的来源。之前的评估大多使用玩具模式或单一来源集合。将 Kubernetes 配置与函数调用签名并列是正确且具有对抗性的多样化尝试。复杂度分层(从简单到极难)也为从业者提供了校准曲线:如果你的模式类似于 GlaiveAI 函数调用,XGrammar 或 Guidance 都可以;如果它们类似于 Kubernetes 配置清单,你的选择范围会迅速缩小。
主要弱点在于单样本贪婪搜索评估。通过每个模式一次生成来衡量覆盖率低估了真实能力——一个框架可能在 20% 的时间内失败,但在重试时成功。论文承认了这一点,但没有报告温度采样下的 pass@k 数据,这对于失败会重试的生产系统至关重要。
比较中还混杂了不可比的模型。开源框架(Guidance、Outlines、Llamacpp、XGrammar)在 Llama-3.2-1B 上进行测试,而 OpenAI 和 Gemini 运行的是它们各自未公开的模型。OpenAI 在 GitHub-Hard 上 9% 的覆盖率可能既反映了约束解码架构,也反映了模型能力。公平的比较需要受控的模型访问权限——这显然是作者无法强迫闭源供应商提供的。
为什么这对金融 AI 很重要
每一个 Beancount 写回代理都会生成结构化输出。如果代理在转换为 .beancount 语法之前先将 Beancount 指令输出为 JSON,或者它通过 JSON 模式调用工具,那么这种 JSON 生成的可靠性就不是细节问题——它是成败的关键。FinTrace 论文显示,尖端模型在处理工具输出的推理上会失败;JSONSchemaBench 揭示了一个交错的问题:甚至在推理之前,格式化层就可能静默地输出不合规的结果。
Kubernetes 的测试结果对 Beancount 尤其具有启发意义。账本模式并非扁平的键值对集合。账户层级、交易元数据和标签结构创建了类似于 Kubernetes API 对象的嵌套递归模式。一个在 Kubernetes 上得分仅为 7% 的框架,无论其每个 Token 的开销有多快,都不足以处理复杂的账本模式。
欠约束失败模式是我最担心的。使用 XGrammar 的 Beancount 代理可能会发出一个通过框架内部验证检查但违反实际模式的交易——而代理没有任何理由重试。静默的数据损坏比显性的失败更糟糕。
延伸阅读
- XGrammar (arXiv:2411.15100, Dong et al.) —— 被测试的最快框架之一背后的技术论文,解释了上下文无关/相关 Token 拆分以及为什么 Kubernetes 模式会使其面临压力。
- Grammar-Aligned Decoding / ASAp (NeurIPS 2024) —— 表明约束解码中的 Token 掩码可能会扭曲模型的概率分布,并提出了一种修正的采样算法;这是该基准测试仅间接衡量的质量问题的理论基础。
- XGrammar-2 (arXiv:2601.04426) —— 续作,将 XGrammar 扩展到智能体场景下的动态模式,即模式本身在多轮对话中会发生变化,这与根据哪些账户类型处于活跃状态而调整输出格式的 Beancount 代理直接相关。