跳到主要内容
版本:1.6.0

Responses API 代理

Responses API 是 OpenAI 面向使用输入/输出项格式而不是 Chat Completions 消息格式的应用提供的响应生成端点。AISIX AI 网关为 Responses API 客户端暴露该路由,同时将调用方认证、模型别名、上游凭证和网关策略保留在网关中。

当应用或工具已经使用 Responses API 时,请使用该路由。当模型的上游自身提供 Responses API 时,AISIX 会把请求转发过去;否则 AISIX 会通过服务提供方适配器转换请求,并向调用方返回 Responses API 结果。

准备工作​

请先准备以下内容:

  • 一个可以处理代理请求的 AISIX 网关。
  • 一个可以访问该模型别名的调用方 API Key。
  • 一个由原生提供 Responses API 或支持转换后请求形态的上游支撑的模型别名。

导出网关连接和请求值:

# AISIX_PROXY 不含尾部斜杠或 /v1 等端点路径
# 本地快速入门使用 http://127.0.0.1:3000。
export AISIX_PROXY="YOUR_AISIX_GATEWAY_URL"
export AISIX_API_KEY="YOUR_CALLER_API_KEY"
export AISIX_MODEL="gpt-4o-prod"

发送 Responses 请求​

通过网关代理发送请求,并在请求体中使用 AISIX 模型别名:

curl -sS -X POST "${AISIX_PROXY}/v1/responses" \
-H "Authorization: Bearer ${AISIX_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"model": "'"${AISIX_MODEL}"'",
"input": "Say hello from AISIX."
}'

AISIX 会解析模型别名,并为选中模型选择服务提供方路径。当服务提供方密钥选择原生 Responses 协议面时,AISIX 会直接转发请求,不转换请求体;否则会使用跨服务提供方桥接。

响应体会以 Responses API 格式返回:

{
"id": "resp_***",
"object": "response",
"model": "gpt-4o-prod",
"output": [
{
"type": "message",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "Hello from AISIX."
}
]
}
],
"usage": {
"input_tokens": 12,
"output_tokens": 5,
"total_tokens": 17
}
}

示例只列出了本页讨论的字段。在桥接路径上,具备推理能力的上游会在该 message item 之前先返回一个 reasoning item,Response 对象也会携带 Responses API 定义的全部顶层字段。参见桥接路径返回什么。

服务提供方行为​

AISIX 通过两种方式处理 Responses 请求:

上游行为
提供 Responses APIAISIX 将请求 model 改写为上游模型 ID,并将请求转发到上游 Responses API。当上游支持时,上游专属 Responses 能力会透传。
不提供AISIX 将受支持 Responses 字段转换为网关聊天格式,通过服务提供方适配器调度,并返回 Responses API 结果。

走哪一条按服务提供方密钥逐个决定。没有协议面声明时,AISIX 对 openai 服务提供方原生转发,对其它服务提供方一律转换。这个默认判断对某些端点在两个方向上都会出错:通过 openai 服务提供方 + 自定义 api_base 接入的 OpenAI 兼容端点可能根本没有 /v1/responses,而其它服务提供方的端点反而可能有。声明该密钥的 API 协议面即可说明属于哪一种,见声明 API 协议面。

桥接路径支持常见的文本、工具调用、工具结果、采样和流式字段,以及下文所述的消息 part、工具参数和结构化输出格式。它会把 reasoning.effort 带入规范化聊天请求的 reasoning_effort;支持推理力度控制的服务提供方适配器再将其转换为对应的线上字段。例如,Anthropic 收到的是 output_config.effort;对于拒绝该字段的 Claude 系列,则收到思考预算,参见旧版 Claude 模型的推理力度。你还可以通过推理力度映射按直连模型重写该值。聊天格式无法表达的 Responses 能力会被丢弃,而不是原样平铺到上游请求体上,这样请求仍能到达服务提供方而不会被其拒绝;属于这一类的字段见经过转换的请求会忽略哪些字段。

消息 part​

除文本 part 外,桥接路径还会把消息中的非文本 part 转换为对应的聊天内容块:

Responses part聊天内容块
input_imageimage_url,图片以 URL 或 data: URL 寻址;调用方给出 detail 时一并带上
input_filefile,携带调用方发送的 file_data、filename、file_id 中的相应字段
input_audioinput_audio,携带 data 和 format

只要某个消息槽产生了任意非文本块,其 content 就以数组形式发送;纯文本槽仍保持原有的裸字符串形态。

工具参数​

  • 只要有至少一个工具在转换后保留下来,parallel_tool_calls 就按调用方发送的值转发。
  • 每一种 tool_choice 形态都会归一化为服务提供方中立的聊天形态。auto、none、required 和 {"type": "function", "name": ...} 保持不变;{"type": "any"} 变为 required;{"type": "custom", "name": ...} 和 {"type": "tool", "name": ...} 变为同名函数;{"type": "allowed_tools", "mode": ...} 只保留其 mode,它列出的工具子集会被丢弃——聊天上游无法被限制为只能使用所给工具的某个子集。
  • custom(自由格式)工具会转换为只接受一个必填字符串参数的函数工具,该工具的语法规则折叠进这个参数的描述中——那是聊天上游唯一会读到它的地方。
  • namespace 工具(例如 Codex 发送的 multi_agent_v1)会被展平:其中每个 function 子工具都以名为 <namespace>__<tool> 的函数工具提供给模型,描述为 namespace 的描述后接子工具自己的描述。展平后的名称与某个顶层函数工具重名的子工具不会提供给模型。携带 namespace 的回放 function_call,以及用 namespace 指明某个子工具的 tool_choice,都以展平后的名称发往上游。
  • 当工具结果的 output 是 JSON 对象、数字或布尔值时,它会被序列化为字符串后发给上游,因为聊天的 tool 消息只能携带字符串。null 和缺省的 output 仍为空字符串。

当转换后没有任何工具保留下来时(例如携带 "tools": [] 的上下文压缩请求,或只携带托管工具的请求),tool_choice 和 parallel_tool_calls 都会被丢弃,强制调用工具的形态同样如此。没有 tools 列表时上游会拒绝这两个字段,因此它们都不会被发出,该请求按普通的无工具请求继续处理。

结构化输出​

text.format 会转换为聊天的 response_format:json_schema 格式变为 {"type": "json_schema", "json_schema": {...}},携带调用方发送的 name、schema、strict、description 中的相应成员;json_object 直接对应;text 格式或完全没有 text 成员时不发送任何内容。

该字段由 OpenAI 兼容的聊天上游支持,Anthropic、Gemini 和 Bedrock 上游同样支持,它们各有自己的结构化输出字段。这几种映射在模型支持范围以及服务提供方接受的 schema 内容上各不相同:Anthropic 参见结构化输出,Gemini 参见 Gemini 上的结构化输出,Bedrock 参见结构化输出。

回放的对话历史​

Codex 等客户端每一轮都会重新发送整段对话,其中包括模型之前的推理内容和工具调用。桥接路径按以下方式由这些 item 重建聊天消息:

  • 推理 item。 reasoning 输入 item 会以 reasoning_content 的形式发往上游,挂在它所属的那条 assistant 消息上。文本取自该 item 的 content part;content 不含文本时改取 summary part。encrypted_content 从不读取,没有可读文本的 item 不回放任何内容。连续的多个推理 item 以换行拼接。这些推理内容以前会被丢弃,因此回放推理的多轮请求现在发往上游的输入 Token 会增加。
  • 之后没有回答的推理。 后面没有 assistant 内容跟随的推理,会作为一条只携带 reasoning_content 的独立 assistant 消息发送。经 anthropic、bedrock、vertex 适配器到达的上游没有承载回放推理的字段:assistant 消息同时带有文本或工具调用时,它们忽略其中的推理;assistant 消息只有 reasoning_content 时,它们跳过整条消息。
  • assistant 文本后接工具调用。 assistant message item 与紧随其后的 function_call 或 custom_tool_call item 会合并成一条同时携带 content 和 tool_calls 的 assistant 消息发送,与模型产生它们时的形态一致,而不再是两条相邻的 assistant 消息。

reasoning_content 会发给所有 OpenAI 兼容上游。如果某个 OpenAI 兼容上游的请求 schema 拒绝未知的消息字段,它会拒绝回放了推理的请求;目前没有关闭回放的配置项。

桥接路径返回什么​

当所选模型使用 anthropic 适配器,或是 Vertex AI 上的 Claude 模型时,非流式和流式路径都会把 Claude 的 tool_use 块转换为 function_call item。流式响应中,每个块按到达顺序各成为一个 function_call item,其参数 delta 作为该 item 的参数流式返回。未流式返回任何参数的调用,其参数为 "{}"。

Response 对象。 桥接路径发出的每个 Response——非流式响应体,以及每个流式生命周期事件(response.created、response.in_progress、response.completed、response.incomplete、response.failed)中的 response——都携带 Responses API 定义的全部顶层字段,包括 tools、tool_choice、parallel_tool_calls、text、reasoning、instructions、temperature、top_p、max_output_tokens、truncation、store、metadata、completed_at、error、incomplete_details 和 usage。请求设置了的字段回显请求中的值,否则取 API 的默认值或 null。在回显的 tools 中,缺少 description、parameters 或 strict 的函数工具会把缺少的字段填为 null;其他类型的工具按发送时的样子回显。原生 Responses 路径则原样返回上游的结果。

桥接路径上有些输出 item 并非来自会说 Responses 的上游,而是 AISIX 根据聊天响应构造出来的。

推理内容。 当聊天上游以 reasoning_content 上报其思维链时,AISIX 会把它作为一个 reasoning 输出 item 返回,位置在 message item 之前。非流式响应中,该 item 以 summary[{"type": "summary_text"}] 携带文本。流式响应中,它依次表现为 response.output_item.added、response.reasoning_summary_part.added、一个或多个 response.reasoning_summary_text.delta,以及相应的 done 事件;message item 在下一个 output_index 上开启。

自定义工具调用。 当模型调用经桥接转换的 custom 工具时,返回的是携带自由格式文本的 custom_tool_call item(字段为 input),而不是 function_call item:请求方向包裹它的那个字符串参数会被重新解开。流式响应中,该 item 只包含一个携带该输入的 response.custom_tool_call_input.delta,随后是 response.custom_tool_call_input.done。

namespace 工具调用。 当模型调用展平后的 namespace 子工具时,返回的是 function_call item,其 name 为子工具自己的名称,并携带它所属的 namespace,非流式和流式路径都是如此。只对应唯一一个子工具的裸子工具名也会按同样方式映射回来。

桥接路径上的流失败​

桥接流开始之后如果失败,AISIX 会先发送扁平的 error 事件,再发送 response.failed 事件。response.failed 携带一个完整的 Response 对象,其中 status: "failed"、output: []、usage: null,以及 error: {"code": ..., "message": ...}。如果终止事件已经发送到客户端,则不再发送 response.failed。如果失败是客户端将收到的第一个内容,AISIX 会先打开流:先发送 response.created 和 response.in_progress,两者都携带 status: "in_progress"、output 为空的 Response 对象,然后发送 error 和 response.failed,sequence_number 依次为 0 到 3。这适用于上游在第一个 chunk 之前失败、上游返回空流,以及输出安全护栏拦截被暂存的响应或响应超出其缓冲区。在暂存的情况下,由于被扣留的事件从未发送,编号从 0 开始。因此,要求首个事件为 response.created 的流式辅助方法(例如 OpenAI Python SDK 的 responses.stream())会报告真实的失败。已经发送过 response.created 的流不会再收到第二个。

response.failed 中的 error.code 取决于流停止的原因:

原因response.failed 中的 error.code
输出安全护栏阻断了响应,或暂扣的响应超出安全护栏的缓冲区invalid_prompt
上游在流内发送的错误码为 context_length_exceeded、insufficient_quota、rate_limit_exceeded、server_is_overloaded 或 slow_down原样透传该错误码
其他上游失败与 error 事件相同的错误码,例如 upstream_error

客户端依据这个错误码决定是否重试。例如,Codex 会显示错误消息,并且不会重试以 invalid_prompt 失败的轮次,因为重发只会再次被同一个安全护栏拦下。

有两种流结束方式会被特殊处理:

  • 结束后连接断开。 如果上游已经发送了 finish_reason,随后在用量 chunk 或 [DONE] 到达之前连接断开,响应会正常完成。用量 chunk 始终没有到达时,用量为估算值。
  • 上游返回空流。 流结束时没有产生任何内容、推理、工具调用或 finish_reason(例如只有一个 [DONE],或只有用量 chunk),会以 error 事件和 response.failed 事件失败,两者的错误码都是 upstream_error,消息说明上游返回了空流。该错误码可重试。产生了内容之后没有 finish_reason 就结束的流,仍然正常完成。

经过转换的请求会忽略哪些字段​

  • reasoning 中除 effort 外的成员,例如 summary
  • text.verbosity
  • store
  • previous_response_id
  • metadata、service_tier 以及其它 OpenAI 专属控制项
  • web_search、file_search、code_interpreter、mcp、computer_use、image_generation 等托管工具

custom 工具已不在此列,它会按工具参数所述进行转换。

还有两项限制,原因是聊天格式没有对应表达:

  • 只以 file_id 寻址的 input_image 不会被转发。聊天的图片 part 只能以 URL 或 data: URL 寻址。
  • 工具结果内部的非文本 part 不会被转发。聊天的 tool 角色只支持文本,OpenAI 兼容上游会拒绝其中的图片。

策略与用量行为​

输入安全护栏可以在 AISIX 调用服务提供方之前检查请求文本。输出安全护栏可以在非流式响应到达调用方之前检查响应内容。

当客户端重放会话历史时,安全护栏可以把输入窗口限制到最新一轮。AISIX 使用 Responses item 类型识别 assistant 轮次和当前轮次的工具结果。参见选择输入消息窗口。

在桥接路径上,模型生成的推理内容不在输出安全护栏的筛查和脱敏范围内,这与逐字转发路径一致。参见推理与思考内容。

如果输出安全护栏阻断响应,AISIX 会向调用方返回内容策略错误,并记录该阻断请求以便观测。在桥接流上,阻断会按桥接路径上的流失败所述结束该流。

对于流式请求,AISIX 会保持 Responses SSE 形态。原生 Responses 目标可以直接透传上游 SSE;桥接目标则由 AISIX 把服务提供方流式分片编码为 Responses 事件。如果启用了输出安全护栏,AISIX 会先缓冲流内容进行策略检查,再决定返回或阻断。缓冲区中 AISIX 无法解析的帧会被丢弃,而不是未经扫描就放行;如果因此没有任何内容可返回,响应会被以 422 拒绝。参见 AISIX 无法扫描的帧。

对于成功响应,当上游响应包含 Token 用量时,网关会记录用量。流式用量会在 AISIX 从流中收到终止用量信息后发出。

当上游没有上报 Token 用量时,AISIX 会在本地估算计数,并把同一组数字返回给调用方:非流式响应放在 usage 中,流式响应放在 response.completed 事件的 usage 中。上游确实上报过的计数保持不变。响应中没有任何标识说明某个计数是估算出来的,用量记录上的 usage_estimated 字段才是判断依据。参见用量上报。

对于 Token 计数方式与 OpenAI 不同的桥接服务提供方,AISIX 会将计数转换为 Responses API 的形式:input_tokens 是完整输入,input_tokens_details.cached_tokens 是其中缓存读取的子集,input_tokens_details.cache_creation_tokens 是缓存写入的子集(仅在上游报告了写入时出现),total_tokens 等于 input_tokens + output_tokens。以 Anthropic 上游为例的完整说明,请参阅 Token 用量——那里用的是 Chat Completions 的字段名,与这里一一对应(prompt_tokens 对应 input_tokens,completion_tokens 对应 output_tokens,prompt_tokens_details 对应 input_tokens_details,completion_tokens_details 对应 output_tokens_details)。有一处形态差异:output_tokens_details.reasoning_tokens 在 Responses 响应中恒存在,包括值为 0 时;而 Chat Completions 会整块省略。

下一步​

你已经了解何时通过 AISIX 使用 Responses API,以及直接转发和桥接时的服务提供方处理差异。旧版 completions 路由请继续阅读文本补全;如果 Responses API 客户端依赖 SSE 行为,请继续阅读流式响应。