跳过正文
结构化输出:从「能解析的 JSON」到 logit 级约束生成
  1. 文章/

结构化输出:从「能解析的 JSON」到 logit 级约束生成

·4740 字·10 分钟
NeatGuyCoding
作者
NeatGuyCoding

结构化输出:从「能解析的 JSON」到 logit 级约束生成
#

RAG 管道、工具调用、评测脚本和 compound agent 都有一个共同痛点:下游代码需要可验证的结构,而 LLM 默认输出是开放文本。常见补救是 prompt 里写「只返回 JSON」、再用正则或二次模型抽取——这在 demo 里够用,在生产里会把失败推迟到 json.loads 或业务校验层。另一条路径是在解码每一步限制合法 token,使输出在生成过程中就落在 grammar / schema 定义的集合里;OpenAI Structured OutputsOutlines 代表的产品层与开源实现层,常被混称为同一概念,但机制并不相同。

下文把 structured outputs(应用目标:可解析、可入库)与 structured generation(实现:logit 掩码 + 有限状态机)分开讨论;机制段以 Efficient Guided Generation(arXiv:2307.09702)Outlines Core 公开设计为准,性能与评测争议处标注可核对边界与演讲者观点(dottxt 联合创始人 Will Kurt、Cameron Pfiffer 在 Weaviate Podcast 上的讨论,在本环境复现其内部 benchmark)。

图:节目标题卡可见 Structured Outputs、#119、Will Kurt / Cameron Pfiffer / Connor Shorten 与 .TXT / Weaviate 标识


为什么需要「生成时」约束,而不只靠事后解析
#

为什么: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. 2023little overhead 定性表述(统一微秒级对照表)。

图:约 8 分钟处三路视频画面,右侧嘉宾佩戴耳机发言,左下可见 Weaviate podcast 背景字


有限状态机:regex、grammar 与 Index
#

为什么:JSON Schema、工具参数、分类标签都可编译为可判定的 token 序列集合;在 BPE 词表上跟踪「当前处于 automaton 哪一状态」,才能高效算 allowed_tokens

机制/约束:公开实现路径大致为:正则或 grammar → Indexoutlines-core READMEfinite-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(递归深度上界节目未给出)。

Mermaid diagram 1

怎么做:用库侧 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 都能用单一正则表示,需工程折衷。

图:画面 OCR 可见 ast podc 与 Weaviate 播客角标,无技术幻灯片 API 名

图:约 7:57 主持人持杯、左下 Weaviate podcast 标识,嘉宾分屏讨论约束解码


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 偏「更长路径」的预研未发表

图:OCR 片段 ast © 2 Re} = 8 = podca ®,画面为播客分屏无架构幻灯片


推理栈集成:vLLM、xgrammar、Outlines Core
#

为什么:约束解码必须在与词表对齐的推理进程内执行;否则 mask 与 tokenizer 不一致会导致非法或死锁。

机制/约束vLLM Structured Outputs 支持多后端;StructuredOutputsConfig.backendautoxgrammarguidanceoutlines 等。sampling_params.pyauto 常先尝试 xgrammar,失败时回退 guidance 或 outlines一律 Outlines)。演讲者观点(Cameron):需要 inline regex 等中等特性时回退 Outlines;vLLM 捆绑「旧 Python Outlines」——与当前 main 使用 outlines_core(Rust) 的叙述部分冲突,以部署版本 README 为准。

Mermaid diagram 2

怎么做:生产环境显式指定 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。

图:约 24 分钟三路讨论,左下 Weaviate podcast 字牌与书架背景

图:OCR 可见 a. 25 qg2 G5 Oo SS or ‘&] 等叠字,画面仍为播客访谈


质量争议:格式约束是否损害推理
#

为什么:选型需要回答「加结构会不会掉点」;若结论依赖非对等评测,工程决策会偏。

机制/约束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 源码定位该规则(标为访谈观点直至定位)。

图:约 40 分钟嘉宾正视镜头,深色背景分屏,讨论评测与分布不匹配

图:OCR 可见 p@& Weaviate See podcast 播客角标


分布不匹配与「一次生成多字段」
#

为什么:直觉认为互联网语料不像 JSON,加约束会 OOD;嘉宾反驳称邮件、推文、标签等本身有结构,问题在约束强度与任务匹配演讲者观点,共识级)。

机制/约束:真正风险是 mask 掉高概率 token 路径导致局部分布偏移——研究缺口演讲者观点);DeepLearning.AI 课程示例称强制 { "name": 后条件分布与无约束时局部对齐(未在本环境复现)。复合任务上,演讲者观点(Will/Cameron):一个 structured JSON 同时完成摘要、部门、工单号等,常优于多步流水线——理由包括字段间自上下文、更少 prefill;Connor 提及 multi-task inference 论文字幕未给题名(unable to verify)。

常见误区:把软件工程「拆小函数」直接套到 LLM——模块化人类代码的论据在这里常不成立演讲者观点)。另一误区:为多轮对话加长 context 而忽视可靠性与 streaming 提前下游(Cameron 强调 agent 作为可组合工具,而非聊天轮数竞赛——演讲者观点)。

图:OCR 可见 p@® Weaviate Bf podcast 角标

图:OCR 可见 ec )) Weaviate Sf podcast 播客画面


Agent 与 compound 系统:JSON 作为 RPC
#

为什么:Orchestrator 调度 specialist 时,需要稳定消息格式(与 Weaviate transformation agent 等协议同构的讨论方向)。

机制/约束:结构化 JSON/RPC 使多 agent 组合成更大程序(演讲者观点,Cameron);现场所见交互多为 <10 turns演讲者观点)。Will 设想 LLM 系统或形成新抽象层——未来设想,非产品承诺。

怎么做:协议层定义 schema 版本;推理层用 constrained decoding 保证可解析;评测层分离「格式分」与「任务分」。

常见误区:把 agent 等同于长聊天。另一误区:未区分 API structured outputslogit 级 generation 在合规与可审计上的差异。


若你要落地
#

  1. 先写业务 schema 与失败语义,再选 OpenAI Structured Outputs 或自托管 Outlines / vLLM structured output;锁定 backend 与版本,记录 auto 实际 fallback。
  2. 推理模型(R1 类)分区约束:思考段自由文本,答案段单独 schema(参考 vLLM Reasoning Outputs)。
  3. 评测协议写清:是否用二次 LLM 解析、schema 字符预算、解析器实现;对标 arXiv:2408.02442 时核对附录 prompt 变体。
  4. **优先尝试「单次多字段 JSON」**替代多步链式调用——若任务字段强相关;用集成测试验证,勿仅凭模块化直觉拆分。
  5. 监控非法绕过(非常规 Unicode、工具参数边缘类型)与 coalescence 版本;性能声称以你方 schema 实测为准,勿直接采用 5× 或 2–3× 口述数字。

参考与延伸阅读
#

相关文章