Skip to content

结构化输出

结构化输出让模型的响应不再是自由格式的文本,而是严格符合你指定的 JSON 格式。RouteAPI 支持两种结构化输出模式:JSON mode(要求模型输出合法 JSON)和 JSON Schema(保证输出符合特定 schema)。

结构化输出是一种控制模型响应格式的机制。不同于普通对话中模型自由生成文本,结构化输出强制模型按你定义的格式生成 JSON 数据。这对于需要程序化处理模型输出的场景(数据提取、表单生成、API 响应解析)至关重要。

RouteAPI 提供两种结构化输出模式:

对比项JSON modeJSON Schema
保证输出是合法的 JSON输出符合指定的 schema
参数response_format: { type: "json_object" }response_format: { type: "json_schema", json_schema: {...} }
schema 定义不需要,但需在 prompt 中说明格式必须提供完整的 JSON Schema
严格程度仅保证可解析,不保证结构保证字段、类型、必填项完全符合
适用场景格式简单、模型能从 prompt 理解结构复杂嵌套结构、需要严格类型校验

简单理解:JSON mode 只保证「能被 JSON.parse 成功解析」,但不管里面有什么字段;JSON Schema 不仅保证合法,还保证结构、字段名、类型、必填项都符合你的定义。

场景说明推荐模式
数据提取从非结构化文本中提取结构化信息(姓名、地址、日期)JSON Schema
表单生成让模型生成表单初始值或配置对象JSON Schema
API 响应解析模型输出需要对接到下游系统的 APIJSON Schema
简单键值对只需要几个字段,结构简单明确JSON mode
分类任务输出固定枚举值(如情感分类:positive/negative/neutral)JSON Schema + enum

JSON mode 是最简单的结构化输出方式:你在请求中设置 response_format.type 为 "json_object",模型就会输出合法的 JSON,而不是普通文本。

{
"model": "gpt-5.5",
"messages": [
{
"role": "system",
"content": "你是一个数据提取助手。请从用户输入中提取姓名、年龄、城市三个字段,以 JSON 格式返回。"
},
{
"role": "user",
"content": "我叫李明,今年 28 岁,住在上海。"
}
],
"response_format": { "type": "json_object" }
}
  1. 必须在 prompt 中说明 JSON 格式:模型不知道你要什么字段,必须通过 system message 或 user message 明确告诉它输出哪些字段、什么类型。上面的例子中,"请从用户输入中提取姓名、年龄、城市三个字段,以 JSON 格式返回" 就是格式说明。

  2. 不保证 schema 符合性:模型可能输出 {"name": "李明", "age": 28, "city": "上海"},也可能输出 {"姓名": "李明", "年龄": 28},甚至 {"person": {"name": "李明"}}。只要是合法 JSON,就算符合要求。

  3. 如何使用:适合格式简单、字段少、模型能从自然语言理解结构的场景。如果需要严格校验字段名或类型,用 JSON Schema。

请求(curl):

Terminal window
curl https://api.routeapi.ai/v1/chat/completions \
-H "Authorization: Bearer $ROUTEAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"messages": [
{
"role": "system",
"content": "你是一个数据提取助手。从用户输入中提取 name(姓名)、age(年龄)、city(城市)三个字段,以 JSON 格式返回。"
},
{
"role": "user",
"content": "我叫李明,今年 28 岁,住在上海。"
}
],
"response_format": { "type": "json_object" }
}'

响应:

{
"id": "chatcmpl_xxx",
"object": "chat.completion",
"created": 1730000000,
"model": "gpt-5.5",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "{\"name\": \"李明\", \"age\": 28, \"city\": \"上海\"}"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 58,
"completion_tokens": 18,
"total_tokens": 76
}
}

注意 message.content 是一个 JSON 字符串,需要你自己 JSON.parse / json.loads 解析。

JSON Schema 模式让你精确定义输出的结构,模型保证生成的 JSON 完全符合你的 schema 定义。

{
"model": "gpt-5.5",
"messages": [
{
"role": "user",
"content": "我叫李明,今年 28 岁,住在上海。"
}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "person_extraction",
"strict": true,
"schema": {
"type": "object",
"properties": {
"name": { "type": "string", "description": "姓名" },
"age": { "type": "integer", "description": "年龄" },
"city": { "type": "string", "description": "城市" }
},
"required": ["name", "age", "city"],
"additionalProperties": false
}
}
}
}
字段类型必填说明
typestring是固定为 "json_schema"
json_schema.namestring是schema 名称,用于标识,字母、数字、下划线、连字符
json_schema.strictboolean否是否启用严格模式,默认 false
json_schema.schemaobject是标准 JSON Schema 定义
模式strict行为
严格模式true模型必须完全按 schema 生成,字段名、类型、必填项、additionalProperties 都严格遵守
非严格模式false模型尽力符合 schema,但不保证完全一致,可能缺少字段或添加额外字段

推荐:生产环境使用 strict: true,这是 JSON Schema 模式的核心价值。非严格模式的行为接近 JSON mode + prompt 说明,意义不大。

schema 字段遵循标准 JSON Schema 规范(Draft 2020-12),常用字段:

字段说明
type数据类型:"object", "array", "string", "number", "integer", "boolean", "null"
properties对象的字段定义(type 为 "object" 时使用)
required必填字段名数组
additionalProperties是否允许未定义的额外字段(严格模式下建议 false)
items数组元素的 schema(type 为 "array" 时使用)
enum枚举值列表,限定取值范围
description字段说明,帮助模型理解语义
{
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "integer" },
"score": { "type": "number" },
"is_active": { "type": "boolean" },
"notes": { "type": ["string", "null"] }
}
}

类型说明:

  • "string": 字符串
  • "integer": 整数
  • "number": 数字(包括整数和小数)
  • "boolean": 布尔值
  • "null": 空值
  • ["string", "null"]: 允许字符串或 null(可选字段)
{
"type": "object",
"properties": {
"user": {
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string" }
},
"required": ["name"]
},
"tags": {
"type": "array",
"items": { "type": "string" }
},
"scores": {
"type": "array",
"items": { "type": "number" }
}
}
}
{
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "integer" },
"city": { "type": "string" }
},
"required": ["name", "age"]
}

required 数组列出必须存在的字段。上例中 name 和 age 必须有,city 可以没有。

{
"type": "object",
"properties": {
"sentiment": {
"type": "string",
"enum": ["positive", "negative", "neutral"],
"description": "情感分类结果"
},
"priority": {
"type": "integer",
"enum": [1, 2, 3],
"description": "优先级:1-低,2-中,3-高"
}
},
"required": ["sentiment"]
}

enum 限定字段只能取列表中的值,模型不会生成其他值。

{
"type": "object",
"properties": {
"user": {
"type": "object",
"properties": {
"name": { "type": "string" },
"address": {
"type": "object",
"properties": {
"city": { "type": "string" },
"street": { "type": "string" }
},
"required": ["city"]
}
},
"required": ["name", "address"]
}
},
"required": ["user"]
}

对象可以无限嵌套,但过深的嵌套可能影响模型生成质量和性能。

{
"type": "object",
"properties": {
"date": {
"type": "string",
"description": "日期,格式 YYYY-MM-DD"
},
"amount": {
"type": "number",
"description": "金额,单位:元"
}
}
}

description 不是必填,但强烈建议加上。它帮助模型理解字段语义、取值范围和格式约定,能显著提高生成准确度。

从非结构化文本中提取用户信息:

{
"name": "user_info_extraction",
"strict": true,
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "用户姓名"
},
"age": {
"type": "integer",
"description": "年龄"
},
"email": {
"type": ["string", "null"],
"description": "电子邮箱,如果文本中没有则为 null"
},
"phone": {
"type": ["string", "null"],
"description": "手机号,如果文本中没有则为 null"
},
"city": {
"type": "string",
"description": "所在城市"
}
},
"required": ["name", "age", "city"],
"additionalProperties": false
}
}

让模型生成一个商品数组:

{
"name": "product_list",
"strict": true,
"schema": {
"type": "object",
"properties": {
"products": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "商品名称"
},
"price": {
"type": "number",
"description": "价格,单位:元"
},
"category": {
"type": "string",
"enum": ["electronics", "clothing", "food", "other"],
"description": "商品分类"
},
"in_stock": {
"type": "boolean",
"description": "是否有货"
}
},
"required": ["name", "price", "category", "in_stock"],
"additionalProperties": false
}
},
"total_count": {
"type": "integer",
"description": "商品总数"
}
},
"required": ["products", "total_count"],
"additionalProperties": false
}
}

订单信息提取,包含多层嵌套:

{
"name": "order_extraction",
"strict": true,
"schema": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "订单号"
},
"customer": {
"type": "object",
"properties": {
"name": { "type": "string" },
"phone": { "type": "string" },
"address": {
"type": "object",
"properties": {
"province": { "type": "string" },
"city": { "type": "string" },
"street": { "type": "string" }
},
"required": ["province", "city", "street"],
"additionalProperties": false
}
},
"required": ["name", "phone", "address"],
"additionalProperties": false
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"product_name": { "type": "string" },
"quantity": { "type": "integer" },
"unit_price": { "type": "number" }
},
"required": ["product_name", "quantity", "unit_price"],
"additionalProperties": false
}
},
"total_amount": {
"type": "number",
"description": "订单总金额"
}
},
"required": ["order_id", "customer", "items", "total_amount"],
"additionalProperties": false
}
}

大多数 RouteAPI 聚合的主流模型都支持 JSON mode(response_format: { type: "json_object" }),包括:

  • OpenAI GPT 系列(gpt-4o, gpt-4-turbo, gpt-3.5-turbo 等)
  • Claude 系列(claude-3.5-sonnet, claude-3-opus, claude-3-haiku 等)
  • Gemini 系列(gemini-2.0-flash, gemini-1.5-pro 等)
  • 其他支持 OpenAI 格式的模型

JSON Schema(response_format: { type: "json_schema" })对模型能力要求更高,目前支持的模型:

  • OpenAI: gpt-4o 系列、gpt-4-turbo 系列(2024-08-06 及以后版本)
  • Claude: claude-3.5-sonnet 系列、claude-3-opus 系列
  • Gemini: gemini-2.0-flash-exp、gemini-1.5-pro 系列
  • 其他:部分新一代模型

如何确认:调用前通过 Models 接口 查询模型能力,检查 supports_response_format 字段。

能力维度JSON modeJSON Schema说明
支持模型范围广泛(几乎所有主流模型)有限(新一代模型)JSON mode 支持度更高
schema 复杂度不适用建议不超过 3 层嵌套过深影响生成质量
最大字段数不适用建议不超过 50 个顶层字段字段过多影响性能
严格保证仅保证合法 JSON保证符合 schema严格模式下 100% 符合
性能快相对较慢schema 校验有额外开销
Token 消耗低稍高schema 定义会占用 prompt tokens
  1. schema 大小限制:单个 schema 定义建议不超过 10KB,过大的 schema 可能被截断或拒绝。

  2. 嵌套深度限制:建议嵌套层数不超过 3-4 层,过深的嵌套会降低模型生成质量和速度。

  3. 性能影响:JSON Schema 模式的响应时间通常比普通请求慢 10%-30%,因为模型需要在生成过程中实时校验结构。

  4. 不支持的 schema 特性:部分高级 JSON Schema 特性(如 $ref、allOf、anyOf、oneOf、正则表达式)可能不被所有模型支持。

  5. 流式输出:JSON Schema 模式支持流式输出(stream: true),但 content 是逐片段返回的,完整 JSON 需要拼接后再解析。

示例 1:从非结构化文本提取结构化信息

Section titled “示例 1:从非结构化文本提取结构化信息”

场景:从客服对话中提取客户信息和问题分类。

curl:

Terminal window
curl https://api.routeapi.ai/v1/chat/completions \
-H "Authorization: Bearer $ROUTEAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": "客户李明(手机 13800138000)反馈在北京市朝阳区的订单 ORD-2024-001 一直没发货,比较着急。"
}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "customer_inquiry",
"strict": true,
"schema": {
"type": "object",
"properties": {
"customer_name": { "type": "string", "description": "客户姓名" },
"phone": { "type": ["string", "null"], "description": "客户手机号" },
"location": { "type": ["string", "null"], "description": "客户所在地" },
"order_id": { "type": ["string", "null"], "description": "订单号" },
"issue_category": {
"type": "string",
"enum": ["delivery", "quality", "refund", "other"],
"description": "问题分类:delivery-物流,quality-质量,refund-退款,other-其他"
},
"urgency": {
"type": "string",
"enum": ["low", "medium", "high"],
"description": "紧急程度"
}
},
"required": ["customer_name", "issue_category", "urgency"],
"additionalProperties": false
}
}
}
}'

响应:

{
"choices": [
{
"message": {
"role": "assistant",
"content": "{\"customer_name\":\"李明\",\"phone\":\"13800138000\",\"location\":\"北京市朝阳区\",\"order_id\":\"ORD-2024-001\",\"issue_category\":\"delivery\",\"urgency\":\"high\"}"
},
"finish_reason": "stop"
}
]
}

Python:

import os
import json
from openai import OpenAI
client = OpenAI(
api_key=os.environ["ROUTEAPI_KEY"],
base_url="https://api.routeapi.ai/v1",
)
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "user",
"content": "客户李明(手机 13800138000)反馈在北京市朝阳区的订单 ORD-2024-001 一直没发货,比较着急。",
}
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "customer_inquiry",
"strict": True,
"schema": {
"type": "object",
"properties": {
"customer_name": {"type": "string", "description": "客户姓名"},
"phone": {"type": ["string", "null"], "description": "客户手机号"},
"location": {"type": ["string", "null"], "description": "客户所在地"},
"order_id": {"type": ["string", "null"], "description": "订单号"},
"issue_category": {
"type": "string",
"enum": ["delivery", "quality", "refund", "other"],
"description": "问题分类",
},
"urgency": {
"type": "string",
"enum": ["low", "medium", "high"],
"description": "紧急程度",
},
},
"required": ["customer_name", "issue_category", "urgency"],
"additionalProperties": False,
},
},
},
)
data = json.loads(response.choices[0].message.content)
print(f"客户: {data['customer_name']}")
print(f"问题类型: {data['issue_category']}")
print(f"紧急程度: {data['urgency']}")

Node.js:

import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.ROUTEAPI_KEY,
baseURL: 'https://api.routeapi.ai/v1',
});
const response = await client.chat.completions.create({
model: 'gpt-4o',
messages: [
{
role: 'user',
content: '客户李明(手机 13800138000)反馈在北京市朝阳区的订单 ORD-2024-001 一直没发货,比较着急。',
},
],
response_format: {
type: 'json_schema',
json_schema: {
name: 'customer_inquiry',
strict: true,
schema: {
type: 'object',
properties: {
customer_name: { type: 'string', description: '客户姓名' },
phone: { type: ['string', 'null'], description: '客户手机号' },
location: { type: ['string', 'null'], description: '客户所在地' },
order_id: { type: ['string', 'null'], description: '订单号' },
issue_category: {
type: 'string',
enum: ['delivery', 'quality', 'refund', 'other'],
description: '问题分类',
},
urgency: {
type: 'string',
enum: ['low', 'medium', 'high'],
description: '紧急程度',
},
},
required: ['customer_name', 'issue_category', 'urgency'],
additionalProperties: false,
},
},
},
});
const data = JSON.parse(response.choices[0].message.content);
console.log(`客户: ${data.customer_name}`);
console.log(`问题类型: ${data.issue_category}`);
console.log(`紧急程度: ${data.urgency}`);

场景:让模型根据自然语言描述生成表单初始值。

Python:

response = client.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "system",
"content": "你是一个表单填充助手。根据用户描述生成表单数据。",
},
{
"role": "user",
"content": "创建一个新员工入职表单:张伟,男,1995 年出生,本科学历,软件工程师职位,月薪 15000。",
},
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "employee_form",
"strict": True,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"gender": {"type": "string", "enum": ["male", "female"]},
"birth_year": {"type": "integer"},
"education": {
"type": "string",
"enum": ["high_school", "bachelor", "master", "phd"],
},
"position": {"type": "string"},
"salary": {"type": "number", "description": "月薪,单位:元"},
},
"required": ["name", "gender", "birth_year", "education", "position", "salary"],
"additionalProperties": False,
},
},
},
)
form_data = json.loads(response.choices[0].message.content)
print(json.dumps(form_data, ensure_ascii=False, indent=2))

输出:

{
"name": "张伟",
"gender": "male",
"birth_year": 1995,
"education": "bachelor",
"position": "软件工程师",
"salary": 15000
}

场景:让模型从第三方 API 的非结构化响应中提取关键信息。

Node.js:

const apiResponse = `
订单状态:已发货
物流公司:顺丰速运
运单号:SF1234567890
预计送达:2024 年 1 月 20 日
当前位置:北京市分拨中心
`;
const response = await client.chat.completions.create({
model: 'gpt-4o',
messages: [
{
role: 'user',
content: `从以下物流信息中提取结构化数据:\n${apiResponse}`,
},
],
response_format: {
type: 'json_schema',
json_schema: {
name: 'logistics_info',
strict: true,
schema: {
type: 'object',
properties: {
status: {
type: 'string',
enum: ['pending', 'shipped', 'in_transit', 'delivered'],
},
carrier: { type: 'string', description: '物流公司' },
tracking_number: { type: 'string', description: '运单号' },
estimated_delivery: {
type: ['string', 'null'],
description: '预计送达日期,格式 YYYY-MM-DD',
},
current_location: { type: ['string', 'null'], description: '当前位置' },
},
required: ['status', 'carrier', 'tracking_number'],
additionalProperties: false,
},
},
},
});
const data = JSON.parse(response.choices[0].message.content);
console.log(data);
// 输出: { status: 'shipped', carrier: '顺丰速运', tracking_number: 'SF1234567890', estimated_delivery: '2024-01-20', current_location: '北京市分拨中心' }

如果你的 schema 定义本身有问题(语法错误、不支持的特性),请求会直接返回 400 错误:

{
"error": {
"message": "Invalid JSON schema: ...",
"type": "invalid_request_error",
"param": "response_format.json_schema.schema",
"code": "invalid_json_schema"
}
}

解决:

  • 检查 schema 语法是否符合 JSON Schema 规范
  • 移除不支持的高级特性(如 $ref、allOf)
  • 简化过深的嵌套结构

极少数情况下,模型可能无法生成符合 schema 的内容(如用户输入与 schema 要求完全冲突),此时会返回错误或降级为普通文本输出。错误示例:

{
"error": {
"message": "Failed to generate valid output matching the provided schema after maximum retries.",
"type": "model_error",
"code": "schema_generation_failed"
}
}

解决:

  • 检查 prompt 是否与 schema 要求一致
  • 简化 schema,移除过于严格的约束
  • 在 prompt 中明确说明输出要求
  • 对于可选字段,使用 ["string", "null"] 类型而不是仅 "string"

对于偶发的生成失败,可以实现自动重试:

import time
def call_with_retry(client, **kwargs):
max_retries = 3
for attempt in range(max_retries):
try:
response = client.chat.completions.create(**kwargs)
content = response.choices[0].message.content
data = json.loads(content) # 验证是否能解析
return data
except (json.JSONDecodeError, Exception) as e:
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt) # 指数退避
  1. 从简单开始:先用 JSON mode 验证可行性,确认需要严格校验后再升级到 JSON Schema。

  2. 明确必填和可选:必填字段放 required 数组,可选字段用 ["type", "null"] 或干脆不放 required。

  3. 用 enum 限定枚举:凡是取值有限的字段(状态、分类、优先级),都用 enum 显式列出,能大幅降低错误率。

  4. 加 description:每个字段都加 description,说明含义、格式、取值范围,模型据此生成更准确。

  5. 设置 additionalProperties: false:严格模式下,加这个能防止模型输出未定义的额外字段。

问题说明建议
嵌套过深超过 4 层嵌套会降低生成质量拆分为多个扁平对象,或用多轮对话分步提取
字段过多单个对象超过 50 个字段按业务逻辑分组,拆分为多个子对象
过度约束所有字段都是必填,没有灵活性只把核心字段标为必填,其他字段允许 null
无 description模型不理解字段语义每个字段都加 description,说明清楚
  1. 减少 schema 大小:schema 定义会占用 prompt tokens,过大的 schema 会增加延迟和成本。

  2. 缓存 schema 定义:同一个 schema 反复使用时,在代码中定义为常量,避免每次请求都重新构造。

  3. 选择合适的模型:不是所有任务都需要最强的模型,简单的结构化提取可以用 gpt-3.5-turbo + JSON mode。

  4. 批量处理:如果有多个相似任务,可以设计一个数组 schema,让模型一次处理多条数据。

因素影响优化建议
schema 大小schema 定义占用 prompt tokens精简 description,移除冗余字段
模型选择JSON Schema 通常需要较强模型简单任务用 JSON mode + 弱模型
响应长度JSON 输出通常比文本更长(键名、引号、括号)缩短字段名,用枚举代替长字符串
重试次数生成失败重试会翻倍计费优化 schema 和 prompt 降低失败率

实用 tip:对于高频调用的场景,先用 JSON mode 测试,确认模型能稳定输出正确格式后再升级到 JSON Schema。JSON mode 的 token 消耗和成本通常比 JSON Schema 低 10%-20%。

  • JSON mode 和 JSON Schema 的支持度取决于所选模型,请通过 Models 接口 查询 supports_response_format 字段确认。
  • response_format 与 tools(工具调用)互斥:同一个请求不能同时使用结构化输出和工具调用。
  • 流式输出(stream: true)下,content 是逐片段返回的,需要完整拼接后再 JSON.parse。
  • 明确传入 0 或 false 的可选参数会被视为用户显式设置,不会当作缺省丢弃。
  • 记录每次请求的 request ID、模型 ID、状态码和 token 用量,便于排查。错误结构详见 错误与调试。