结构化生成与约束解码#
这章解决什么问题#
这一章解决的是“模型输出怎样被约束成目标格式”。如果不单独讲这一层,读者很容易把结构化生成误读成“API 层的附加能力”,而忽略它其实直接插在 generation path 里,和 sampling、logits 选择、tool parser 一起工作。
从官方文档看,SGLang 把 structured outputs 定义得很直接:你可以为请求指定 json_schema、regex 或 ebnf,并且这三种约束参数是互斥的,只能选一种。这不是外围包装,而是 generation 过程本身的一部分。
为什么它属于执行链,而不是纯 API 功能#
python/sglang/srt/sampling/sampling_params.py 里,SamplingParams 直接包含 json_schema、regex、ebnf 和 structural_tag 字段,并在 verify(...) 里明确检查 “Only one of regex, json_schema, or ebnf can be set.”。这说明约束解码不是协议层的装饰,而是采样参数对象的一部分。
这也是本章和上一章衔接的原因。执行模型章节已经解释了 sampling 参数怎样参与 token 选择;这里进一步说明,当这些参数变成 grammar constraint 时,输出就不再只是“按概率采样”,而是“在满足约束的前提下继续生成”。从系统设计上看,这比单纯在最终文本上做后处理要更稳,因为约束是在生成过程中被满足,而不是生成后再去修正。
这里最适合补图,因为“约束到底插在哪里”很难靠一两段话稳稳说清。下面这张图回答的是:Frontend / HTTP 两侧传入的 json_schema、regex、tool parser 配置,最终怎样汇入 sampling / generation path。
flowchart TB
A["Frontend gen(...)\nregex / json_schema"] --> C["SamplingParams"]
B["HTTP / OpenAI-compatible request\nresponse_format / extra body"] --> C
C --> D["Grammar backend\nXGrammar / Outlines / llguidance"]
C --> E["tool parser / function calling parser"]
D --> F["generation path\nconstrained token selection"]
E --> F
F --> G["structured output / tool call payload"]相对于纯文字,这张图多解释了“参数对象是汇合点”这一层。调用方可能从不同表面进入系统,但只要最后落到 SamplingParams 和对应 parser / grammar backend,结构化生成就不是外围技巧,而是 runtime 能力。
支持哪些约束形式#
官方 docs/advanced_features/structured_outputs.ipynb 当前明确列出了三类约束:JSON Schema、regular expression 和 EBNF。文档还指出,不同 grammar backend 对这三类约束的支持范围不一样,默认 backend 是 XGrammar,而 Outlines 与 llguidance 也可以通过 server 参数切换。
这里最值得读者抓住的,不是“有哪些后端名字”,而是设计方向:SGLang 把结构化生成看作一层 grammar backend abstraction。调用方通过 json_schema / regex / ebnf 描述约束,运行时通过 grammar backend 把这些约束落实到 generation path 上。这样做的好处,是约束形式可以演进,而上层调用接口保持稳定。
tool parser 和 function calling 在哪里接进来#
docs/advanced_features/tool_parser.ipynb 展示了另一条重要能力线:不同模型可以通过不同 parser 解释 function calling 结果,例如 deepseekv3、glm、qwen、pythonic 等 parser。这里的重点不是模型名单,而是它暴露出一个事实:结构化输出不仅可以约束成 JSON 或 regex,还可以约束成工具调用语义。
这条线和纯 grammar constraint 的关系是:前者更偏“把输出解释成可执行结构”,后者更偏“在 token 生成时限制格式空间”。两者放在同一章,是因为它们都在回答“模型怎样稳定地产生可消费结构”,但仍然需要分开理解,避免把 parser 逻辑误写成 grammar backend 逻辑。
Frontend 与 HTTP 两侧是怎么使用这些约束的#
在 frontend language 一侧,python/sglang/lang/api.py 里的 gen(...) 已经直接接受 regex 和 json_schema 参数。这说明约束生成不需要一定经过 HTTP server 才能使用,它本来就是语言层 API 的一部分。对使用者来说,这条路径更像“在 prompt program 里直接声明结构”。
在 HTTP / OpenAI-compatible 一侧,官方 structured outputs 文档展示了通过 response_format 或额外 body 参数传入 schema / regex 的方式。两侧的共同点是:最终都会落到 sampling 参数和 grammar backend。也就是说,接口表面可以不同,但内部真正消费约束的地方仍然在 generation path。
本章对应哪些代码路径#
这一章最重要的文件与文档锚点包括 python/sglang/srt/sampling/sampling_params.py、python/sglang/lang/api.py、docs/advanced_features/structured_outputs.ipynb、docs/advanced_features/tool_parser.ipynb 与 docs/basic_usage/sampling_params.md。
要继续深挖,推荐顺序是:先看 SamplingParams 里的约束字段和校验逻辑,再看 lang/api.py 里 frontend gen(...) 怎样暴露这些参数,然后回到 structured outputs / tool parser 文档看调用样式。这样能把“参数对象”“语言入口”“文档示例”三者连成一条线。
小结#
结构化生成这一层的关键,不是“格式更漂亮”,而是把输出空间主动收窄到目标结构。SGLang 之所以把它设计成 generation path 的一部分,而不是事后修补文本,是因为只有这样,json_schema、regex、EBNF 和 tool parser 才能真正成为 runtime 能力,而不是外围技巧。
叶王 © 2013-2026 版权所有。如果本文档对你有所帮助,可以请作者喝饮料。