每日新闻 / 2026-05-29

LLM JSON 结构化输出完全指南:让模型响应直接被代码解析的最优实践

XycAi
LLM JSON 结构化输出完全指南:让模型响应直接被代码解析的最优实践

把 LLM 接入生产系统,最先撞墙的往往不是模型能力,而是输出格式。模型返回一段夹杂 Markdown、注释、道歉语的文字,你的 json.loads() 直接抛异常,整条链路挂掉。LLM JSON 结构化输出就是解决这个问题的:通过 API 层的 schema 约束,强制模型输出符合你预期结构的 JSON,让下游代码零解析负担直接消费。

这篇文章覆盖三件事:各家 API 的约束机制怎么用、schema 怎么写才不踩坑、输出出问题时怎么兜底。

各家 API 的 JSON Schema 约束机制

OpenAI(GPT-5.5 / GPT-5.4 / GPT-5.4-mini)

OpenAI 的 Structured Outputs 在 response_format 字段里传 json_schema 对象,并设 strict: truestrict 模式下模型输出会被词元级别的有限状态机约束,理论上 100% 符合 schema,不会多字段也不会少字段。

response = client.chat.completions.create(
    model="gpt-5.4-mini",
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "product_review",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "sentiment": {"type": "string", "enum": ["positive", "neutral", "negative"]},
                    "score": {"type": "number", "minimum": 0, "maximum": 10},
                    "summary": {"type": "string"}
                },
                "required": ["sentiment", "score", "summary"],
                "additionalProperties": False
            }
        }
    },
    messages=[{"role": "user", "content": "评价这款产品:..."}]
)

注意 additionalProperties: falsestrict 模式的必须项,漏掉会报 400 错误。

Anthropic(Claude Opus 4.8 / Sonnet 4.6 / Haiku 4.5)

Anthropic 没有独立的 JSON Mode,而是通过 Tool Use(工具调用)来实现结构化输出。定义一个只有 input_schema 的工具,强制模型调用它,工具参数就是你要的 JSON。

response = client.messages.create(
    model="claude-sonnet-4-6",
    tools=[{
        "name": "extract_review",
        "description": "提取评价结构化数据",
        "input_schema": {
            "type": "object",
            "properties": {
                "sentiment": {"type": "string", "enum": ["positive", "neutral", "negative"]},
                "score": {"type": "number"},
                "summary": {"type": "string"}
            },
            "required": ["sentiment", "score", "summary"]
        }
    }],
    tool_choice={"type": "tool", "name": "extract_review"},
    messages=[{"role": "user", "content": "评价这款产品:..."}]
)
result = response.content[0].input  # 直接拿到 dict

tool_choice 设成指定工具名,模型不会跳过调用去直接回答文字。

Google(Gemini 3 系列)

Gemini 在 generation_config 里设 response_mime_type: "application/json"response_schema,底层也是有限状态机约束。

response = client.generate_content(
    contents="评价这款产品:...",
    generation_config={
        "response_mime_type": "application/json",
        "response_schema": {
            "type": "OBJECT",
            "properties": {
                "sentiment": {"type": "STRING"},
                "score": {"type": "NUMBER"},
                "summary": {"type": "STRING"}
            },
            "required": ["sentiment", "score", "summary"]
        }
    }
)

Gemini 的 schema 类型用大写(STRINGNUMBEROBJECTARRAY),和标准 JSON Schema 小写不同,混用会静默失效而不报错,这是最常见的踩坑点。

横向对比

能力 OpenAI GPT-5.x Claude 4.x Gemini 3
约束机制 json_schema + strict Tool Use input_schema response_schema
词元级约束 ✅ strict 模式
原生 enum 支持
嵌套对象
返回位置 message.content content[0].input text(需 parse)

写好 JSON Schema 的关键细节

schema 写得不好,即使 API 不报错,输出质量也会很差。几个具体要点:

描述字段不能省。 description 不只是文档,模型在生成时会读它来理解字段语义。"score" 没有描述,模型不知道是 1-10 还是 0-100,加上 "description": "评分,范围 0-10,越高越好" 后准确率明显提升。

enum 优先于 string + 说明。 能用 enum 列举的值域就列举,别写 "type": "string", "description": "只能是 positive/neutral/negative"——后者在 strict 模式外无约束力。

数组要指定 items {"type": "array"} 没有 items 在某些模型上会导致输出退化成字符串。正确写法是 {"type": "array", "items": {"type": "string"}}

嵌套层级别超过 4 层。 过深的嵌套会让模型在生成中途"迷失",实测 Haiku 4.5 和 GPT-5.4-mini 在 5 层以上嵌套时合规率下降约 15%。如果业务确实需要复杂结构,考虑分两次调用分别提取。

LLM JSON 结构化输出的容错策略

即使启用了 schema 约束,生产环境里仍然会遇到三类问题:模型触发安全过滤返回空输出、finish_reasonlength 导致 JSON 截断、旧版 API 不支持 strict 模式时的降级输出。

第一道防线:校验 finish_reason

choice = response.choices[0]
if choice.finish_reason != "stop":
    raise ValueError(f"输出异常终止: {choice.finish_reason}")

length 意味着被 token 上限截断,这种情况下 JSON 必然不完整,直接重试并调大 max_tokens,不要尝试修复截断的字符串。

第二道防线:JSON 修复库

对于不支持 strict 约束的旧接口或国产模型(DeepSeek V3、Qwen 3 等),输出里常见的问题是尾部多了解释文字、数字值用了引号、出现了 Python 风格的单引号。json-repair 库(Python)可以处理大多数这类情况:

from json_repair import repair_json
import json

raw = response.choices[0].message.content
try:
    result = json.loads(raw)
except json.JSONDecodeError:
    result = json.loads(repair_json(raw))

json-repair 能修复约 80% 的轻微格式错误,但不要依赖它处理语义错误(字段名拼错、值域超出 enum)。

第三道防线:schema 校验 + 重试

输出合法 JSON 不等于符合你的 schema。用 jsonschema 做二次校验,失败时把错误信息塞回 prompt 让模型自我修正:

import jsonschema

def validate_or_retry(client, messages, schema, max_retries=2):
    for attempt in range(max_retries + 1):
        response = call_llm(client, messages)
        try:
            data = json.loads(response)
            jsonschema.validate(data, schema)
            return data
        except (json.JSONDecodeError, jsonschema.ValidationError) as e:
            if attempt == max_retries:
                raise
            messages.append({"role": "assistant", "content": response})
            messages.append({"role": "user", "content": f"输出不符合要求:{e},请重新生成"})

重试不超过 2 次,超过说明 prompt 或 schema 本身有问题,应该修源头而不是无限循环。

国产模型的现状与接入注意

DeepSeek V3、GLM-5.2、Qwen 3 均支持 response_format: {"type": "json_object"} 这一基础 JSON Mode,但不支持 OpenAI 式的 json_schema strict 约束。这意味着模型只被告知"输出 JSON",但字段结构不受词元级约束,字段缺失、额外字段、类型不符都可能发生。

对这些模型,最佳实践是:在 system prompt 里放完整的 schema 示例(不是描述,是完整的 JSON 示例对象),配合上面的 json-repair + jsonschema 双重兜底,合规率可以稳定在 90% 以上。相比 OpenAI strict 模式的 99%+ 确实有差距,但对大多数非强实时场景够用。


如果你在项目里同时调多家模型做 A/B 测试或成本优化,每家 API 维护一套鉴权和 schema 适配逻辑会很烦。我现在用 XycAi 词元平台 来统一管理——一个 OpenAI 兼容接口接进去,GPT-5.5、Claude Opus 4.8、Gemini 3、DeepSeek V3 都能调,schema 那层的差异在平台侧做了抹平。Claude Code 和 Gemini CLI 也能一键接入,对于本文这种多模型结构化输出场景,省了不少胶水代码。官方模型价格低至官方价 1.4 折起,有合规需求的话也支持大模型算法备案和开全球发票。

一个 API 接入 200+ 全球 AI 模型

GPT · Claude · Gemini 官方模型低至 1.4 折起,持大模型算法备案号,CN2 直连低至 5ms,可开全球合规发票。

立即体验 XycAi →