结构化输出:从「能解析的 JSON」到 logit 级约束生成#
RAG 管道、工具调用、评测脚本和 compound agent 都有一个共同痛点:下游代码需要可验证的结构,而 LLM 默认输出是开放文本。常见补救是 prompt 里写「只返回 JSON」、再用正则或二次模型抽取——这在 demo 里够用,在生产里会把失败推迟到 json.loads 或业务校验层。另一条路径是在解码每一步限制合法 token,使输出在生成过程中就落在 grammar / schema 定义的集合里;OpenAI Structured Outputs 与 Outlines 代表的产品层与开源实现层,常被混称为同一概念,但机制并不相同。
下文把 structured outputs(应用目标:可解析、可入库)与 structured generation(实现:logit 掩码 + 有限状态机)分开讨论;机制段以 Efficient Guided Generation(arXiv:2307.09702) 与 Outlines Core 公开设计为准,性能与评测争议处标注可核对边界与演讲者观点(dottxt 联合创始人 Will Kurt、Cameron Pfiffer 在 Weaviate Podcast 上的讨论,未在本环境复现其内部 benchmark)。

为什么需要「生成时」约束,而不只靠事后解析#
为什么:Agent 编排、向量库写入、函数调用参数传递,都假设字段名、类型、嵌套关系稳定。若只在生成后解析,模型仍可能输出合法自然语言但非法 JSON;修复往往再调一次 LLM 或手写 parser,延迟与成本叠加。OpenAI Cookbook 对 Structured Outputs 的说明强调 API 层对 JSON Schema 的遵守;自托管栈则多在推理引擎内挂 logits processor(演讲者观点:Cameron 将最松情形仍视为 regex .* 级别的约束,哲学上不存在真正的「无结构生成」——此说法未形式化证明,宜作概念框架)。
机制/约束:约束解码在每一步根据已生成前缀计算允许 token 集,对其余 logits 置 (-\infty) 再采样;Outlines Logits Processors 文档描述为对 logits 施加 mask。与「生成完再校验」相比,失败模式从「解析异常」前移到「无法进入非法分支」——但若 schema 与任务语义不匹配,仍可能得到语法合法、语义错误的输出。
怎么做(最小示例,概念层):
# 伪代码:每步 allowed = index.allowed_tokens(state); logits[~allowed] = -inf
常见误区:把「模型说了 JSON」等同于 structured generation;prompt 约束不保证 token 级合法。另一误区是认为约束一定更慢——见后文 coalescence 与 Willard et al. 2023 的 little overhead 定性表述(无统一微秒级对照表)。

有限状态机:regex、grammar 与 Index#
为什么:JSON Schema、工具参数、分类标签都可编译为可判定的 token 序列集合;在 BPE 词表上跟踪「当前处于 automaton 哪一状态」,才能高效算 allowed_tokens。
机制/约束:公开实现路径大致为:正则或 grammar → Index(outlines-core README 称 finite-state automation / DFA)+ Vocabulary(tokenizer 与词表对齐);每步 next_state 后取允许 token。vLLM backend_outlines.py 通过 Guide.write_mask_into 写入 mask。演讲者观点(Cameron):实现上可能经 NFA 再确定化;当前 README 未逐步写出 NFA→DFA,标为 partially verified。CFG 表达力高于正则;Outlines Output Types 支持 Lark CFG,工程上常对 JSON 等结构做 regex 近似或有界 unroll(递归深度上界节目未给出)。

怎么做:用库侧 regex / json_schema / CFG 构造 Index,在自托管引擎注册 logits processor;勿手写「禁止出现 }」类 ad-hoc 规则替代 FSM。
常见误区:以为禁止某个字符(如数字 0-9)等于禁止数值能力——演讲者观点(Cameron,演示级):模型可能改用 Unicode 上标等路径,无系统级 benchmark 佐证(P09,unable to verify)。另一误区:CFG 等于「任意 JSON Schema 精确表达」——Coalescence 博客指出并非所有合法 JSON schema 都能用单一正则表示,需工程折衷。


Schema 设计:先固定字段,还是让模型推断#
为什么:生产系统要可测试、可版本化的契约;RAG 抽取、工单分类、10-K 字段映射通常业务方先知道列名。
机制/约束:固定 JSON Schema 或 Pydantic 模型 → 编译为 FSM;动态 function calling 可从 Python 签名生成运行时 schema(演讲者观点,Will)——介于 rigid 与 fully inferred 之间。演讲者观点(Cameron):多数场景应先定 schema 再写下游;「先看数据再让模型提议 schema」可行但波动大,强用例未见。Will 补充可用强模型生成 regex 再喂 Outlines——实验性。
怎么做:
from pydantic import BaseModel
class Ticket(BaseModel):
summary: str
department: str
ticket_id: str
# outlines / vLLM: response_format 或 grammar 绑定该 schema
常见误区:schema 越细越好。Last-letter 类任务上,演讲者观点(Will):把 chain-of-thought 模板硬编码进 schema 优于完全无结构,但劣于只约束最终答案、思考字段放开——说明「结构强度」本身是超参。另一误区:忽略字段间依赖;单次 JSON 内多字段可让后续键「看见」前文(见下文复合任务),与「每个字段一次 API」不同。
Coalescence:确定性片段与加速声称#
为什么:JSON 中大量 token 高度可预测(如 { 后接 ");若每步都调用完整采样,算力花在低熵片段上。
机制/约束:Coalescence: making LLM inference 5x faster(Will Kurt)描述:在 FSM 判定为确定性的片段跳过采样,直接推进状态;基于 regex 与 FSM 等价性。演讲者观点(Will):约 2–3×+;博客标题与正文写 约 5×——二者未在本环境对照 benchmark,正文应并列引用并标边界。
怎么做:依赖支持 coalescence 的 Outlines / 推理栈版本;不可假定所有 vLLM 构建默认开启。
常见误区:把 coalescence 与「约束必然降延迟」划等号;加速取决于 schema 可预测片段占比。另一误区:与 token 粒度假说混谈——演讲者观点(P10):dog 单 token 与 D+O+G 路径概率可能不同,偏小 token 偏好「有论文」但篇名未给出(unable to verify);coalescence 偏「更长路径」的预研未发表。

推理栈集成:vLLM、xgrammar、Outlines Core#
为什么:约束解码必须在与词表对齐的推理进程内执行;否则 mask 与 tokenizer 不一致会导致非法或死锁。
机制/约束:vLLM Structured Outputs 支持多后端;StructuredOutputsConfig.backend 含 auto、xgrammar、guidance、outlines 等。sampling_params.py 中 auto 常先尝试 xgrammar,失败时回退 guidance 或 outlines(非一律 Outlines)。演讲者观点(Cameron):需要 inline regex 等中等特性时回退 Outlines;vLLM 捆绑「旧 Python Outlines」——与当前 main 使用 outlines_core(Rust) 的叙述部分冲突,以部署版本 README 为准。

怎么做:生产环境显式指定 backend 并锁定 vLLM / outlines-core 版本;对 DeepSeek-R1 等推理模型,vLLM Reasoning Outputs 表明可对答案段做 json/regex 结构化——演讲者观点(Cameron):整段 JSON 封死 think 块会损害推理,宜保留 think 自由文本、仅约束最终答案(机制合理,gist URL 未验证)。
常见误区:假设 API 托管 Structured Outputs 与自托管 logits 约束可互换评测。另一误区:忽略 xgrammar 与 Outlines 的 schema 特性差异导致 silent fallback。


质量争议:格式约束是否损害推理#
为什么:选型需要回答「加结构会不会掉点」;若结论依赖非对等评测,工程决策会偏。
机制/约束:Let Me Speak Freely?(arXiv:2408.02442)(Tam et al.)摘要写明:格式限制下 LLM 推理能力显著下降;GSM8K 等任务上自由文本优于受限格式(文献/文档支持)。论文 §3.3 对非结构化分支用 LLM 抽取最终答案(附录选 Claude 等作抽取模型)——文献/文档支持。演讲者观点(Will):与 dottxt 在 GSM8K 上的 eval 不符,怀疑 prompt、解析路径、二次 LLM 成本不对等;认可论文「手写 parser vs 约束解码」工程角度。同一模型「请输出 JSON」vs 不用 Outlines:有的崩盘、有的变好(演讲者观点)——说明评测应固定 prompt × 模型 × 解析器 三元组。
怎么做:复现争议时同时报告:约束解码栈、schema 字符/ token 预算、是否用外部 LLM 解析。演讲者观点(Will,P07):GSM8K 结构化推理步字符上限过紧时先逊于 baseline,放宽后可超 baseline——无公开表格,unable to verify。
常见误区:用单一论文否定所有 constrained decoding。另一误区:用 Berkeley Function Calling Leaderboard 的格式细节(如 float 必须 .0)作为主论据——演讲者观点称存在不公平,本轮未在 BFCL 源码定位该规则(标为访谈观点直至定位)。


分布不匹配与「一次生成多字段」#
为什么:直觉认为互联网语料不像 JSON,加约束会 OOD;嘉宾反驳称邮件、推文、标签等本身有结构,问题在约束强度与任务匹配(演讲者观点,共识级)。
机制/约束:真正风险是 mask 掉高概率 token 路径导致局部分布偏移——研究缺口(演讲者观点);DeepLearning.AI 课程示例称强制 { "name": 后条件分布与无约束时局部对齐(未在本环境复现)。复合任务上,演讲者观点(Will/Cameron):一个 structured JSON 同时完成摘要、部门、工单号等,常优于多步流水线——理由包括字段间自上下文、更少 prefill;Connor 提及 multi-task inference 论文,字幕未给题名(unable to verify)。
常见误区:把软件工程「拆小函数」直接套到 LLM——模块化人类代码的论据在这里常不成立(演讲者观点)。另一误区:为多轮对话加长 context 而忽视可靠性与 streaming 提前下游(Cameron 强调 agent 作为可组合工具,而非聊天轮数竞赛——演讲者观点)。


Agent 与 compound 系统:JSON 作为 RPC#
为什么:Orchestrator 调度 specialist 时,需要稳定消息格式(与 Weaviate transformation agent 等协议同构的讨论方向)。
机制/约束:结构化 JSON/RPC 使多 agent 组合成更大程序(演讲者观点,Cameron);现场所见交互多为 <10 turns(演讲者观点)。Will 设想 LLM 系统或形成新抽象层——未来设想,非产品承诺。
怎么做:协议层定义 schema 版本;推理层用 constrained decoding 保证可解析;评测层分离「格式分」与「任务分」。
常见误区:把 agent 等同于长聊天。另一误区:未区分 API structured outputs 与 logit 级 generation 在合规与可审计上的差异。
若你要落地#
- 先写业务 schema 与失败语义,再选 OpenAI Structured Outputs 或自托管 Outlines / vLLM structured output;锁定
backend与版本,记录auto实际 fallback。 - 推理模型(R1 类)分区约束:思考段自由文本,答案段单独 schema(参考 vLLM Reasoning Outputs)。
- 评测协议写清:是否用二次 LLM 解析、schema 字符预算、解析器实现;对标 arXiv:2408.02442 时核对附录 prompt 变体。
- **优先尝试「单次多字段 JSON」**替代多步链式调用——若任务字段强相关;用集成测试验证,勿仅凭模块化直觉拆分。
- 监控非法绕过(非常规 Unicode、工具参数边缘类型)与 coalescence 版本;性能声称以你方 schema 实测为准,勿直接采用 5× 或 2–3× 口述数字。
参考与延伸阅读#
- Outlines(GitHub) — 结构化生成主库与文档入口
- Outlines Core(GitHub) — Rust
Vocabulary/Index/Guide - Efficient Guided Generation(arXiv:2307.09702) — 约束解码与 FSM 早期论文
- Let Me Speak Freely?(arXiv:2408.02442) — 格式约束与推理质量对立研究
- Coalescence 博客 — Will Kurt — 跳过确定性片段与加速声称
- Outlines — Logits Processors — mask 机制说明
- Outlines — Output Types / CFG — regex、JSON schema、CFG
- vLLM Structured Outputs 文档 — 多后端与配置
- vLLM
sampling_params.py(auto 路由) — xgrammar / guidance / outlines 回退 - vLLM
backend_outlines.py—outlines_core集成 - vLLM Reasoning Outputs — DeepSeek R1 等推理解析
- OpenAI Structured Outputs 开发者文档 — API 层 JSON Schema 保证
- OpenAI Cookbook — Structured Outputs Intro — 托管 API 示例
- DeepSeek-R1(Hugging Face) — 推理模板与 think 段说明
- Berkeley Function Calling Leaderboard — 工具调用评测(格式细节需自行核对)
- dottxt.ai — 商业支持与博客索引



