2026 年大半的 production LLM 应用要模型吐特定形状的 JSON,不是自由文字。从简历抽字段、把客户意图分到 12 类之一、生成有命名步骤的行动计划。三年前你只能对自由文字跑 regex 然后祈祷,现在有三个真实选项,各有取舍。
三条路
- JSON mode。 跟模型说「输出有效 JSON」,让它自己想形状。便宜、低额外负担、没 schema 强制。
- Tool use / function calling。 定义一个工具,给它的参数 JSON schema。即使工具是虚构的,模型被强制吐出符合 schema 的参数(配合 strict mode)。
- 约束解码 / strict 结构化输出。 厂商在 sampling 时强制 schema 合规 —— 不合 schema 的 token 直接 mask 掉,模型字面意义不能吐出无效 JSON。
2026 年三种在主要厂商都有,但功能名跟限制不太一样。
JSON mode:最简单的工具
response = client.chat.completions.create(
model="gpt-5",
response_format={"type": "json_object"},
messages=[
{"role": "system", "content": "You output a JSON object with fields name, email, role."},
{"role": "user", "content": "..."},
],
)
模型吐某个 JSON 对象,你 parse 它。system prompt 把 schema 讲清楚通常会中。
强项: 最便宜、普遍支持、好设定。
弱项:
- Schema 强制是 best-effort,模型可能漏字段或加多余的。
- 没类型保证。「age」可能回
"forty"不是40。 - 你还是要在下游写 Pydantic / zod 验证。
Schema 简单、成本要省、下游能验证 —— 用 JSON mode。
Tool use:schema 强制 JSON
定义一个其实不是工具的工具,只是用来约束输出:
tools = [{
"type": "function",
"function": {
"name": "submit_extraction",
"description": "提交抽取出的字段。",
"strict": True,
"parameters": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string", "format": "email"},
"age": {"type": "integer", "minimum": 0, "maximum": 120},
"role": {"type": "string", "enum": ["engineer", "designer", "manager", "other"]}
},
"required": ["name", "email", "role"],
"additionalProperties": False
}
}
}]
response = client.chat.completions.create(
model="gpt-5",
tools=tools,
tool_choice={"type": "function", "function": {"name": "submit_extraction"}},
messages=[...],
)
args = json.loads(response.choices[0].message.tool_calls[0].function.arguments)
强项:
- 开
strict: True,OpenAI 保证输出符合 schema。没无效 JSON、没缺字段、没类型错。Anthropic 跟 Gemini 有类似 strict mode。 - enum 限制特定值。
role永远不会回"chief vibemaster"。 - Schema 是模型输入的一部分 —— 比「system prompt 说输出 X」遵守得更好。
弱项:
- Strict 加延迟(厂商每次 request 把 schema 编译成约束解码状态机),通常慢 10-20%。
- Strict 有微妙限制:root 不能
oneOf、所有字段必须required、递归 schema 受限。读厂商文档。 - 强迫 tool-call 响应形状,你要去 args 挖出来。
2026 年 production 抽取的对的默认值。
约束解码(strict 结构化输出)
OpenAI 的 response_format={"type": "json_schema", "strict": true, ...} 跟 Anthropic / Gemini 对应的版本,是 tool-use-as-extraction 的干净版:
response = client.chat.completions.create(
model="gpt-5",
response_format={
"type": "json_schema",
"json_schema": {
"name": "extraction",
"strict": True,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"role": {"type": "string", "enum": ["engineer", "designer", "manager"]}
},
"required": ["name", "role"],
"additionalProperties": False
}
}
},
messages=[...],
)
result = json.loads(response.choices[0].message.content)
跟 strict tool use 一样的保证,但 response 是普通 content(不是 tool call)。概念上不需要「函数」包装就用这个更干净。
自架(vLLM、llama.cpp)通过 Outlines、llguidance、XGrammar 等库实作,在 sampling 时 mask 无效 token。一样保证,不打厂商 API。
开 strict 之后实际发生什么
模型字面意义不能吐无效 JSON。decoder 每个 token step,看所有 20 万个 vocabulary token,检查哪些能让输出在已 emit 的 token 跟 schema 下保持有效,只允许从有效 token 采样。
这代表:
- schema 写 integer,你永远不会拿到
"age": "forty"。 - 永远不会缺 required 字段。
- 永远不会 JSON 语法错。
- 还是可能拿到语义错的数据 ——
"age": 999是有效 integer,只是错。
验证是结构性的,不是事实性的。你还是要 sanity check 范围、商业规则、内容质量。
production 上的五个惊喜
- Strict 对复杂嵌套 schema 失败。 有些 schema 厂商编译不了(深嵌套、递归、太多 enum 值)。你会看到 400 错误。要嘛简化,要嘛 fall back 到没 strict 的 tool use。
- 延迟负担是真的。 简单抽取从 200ms 变 280ms。批次抽取就重要了。
- 模型偶尔会拒答。 「我无法从输入判断 X」—— 它必须符合 schema,但 schema 要求模型推不出的字段,可能吐 placeholder 或调用失败。加
"unknown"enum 值或让选填字段真的选填。 - 跨厂商 schema 漂移。 OpenAI、Anthropic、Gemini 的 schema 方言略不同。OpenAI 过的 schema Anthropic 可能失败。换厂商要测。
- 长 enum 的成本。 200 个 enum 值的
role字段让 schema 膨胀加延迟。选项多就考虑自由 string + 下游验证,或拆成 category + subcategory。
怎么挑
- 一次性原型、schema 简单、会下游验证: JSON mode。出货最快。
- Production 抽取、不能坏: strict tool use 或 strict
response_formatjson_schema。schema 是你的合约。 - Schema 巨大或复杂(很多 enum、嵌套、递归): 没 strict 的 tool use,下游用 Pydantic / zod 验。99% 遵守而且避免厂商编译错。
- 自架想要保证: Outlines 或 XGrammar 加 vLLM。Strict 没每调用成本。
- 阶层任务(计划 + 步骤 + 每步骤参数): Tool use,每个「工具」代表一个步骤类型。模型吐一连串 tool call。
库辅助
2026 年热门 wrapper:
- Pydantic + Instructor(Python)。
instructor.from_openai(client)给你client.chat.completions.create(response_model=MyModel, ...),回 typed Pydantic 对象。藏掉 strict mode 接线。 - zod-to-json-schema(TypeScript)。 zod schema 转 JSON schema,送 API,response 过 zod 解。双向类型安全。
- Outlines(Python)。 本地模型或想完全控制约束算法时。
大部分团队:Instructor(Python)或 zod(TypeScript)叠在 strict API 上。除非你在做奇怪的事,别自己写验证层。
什么时候不要用结构化输出
结构化输出对抽取、分类、计划生成很棒,对这些不好:
- 长篇自然语言输出。 别硬塞文章到
{"text": "..."}schema,直接用纯文字。 - 创意写作。 Schema 约束降低创意,可量测微小;在乎质量就跳过。
- 用户可见 UI 的 streaming。 Strict 结构化输出 streaming 不顺。聊天 UI 纯文字比较友善。
下一步
- OpenAI 结构化输出文档。
- Anthropic tool use 文档。
- Outlines 库(开源结构化生成)。
- Efficient Guided Generation for Large Language Models(Willard & Louf, 2023)。
- 查这些词:JSON Schema、Pydantic、Instructor library、XGrammar。