← 返回攻略列表

大模型API返回JSON解析失败怎么排查?结构化输出(json_schema strict)五家能力对照与避坑手册

语音转文字API与文字转语音API价格对比与选型指南
大模型API结构化输出JSON Schema能力对照

上周处理一个工单,用户贴了一句报错:JSONDecodeError: Expecting value: line 1 column 1 (char 0)。他的代码完全正确——提示词里写了"请只返回 JSON",模型也确实返回了看起来像 JSON 的东西。真正的问题在返回内容的最前面多了六个字符:```json。

这不是个例。把"大模型 JSON 解析失败"当作一个搜索词去看社区提问,你会发现大量开发者卡在同一个认知错位上:以为"让模型输出 JSON"和"让模型按我的 schema 输出 JSON"是同一件事。它们在 2026 年已经是两套完全不同的技术栈,而各家平台的实现口径、参数名、能力边界还都不一样。

这篇文章不讲价格,也不讲省钱。我按各家官方文档逐项对照了一遍结构化输出的真实能力边界,把它整理成一份排错手册:先给症状和根因的对应关系,再给五家平台的能力对照表,然后是 10 个我在实际项目里踩到的坑,最后是一棵按报错信息直接定位问题的决策树和可复用的兜底代码。文中所有参数名和能力限制都来自官方文档,不含推测。

一、先分清两种失败:格式错,还是结构错

排错的第一步不是看代码,是看模型到底返回了什么。把异常现场归到两类,解决方式完全不同:

症状典型返回根因正确做法
格式错 ```json {...} ``` 没用 response_format,只靠提示词;模型把 JSON 当代码块 启用结构化输出,从解码层消除可能
结构错 {"name":"张三","age":"28"} 用了 json_object,字段类型靠提示词约束 换 json_schema strict
截断 {"items":[{"id":1},{"id":2 finish_reason 为 length,输出撞上 max_tokens 调大 max_tokens,不要盲目重试
拒答 message.refusal 非空 模型基于安全策略拒绝生成 走业务兜底,不要重试

最后一行的拒答是最容易被忽略的。启用 strict 之后,schema 约束会被安全策略绕过——refusal 发生时没有可解析的 payload,你的代码如果直接访问 .parsed,会在两行之后收到一个莫名其妙的 AttributeError: 'NoneType' object has no attribute ...,排查方向完全跑偏。这一点在 OpenAI 官方文档里写得很明确:always check for it before parsing。

二、五家平台能力对照(按官方文档口径)

先说结论:能真正做 schema 级约束的只有三家半。OpenAI 最成熟,Claude 和 Gemini 各自有清晰实现,通义千问只覆盖部分新模型,DeepSeek 在 Chat Completions 接口上至今没有 schema 约束能力。下表是逐项对照:

能力项OpenAIClaudeGeminiDeepSeek通义千问
参数名 response_format output_config.format responseSchema response_format response_format
仅保证合法 JSON 支持 不支持 支持 支持 支持
schema 强约束 strict:true json_schema schema 字段 无(Responses 接口有) strict:true(限部分模型)
可选字段写法 类型联合 null 类型联合 null 类型联合 null 靠提示词 类型联合 null
拒答信号 refusal 字段 异常/错误 安全阻断 可能返回空内容 可能返回空内容
额外计费 无 无 无 无 无

几个容易踩的细节值得单独说:

OpenAI:strict 是"编译期约束",不是"校验器"

OpenAI 的 strict 模式走的是约束解码——在每个 token 采样时把不符合 schema 的 logit 屏蔽掉。所以它保证的是"输出一定符合结构",而不是"内容一定对"。官方文档列明的限制有三条最关键:

还有两个容易漏的:strict: true 会禁用并行工具调用,需要显式设 parallel_tool_calls: false;新 schema 的第一次调用要编译语法,有大约 100~500 毫秒的一次性开销,之后走缓存,影响不大。这个"schema 也算输入 token"的点,在做 API Token 计费原理 那篇里也提过——复杂 schema 定义会带来几百个额外输入 token,量大时不是小数目。

Claude:没有 json_object,prefill 已被废弃

Claude 的结构化输出走 output_config.format,底层复用了工具调用机制:把 schema 当作一个虚拟工具的输入,强制模型调用它,再从 tool_use 块里取结果。2026 年 2 月 4 日起该能力在 Claude Developer Platform 和 Amazon Bedrock 上 GA,覆盖 Sonnet 4.5、Opus 4.5、Haiku 4.5。

两个专属坑:

另外 Claude 的 schema 编译对复杂 schema 可能要 2~5 秒,且缓存按账号和模型走,schema 内容哪怕只改一个空格都会让缓存失效。生产环境建议启动时发一个预热请求。

Gemini:OpenAPI 子集 + 一个别家没有的字段

Gemini 用 responseMimeType: "application/json" 加 responseSchema。它的 schema 是 OpenAPI 3.0 的子集,不是完整 JSON Schema——这意味着很多 JSON Schema 关键字(anyOf、pattern 等)不被支持,且不支持的字段往往不会明确报错。

Gemini 有一个别家都没有的能力:propertyOrdering。标准 JSON Schema 里键顺序无所谓,但 Gemini 会受顺序影响输出质量,用它可以指定字段输出顺序,比如让模型先给证据再给结论。对纯分类任务还能直接用 responseMimeType: "text/x.enum" 返回单个枚举值,完全不需要后处理。

迁移坑:Gemini 的 type 关键字是大写的(OBJECT、STRING、INTEGER),而 JSON Schema 标准写法是小写。跨平台维护同一套 schema 时,这一个大小写差异就能让你排查半天。Gemini 官方文档自己也建议:即使是合法 JSON,也要始终在应用侧校验字段值。

DeepSeek 与通义千问:能力分界线要看清

DeepSeek 的 Chat Completions 接口在 API Reference 里 response_format.type 只列了 text 和 json_object 两值,没有 json_schema。它的 JSON 模式有三个必须遵守的条件,任何一条不满足都会出问题:

另外 DeepSeek V4 的思考模式会分开返回 reasoning_content 和 content,解析时只取 content,别去解 reasoning 字段。思考模式默认开启,做结构化抽取时官方建议显式关掉。相关成本细节可以看 推理模型思考 Token 计费规则。

通义千问的 JSON Object 模式覆盖面很广(Qwen、Kimi、GLM、DeepSeek 系列都支持),但 JSON Schema 模式目前只覆盖 Qwen3.8-Max、Qwen3.7-Max、Qwen3.7-Plus 等部分新模型,官方文档明确写着"更多模型即将支持"。还有个关键提醒:官方标注为"非思考模式"的模型在开启思考模式后,结构化输出可能不生效,甚至返回不严格合法的 JSON。如果你走的是思考模型,务必实测验证,别默认它按文档工作。

三、10 个真实踩坑点

下面这些不是理论推演,是几个项目里实际卡住过的点:

  1. optional 字段在 strict 下报 400。 strict 要求 properties 里所有字段都进 required。可选字段要写成类型联合 null,别用"不写进 required"的方式。
  2. schema 里用了 pattern 以为是正则约束。 OpenAI 和 Gemini 都不执行 pattern、minLength 这类关键字。长度/格式校验必须自己写。
  3. 顶层写了 array 被拒。 OpenAI 顶层必须是 object,数组要包一层。
  4. refusal 没检查,.parsed 是 None 引发 AttributeError。 安全拒答时 schema 约束被跳过,没有 payload 可解析。
  5. finish_reason=length 当成解析失败无限重试。 这是截断,调大 max_tokens 就行,重试只会重复烧钱。
  6. DeepSeek 提示词里没写"json"被拒。 错误信息类似 "messages must contain the word 'json' in some form"。
  7. DeepSeek content 为空没兜底。 官方承认可能返回空内容。
  8. Claude prefill 在新模型上失效。 2025 年的教程大多是 prefill,2026 年已废弃。
  9. Gemini schema 用了 anyOf/pattern 被静默忽略。 它是 OpenAPI 子集,不支持的字段不报错,只是没效果。
  10. schema 每次请求都动态生成。 既失去 grammar 缓存,又把 schema 变成持续输入 token 成本。schema 应当按功能点固定下来复用。

四、按报错信息定位的决策树

排错的时候对着这张表走,比逐个 try 试快得多:

JSON 解析失败 / 返回不稳定
        │
        ├─ 返回带 ```json 围栏?
        │    └─ 是 → 没启用 response_format。启用结构化输出,不要靠正则剥围栏
        │
        ├─ finish_reason == "length"?
        │    └─ 是 → 输出被 max_tokens 截断。调大上限,不要重试
        │
        ├─ 有 refusal 字段且非空?
        │    └─ 是 → 安全拒答,schema 约束被绕过。走业务兜底,不要重试
        │
        ├─ content 为空字符串?
        │    └─ 是 → DeepSeek/通义的已知问题。空内容兜底 + 有限重试
        │
        ├─ 400 且报 schema 不合法?
        │    └─ 检查:可选字段用了联合 null?顶层是 array?嵌套超 5 层?
        │
        └─ 解析成功但字段不对?
             └─ 用的是 json_object 而非 json_schema。升级为 strict 约束

五、可直接复用的兜底重试代码

无论哪家平台,生产代码都应该有三层防护:空内容兜底 → 按根因分类 → 有限重试。下面是 Python 版的核心骨架:

import json, time

def extract_json(client, prompt, schema, max_retries=2):
    """三层防护:空内容兜底 → 按根因分类 → 有限重试"""
    for attempt in range(max_retries + 1):
        resp = client.chat.completions.create(
            model="gpt-4.1",
            messages=[{"role": "user", "content": prompt}],
            response_format={
                "type": "json_schema",
                "json_schema": {
                    "name": "extraction_result",
                    "strict": True,
                    "schema": schema,   # 可选字段记得用 ["string", "null"]
                },
            },
            max_tokens=2048,          # 给够,避免 length 截断
            temperature=0,
        )
        msg = resp.choices[0].message
        finish = resp.choices[0].finish_reason

        # 层1:安全拒答 —— schema 约束被绕过,重试无意义
        refusal = getattr(msg, "refusal", None)
        if refusal:
            raise SchemaRefusalError(refusal)

        # 层2:截断 —— 调大上限,别在这里重试
        if finish == "length":
            raise TokenLimitError("输出被 max_tokens 截断,需要调大上限")

        # 层3:空内容 —— DeepSeek/通义官方承认的已知问题
        raw = msg.content or ""
        if not raw.strip():
            if attempt < max_retries:
                time.sleep(2 ** attempt)     # 指数退避
                continue
            raise EmptyContentError("模型返回空内容")

        # 层4:真正需要重试的只有校验失败
        try:
            data = json.loads(raw)
        except json.JSONDecodeError as e:
            if attempt < max_retries:
                prompt += "\n上次输出不是合法 JSON(%s)。只返回 JSON,不要任何其他文字。" % e
                time.sleep(2 ** attempt)
                continue
            raise

        # 内容层校验:strict 不执行 pattern/minLength/format,必须自己查
        validate_business_rules(data, schema)
        return data

    raise RetryExhaustedError("已达最大重试次数")

三点要强调:

六、怎么把这些接进你的工程

结构化输出真正落地,通常还牵扯三件事:

如果你的服务要同时接多家模型,建议把 schema 定义收敛成一份内部标准,再按各家方言做适配层——包括 DeepSeek 和 Gemini 那些"能跑但有坑"的地方。迁移相关的问题可以看 AI API 迁移完全指南,多模型并行的调度思路在 AI 模型智能路由实战里有完整方案。

七、写在最后

结构化输出这件事,2026 年最大的变化是它从"提示词技巧"变成了"基础设施能力"。但五家平台的能力边界至今没有拉齐:OpenAI 的 strict 最完整,Claude 的 output_config 最优雅,Gemini 的 propertyOrdering 最有想法,DeepSeek 和通义千问还在从 JSON 模式往 schema 约束过渡。

所以我的建议很直接:新项目一律从 json_schema strict 起步,不要从 json_object 起步。json_object 看着简单,但它把"字段对不对"这件事全推给了提示词,而提示词约束在生产环境里的可靠性,远不如解码层约束。已经在用 json_object 的老项目,值得花时间升级一轮。

至于各家文档——本文引用的都是官方页面,建议对照着看,因为各家都在快速迭代:

参考来源

  1. OpenAI Structured Outputs 官方文档 —— strict 模式约束、不支持关键字清单、refusal 处理
  2. Claude Developer Platform 结构化输出文档 —— output_config.format、严格工具使用、语法编译缓存
  3. Anthropic 官方博客:Structured outputs on the Claude Developer Platform —— GA 时间线与模型覆盖范围
  4. Gemini API 结构化输出文档 —— responseSchema 支持的 OpenAPI 子集、propertyOrdering、text/x.enum
  5. DeepSeek API JSON Output 指南 —— json_object 三项前提、max_tokens 建议、空内容说明
  6. Qwen Cloud 结构化输出文档 —— JSON Object 与 JSON Schema 模式对照、支持模型清单、思考模式注意事项

参考资料

  1. OpenAI Structured Outputs 官方文档(strict 模式约束、不支持关键字清单、refusal 处理):platform.openai.com/docs/guides/structured-outputs
  2. Claude Developer Platform 结构化输出文档(output_config.format、严格工具使用、语法编译缓存):platform.claude.com/docs/en/build-with-claude/structured-outputs
  3. Anthropic 官方博客:Structured outputs on the Claude Developer Platform(GA 时间线与模型覆盖):claude.com/blog/structured-outputs
  4. Gemini API 结构化输出文档(responseSchema 支持的 OpenAPI 子集、propertyOrdering、text/x.enum):ai.google.dev/gemini-api/docs/structured-output
  5. DeepSeek API JSON Output 指南(json_object 三项前提、max_tokens 建议、可能返回空内容):api-docs.deepseek.com/guides/json_mode
  6. Qwen Cloud 结构化输出文档(JSON Object 与 JSON Schema 模式对照、支持模型清单、思考模式注意):docs.qwencloud.com/developer-guides/text-generation/structured-output