JSON mode 与结构化输出:哪些中转站真的支持,怎么测
两种不同等级的能力
「结构化输出」实际包含两个差别很大的能力等级,很多站把它们混为一谈:
等级一:JSON mode(response_format: {"type": "json_object"})
只保证返回的是一段合法 JSON,不保证字段符合你的要求。
- 你必须自己在 system prompt 里把字段结构描述清楚;
- 模型可能多给字段、少给字段、改变类型(该是数字的给了字符串);
- 但至少
json.loads()不会失败。
等级二:JSON Schema(response_format: {"type":"json_schema","json_schema":{...}})
保证返回的 JSON 严格符合你给的 schema:字段齐全、类型正确、枚举值受限。
- 这是真正意义上的「结构化输出」;
- 实现难度比等级一高一个量级,因为它需要在上游做约束解码。
关键判断:一家站声称支持 JSON mode,不代表支持 json_schema。对你的项目来说,需要哪一个取决于你是否能容忍字段缺失。
三类常见失效表现
一、参数被静默忽略
最常见的失效。你传了 response_format,网关没报错,但也没生效 —— 模型自由输出,包含解释性文字和 markdown 代码块包裹。
症状:返回值以「好的,以下是 JSON:」开头,或者被 ``` `json `` 包住,json.loads()` 失败。
二、只在部分模型上生效
网关把 response_format 透传给上游,而上游不一定支持。表现是:同一个网关,模型 A 正常、模型 B 无效。
这本身不一定是网关的错 —— 但如果它不做兼容处理(比如对不支持的模型明确报错),就会变成静默失效。
三、schema 被降级
声明支持 json_schema,实际内部降级成 json_object。表现是:JSON 合法,但字段时有缺失或类型漂移。
这一类最难判断,因为结果「看起来对」。
一套分辨率极高的测试
测试 1:参数是否生效
curl -s https://<base_url>/v1/chat/completions \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{
"model": "<model>",
"response_format": {"type": "json_object"},
"messages": [{"role": "user",
"content": "用一句话介绍北京。以 JSON 返回。"}]
}'
判据:choices[0].message.content 是否是一个可以直接解析的 JSON(不含 ``` 包裹、不含前后解释文字)。
测试 2:schema 是否严格生效
给一个带必填 + 枚举 + 类型约束的 schema:
{
"type": "json_schema",
"json_schema": {
"name": "city_info",
"strict": true,
"schema": {
"type": "object",
"properties": {
"city": {"type": "string"},
"population": {"type": "integer"},
"tier": {"type": "string", "enum": ["一线", "二线", "三线"]}
},
"required": ["city", "population", "tier"],
"additionalProperties": false
}
}
}
判据(三条都要过):
- 三个字段全部存在;
population是数字而不是字符串"21540000";tier的值在枚举范围内。
任一不过 → 不支持严格 schema,最多只做到等级一。
测试 3:抗干扰测试(最能分辨真伪)
上面两个测试,很多站能通过一次性抽查。加上干扰才看得出真实水平:
在 prompt 里主动诱导模型破坏格式:
「请以 JSON 返回。另外,在 JSON 之前先用一段中文说明你的推理过程,并在结尾加一句总结。」
判据:
- 真正支持约束解码的实现:仍然只返回纯 JSON(约束优先级高于用户指令);
- 只做 prompt 包装的实现:会顺从用户指令,输出变成「说明 + JSON + 总结」。
这一条是三个测试里最有分辨力的,因为它区分的是「约束解码」和「在 system prompt 里偷偷加了一句『请返回 JSON』」。
测试 4:稳定性重复
同一个请求跑 20 次,统计失败率。
- 失败率 0% → 可用;
- 失败率 5%–20% → 只能当「大概率可用」,代码里必须加重试与兜底解析。
单次成功没有意义 —— 结构化输出的价值在于「不需要兜底」,如果需要兜底,那它就不能作为架构依赖。
为什么这项对工程影响特别大
如果你的代码里有这样的写法:
data = json.loads(resp.choices[0].message.content)
那么一旦模型输出被 markdown 包裹,整个流程直接抛异常。生产环境里的表现是「偶发任务失败」——最难排查的一类问题。
三种可选的处理方式,按可靠性排序:
- 要求严格的 json_schema(最优,但依赖服务端实现);
- 加重试 + 健壮解析(提取第一个
{到最后一个}之间的内容再解析,失败则重试); - 改用函数调用(如果只是要抽取结构化数据,工具调用的参数序列化通常比 JSON mode 更稳,因为它是协议层能力而不是提示层要求)。
第 3 条是一个常被忽略的实用技巧:同样的需求,用「让模型调用一个参数就是你要的数据的工具」来实现,往往比 JSON mode 更可靠。
小结
对结构化输出的评估,按这个顺序走:
- 先问清你要的是等级一还是等级二;
- 跑测试 1、2 确认基本能力;
- 跑测试 3 分辨真伪(这一步不能省);
- 跑测试 4 看稳定性,决定代码里要不要写兜底。
本站的选型文章里反复出现同一个原则,这里同样适用:能自己测的,不要靠标称值。
这篇文章对你有帮助吗?如果你在实际使用中发现了文中没提到的情况,欢迎 提交纠错或补充,我们会核验后更新到正文里。
相关文章
顺着看下去:本站收录的部分中转站
本文没有指向特定模型系列,下面是本站收录站点中综合评分靠前的几家,供你顺着看下去。排序依据是评分与可用率,不构成推荐。