多协议适配管线:Responses API ↔ Chat Completions 翻译层设计与实现
一、为什么需要多协议翻译
客户端生态的碎片化
AI 编程工具生态中,不同客户端各自为政,催生了三种互不兼容的 API 协议:
-
Claude Code → 使用 Chat Completions 协议
-
Codex CLI → 使用 Responses API 协议
-
Anthropic SDK → 使用 Messages 协议
三个客户端,三种协议,但上游服务只提供 Chat Completions 一种接口。这就像一个插座要适配三种不同标准的插头,硬接只会短路。
传统方案的痛点
面对这种局面,直觉方案各有短板:
-
跑两个代理:codex-proxy 做协议翻译 + mimo-proxy 做推理缓存 → 运维复杂度翻倍,排查问题时要在两个代理之间来回跳转
-
改客户端代码:侵入式修改上游开源项目 → 维护成本巨大,上游一更新就得重新适配
-
单代理只支持一种协议:为了兼容多客户端不得不部署多个实例 → 资源浪费,且共享缓存等能力无法复用
设计目标
基于以上痛点,确立了三个核心目标:
-
单代理,多协议入口:一个服务同时暴露三种协议的端点
-
统一缓存推理内容:无论请求从哪个协议入口进来,reasoning 都走同一套缓存逻辑
-
上游只对接 Chat Completions:对外多协议,对内统一出口,降低维护成本
二、整体架构
text
Claude Code ──→ /v1/chat/completions ──┐
Codex CLI ──→ /v1/responses ──→┤
Anthropic SDK──→ /v1/messages ──→┤
├──→ reasoning 注入 → Chat Completions → 上游
仪表盘 ──→ /api/* ──→┘
请求进入代理后,经过一条三层处理流水线:
-
协议翻译层:将 Responses API 或 Anthropic Messages 协议的请求翻译为 Chat Completions 格式
-
Reasoning 注入层:统一调用
inject_reasoning,为每条消息补全推理内容 -
上游转发层:通过
stream_proxy完成流式或非流式的向上游转发
三层各司其职,协议差异被封印在翻译层内部,后续链路无感知。
三、Responses API 翻译核心
请求翻译:extract_messages
Responses API 的 input 数组包含三种 item 类型,需要一一映射:
| 原始类型 | 翻译后 |
|---|---|
type: "message" |
role: user 或 role: assistant |
type: "function_call" |
合并为一条 assistant + tool_calls[] |
type: "function_call_output" |
role: tool |
几个关键处理细节:
-
连续 function_call 合并:当多条 function_call 连续出现时,合并为同一条 assistant 消息,tool_calls 按序排列——这是 Chat Completions 协议对多工具调用的标准表示
-
instructions → system message:将 Responses API 顶层的系统指令字段映射为 Chat Completions 的 system 角色消息
-
消息重排:确保
tool消息紧跟在对应的assistant消息之后,避免上游因消息顺序不当而拒绝请求 -
Schema 字段清理:剔除
additionalProperties和strict等 Chat Completions 不兼容的字段,防止校验报错
流式响应翻译:stream_responses_sse
这是整个翻译层最精细的部分——需要将 Chat Completions 返回的 SSE 事件块,实时重组为 Responses API 规定的事件序列。
纯文本路径的事件序列:
text
response.created
→ response.in_progress
→ response.output_item.added (type=message)
→ response.content_part.added
→ response.output_text.delta (×N 次增量)
→ response.output_text.done
→ response.content_part.done
→ response.output_item.done
→ response.completed
工具调用路径的事件序列:
text
response.output_item.added (type=function_call) → response.function_call_arguments.delta (×N 次增量) → response.function_call_arguments.done → response.output_item.done → response.completed
每一个 delta 事件都精确对应 Chat Completions chunk 中的内容增量,下游客户端完全感知不到中间经过了一层协议转换。
四、Anthropic Messages 翻译核心
请求翻译:extract_anthropic_messages
Anthropic 的 Messages 协议在结构上与 Chat Completions 有显著差异,映射关系如下:
| Anthropic 字段 | Chat Completions 字段 |
|---|---|
system(顶层字段) |
role: system |
content[].type: text |
role: assistant + content |
content[].type: tool_use |
assistant + tool_calls[] |
content[].type: tool_result |
role: tool |
tools[].input_schema |
tools[].function.parameters |
Anthropic 将文本、工具调用、工具结果全部平铺在 content 数组中,而 Chat Completions 是按角色分层的。翻译层需要完成这种“扁平 → 分层”的结构转换。
流式响应翻译:stream_anthropic_sse
Anthropic 的流式事件同样有其独特的事件类型序列:
text
message_start
→ content_block_start (type=text)
→ content_block_delta (text_delta ×N 次增量)
→ content_block_stop
→ content_block_start (type=tool_use)
→ content_block_delta (input_json_delta ×N 次增量)
→ content_block_stop
→ message_delta
→ message_stop
每个 content_block 对应一段独立的内容块(文本或工具调用),块内通过 delta 事件逐步传递增量内容,块之间通过 start/stop 事件明确边界。
五、统一 Reasoning 缓存
架构上最优雅的一点在于:无论请求从哪个协议入口进来,最终都汇入同一条缓存链路。
-
协议翻译层将各类请求统一转成 Chat Completions 格式
-
随后与原生 CC 路由走完全相同的
inject_reasoning逻辑 -
无需为 Responses API 或 Anthropic 单独维护缓存代码
-
save_reasoning在流结束时统一触发,将本轮推理内容持久化
这种“翻译先行,缓存统一”的设计,让新增一个协议入口的成本降到最低——只需实现翻译层,缓存能力自动复用。
六、模型路由:上游调度
每个上游服务声明一个模型标识,实现请求的精准分发:
yaml
upstreams:
- name: "DeepSeek"
model: "deepseek-chat"
url: "https://api.deepseek.com/v1"
- name: "MiMo"
model: "mimo-v4"
url: "https://api.mimo.com/v1"
路由逻辑简洁明确:请求中的 model 字段与上游配置精确匹配,未命中时自动回退到活跃上游。配合 SQLite 持久化的上游列表,模型的新增和切换都可以在线完成,无需重启。
七、错误透传设计
代理层刻意去掉了内部的自动重试循环,采用“透明代理”的错误处理哲学:
-
上游返回 503 → 原样返回 503 给下游
-
下游客户端(Claude Code、Codex CLI 等)各自按自己的指数退避策略重连
-
不吞错误、不包装错误信息、不修改状态码
-
流式请求中发生错误时,抛出
UpstreamError→ 路由层捕获 → 以原始状态码响应客户端
这样做的好处是:客户端看到的是“直连上游”的错误行为,不会因为代理的额外重试而引入超时累积或重复请求,也避免了“代理层重试成功但客户端已超时断开”的尴尬局面。
八、代码结构概览
| 文件 | 职责 |
|---|---|
src/responses.py |
Responses API 请求翻译 + 流式响应事件映射 |
src/anthropic_translate.py |
Anthropic Messages 请求翻译 + 流式响应事件映射 |
src/routes.py |
路由注册 + 请求分发(按路径指向不同翻译层) |
src/proxy.py |
reasoning 注入与缓存 + 流式转发核心逻辑 |
src/upstreams.py |
上游模型路由 + SQLite 持久化上游列表 |
每个模块职责单一,协议翻译逻辑与核心代理逻辑完全解耦,新增协议只需增加翻译文件并注册路由。
九、总结
这套多协议翻译管线实现了三个“统一”:
-
统一入口:一个代理同时承载三种协议的端点,彻底告别多代理运维的噩梦
-
统一缓存:外来请求协议各不相同,但在推理缓存层面殊途同归,一份代码服务所有入口
-
统一出口:上游始终只对接 Chat Completions,内部复杂度对外部完全透明
流式事件映射做到了位,下游客户端感知不到代理的存在。模型路由让一个代理同时服务多个供应商,而错误透传设计保证了代理不成为链路中的“黑盒”。整个方案的核心思路可以概括为一句:协议差异是入口的事,缓存和转发是所有人的事。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)