GPT 5.5 多模态JSON结构化抽取实战测评
多模态大模型做结构化抽取,能力确实存在,但稳定输出完全是另一回事。
GPT 5.5 的视觉理解能力很强,丢一张发票或一张表格,基本都能识别。但问题在于:它未必每次都严格按你要求的格式输出。有时多一句废话,有时字段名变了,有时格式直接崩溃——下游解析一报错,整个 pipeline 就废了。
「能跑通」和「能稳定跑通」完全是两个概念。这篇聚焦后者:GPT 5.5 多模态结构化抽取的稳定性挑战到底是什么,工程上该如何应对。
先说下测试环境。要复现稳定性问题,需要大量重复调用并控制变量——同一张图、同一个 prompt,反复跑几十次,观察输出分布。这里的测试是在一个多模态聚合平台上完成的,平台把 GPT 5.5、Gemini、Claude 等模型整合在一起,调用参数、图片预处理、计费口径全部对齐,保证在完全相同的条件下做批量对比实验,观测输出的一致性和漂移情况。
环境对齐后,进入正题。
稳定性问题的几种典型表现
先把问题具体化。同一张图、同一个 prompt,跑 50 次,你会陆续遇到这些情况:
格式污染
你要的是纯 JSON,但它偶尔会在前面加一句:「根据图片内容,提取结果如下:」,后面补一段:「以上是提取的字段,如有遗漏请补充」,或者用 markdown 代码块包一层:json ...。下游 JSON.parse() 直接报错。
字段名漂移
你定义的 schema 是 invoice_number,但它可能返回 invoiceNumber(驼峰变体)、invoice_no(缩写变体)、发票号码(中文变体),甚至 invNum(不知来源的缩写)。下游字段映射全乱,取不到值。
字段顺序不一致
JSON 理论上是无序的,但如果下游用了顺序敏感的处理(或者人眼要看),字段顺序每次都不一样挺烦人。
类型不稳定
同一个字段,有时是字符串 "100.00",有时是数字 100,有时是 "一百元"。下游类型校验或计算逻辑直接崩。
幻觉填充
图里没有的字段,它不返回 null,而是编一个「看起来合理」的值。比如图里没写日期,它给你编一个今天的日期。
嵌套结构坍塌
你要的是这样的结构:
{
"items": [
{"name": "商品A", "price": 10},
{"name": "商品B", "price": 20}
]
}
它却返回这个:
{
"item1_name": "商品A",
"item1_price": 10,
"item2_name": "商品B",
"item2_price": 20
}
结构被拍平了,下游遍历逻辑全废。
这些问题单独看都不难处理,但它们随机出现、组合出现,防不胜防。所以,稳定性的敌人不是某一处 bug,而是随机性本身。
为什么会不稳定
理解原因,才能治本。
大模型的本质是概率采样
即使 temperature 设成 0,输出也不是 100% 确定性的(很多实现里 temperature=0 只是近似贪婪,不是真正的 argmax)。同一个输入多次推理,采样路径可能略有不同,输出就漂了。
指令遵循是「尽力而为」,不是「强制约束」
你在 prompt 里写「只输出 JSON」,模型会尽量遵守,但它不是编译器,没有硬性语法约束。它的训练目标是「生成合理的下一个 token」,不是「严格符合 schema」。
视觉输入增加了不确定性
图片经过视觉编码器后变成一堆 token,这个编码过程本身就有信息损耗和抽象。文字清晰还好,文字模糊、版式复杂时,视觉 token 的表示就不太稳定,下游理解也跟着漂。
长输出放大漂移
JSON 越长,出错的机会点越多。一个字段错了,后面的结构可能跟着崩,比如少个逗号、多个引号。
解决思路:分层防御
稳定性不是单点能解决的问题,得分层防御,每层兜一部分风险。
第一层:Prompt 约束——把规矩立清楚
Prompt 写法对稳定性影响巨大。几个实测有效的技巧:
显式给完整 schema
不要只说「提取发票信息」,要把期望的 JSON 结构完整写出来:
请从图片中提取发票信息,严格按以下 JSON 格式输出:
{
"invoice_number": "字符串,发票号码",
"date": "字符串,格式 YYYY-MM-DD",
"total_amount": "数字,金额",
"items": [
{"name": "字符串", "quantity": "数字", "unit_price": "数字"}
]
}
负向约束
明确告诉它不要做什么:
只输出 JSON,不要任何解释、前言、后缀。
不要使用 markdown 代码块。
如果某个字段在图中找不到,该字段值设为 null,不要猜测或编造。
Few-shot 示例
给一两个输入输出的例子,让它照着格式来。Few-shot 对格式稳定性的提升非常明显。
第二层:API 参数——用平台能力强制约束
GPT 5.5 的 API 提供了一些结构化输出的能力,要用上:
JSON Mode
如果平台支持 response_format: { type: "json_object" },开启它。这会在解码层面强制输出合法 JSON,至少不会出现 JSON 语法错误。
Function Calling / Tool Use
把期望的输出结构定义成一个 function 的参数 schema,让模型以 function call 的方式返回,这比纯文本生成的格式约束更强。
tools = [{
"type": "function",
"function": {
"name": "extract_invoice",
"parameters": {
"type": "object",
"properties": {
"invoice_number": {"type": "string"},
"date": {"type": "string"},
"total_amount": {"type": "number"},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"quantity": {"type": "number"},
"unit_price": {"type": "number"}
}
}
}
},
"required": ["invoice_number", "date", "total_amount"]
}
}
}]
Temperature 调低
结构化抽取不需要创意,temperature 设成 0 或接近 0,减少随机性。
第三层:输出校验——不合规就重试
即使前两层做了,仍然可能有漏网之鱼。第三层是后处理校验:
JSON 语法校验
先 JSON.parse(),失败就重试。
Schema 校验
用 JSON Schema 或类似工具(如 zod、ajv)校验结构是否符合预期:字段是否存在、类型是否正确、必填字段是否有值。
const schema = {
type: "object",
required: ["invoice_number", "date", "total_amount"],
properties: {
invoice_number: { type: "string" },
date: { type: "string", pattern: "^\d{4}-\d{2}-\d{2}$" },
total_amount: { type: "number" },
items: {
type: "array",
items: {
type: "object",
required: ["name", "quantity", "unit_price"],
properties: {
name: { type: "string" },
quantity: { type: "number" },
unit_price: { type: "number" }
}
}
}
}
};
业务规则校验
比如金额和明细的勾稽关系:total_amount 应该等于 items 里各项的 quantity * unit_price 之和,不等就标记异常。
重试策略
校验失败后自动重试,可以:直接重试(靠随机性碰一个对的),或者带反馈重试——把错误信息喂回去,让模型修正。
上一次输出的 JSON 校验失败,错误信息:缺少必填字段 "date"。
请重新提取,确保包含所有必填字段。
第四层:兜底机制——人工介入
自动化能解决大部分情况,但总有长尾 case。设计兜底:
连续重试 N 次仍失败 → 进人工队列
置信度低的字段 → 标记待复核
高价值/高风险场景 → 全量人工复核
稳定性指标怎么度量
上线前要有量化指标,别凭感觉说「好像稳了」。建议跟踪这几个指标:
| 指标 | 定义 | 目标参考 |
|---|---|---|
| 格式合规率 | JSON 语法 + Schema 校验通过率 | > 95% |
| 首次成功率 | 不重试直接通过的比例 | > 90% |
| 重试后成功率 | 最多 N 次重试后通过的比例 | > 99% |
| 字段准确率 | 单个字段值正确的比例 | 按业务定,关键字段 > 98% |
| 人工介入率 | 进入人工队列的比例 | < 1% |
跑一批测试集,把这些数字跑出来,才知道能不能上生产。
最后
GPT 5.5 的多模态结构化抽取能力是有的,但「能力有」和「稳定可用」之间,隔着一整套工程防御体系。
Prompt 约束、API 参数、输出校验、兜底机制——四层防线,每层解决一部分问题。不是因为模型不行,而是大模型的本质就是概率系统,你不能指望它像编译器一样确定性输出。
接受这个现实,用工程手段把随机性收敛到可接受范围,它就能成为生产可用的结构化抽取引擎。
欢迎在评论区聊聊,你们做结构化抽取时,重试策略是怎么设计的?有没有遇到过重试 N 次都过不了的魔鬼 case?
