跳转到主要内容
模型在结构化输出场景下返回的 JSON 有时无法通过解析:输出被 max_tokens 截断、带有尾随逗号、内容被包在 Markdown 代码块里。应用侧通常要为此编写重试或手工修复逻辑。 结构化输出修复(Structured Output Repair)是一个 Key 级别的能力,默认关闭。开启后,对声明了结构化 JSON 输出的非流式请求,当模型返回的 JSON 存在格式错误时,AIHubMix 网关在返回前自动将其修复为可解析的合法 JSON;客户端代码无需改动。
该开关默认关闭;未开启时所有响应原样返回。开关按 Key 独立配置,开启后立即生效。

前往 Key 管理页配置

创建或编辑 Key,打开「结构化输出修复」(Structured Output Repair)开关。
在 AIHubMix 编辑 Key 面板中开启结构化输出修复开关

1. 生效条件

修复只在以下条件全部满足时发生:
  1. 请求使用的 Key 已开启「结构化输出修复」。
  2. 请求为非流式(未设置 stream: true)。
  3. 请求声明了结构化 JSON 输出(各协议的声明字段见下表)。
  4. 模型返回的文本内容无法通过 JSON 解析。
协议端点结构化输出声明字段
Chat Completions/v1/chat/completionsresponse_format.typejson_objectjson_schema
Responses/v1/responsestext.format.typejson_schemajson_object
Claude Messages/v1/messagesoutput_config.format.typejson_schema
Gemini/gemini/v1beta/models/{model}:generateContentgenerationConfig.responseMimeTypeapplication/json,或设置了 responseSchema
内容本身是合法 JSON 时不做任何改动,原样返回。

2. 可修复的格式错误

输出截断导致括号 / 引号未闭合(如 finish_reasonlength):
修复前
{"items": [{"name": "Widget", "price": 9.99}, {"name": "Gad
修复后
{"items": [{"name": "Widget", "price": 9.99}, {"name": "Gad"}]}
Markdown 代码块包裹
修复前
```json
{"status": "ok", "count": 3}
```
修复后
{"status": "ok", "count": 3}
尾随逗号
修复前
{"a": 1, "b": 2,}
修复后
{"a": 1, "b": 2}
单引号
修复前
{'name': 'Widget'}
修复后
{"name": "Widget"}
键名未加引号
修复前
{name: "Widget", price: 9.99}
修复后
{"name": "Widget", "price": 9.99}
文本与 JSON 混杂(JSON 前后带说明文字):
修复前
Here is the JSON you requested: {"name": "Widget", "price": 9.99}
修复后
{"name": "Widget", "price": 9.99}
修复采取保守策略:
  • 修复只补全或规范化 JSON 语法;数值与字符串内容保持原文,不发生精度损失或改写
  • 修复产物必须是合法的 JSON 对象或数组,且与原文差异在合理范围内;任一条件不满足时放弃修复、原样返回原始内容。

3. 如何判断响应被修复过

发生修复时,有两个可编程 / 可查看的信号:
  1. 响应头 X-JSON-Repaired: true(未发生修复时无此响应头)。
  2. 调用日志备注调用记录页 对应请求的备注列显示 This request gateway automatically corrected the JSON format issue in the model output.
Token 用量与计费按模型的原始输出计算,修复不改变计费结果。

4. 完整示例

以生成商品信息为例,请求使用 json_schema 声明结构化输出;开启开关后,即使模型输出存在上述格式错误,拿到的 content 也可以直接解析:
import json
import os
import requests

response = requests.post(
    "https://aihubmix.com/v1/chat/completions",
    headers={
        "Authorization": f"Bearer {os.environ['AIHUBMIX_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "model": "gpt-5.2",
        "messages": [
            {"role": "user", "content": "Generate a product listing for a wireless keyboard."}
        ],
        "response_format": {
            "type": "json_schema",
            "json_schema": {
                "name": "product",
                "schema": {
                    "type": "object",
                    "properties": {
                        "name": {"type": "string"},
                        "price": {"type": "number"},
                        "description": {"type": "string"},
                    },
                    "required": ["name", "price"],
                    "additionalProperties": False,
                },
            },
        },
    },
)

repaired = response.headers.get("X-JSON-Repaired") == "true"
product = json.loads(response.json()["choices"][0]["message"]["content"])
print(product, "repaired:", repaired)
const response = await fetch("https://aihubmix.com/v1/chat/completions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.AIHUBMIX_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "gpt-5.2",
    messages: [
      { role: "user", content: "Generate a product listing for a wireless keyboard." },
    ],
    response_format: {
      type: "json_schema",
      json_schema: {
        name: "product",
        schema: {
          type: "object",
          properties: {
            name: { type: "string" },
            price: { type: "number" },
            description: { type: "string" },
          },
          required: ["name", "price"],
          additionalProperties: false,
        },
      },
    },
  }),
});

const repaired = response.headers.get("X-JSON-Repaired") === "true";
const data = await response.json();
const product = JSON.parse(data.choices[0].message.content);
console.log(product, "repaired:", repaired);
curl -sD - https://aihubmix.com/v1/chat/completions \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.2",
    "messages": [
      {"role": "user", "content": "Generate a product listing for a wireless keyboard."}
    ],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "product",
        "schema": {
          "type": "object",
          "properties": {
            "name": {"type": "string"},
            "price": {"type": "number"},
            "description": {"type": "string"}
          },
          "required": ["name", "price"],
          "additionalProperties": false
        }
      }
    }
  }'
想主动观察一次修复,可以把 max_tokens 设置为较小的值(如 80)让输出被截断:响应的 finish_reasonlengthcontent 是修复后的合法 JSON,响应头带有 X-JSON-Repaired: true

5. 边界与说明

流式请求(stream: true)不做修复,按原样转发。需要修复能力时请使用非流式请求。
修复保证内容可以通过 JSON 解析;被截断而缺失的数据无法恢复。建议同时检查 finish_reason / stop_reason,输出被截断时按需增大 max_tokens 后重试。
  • 语法修复:字段名、字段类型是否符合你的 schema 仍需应用侧校验。
  • 工具调用参数不在修复范围tool_calls / function_call 的参数 JSON 不做修复;工具参数在执行前应始终由应用侧校验。
  • 多文本块输出不做修复:模型输出包含多个文本块(例如 Gemini 返回多个 parts)时按原样返回。
  • 推理内容不受影响reasoning_content、Gemini 的 thought 部分等推理文本不参与修复。

常见问题

修复会改变返回内容里的数值或文本吗? 不会。修复只补全或规范化 JSON 语法(补全闭合括号、去除尾随逗号、引号规范化、剥离代码块标记),数值与字符串内容保持模型输出原文。 开启开关会影响未声明结构化输出的普通请求吗? 不会。未声明 response_format 等结构化输出字段的请求,以及所有流式请求,响应均原样返回。 修复会额外计费或改变 token 用量吗? 不会。Token 用量与计费按模型的原始输出计算,修复过程不产生额外费用。 修复后的 JSON 一定符合我定义的 schema 吗? 修复保证内容可以通过 JSON 解析;字段名与字段类型是否符合 schema 取决于模型输出本身,建议应用侧继续保留 schema 校验。