mlc-ai/xgrammar · 上手攻略
- 仓库:mlc-ai/xgrammar
- 链接:https://github.com/mlc-ai/xgrammar
- 分类:LLM 推理 / 结构化生成 / 约束解码(constrained decoding)
- 作者:spark
- 更新:2026-09-21
是什么
XGrammar 是 MLC AI 开源的高效、灵活、可移植的结构化生成引擎。它把 LLM 的解码过程约束在一个上下文无关文法(context-free grammar)内,保证输出 100% 符合给定 schema(JSON、正则、自定义 CFG 等),同时把约束解码的开销压到接近零——在 JSON 生成场景下做到「near-zero overhead」,是目前主流推理引擎里最快的结构化生成后端之一。
它不是单独跑的 LLM 服务,而是一个被推理引擎内嵌的库:vLLM、SGLang、TensorRT-LLM、MLC-LLM、OpenVINO GenAI、Modular MAX、Mirai/Uzu 等都把它当作默认结构化输出后端。XGrammar-2(2026 年 5 月发布)进一步强化了 agentic LLM 场景下的动态高效结构化生成。
解决什么问题
LLM 默认是「下一个 token 概率分布 + 采样」,要拿到结构化输出(JSON、SQL、tool-call 参数、状态机转移)传统做法是后处理 + retry / 重 parse,问题是:
- 格式错误率高:模型经常少逗号、多引号、漏字段;
- 重试成本:parse 失败就要重生成,几倍延迟;
- schema 复杂时几乎不可行:嵌套 JSON、union 类型、discriminated union、tagged union,正则根本表达不了;
- 跨硬件不可移植:很多结构化方案绑死在某个推理后端上。
XGrammar 用 约束解码思路:把给定 schema 编译成一个词级(token-level)有限状态自动机,每生成一个 token 都先 mask 掉不在合法 token 集合里的候选,确保每一步解码都 100% 落在 schema 内,从根上消灭格式错误。
快速安装
跨平台:Linux / macOS / Windows;硬件 CPU / NVIDIA GPU / AMD GPU / Apple Silicon / TPU 都支持;提供 Python、C++、JavaScript、Swift API。
pip install xgrammar
# Apple Silicon (MPS) 额外依赖
pip install "xgrammar[metal]"
确认可用:
import xgrammar as xgr
print(xgr.__version__)
⚠️ Wheel 与 CUDA / PyTorch 版本绑定严格——如果你本地推理用 CUDA 12 的 PyTorch,要装对应 CUDA 12 的 xgrammar wheel;否则会有 libxxx.so 找不到的错。遇到这种情况请去 PyPI 选对应 cuda tag。
核心用法
XGrammar 通常不直接调用,而是作为推理引擎后端(vLLM/SGLang 等)使用。这里给两类典型用法:
1) 在 vLLM 里用 XGrammar(结构化 JSON 输出)
from vllm import LLM, SamplingParams
from vllm.sampling_params import GuidedDecodingParams
llm = LLM(model="Qwen/Qwen2.5-7B-Instruct")
# 用 JSON schema 约束
guided = GuidedDecodingParams(
json_schema={
"type": "object",
"properties": {
"team": {"type": "string", "enum": ["billing", "tech", "sales"]},
"urgent": {"type": "boolean"},
},
"required": ["team"],
},
backend="xgrammar", # 显式指定后端
)
sp = SamplingParams(temperature=0.0, guided_decoding=guided)
out = llm.generate(
prompts=[{"role": "user", "content": "我的订单 #123 扣款两次,请帮我处理。"}],
sampling_params=sp,
)
print(out[0].outputs[0].text) # 100% valid JSON,结构必含 team
支持的后端开关(vLLM 0.6+):backend="xgrammar" / "outlines" / "lm-format-enforcer"。⚠️ XGrammar 在 vLLM 上是默认且最快的,常规情况无需改。
2) 直接用 XGrammar 编译 Grammar + 自定义解码
如果你的推理栈不是上面几个,XGrammar 也提供编译 grammar + token mask 的底层 API,可嵌入任意推理循环:
import xgrammar as xgr
# 1) 定义 grammar(JSON schema、regex、EBNF 都可)
grammar = xgr.Grammar.from_json_schema({
"type": "object",
"properties": {
"score": {"type": "integer", "minimum": 0, "maximum": 2},
"label": {"type": "string", "enum": ["low", "mid", "high"]},
},
"required": ["score", "label"],
})
# 2) 用 tokenizer + grammar 构造 matcher
tokenizer = ... # HuggingFace tokenizer
matcher = xgr.GrammarCompiler(grammar).compile(tokenizer)
# 3) 在你自己的 decode 循环里:
while not matcher.is_terminated():
logits = model.forward(...) # (vocab_size,)
mask = matcher.accept_token(logits) # 0/1 mask,屏蔽非法 token
masked_logits = logits.masked_fill(mask == 0, float("-inf"))
next_token = sample(masked_logits)
matcher.accept_token(next_token) # 也可只推进不接受新 logits
EBNF 写法(更复杂的 union / 递归结构):
grammar = xgr.Grammar.from_ebnf(r"""
root ::= person
person ::= "{" ws "\"name\":" ws string "," ws "\"age\":" ws int "}"
string ::= "\"{" ([a-zA-Z0-9 ])* "}\""
int ::= [0-9]+
ws ::= " "
""")
正则约束同样可以:xgr.Grammar.from_regex(r"\d{4}-\d{2}-\d{2}")。
3) 在 SGLang / TensorRT-LLM / MLC-LLM 里启用
基本都是 backend 切到 xgrammar 即可。SGLang:
python -m sglang.launch_server --model Qwen/Qwen2.5-7B-Instruct \
--grammar-backend xgrammar
客户端调用时传 --response-format '{"type":"json_schema","json_schema":{...}}' 即可。
典型适用场景
- Agent tool-call 解析:tool 入参 schema 严格编译进 grammar,模型必须按 schema 输出参数,function calling 不用再写 try/except parse;
- RAG 抽取:从文档里抽实体 / 关系 / 表格到固定 JSON schema,零后处理;
- 结构化数据生成:表单填写、SQL 生成、CSV 行、API 请求体;
- 多轮对话状态机:用 CFG 表达合法状态转移,避免模型输出非法 transition;
- 代码生成约束:函数签名 / import 顺序 / 类型注解的强制约束;
- 评测管线:评测模型必须输出指定 JSON 模板,XGrammar 保证 100% 可 parse,评测不被格式错误污染。
坑与注意
⚠️ JSON schema ≠ JSON 实例:传给 from_json_schema 的是schema(描述合法结构),不是合法 JSON 字符串本身。常见误用:把一个 JSON 对象当 schema 传,导致约束太松或太严。
⚠️ enum 顺序不影响生成概率,只影响 mask 范围:如果某个 enum 值的 token 序列会触发歧义(比如首 token 相同),要靠 grammar 本身的规则去重,XGrammar 不替你合并。
⚠️ 大 schema 编译有常数开销:极深的嵌套 / 几千个 property 的 schema 编译时间可能到秒级;运行时 hot path 极快,瓶颈只在首次编译。建议对同一 schema 复用同一个 Grammar 对象。
⚠️ 跨模型 token 化要重编:grammar 与具体 tokenizer 绑定;换模型(HuggingFace tokenizer 改了)必须重新 compile(tokenizer),否则 mask 错位。
⚠️ backend 自动回退:vLLM 在某些情况会自动从 XGrammar 切到其它 backend(schema 太复杂不支持时)。想确认实际生效,在 vLLM 日志里搜 Using XGrammar backend;或在代码里 guided_decoding.backend 显式断言。
⚠️ JSON 中的 additionalProperties: false 要写清楚,否则 XGrammar 会允许任意额外字段,schema 等于没约束。
⚠️ tool-call 嵌套 schema 容易撑爆 grammar 编译时间:嵌套到三层以上或 union 复杂时,编译可能 O(秒)。Agent 场景下建议把工具 schema 拆成单工具调用 + 后处理拼装。
与同类对比
| 项目 | 思路 | 速度 | Schema 表达力 | 跨引擎 |
|---|---|---|---|---|
| mlc-ai/xgrammar | 编译 CFG → token-level FSM mask | JSON 接近 0 开销 | JSON / regex / EBNF / CFG | vLLM / SGLang / TensorRT-LLM / MLC-LLM / MAX / Mirai / OV |
| Outlines | regex → token mask | 较快 | JSON / regex / CFG | Transformers / vLLM |
| lm-format-enforcer | JSON schema → trie mask | 中等 | JSON schema 为主 | vLLM / HF |
| Guidance | token-level mask + 控制流 | 中等 | JSON + 自由控制流 | HF / 独立 |
| Instructor | Python schema + retry 后处理 | 慢 | JSON / Pydantic | OpenAI / HF 等 |
| OpenAI Structured Outputs | 云端内置约束 | — | JSON schema 严格子集 | OpenAI only |
XGrammar 的差异化是「速度 + 跨推理引擎 + 复杂 CFG」三件套:vLLM / SGLang / TensorRT-LLM / MLC-LLM / MAX 全都把它做默认后端,schema 支持到通用 CFG / EBNF(不只 JSON schema),跨平台 CPU / NVIDIA / AMD / Apple Silicon / TPU 一致。
一句话推荐结论
如果你在做 LLM 结构化输出(agent tool-call、RAG 抽取、状态机对话),并且已经在用 vLLM / SGLang / TensorRT-LLM / MLC-LLM / MAX 中任何一个——XGrammar 已经是默认后端,你大概率已经在用了,把它写进 guided_decoding.backend="xgrammar" 显式断言即可获得 JSON 生成接近零开销的红利;如果是自定义推理栈,XGrammar 的 Python Grammar + matcher 底层 API 也能干净嵌入。
来源:XGrammar GitHub README(mlc-ai/xgrammar · 抓取 2026-09-21);XGrammar 官方文档(xgrammar.mlc.ai/docs/);XGrammar-2 公告博客(blog.mlc.ai/2026/05/04/xgrammar-2);MLC 2024 技术报告(arXiv:2411.15100)。
不确定处:⚠️ 当前 PyPI 最新版本号未在 README 抓取中显示,按 PyPI 公告应在 0.1.x 系列(XGrammar-2 发布后),实际装包版本以 pip show xgrammar 输出为准;⚠️ XGrammar-2 vs v1 的具体 API 改动(Grammar.from_* 命名是否变化)建议看官方 docs 当前页;⚠️ 与 Modular MAX / Mirai(Uzu) 的集成 API 是 backend 透传还是额外参数,需看各自文档当前版。