# 结构化输出：JSON mode 与 JSON Schema，同一套请求格式

> https://router.one/zh/llm-structured-outputs 的 Markdown 镜像，供 AI 助手与爬虫使用。Router One 是 OpenAI 兼容的统一 LLM API 网关。
> 最后更新：2026-08-22

结构化输出就是让模型返回 JSON 而不是一段文字——要么是任意合法的 JSON 对象（json_object），要么是遵循你指定 schema 的 JSON（json_schema）。Router One 遵循 OpenAI Chat Completions 规范，response_format 字段直接附在你发给目录中任何对话模型的同一个请求上。网关先检查信封：schema 部分不完整时，在调用任何模型之前就返回 400；完整时把 response_format 原样转发给模型，不做改写。Schema 是否被强制执行取决于模型——支持程度和严格度因模型而异，所以拿到回复后一定要解析并校验。

## 用 json_object 还是 json_schema？

两者都放在 response_format 里，按你需要多少「形状约束」来选：

### json_object——JSON mode

模型返回一个语法合法的 JSON 对象，键名和嵌套由你的 prompt 决定。务必在 prompt 里说明要 JSON 并描述字段——这个标记本身不会定义字段。

### json_schema——带名字的 schema

附上一份带 name 的 JSON Schema，可选 description 和 strict: true。支持它的模型会把输出约束到这个 schema；不支持的模型照样回复，只是没有约束。这个差别正是必须在客户端校验的原因。

## JSON mode 请求

从 /models 目录选任意一个对话模型 ID。system 消息负责列字段，response_format 负责声明模式：

`json_object.sh`

```bash
curl https://api.router.one/v1/chat/completions \
  -H "Authorization: Bearer sk-your-router-one-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<model-id-from-/models>",
    "messages": [
      {"role": "system", "content": "只返回一个 JSON 对象，键为 city 和 date。"},
      {"role": "user", "content": "抽取城市和日期：9 月 3 日在上海开会。"}
    ],
    "response_format": {"type": "json_object"}
  }'
```

## JSON Schema 请求

json_schema 对象必须带 name 和 schema；description 与 strict 可选，会随其余字段一起转发：

`json_schema.sh`

```bash
curl https://api.router.one/v1/chat/completions \
  -H "Authorization: Bearer sk-your-router-one-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<model-id-from-/models>",
    "messages": [{"role": "user", "content": "抽取城市和日期：9 月 3 日在上海开会。"}],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "meeting",
        "strict": true,
        "schema": {
          "type": "object",
          "properties": {
            "city": {"type": "string"},
            "date": {"type": "string"}
          },
          "required": ["city", "date"],
          "additionalProperties": false
        }
      }
    }
  }'
```

## 转发前网关检查什么

type 为 json_schema 时，Router One 会校验信封；不完整的请求在调用任何模型之前就以 400 invalid_request 拒绝——请求根本没到模型，也就没有可计费的 token 用量。三条报错原文如下：

| 请求情况 | 400 报错原文 | 修法 |
| --- | --- | --- |
| type 为 json_schema 但没有 json_schema 对象 | response_format.json_schema is required when response_format.type is json_schema | 补上带 name 和 schema 的 json_schema 对象 |
| json_schema 缺 name | response_format.json_schema.name is required | 给 schema 起一个短标识，比如 meeting |
| json_schema 缺 schema | response_format.json_schema.schema is required | 把 JSON Schema 对象放到 schema 字段下 |

- 信封完整时原样转发——name、description、strict、schema 都在。网关不裁剪、不改写、不重新校验 schema 正文。
- 网关也不会拿你的 schema 去校验模型的回复。强制执行是模型的事，验证是你的事。

## 支持程度与严格度因模型而异

- json_object 支持面很广：你会拿到格式良好的 JSON，形状由 prompt 决定。
- json_schema 与 strict 只有一部分模型会遵守。模型不应用 schema 时你照样收到 200 和回复——只是没有约束。走原生非 OpenAI 协议的模型可能只认 json_object，或者完全不应用 schema。
- /models 目录按模型标注了对话、流式、工具调用、视觉能力，没有「结构化输出」标记。动手前先对目标模型发一个小请求，在 Trace 里看回复再决定。
- 需要一个跨模型家族都成立的形状？声明一个工具并用 tool_choice 强制调用——在支持工具调用的模型上，参数会以遵循你 parameters schema 的 JSON 字符串返回。无论哪种方式，用之前先校验。

## Schema 方言：$defs、$ref 与候选换路

有些模型线路不接受 JSON Schema 里的 $defs 和 $ref，因为它们的原生 schema 方言没有对应写法。Router One 把这种拒绝视为该候选线路自身的问题，而不是模型的问题：请求会换到同一个模型的下一个候选继续，这次拒绝也不计入模型的健康度。如果所有候选都拒绝这种方言，400 才会返回给你——把定义内联（展开每个 $ref），同一份 schema 就能通过。最终服务这次请求的候选，就是 Dashboard → Logs 里显示的那条。

## 流式与 SDK 用法

照常设置 stream: true。json_schema 下 JSON 会以 content delta 的形式按正常 SSE 分块到达——累积 content 直到最后一块再一次性解析，绝不要在流中途解析半个对象。用 OpenAI SDK 时，response_format 就是一个普通的关键字参数：

`structured.py`

```bash
from openai import OpenAI
import json

client = OpenAI(
    base_url="https://api.router.one/v1",
    api_key="sk-your-router-one-key",
)

schema = {
    "type": "object",
    "properties": {"city": {"type": "string"}, "date": {"type": "string"}},
    "required": ["city", "date"],
    "additionalProperties": False,
}

resp = client.chat.completions.create(
    model="<model-id-from-/models>",
    messages=[{"role": "user", "content": "抽取城市和日期：9 月 3 日在上海开会。"}],
    response_format={
        "type": "json_schema",
        "json_schema": {"name": "meeting", "strict": True, "schema": schema},
    },
)
data = json.loads(resp.choices[0].message.content)
# 使用前先按 schema 校验 data
```

## 在 Dashboard → Logs 里看结果

每次尝试都是一行请求记录，带模型、Token、花费、延迟和状态标记；点开可看请求详情。网关自身检查返回的 400，状态显示 400 并附上面的报错原文——改请求。回复是一段文字或形状松散的 JSON 的 200，说明你指定的模型没有应用 schema——收紧 prompt、加一步校验重试，或者换模型。改代码之前，Trace 已经告诉你遇到的是哪一种。

## 常见问题

### json_object 和 json_schema 有什么区别？

json_object（JSON mode）只保证回复是语法合法的 JSON 对象，键名由 prompt 决定。json_schema 附上一份带名字的 JSON Schema，可选 strict: true，支持它的模型会把输出约束到这个形状。两者都通过 /v1/chat/completions 上同一个 response_format 字段发送。

### Router One 保证回复符合我的 schema 吗？

不保证。网关校验请求信封，并把 response_format 原样转发给模型；它不改写 schema，也不拿 schema 去检查模型输出。是否强制执行取决于你指定的模型，且因模型而异，所以使用前请在客户端解析并校验回复。

### schema 算 Token 吗？

算——schema 随 prompt 一起发送，每次请求都按输入 Token 计费；JSON 回复按该模型的公示费率计为输出 Token。被网关自身检查以 400 拒绝的请求没有到达模型，没有 Token 用量。Dashboard → Logs 的每请求 Trace 会立刻显示大 schema 对 Token 和费用的影响。

### json_schema 可以配合流式用吗？

可以。设置 stream: true 后，JSON 会以 content delta 的形式、按和其他流式回复一样的 SSE 分块格式到达。累积 content 片段直到流结束再一次性解析——半个对象是解析不了的。分块格式、超时与取消见 LLM 流式输出指南。

### 该用工具调用还是结构化输出？

结构化输出适合「只要固定 JSON 形状的回复、没有真函数要跑」。工具调用适合「要执行动作」：模型请求调你的函数，你把结果喂回去。如果目标模型不遵守 json_schema，又需要符合 schema 的回复，强制调用单个工具是跨模型通用的替代方案——参数会遵循你的 parameters schema。很多应用两者并用。

### 这个 400 是什么意思，会扣费吗？

指向 response_format.json_schema、.name 或 .schema 的 400 invalid_request，表示信封不完整，网关在调用任何模型之前就拦下了——没有任何计费。补上缺的字段重发即可。其他 400 来自模型线路本身；最常见的是所有候选都重复拒绝 $defs/$ref，把 schema 展平就能解决。

## 相关页面

- LLM 工具调用：https://router.one/zh/llm-tool-calling
- LLM 流式输出：https://router.one/zh/llm-streaming
- OpenAI 兼容 API：https://router.one/zh/openai-compatible-api
- 错误码速查：https://router.one/zh/llm-api-error-codes
- Chat Completions 接口参考：https://router.one/zh/docs/chat/createChatCompletion
- 每请求 Trace：https://router.one/zh/llm-observability
- OpenAI SDK 接入：https://router.one/zh/integrations/openai-sdk
- API 文档：https://router.one/zh/docs
- 本页规范地址：https://router.one/zh/llm-structured-outputs
- 模型与每模型 token 价格：https://router.one/zh/models（markdown：https://router.one/zh/models.md）
- 定价：https://router.one/zh/pricing
- API 文档（markdown）：https://router.one/zh/docs.md
- 公司事实（markdown）：https://router.one/zh/facts/company.md
