Responses 格式
创建一个模型响应:用 input 传入文本或一组输入项,返回带 id 的响应对象,输出放在 output 数组里,按项区分消息、函数调用和推理内容,支持流式。兼容 OpenAI Responses API:OpenAI SDK 的 client.responses 和使用 Responses 协议的客户端(例如 Codex CLI)把 Base URL 设为 https://api.lyrouter.com/v1 即可使用,与 Chat Completions 相同。网关按 Responses 协议转发请求、不做协议转换,所选模型和服务它的通道都必须支持 Responses。
/v1/responses鉴权
在请求头中携带 API Key:
Authorization: Bearer <API Key>
Bearer 区分大小写。也可以改用 x-api-key: <API Key>,两者同时存在时只读 Authorization。API Key 在工作空间的 API 密钥 页面创建,只用于 LYRouter 鉴权,不会转发给上游服务商。完整说明见接入地址与鉴权。
请求头
| 请求头 | 必填 | 说明 |
|---|---|---|
Content-Type |
否 | 建议携带 application/json。不带时网关按 JSON 解析请求体;写成其他值(如 application/x-www-form-urlencoded)时返回 400 |
请求体
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
model |
string | 否 | 模型 ID,也可以写 @vmodel/<名称>(虚拟模型)或 lyrouter/auto(Auto 智能路由)。省略、为 "" 或 null 时使用工作空间的默认模型,没有默认模型时返回 400 |
input |
string / array | 是 | 字符串等同于一条用户消息;数组按顺序放入消息项和其他输入项,见下文「input 数组中的项」 |
instructions |
string | 否 | 系统级指令。使用虚拟模型时,虚拟模型的系统提示词追加在这段指令之后 |
stream |
boolean | 否 | true 时以 SSE 事件流返回,见下文「流式响应」 |
tools |
array | 否 | 函数工具写作 {"type": "function", "name", "description", "parameters", "strict"},字段平铺,不像 Chat 那样包在 function 里。内置工具(如 web_search)原样转发,是否可用取决于上游 |
tool_choice |
string / object | 否 | auto、none、required,或 {"type": "function", "name": "..."} |
parallel_tool_calls |
boolean | 否 | 是否允许一次返回多个函数调用 |
max_output_tokens |
integer | 否 | 输出上限,包含推理 Token。请使用这个字段;Chat 的 max_tokens 会原样转发,是否生效由上游决定 |
reasoning |
object | 否 | 例如 {"effort": "low", "summary": "auto"}。透传给上游,是否生效取决于模型 |
text.format |
object | 否 | 输出格式:{"type": "text"}、{"type": "json_object"},或 {"type": "json_schema", "name": "...", "schema": {...}, "strict": true}。效果取决于上游:例如有的上游在 json_object 时仍把 JSON 包在 Markdown 的 json 代码块里返回,在 json_schema 时不按 schema 输出(响应回显的 text.format 中字段名为 schema_)。依赖结构化输出前请先用所选模型验证,解析前去掉代码块标记 |
temperature、top_p |
number | 否 | 透传给上游,推理模型通常不支持 |
store |
boolean | 否 | 是否让上游保存这次响应。网关原样转发,不填时按上游默认(OpenAI 默认为 true) |
previous_response_id |
string | 否 | 接续上一轮响应,由上游拼接上下文,见下文「多轮对话与函数调用」 |
include |
array | 否 | 附加返回内容,例如 ["reasoning.encrypted_content"]。透传 |
metadata |
object | 否 | 透传 |
input 数组中的项
| 项 | 写法 | 用途 |
|---|---|---|
| 消息 | {"role": "user", "content": "..."}。role 可为 user、assistant、system、developer;content 可以是字符串,也可以是 input_text、input_image 等内容部分的数组 |
传入对话内容。图片输入需要模型支持,见多模态能力 |
| 函数调用结果 | {"type": "function_call_output", "call_id": "...", "output": "..."} |
把工具执行结果交回模型 |
| 上一轮的输出项 | 上一次响应 output 中的 message、function_call、reasoning 项,原样放回 |
不使用 previous_response_id 时,由客户端自己携带历史 |
请求示例
curl https://api.lyrouter.com/v1/responses \
-H "Authorization: Bearer $LYROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "MODEL_ID",
"instructions": "你是一个简洁的助手。",
"input": "用一句话解释什么是 API 网关。",
"max_output_tokens": 1024
}'
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["LYROUTER_API_KEY"],
base_url="https://api.lyrouter.com/v1",
)
response = client.responses.create(
model="MODEL_ID",
instructions="你是一个简洁的助手。",
input="用一句话解释什么是 API 网关。",
max_output_tokens=1024,
)
print(response.output_text)
print(response.usage)
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.LYROUTER_API_KEY,
baseURL: "https://api.lyrouter.com/v1",
});
const response = await client.responses.create({
model: "MODEL_ID",
instructions: "你是一个简洁的助手。",
input: "用一句话解释什么是 API 网关。",
max_output_tokens: 1024,
});
console.log(response.output_text);
console.log(response.usage);
将 MODEL_ID 替换为工作空间中已开通、且支持 Responses 的模型 ID。output_text 是 SDK 提供的便捷属性,会把所有 output_text 内容拼接起来;原始 JSON 中没有这个字段,直接解析 HTTP 响应时请遍历 output。Python 示例需要 openai Python SDK 1.66 或更高版本,旧版本没有 client.responses,会报 AttributeError: 'OpenAI' object has no attribute 'responses'。
响应
非流式请求返回一个 response 对象;出错时返回 error 对象。下面的 200 是带推理摘要的典型响应,400 是所选模型不支持 Responses,403 是工作空间没有开通这个模型。
{
"id": "resp_68ee1b9a52c48190a71c3e7d6a0b2f5e",
"object": "response",
"created_at": 1760427600,
"status": "completed",
"error": null,
"incomplete_details": null,
"model": "gpt-5.5",
"instructions": "你是一个简洁的助手。",
"output": [
{
"id": "rs_68ee1b9ab1d08190b3a2c5e7f9013d4a",
"type": "reasoning",
"summary": [
{ "type": "summary_text", "text": "用户要一句话定义,直接给出结论。" }
]
},
{
"id": "msg_68ee1b9b0e7c8190b9f2d4a6c8e01a37",
"type": "message",
"status": "completed",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "API 网关是位于客户端与后端服务之间的统一入口,负责鉴权、路由、限流和监控。",
"annotations": []
}
]
}
],
"max_output_tokens": 1024,
"previous_response_id": null,
"reasoning": { "effort": "low", "summary": "auto" },
"store": true,
"text": { "format": { "type": "text" } },
"tool_choice": "auto",
"tools": [],
"usage": {
"input_tokens": 1830,
"input_tokens_details": { "cached_tokens": 1536 },
"output_tokens": 196,
"output_tokens_details": { "reasoning_tokens": 128 },
"total_tokens": 2026
},
"metadata": {}
}
{
"error": {
"message": "This model does not support the responses API",
"type": "invalid_request_error",
"code": "unsupported_model_api"
}
}
{
"error": {
"message": "Model is not available in the model plaza",
"type": "access_denied",
"code": "model_not_available"
}
}
| 字段 | 说明 |
|---|---|
id |
响应 ID,由上游生成,常见为 resp_ 开头(也有上游返回 resp- 开头的 ID),不是 LYRouter 的 Request ID。用于 previous_response_id |
status |
completed;输出达到 max_output_tokens 时为 incomplete,原因在 incomplete_details.reason;还可能是 failed、cancelled,后台执行时有 queued、in_progress |
model |
上游返回的实际模型名,可能与请求中的模型 ID 不同。对应日志的「模型(实际)」 |
output |
输出项数组,按 type 分别处理:message 的 content 里是 output_text;function_call 是函数调用;reasoning 是推理摘要或加密的推理内容。不能按 Chat 的 choices[0].message 解析 |
usage |
本次 Token 用量,网关原样返回上游的值,见下表 |
usage 与日志中的四项 Token
usage 与请求日志「Token 消耗」四项的对应关系:
usage 字段 |
含义 | 日志中的对应项 |
|---|---|---|
input_tokens |
全部输入,包含缓存命中的部分 | 「输入(缓存未命中)」+「输入(缓存命中)」 |
input_tokens_details.cached_tokens |
命中上游提示词缓存的输入 | 「输入(缓存命中)」 |
output_tokens |
全部输出,包含推理 Token | 「输出」 |
output_tokens_details.reasoning_tokens |
输出中用于推理的部分 | 包含在「输出」中 |
上例中「输入(缓存未命中)」为 1830 − 1536 = 294,「输入(缓存命中)」为 1536,「输出」为 196;Responses 上游不产生「显式缓存创建」。计费方法见调用费用与缓存计费。
流式响应
设置 "stream": true 后,响应是 text/event-stream。每个事件由 event: 行和 data: 行组成,data 中的 type 与事件名相同,sequence_number 递增。实际的流中每个事件前还有一行 id:,event:、data: 的冒号后也可能没有空格(如 data:{...}),按 SSE 规范两种写法都合法,自己写解析器时要兼容。以下片段省略了 id: 行和 response 对象中的部分字段:
event: response.created
data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_68ee1b9a52c48190a71c3e7d6a0b2f5e","object":"response","status":"queued","output":[],"usage":null}}
event: response.output_item.added
data: {"type":"response.output_item.added","sequence_number":2,"output_index":0,"item":{"id":"msg_68ee1b9b0e7c8190b9f2d4a6c8e01a37","type":"message","status":"in_progress","role":"assistant","content":[]}}
event: response.output_text.delta
data: {"type":"response.output_text.delta","sequence_number":4,"item_id":"msg_68ee1b9b0e7c8190b9f2d4a6c8e01a37","output_index":0,"content_index":0,"delta":"API 网关是"}
event: response.output_text.done
data: {"type":"response.output_text.done","sequence_number":9,"item_id":"msg_68ee1b9b0e7c8190b9f2d4a6c8e01a37","output_index":0,"content_index":0,"text":"API 网关是位于客户端与后端服务之间的统一入口,负责鉴权、路由、限流和监控。"}
event: response.completed
data: {"type":"response.completed","sequence_number":12,"response":{"id":"resp_68ee1b9a52c48190a71c3e7d6a0b2f5e","object":"response","status":"completed","usage":{"input_tokens":1830,"input_tokens_details":{"cached_tokens":1536},"output_tokens":196,"output_tokens_details":{"reasoning_tokens":128},"total_tokens":2026}}}
| 事件 | 含义 |
|---|---|
response.created、response.in_progress |
响应已创建、正在生成。response.created 中的 status 可能是 queued |
response.output_item.added / .done |
一个输出项(消息、函数调用、推理)开始 / 结束 |
response.content_part.added / .done |
消息中的一个内容块开始 / 结束 |
response.output_text.delta / .done |
文本增量 / 完整文本 |
response.reasoning_text.delta / .done |
推理文本增量 / 完整推理文本,推理模型会返回 |
response.function_call_arguments.delta / .done |
函数调用参数增量 / 完整参数 |
response.completed |
正常结束,response.usage 是本次用量 |
response.incomplete、response.failed |
输出被截断或生成失败,同样携带完整的 response 对象 |
- Responses 流没有
data: [DONE],以response.completed(或response.incomplete、response.failed)作为结束。网关不会给 Responses 请求加stream_options,用量只在结束事件里。 - 输出开始后上游中断时,网关在流末尾补一个
response.failed事件后结束:response.status为failed,response.error.code为server_error,response.id是这条流的响应 ID,不再有response.completed。没有收到结束事件就按失败处理,见超时与流式中断。
curl -N https://api.lyrouter.com/v1/responses \
-H "Authorization: Bearer $LYROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"MODEL_ID","input":"写一首两行的小诗","stream":true}'
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["LYROUTER_API_KEY"],
base_url="https://api.lyrouter.com/v1",
)
stream = client.responses.create(
model="MODEL_ID",
input="写一首两行的小诗",
stream=True,
)
for event in stream:
if event.type == "response.output_text.delta":
print(event.delta, end="", flush=True)
elif event.type == "response.completed":
print("\n", event.response.usage)
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.LYROUTER_API_KEY,
baseURL: "https://api.lyrouter.com/v1",
});
const stream = await client.responses.create({
model: "MODEL_ID",
input: "写一首两行的小诗",
stream: true,
});
for await (const event of stream) {
if (event.type === "response.output_text.delta") {
process.stdout.write(event.delta);
} else if (event.type === "response.completed") {
console.log("\n", event.response.usage);
}
}
各协议流式格式的对比见流式响应。
多轮对话与函数调用
多轮对话有两种写法:
previous_response_id:把上一轮响应的id填进下一次请求,只发送新的输入,由上游保存并拼接上下文。需要上游支持,且上一轮没有设置store: false。- 自带历史:每次在
input中带上完整历史,把上一轮output中的项原样追加进去,再加新的输入。这种写法不依赖上游保存的状态,模型有多个通道或凭证时更稳妥。不需要上游保存时可以设置store: false;此时要回传推理项,请同时设置"include": ["reasoning.encrypted_content"]。
网关用 previous_response_id 把同一条接续链的请求归为一个会话,可以在请求日志的「会话」列中查看。网关不会把接续请求固定到创建上一轮响应的那个上游:模型有多个通道或凭证时,接续请求可能落到没有保存这条响应的上游,由上游返回找不到响应的错误。
函数调用的完整流程如下。第一次请求声明工具:
{
"model": "MODEL_ID",
"input": "上海现在天气怎么样?",
"tools": [
{
"type": "function",
"name": "get_weather",
"description": "查询城市的实时天气",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"],
"additionalProperties": false
},
"strict": true
}
]
}
模型决定调用工具时,output 中出现 function_call 项,arguments 是 JSON 字符串:
{
"id": "fc_68ee1c20a3f88190c2d4e6f8a0b1c3d5",
"type": "function_call",
"status": "completed",
"call_id": "call_k3Jw9Xq2mZ7tVb1R",
"name": "get_weather",
"arguments": "{\"city\":\"上海\"}"
}
在客户端执行工具,再把结果以 function_call_output 交回,call_id 必须与调用一致。下面用自带历史的写法,同时带上原来的 tools:
{
"model": "MODEL_ID",
"input": [
{ "role": "user", "content": "上海现在天气怎么样?" },
{
"type": "function_call",
"call_id": "call_k3Jw9Xq2mZ7tVb1R",
"name": "get_weather",
"arguments": "{\"city\":\"上海\"}"
},
{
"type": "function_call_output",
"call_id": "call_k3Jw9Xq2mZ7tVb1R",
"output": "{\"temp_c\":22,\"condition\":\"多云\"}"
}
],
"tools": ["……与第一次请求相同……"]
}
示例中 tools 的值为省略写法,实际请求要放入完整的工具定义。与 Chat Completions 的写法对比见工具调用。
网关行为
- 不做协议转换:网关按 Responses 协议把请求转发到上游的
/responses,不会把它转换成 Chat Completions 或 Anthropic Messages,所以模型和服务它的通道都必须原生支持 Responses。模型声明了支持的接口、且其中没有 Responses 时,网关直接返回 400This model does not support the responses API(unsupported_model_api);没有声明的模型会照常转发,由上游返回错误。 - 请求基本原样转发:网关只做以下改动:把
model换成所选通道的实际模型名;使用虚拟模型时合并参数和系统提示词;把input中以at_开头的项 ID 改为以msg_开头,以兼容要求msg_前缀的上游;重新排列请求 JSON 中对象的键顺序(数组顺序不变),让相同前缀稳定命中上游缓存。store、previous_response_id、background、include和内置工具都原样转发。 - 虚拟模型:虚拟模型的系统提示词在
instructions为空时直接填入,已有内容时空一行后追加;参数按 Chat Completions 的字段名合并,max_tokens不会换成max_output_tokens,见虚拟模型。 - 用量:
usage原样返回上游的值,网关不补全、不改写。 - 失败回退:开始输出之前上游返回错误或连接失败时,网关按失败重试与备用模型换通道,必要时改用备用模型和工作空间默认模型。接手的模型同样要支持 Responses,否则返回上面的
unsupported_model_api。流式输出开始后不再换通道。 - Codex CLI:在 Codex 中配置
wire_api = "responses"就是调用本接口,模型和通道还需要支持函数工具。配置方法见 Codex。 - 配置了配额时,响应带
x-aigw-quota-*响应头,见网关扩展请求头与响应头。
常见错误
网关返回的错误为 {"error":{"message":"...","type":"...","code":"..."}},code 只有部分错误才有。上游原样透传的错误可能是其他形状,例如 previous_response_id 指向的响应不存在时,返回的是 400 {"request_id":"...","code":"InvalidParameter","message":"..."},外层没有 error,解析时请兼容。
| 状态码 | error.message |
error.code |
原因与处理 |
|---|---|---|---|
| 400 | This model does not support the responses API |
unsupported_model_api |
模型不支持 Responses。改用支持的模型,或改用 Chat Completions;Codex 中出现时同样换模型 |
| 400 | you must provide a model parameter |
missing_model |
请求没写 model,工作空间也没有默认模型。写上模型 ID |
| 403 | Model is not available in the model plaza |
model_not_available |
工作空间没有开通该模型,见模型不可用 |
| 503 | all routed candidates exhausted |
no_route_candidate |
找不到可服务这次请求的通道,例如 @vmodel/<名称> 写错、这个虚拟模型不存在。核对名称,见模型不可用 |
| 上游状态码 | 上游原文 | 上游原样 | 例如 previous_response_id 指向的响应在当前上游不存在。在日志「状态说明」中来源显示为「上游」 |
- 401、403 鉴权问题见401 / 403:鉴权与权限,429 见429:限流与配额。
- 500 / 504 是连接上游失败或等待上游超时,响应体是 HTML 页面而不是 JSON,见超时与流式中断。
- 完整列表见错误码与请求标识。网关的响应中不包含请求 ID,排查时请在请求日志中按 API Key 和时间找到这次请求,从日志详情顶部复制 Request ID。