很多 AI 应用已经围绕
/v1/chat/completions写好了对话、流式输出和工具调用。遇到优先使用或仅支持 Responses API 的上游模型时,是否必须立即重构所有客户端?本文从 API 网关的协议转换代码出发,介绍一种对业务侵入更小的迁移方式。
1. 一个容易被低估的接口升级问题
假设你的项目已经稳定运行了一段时间,客户端发送的是大家熟悉的 Chat Completions 请求:
后来,你准备接入的新模型需要使用 /v1/responses。虽然简单文本消息可以较平滑地迁移,但工具调用、结构化输出和流式响应仍存在明显差异:
messages需要转换或传递到input;system或developer消息可以保留在input中,也可以由兼容层汇总到instructions;max_tokens对应max_output_tokens;- 函数调用的结构不同;
- 流式事件不再直接是
chat.completion.chunk; - Token 用量字段也需要重新整理。
如果让每个前端、后端和第三方客户端分别升级,改造范围会迅速扩大。更麻烦的是,同一套业务可能还需要同时调用旧接口模型和新接口模型。
这类问题更适合在 API 网关层解决:客户端继续发送 Chat Completions,网关根据模型和渠道配置,将请求转换成 Responses API,再把响应转换回来。
2. 转换发生在哪里
完整链路可以概括为:
对于兼容层已覆盖的参数,客户端看到的入口和返回格式基本不变,协议升级被收敛在网关内部。不能无损映射的参数仍需明确报错或降级处理。
以 4SAPI 这类聚合中转服务为例,业务侧仍然可以使用 OpenAI SDK,只需要统一配置 base_url 和 API Key:
至于这个模型最终走 Chat Completions 还是 Responses API,可以交给网关配置决定。
3. 第一步:按渠道和模型决定是否转换
协议转换不应该对所有请求强制开启。更稳妥的做法是增加一层策略判断。项目中的核心配置结构可以简化表示为:
这套策略实际包含两层判断:
- 第一层是渠道范围:通过
all_channels、channel_ids或channel_types确定哪些渠道可以转换; - 第二层是模型正则:只有模型名命中
model_patterns,请求才真正进入转换流程。
两层条件需要同时成立。特别要注意:model_patterns 为空时不会匹配任何模型。一个只对指定渠道和 GPT-5 系列模型生效的配置示例如下:
当前实现使用客户端请求中的原始模型名匹配 model_patterns,而不是模型映射后的上游名称。例如客户端请求 my-gpt5,后台将其映射为 gpt-5,正则仍应匹配 my-gpt5。
请求进入网关后,会结合已选择的渠道和原始模型名判断是否需要转换:
此外,全局或当前渠道开启请求体原样透传时,网关不会进入这条 Chat → Responses 转换链路,因为“透传”和“改写请求体”本身就是互斥行为。
这种设计的价值不只是“多一个开关”。它允许团队先用测试渠道验证,再逐步扩大范围,避免一次升级影响全部线上模型。
4. 第二步:把 messages 变成 input
请求转换不能只做字段改名,因为不同角色和内容类型的语义也要保留。
system 与 developer 消息
Chat Completions 中常见的:
Responses API 可以接收消息形式的输入。为了让系统级指令与普通对话更清晰地分离,这套兼容实现选择将 system 和 developer 文本汇总到 instructions:
当请求里同时出现多条 system 或 developer 消息时,兼容层会按照原顺序拼接。需要注意,这是一种网关实现策略,并不是唯一合法的迁移方式。
普通文本消息
用户和助手的历史消息进入 input:
图片、音频与文件
当目标模型和上游接口支持相应模态时,消息可以按内容类型分别映射:
不同模型对图片、音频和文件的支持范围并不相同,兼容层完成格式转换不代表目标模型一定具备对应能力。这一层最好使用结构化 DTO 完成,不建议直接对 JSON 字符串做替换。否则一旦遇到嵌套内容、转义字符或空字段,很容易产生难以排查的请求错误。
5. 第三步:处理工具调用差异
工具调用是协议转换中最容易遗漏的一部分。
Chat Completions 的函数工具通常是嵌套结构:
转换为 Responses API 时,需要将函数信息提升到工具对象:
这里还有一个容易忽略的差异:Chat Completions 的函数工具默认不是严格模式,而 Responses API 的函数工具默认使用严格模式。兼容层应显式保留或设置 strict,并检查 JSON Schema 是否满足严格模式要求。只转换 name、description 和 parameters,并不一定能保证两边行为完全一致。
历史消息中的工具结果也需要通过 call_id 与原调用关联:
如果缺少 call_id,当前实现会将工具输出降级成带有标记的普通用户消息,避免构造一个无法关联的 function_call_output。这种降级虽然能继续请求,但语义已经改变,因此生产环境最好记录告警;如果业务强依赖工具调用完整性,也可以选择直接返回可读错误。
6. 第四步:把 Responses 响应还原给旧客户端
非流式响应相对直接:
- 读取 Responses API 返回体;
- 提取最终文本和工具调用;
- 组装为
choices[0].message; - 将输入、输出和总 Token 映射到 Chat Completions 的
usage。
最终客户端仍然收到熟悉的结构。下面为了突出关键字段,省略了 id、created 和 model 等内容:
如果上游没有返回完整用量,网关可以进行估算,但计费系统应区分“上游真实用量”和“本地估算用量”,避免将估算值当成精确数据。
7. 流式响应才是真正的难点
Chat Completions 的客户端通常期待连续收到:
Responses API 则会按事件类型发送内容增量、工具参数增量、完成状态和用量信息。网关需要维护一小段流状态:
- 是否已经发送 assistant 角色;
- 当前文本已经输出到哪里;
- 是否出现工具调用;
- 工具参数是否仍在追加;
- 是否已经发送结束标记;
- 最终是否拿到了 usage。
对于 response.output_text.delta 这类真正的增量事件,可以直接转发其中的新增文本;如果某些事件或上游返回的是累计快照,则要避免把已有内容重复发送。一个简单做法是记录上一次内容,只输出新增部分:
转换完成后,旧版前端仍可继续使用原来的 SSE 解析逻辑,不需要理解 Responses API 的事件类型。需要额外注意:如果流在结束前中断,最终用量事件可能无法到达,计费与日志逻辑必须准备好处理这种情况。
8. 上线前要验证的兼容边界
协议兼容不等于所有参数都能无损转换。上线前至少要测试:
- 普通文本的流式与非流式请求;
system、developer和多轮历史消息;- 图片、音频、文件等多模态输入;
- 单次和连续工具调用;
- JSON Schema 结构化输出;
- 函数工具的
strict和参数 Schema; max_tokens与max_completion_tokens;store、数据保留策略以及合规要求;- 上游错误码和错误体转换;
- 中途断流、超时及客户端主动取消;
- Token 用量与实际扣费是否一致。
还要明确不支持的参数。例如兼容层无法表达 n > 1 时,应在请求阶段直接返回说明,而不是请求成功后只保留第一条结果。
9. 为什么把兼容逻辑放在中转层
对于同时接入多家模型的应用,把协议转换集中到中转层有三个直接收益:
-
旧业务改动更少
已经接入 OpenAI SDK 的服务可以尽量保留原调用方式。 -
模型切换更灵活
同一客户端可以按模型路由到不同上游,不需要知道每个模型使用哪套接口。 -
治理能力更集中
鉴权、限流、日志脱敏、用量统计、错误映射和渠道切换都可以在同一层完成。
但它也不是“打开开关就永远兼容”。工具调用、流式事件和新参数仍然需要持续维护。使用第三方中转服务时,还应评估数据隐私、服务稳定性和业务合规要求。
10. 最小迁移方案
如果你的项目仍在调用 /v1/chat/completions,可以按下面的顺序迁移:
- 保留现有客户端请求格式;
- 将 OpenAI SDK 的
base_url指向兼容中转地址; - 在中转后台配置上游渠道和模型映射;
- 仅对测试渠道开启 Chat → Responses 转换;
- 分别验证文本、流式和工具调用;
- 检查日志、Token 统计与扣费结果;
- 验证通过后,再按渠道或模型逐步放量。
一句话总结:Responses API 的升级成本,不一定要由每个业务客户端分别承担。通过“请求转换 + 响应还原 + 策略开关”,旧版 Chat Completions 应用可以在明确兼容边界的前提下,以较小改动接入新的上游模型。
需要统一管理多模型接口时,可以参考 4SAPI 的 OpenAI 兼容入口,先用测试 Key 跑通非流式文本请求,再逐步验证流式输出、工具调用和结构化输出等生产能力。具体支持范围应以当前渠道、模型和平台文档为准。



