结构化输出
结构化输出让模型的响应不再是自由格式的文本,而是严格符合你指定的 JSON 格式。RouteAPI 支持两种结构化输出模式:JSON mode(要求模型输出合法 JSON)和 JSON Schema(保证输出符合特定 schema)。
1. 结构化输出概述
Section titled “1. 结构化输出概述”什么是结构化输出
Section titled “什么是结构化输出”结构化输出是一种控制模型响应格式的机制。不同于普通对话中模型自由生成文本,结构化输出强制模型按你定义的格式生成 JSON 数据。这对于需要程序化处理模型输出的场景(数据提取、表单生成、API 响应解析)至关重要。
JSON mode vs JSON Schema
Section titled “JSON mode vs JSON Schema”RouteAPI 提供两种结构化输出模式:
| 对比项 | JSON mode | JSON 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 响应解析 | 模型输出需要对接到下游系统的 API | JSON Schema |
| 简单键值对 | 只需要几个字段,结构简单明确 | JSON mode |
| 分类任务 | 输出固定枚举值(如情感分类:positive/negative/neutral) | JSON Schema + enum |
2. JSON Mode
Section titled “2. JSON Mode”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" }}-
必须在 prompt 中说明 JSON 格式:模型不知道你要什么字段,必须通过 system message 或 user message 明确告诉它输出哪些字段、什么类型。上面的例子中,
"请从用户输入中提取姓名、年龄、城市三个字段,以 JSON 格式返回"就是格式说明。 -
不保证 schema 符合性:模型可能输出
{"name": "李明", "age": 28, "city": "上海"},也可能输出{"姓名": "李明", "年龄": 28},甚至{"person": {"name": "李明"}}。只要是合法 JSON,就算符合要求。 -
如何使用:适合格式简单、字段少、模型能从自然语言理解结构的场景。如果需要严格校验字段名或类型,用 JSON Schema。
请求(curl):
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 解析。
3. JSON Schema(Structured Outputs)
Section titled “3. JSON Schema(Structured Outputs)”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 } } }}response_format 结构
Section titled “response_format 结构”| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type | string | 是 | 固定为 "json_schema" |
json_schema.name | string | 是 | schema 名称,用于标识,字母、数字、下划线、连字符 |
json_schema.strict | boolean | 否 | 是否启用严格模式,默认 false |
json_schema.schema | object | 是 | 标准 JSON Schema 定义 |
严格模式 vs 非严格模式
Section titled “严格模式 vs 非严格模式”| 模式 | strict | 行为 |
|---|---|---|
| 严格模式 | true | 模型必须完全按 schema 生成,字段名、类型、必填项、additionalProperties 都严格遵守 |
| 非严格模式 | false | 模型尽力符合 schema,但不保证完全一致,可能缺少字段或添加额外字段 |
推荐:生产环境使用 strict: true,这是 JSON Schema 模式的核心价值。非严格模式的行为接近 JSON mode + prompt 说明,意义不大。
schema 定义规范
Section titled “schema 定义规范”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 | 字段说明,帮助模型理解语义 |
4. Schema 定义指南
Section titled “4. Schema 定义指南”{ "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 不是必填,但强烈建议加上。它帮助模型理解字段语义、取值范围和格式约定,能显著提高生成准确度。
5. Schema 示例
Section titled “5. Schema 示例”用户信息提取
Section titled “用户信息提取”从非结构化文本中提取用户信息:
{ "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 }}商品列表生成
Section titled “商品列表生成”让模型生成一个商品数组:
{ "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 }}复杂嵌套对象
Section titled “复杂嵌套对象”订单信息提取,包含多层嵌套:
{ "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 }}6. 各模型支持情况
Section titled “6. 各模型支持情况”支持 JSON mode 的模型
Section titled “支持 JSON mode 的模型”大多数 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 的模型
Section titled “支持 JSON Schema 的模型”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 字段。
模型能力对比表
Section titled “模型能力对比表”| 能力维度 | JSON mode | JSON Schema | 说明 |
|---|---|---|---|
| 支持模型范围 | 广泛(几乎所有主流模型) | 有限(新一代模型) | JSON mode 支持度更高 |
| schema 复杂度 | 不适用 | 建议不超过 3 层嵌套 | 过深影响生成质量 |
| 最大字段数 | 不适用 | 建议不超过 50 个顶层字段 | 字段过多影响性能 |
| 严格保证 | 仅保证合法 JSON | 保证符合 schema | 严格模式下 100% 符合 |
| 性能 | 快 | 相对较慢 | schema 校验有额外开销 |
| Token 消耗 | 低 | 稍高 | schema 定义会占用 prompt tokens |
限制和注意事项
Section titled “限制和注意事项”-
schema 大小限制:单个 schema 定义建议不超过 10KB,过大的 schema 可能被截断或拒绝。
-
嵌套深度限制:建议嵌套层数不超过 3-4 层,过深的嵌套会降低模型生成质量和速度。
-
性能影响:JSON Schema 模式的响应时间通常比普通请求慢 10%-30%,因为模型需要在生成过程中实时校验结构。
-
不支持的 schema 特性:部分高级 JSON Schema 特性(如
$ref、allOf、anyOf、oneOf、正则表达式)可能不被所有模型支持。 -
流式输出:JSON Schema 模式支持流式输出(
stream: true),但content是逐片段返回的,完整 JSON 需要拼接后再解析。
7. 完整应用示例
Section titled “7. 完整应用示例”示例 1:从非结构化文本提取结构化信息
Section titled “示例 1:从非结构化文本提取结构化信息”场景:从客服对话中提取客户信息和问题分类。
curl:
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 osimport jsonfrom 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}`);示例 2:生成表单数据
Section titled “示例 2:生成表单数据”场景:让模型根据自然语言描述生成表单初始值。
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}示例 3:API 响应解析
Section titled “示例 3:API 响应解析”场景:让模型从第三方 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: '北京市分拨中心' }8. 错误处理
Section titled “8. 错误处理”Schema 验证失败
Section titled “Schema 验证失败”如果你的 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 的输出
Section titled “模型无法生成符合 schema 的输出”极少数情况下,模型可能无法生成符合 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) # 指数退避9. 最佳实践
Section titled “9. 最佳实践”Schema 设计原则
Section titled “Schema 设计原则”-
从简单开始:先用 JSON mode 验证可行性,确认需要严格校验后再升级到 JSON Schema。
-
明确必填和可选:必填字段放
required数组,可选字段用["type", "null"]或干脆不放required。 -
用 enum 限定枚举:凡是取值有限的字段(状态、分类、优先级),都用
enum显式列出,能大幅降低错误率。 -
加 description:每个字段都加
description,说明含义、格式、取值范围,模型据此生成更准确。 -
设置 additionalProperties: false:严格模式下,加这个能防止模型输出未定义的额外字段。
避免过于复杂的 schema
Section titled “避免过于复杂的 schema”| 问题 | 说明 | 建议 |
|---|---|---|
| 嵌套过深 | 超过 4 层嵌套会降低生成质量 | 拆分为多个扁平对象,或用多轮对话分步提取 |
| 字段过多 | 单个对象超过 50 个字段 | 按业务逻辑分组,拆分为多个子对象 |
| 过度约束 | 所有字段都是必填,没有灵活性 | 只把核心字段标为必填,其他字段允许 null |
| 无 description | 模型不理解字段语义 | 每个字段都加 description,说明清楚 |
-
减少 schema 大小:schema 定义会占用 prompt tokens,过大的 schema 会增加延迟和成本。
-
缓存 schema 定义:同一个 schema 反复使用时,在代码中定义为常量,避免每次请求都重新构造。
-
选择合适的模型:不是所有任务都需要最强的模型,简单的结构化提取可以用 gpt-3.5-turbo + JSON mode。
-
批量处理:如果有多个相似任务,可以设计一个数组 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%。