OpenOCR 现已支持捕获 Gemini 3.1 Flash-Lite 及后续所有引擎返回的空结果
2026 年 10 月 8 日。 OpenOCR 现已能够检测引擎在读取页面后未返回任何文本的情况。您将收到明确说明原因的错误提示,而非看似成功的空结果。对于该次调用尝试,您支付的费用绝不会超过服务商实际收取的金额;在多页 PDF 中,所有成功读取的页面都会被完整保留。
这种异常模式最初发现于 Gemini 3.1 Flash-Lite,但该保护机制适用于 OpenOCR 上的所有引擎。
我们捕获的异常
我们的监控系统在 Google 的 Gemini 3.1 Flash-Lite(OpenOCR 上使用率最高的引擎之一)上标记出一种异常模式。在一小部分真实文档(包含清晰印刷文本的表格、卡片和信件)中,该模型未返回任何文本,而 Google API 却将请求标记为成功。在过去 30 天内,Flash-Lite 处理的页面中约有 3% 出现了这种情况,它几乎占了全平台所观察到的所有空结果的 97%。
在少数几次请求中,3% 的概率很容易被忽略。但在规模化文档处理场景下,这意味着工作流虽然能提取出大量高质量文本,却会悄然丢失可读页面,而服务商返回的成功信号掩盖了这些丢失,使其看起来像是已顺利完成的任务。(特别是在面对包含数千个字符且排版密集的汉字表意系统时,鉴于现有评测大多聚焦于英文及拉丁文本,此类空结果对中文文档识别的实际影响与漏检表现仍需进一步考量。)
重试并没有太大帮助。相同的图片在 80% 到 93% 的情况下依然返回空结果,因此重复发起请求往往只是白白浪费时间。
对于给出了明确拒绝原因的请求,下一步应当是切换至其他引擎。
现在的响应结构
在此次更新之前,一个清晰可读的页面可能会生成一个没有提取出任何文本的“成功”任务。以下是展示相关字段的简化示例:
{
"status": "succeeded",
"extracted_text": ""
}
现在,服务商的拒绝已被明确标出。以下是一个简化示例,其中 Google 对该次尝试进行了计费,因此该请求仅承担该项成本,不包含任何额外加价:
{
"status": "failed_charged",
"error_code": "provider_refused",
"cost_debited": 0.000327,
"provider_error": "Google declined to read this image (reason: SAFETY). Sending it to this engine again will give the same result; try a different engine."
}
该任务不再谎称已交付可用的 OCR 文本。计费方式严格遵循服务商的实际收费,具体说明如下。
面向所有引擎构建,而非仅针对单一模型
我们最初在 Gemini 上捕获了这一问题,但这种失败并非 Google 所独有。任何视觉模型都可能出现类似情况,其他少数引擎也偶尔会有此类表现。因此,我们并没有针对单一引擎打补丁,而是重构了 OpenOCR 处理来自任何引擎空结果的机制:
给出明确的响应,而非空白结果。 空结果会被报告为失败,并附带以下两种错误代码之一:
provider_refused:服务商拒绝处理并告知了原因(例如触发了安全过滤器)。错误信息中会指明具体原因,并告知您再次向该引擎发送该页面仍会得到相同结果。provider_empty:模型未返回任何内容,且未提供任何原因。在最终报错之前,OpenOCR 会自动对该页面再尝试读取一次。
这些错误代码使您的应用程序能够区分已知原因的拒绝与原因不明的空结果。对于后者,OpenOCR 会在返回失败前自动进行一次额外重试。
公平透明的计费。 如果服务商对此次尝试收取了费用,您只需支付该笔确切费用,无任何加价,且绝不会超过您的报价额度。如果服务商未计费,则您无需支付任何费用。这与直接调用服务商 API 的计费标准完全一致。
PDF 文档保留有效页面。 如果一份 30 页的 PDF 文档中有一页返回为空,您依然可以获取其他 29 页的解析结果,同时空白页面会被明确标注其页码。只有在所有页面均未提取出任何文本时,整个文档才会被判定为失败。
以一份发送给 Flash-Lite 处理的 30 页扫描件为例:第 1 至 11 页成功返回文本,第 12 页返回为空,第 13 至 30 页成功返回文本。OpenOCR 将返回这 29 个成功页面的解析结果,并附带一条指明第 12 页异常的告警信息。您可以将该页路由至其他引擎重新处理,或进行人工核对。收取的费用绝不会超过初始报价。
全量引擎持续监控。 我们现已对每个引擎的空结果率进行全天候监控,一旦有其他模型出现类似行为,我们将在 1 小时内收到预警。
Gemini 现在会明确解释拒绝原因
Gemini 3.1 Flash-Lite 现已迁移至 Google 原生 API 运行。我们此前使用的端点仅将这些拒绝报告为“other”。原生 API 则会明确说明页面被拒绝的具体原因,该原因现在会直接透传至您的错误信息中。
此外,卡住的请求现在会在 60 秒后自动放弃,而非之前的 3 分钟,以便您可以更快切换引擎。Flash-Lite 的常规延迟约为 3.6 秒,P99 延迟约为 12 秒。此前 180 秒的超时设置使卡顿请求的等待时间远超正常观察值;缩短至 60 秒既留出了超出常规范围的缓冲余量,又能避免无谓的长时间阻塞。
我们目前的监控机制
我们按小时持续追踪每个引擎的空结果率,一旦某个引擎的空结果率达到 5%,系统就会向团队发出告警。即使某个模型频繁返回空白结果,整体调用成功率仍可能维持在看似正常的水平;这种细化到单一引擎的指标能够精准指示我们需要介入调查的环节。
开发者指南
现有的系统集成不会受到任何破坏性影响。您只是不会再收到看似成功实则空白的结果。如果您的代码会对失败请求进行重试,请检查错误代码:对于 provider_refused 状态的页面,请将其路由至其他引擎,而不是盲目对同一引擎重复发起请求。由于数十个引擎统一接入在同一个 OpenOCR API 密钥和一个账户余额下,切换引擎只需修改 engine 字段中的一个名称即可。
对于 PDF 文档,请在保存返回文本的同时保留页面告警信息,以便识别缺失页面并决定如何对其进行后续处理。
常见疑问解答
我的现有集成会受到破坏吗? 不会。未提取出文本的页面将不再被伪装成成功的 OCR 结果。如果您的代码会对所有失败进行重试,请利用 error_code 避免对标记为 provider_refused 的请求在同一引擎上重复尝试。
我需要为被拒绝的页面付费吗? 仅在服务商对该次尝试向 OpenOCR 收费的情况下才需要付费。您只需按原价支付该笔确切费用,无任何溢价,且绝不会超过初始报价。如果服务商未向我们收取费用,您也无需支付任何费用。
为什么不自动切换引擎? 因为您选择特定模型通常有明确的考量。切换到其他引擎可能会产生不同的价格或输出格式。我们选择如实告知发生的情况,由您自主决定处理策略。
哪些引擎受到了影响? 我们主要在 Gemini 3.1 Flash-Lite 上发现了这种规律,在过去 30 天内,它占了平台所有空结果的约 97%。少数其他引擎偶有发生。目前的检测与报错机制已覆盖平台上的所有引擎。
如何处理空白分隔页? 建议在文档工作流的前置阶段处理已知的空白分隔页。对于空白纸张或扫描件背面,建议针对此类页面使用专用的轻量 OCR 引擎。我们目前也在针对视觉模型在空白页上无中生有(伪造文本)的问题进行专项测试。
下一步:评测文本伪造(幻觉)问题
在测试本次发布版本时,我们向 Gemini 3.1 Flash-Lite 传入了一张完全空白的纯白页面。它并没有返回空内容,而在全部 4 次测试中均凭空伪造了文本:一段关于商业的段落、一道数学证明题、一封客户信件,以及“THE END OF THE CHAPTER”(本章完)。这种情况在 Google 的两个 API 端点上均有出现,说明这属于模型本身的固有行为。
伪造文本的引擎比直接返回空结果更难捕获,因为其输出内容看起来十分逼真。我们正在 OpenOCR 基准测试中加入名为 HallucinationBench 的空白页测试集:这是一个公开测试项目,旨在评估哪些 OCR 模型会在完全空白的页面上“产生幻觉”。测试结果将以幽灵图标的形式展示在 OpenOCR 排行榜上,以便将每个引擎的伪造文本率与其识别准确率一并公开量化。在此之前,如果您的文档中包含空白页面(如分隔页或扫描件背面),建议针对此类页面采用专用的 OCR 引擎进行处理。