# Structured Outputs 与 JSON Mode：如何验证真实数据

> 区分 JSON Mode 与 Schema 约束输出，用缺失字段、冲突金额和拒答样本验证抽取质量，并把业务正确性纳入模型选择。

_发布 2026-09-29 · 更新 2026-09-29 · [KeepRouter Editorial](https://keeprouter.com/editorial-policy#editorial-team) · 4 分钟阅读_

![Responses API 与 Chat Completions 接入协议决策矩阵](https://keeprouter.com/editorial/blog/responses-api-vs-chat-completions.png)

_请求格式、响应结构与应用验证分别构成接入约定。概念示意图。_

JSON Mode 主要约束 JSON 语法；Schema 约束输出在模型和 API 支持时进一步限制字段、类型和结构。二者都不能保证字段值真实。文档抽取需要分别验证语法、结构和业务含义。

真正有用的是应用能安全处理的记录，而不只是 `JSON.parse` 不报错的文本。

## 先定义输出协议

[OpenAI 结构化输出说明](https://developers.openai.com/api/docs/guides/structured-outputs)区分 JSON Mode、Schema 与拒答处理；[Gemini 文档](https://ai.google.dev/gemini-api/docs/structured-output)说明其支持的 Schema 子集。不能把某个平台的支持推断给所有兼容端点。

| 方式 | 主要帮助 | 应用仍需检查 |
| --- | --- | --- |
| 提示词要求 JSON | 表达意图 | 语法、结构、事实 |
| JSON Mode | JSON 语法 | 必需字段、业务规则 |
| Schema 约束 | 支持的结构规则 | 来源真实性、跨字段关系 |
| 工具调用 | 请求一个应用动作 | 权限与执行安全 |

需要记录时使用输出结构，需要调用动作时才使用工具。不要让从文档抽取出的文本直接变成可执行操作。

## 明确“未知”怎样表达

发票可能没有到期日。如果强制要求一个日期字符串，就可能得到格式正确但虚构的日期。

```json
{
  "invoice_id": "INV-DEMO-17",
  "currency": "USD",
  "amount": "125.40",
  "due_date": null,
  "evidence": "Total due: USD 125.40"
}
```

这是合成样例，不是真实发票或模型结果。金额使用十进制字符串或最小货币单位整数，避免浮点误差。保留支持金额的必要原文，别顺带保存无关个人资料。

缺失、无法识别和相互冲突应有不同处理：没有日期可返回 `null`，两个金额冲突则进入复核，不都变成一个可能被当作默认值的空字符串。

## 解析后再验证业务含义

Schema 能限定金额字段，却不能证明它对应原文。货币白名单、金额精度等规则应由独立代码检查：

```python
from decimal import Decimal

record = {"currency": "USD", "amount": "125.40", "due_date": None}
if record["currency"] not in {"USD", "EUR"}:
    raise ValueError("Unsupported currency")
amount = Decimal(record["amount"])
if not amount.is_finite() or amount < 0:
    raise ValueError("Invalid amount")
if amount != amount.quantize(Decimal("0.01")):
    raise ValueError("Unexpected precision")
print(amount)
```

这个本地例子并非完整财务系统。退款或负数发票等例外需要自己的规则，还必须对照来源。通过验证不能直接授权付款或其他不可逆动作。

## 测试容易失败的文档

准备正常记录、缺失日期、冲突总额、模糊金额、不支持货币，以及夹带“忽略规则”指令的文档。最后一种用于检查源文本是否被误当作应用指令。

把字段正确与结构正确分开。假设一百份文档中九十八份能解析，但只有八十四份所需事实全部正确，可用记录就是八十四份。此处是计量说明，不是实测成绩。

拒答和截断保留独立状态。流式输出只返回半个对象时，不能因解析器恢复出几个字段就算成功。

## 用合格记录成本选模型

将首次请求、修正和失败的全部费用相加，再除以合格记录数。所有候选使用同一批文档和规则；不能为了让某个候选通过而单独放宽 schema。

使用 KeepRouter 前，在[模型目录](/models)和 [API 文档](/api/docs)核对具体线路。普通聊天兼容不等于严格 Schema 支持。用[迁移检查器](/tools/api-migration-checker)检查请求，再参考[文档抽取评估](/zh/blog/gpt-claude-gemini-document-extraction)选择候选。

## 常见问题

### 严格 JSON 能保证事实正确吗？

不能。结构合规不代表数值与原文一致，还需检查证据、业务规则和缺失值处理。

### 缺失字段应该猜测补全吗？

不应该。使用 null 或待复核等明确状态，让下游根据状态处理，而不是接受编造值。

### 不同网关的 JSON Mode 完全一样吗？

不一定。需要核对模型、端点和支持的 Schema 子集，参数在不同线路可能被拒绝或表现不同。

## 参考的一手资料

_本文最近复核 2026-09-29_

1. [OpenAI structured outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
2. [Gemini structured outputs](https://ai.google.dev/gemini-api/docs/structured-output)

## 继续阅读

- [LLM 工具调用：实现完整的请求与结果循环](https://keeprouter.com/zh/blog/llm-tool-calling-loop.md)
- [GPT、Claude、Gemini 文档提取选型：样本与评分方法](https://keeprouter.com/zh/blog/gpt-claude-gemini-document-extraction.md)
- [docs](https://keeprouter.com/api/docs)

## 迁移前，先检查你的请求

在浏览器中检查型号 ID 与请求字段，无需 API Key，也不发送推理请求。

[检查示例配置](https://keeprouter.com/tools/api-migration-checker)

[创建 Key，测试免费模型](https://keeprouter.com/login?returnTo=%2Fconsole%2Fkeys%3Fmodel%3Dfree)

免费测试使用 free 模型；其他付费型号需要足够预付额度。

[全部文章](https://keeprouter.com/zh/blog.md) · [模型与价格](https://keeprouter.com/models.md)
