跳到内容

进阶★★★★★9 分钟阅读

从 LLM 拿结构化输出:tool use、JSON mode、schema

让模型吐有效 JSON 的三条路、各自什么时候赢,以及 production 上会吓到你的失败模式。

登入以收藏

2026 年大半的 production LLM 应用要模型吐特定形状的 JSON,不是自由文字。从简历抽字段、把客户意图分到 12 类之一、生成有命名步骤的行动计划。三年前你只能对自由文字跑 regex 然后祈祷,现在有三个真实选项,各有取舍。

三条路

  1. JSON mode。 跟模型说「输出有效 JSON」,让它自己想形状。便宜、低额外负担、没 schema 强制。
  2. Tool use / function calling。 定义一个工具,给它的参数 JSON schema。即使工具是虚构的,模型被强制吐出符合 schema 的参数(配合 strict mode)。
  3. 约束解码 / 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 上的五个惊喜

  1. Strict 对复杂嵌套 schema 失败。 有些 schema 厂商编译不了(深嵌套、递归、太多 enum 值)。你会看到 400 错误。要嘛简化,要嘛 fall back 到没 strict 的 tool use。
  2. 延迟负担是真的。 简单抽取从 200ms 变 280ms。批次抽取就重要了。
  3. 模型偶尔会拒答。 「我无法从输入判断 X」—— 它必须符合 schema,但 schema 要求模型推不出的字段,可能吐 placeholder 或调用失败。加 "unknown" enum 值或让选填字段真的选填。
  4. 跨厂商 schema 漂移。 OpenAI、Anthropic、Gemini 的 schema 方言略不同。OpenAI 过的 schema Anthropic 可能失败。换厂商要测。
  5. 长 enum 的成本。 200 个 enum 值的 role 字段让 schema 膨胀加延迟。选项多就考虑自由 string + 下游验证,或拆成 category + subcategory。

怎么挑

  • 一次性原型、schema 简单、会下游验证: JSON mode。出货最快。
  • Production 抽取、不能坏: strict tool use 或 strict response_format json_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。

最后更新: 2026-04-29

We use cookies

Anonymous analytics help us improve the site. You can opt out anytime. Learn more