尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

跨模型调用实战:Claude Code与Codex互通的协议转换指南

跨模型调用实战:Claude Code与Codex互通的协议转换指南 你有没有过这种时刻电脑上同时装着 Claude Code 和 Codex一个是以会读项目上下文出名的终端智能体一个是 OpenAI 家把指令遵循做到极致的选手。可你打开 Claude Code它只能调 Claude 家的模型打开 Codex它只能调 GPT 系列。明明你手里有 GPT-4o 的额度也明明 Claude 的模型在处理某些重构任务时就是更强——但工具和模型之间就像隔了一堵墙谁都不肯跨过去半步。这堵墙不是能力差距不是算力限制而是协议。Claude Code 只讲 Anthropic Messages API 那套方言Codex 只认 OpenAI 的接口规矩。两边请求结构、鉴权头、流式事件全不一样硬接根本接不上。这篇就把我的完整拆墙过程写出来怎么让 Claude Code 跑 GPT-4o怎么让 Codex 跑 Claude中间踩了什么坑尤其是那个cc switch local proxy failed while handling codex endpoint /responses的报错我是怎么一步步查明白并解决的。全程不说废话能直接照着抄。1. 先搞明白被困住的是什么工具与模型之间的接口方言1.1 现状好用的工具被绑死在自家模型上先说个我自己的使用场景。我同时在两个项目组干活一个重度依赖 Claude Code 的自动改代码流程它能把 todo、搜索、编辑、测试串成一个完整闭环另一个项目风格偏保守团队习惯用 ChatGPT 生态Codex CLI 已经写在内部文档里了。问题来了Claude Code 在处理那种跨文件重构 保留原有风格的活确实有一套但有些时候我需要让 GPT-4o 来掌舵因为它在某些指令理解上更听话、纠错更利索。反过来Codex 的沙盒执行和并行工具调用是真舒服可我想让它用 Claude 的模型来跑复杂推理别老是被 GPT 系的过度自信坑。按正常思路这根本不复杂——把 API 地址换一下不就行了实测下来根本不行。Claude Code 启动时会读ANTHROPIC_BASE_URL和ANTHROPIC_AUTH_TOKEN它发出的请求是POST /v1/messages请求体带anthropic-version头Codex CLI 走的是 OpenAI 的POST /v1/responses或者兼容的chat/completions鉴权用的是Authorization: Bearer。两边语言都不一样你把 Claude Code 的地址指向 OpenAIOpenAI 直接甩你一个 404你把 Codex 指向 Anthropic报错更是五花八门。这不是配错环境变量的问题是两套 API 的根本差异。就像 Modbus 和 CAN 总线都是工业现场总线但你不能把 Modbus 帧直接怼到 CAN 收发器上——中间必须有个网关做协议转换。当时我就想明白了我要写的不是一个配置教程而是一个协议网关。1.2 协议墙的本质以及为什么不能用简单映射糊弄过去很多人第一反应是协议转换不就是字段改名吗system 改成 systemuser 改 user把x-api-key换成Authorization不就行了真不是这么简单。两套协议在三个层面有本质区别。第一是消息结构。Anthropic 把系统提示词放在请求体的system字段里消息数组里只有user和assistant而 OpenAI 把系统提示词塞进messages数组里当system角色。这事看起来是小事但 Claude Code 内部会频繁把上下文压缩、重写、拼接系统提示词转换层要保证顺序和内容都别丢。第二是工具调用的信息组织方式。Anthropic 用content块数组来表达工具调用一个回复可以同时包含文本块和tool_use块工具结果用tool_result块回传OpenAI 则是在 assistant 消息上挂tool_calls数组工具结果单独一条role: tool消息而且工具参数是 JSON 字符串不是结构化对象。转换的时候一个不小心参数就解析错了模型拿到残缺的工具入参能给你把代码改得面目全非。第三是流式事件。两台工具都靠 SSE 流式输出逐字渲染但 Anthropic 的事件是content_block_delta、message_deltaOpenAI 的则是choices[0].delta。转换层要是没把停止原因stop_reason/finish_reason映射对工具会一直等在那里表现就是转圈圈不走下一步。所以结论是拆墙必须有一个完整的双向翻译层而且不能只翻译请求还要翻译响应、翻译流、翻译错误信息。这也是后面所有内容的出发点。2. 协议差异逐个拆Anthropic Messages API 与 OpenAI API 的对照2.1 端点、鉴权与版本头第一道硬门槛我把两套协议的关键差异整理成了一张对照表后面所有的转换代码都是围绕这张表写的。对比项Anthropic Messages APIOpenAI Chat Completions / Responses端点POST /v1/messagesPOST /v1/chat/completions或POST /v1/responses鉴权方式x-api-key: 你的keyAuthorization: Bearer 你的key版本头必须带anthropic-version: 2023-06-01无系统提示词请求体顶层system字段消息数组里的system角色工具调用content块数组tool_use/tool_resultassistant 的tool_calls 单独tool消息流式事件content_block_delta/message_deltachoices[0].deltachat或response.output_text.deltaresponses停止原因stop_reasonend_turn / tool_use / max_tokensfinish_reasonstop / tool_calls / length这里最坑的是版本头。Anthropic 要求所有请求必须带anthropic-version少了直接 400。而 OpenAI 那边的 SDK 根本不会发这个头。反过来OpenAI 要求Authorization: BearerAnthropic 那边虽然也认这种鉴权方式但 Error 信息写得含含糊糊第一次调的时候很容易被误导。我当时在转换层里对头的处理写得很死从入站请求里解析出 Anthropic 风格的鉴权出站请求统一重写成 OpenAI 风格。注意key 的选择权和安全性要留在本地转换层只是翻译而不是保管密钥。我在本地跑了一个监听 127.0.0.1 的小服务把 Anthropic 的 key 映射到 OpenAI 的 key这个映射关系放在环境变量里谁改了都逃不过 git diff。2.2 消息结构的深层差异system 角色怎么安放Anthropic 的请求体大概长这样{ model: claude-sonnet-4-20250514, max_tokens: 8192, system: 你是一个资深 Python 工程师……, messages: [ {role: user, content: 看一下这个项目的结构}, {role: assistant, content: [ {type: text, text: 我先扫描一下目录。} ]} ], tools: [ {name: read_file, description: 读取文件, input_schema: {type: object, properties: {path: {type: string}}}} ] }OpenAI chat 接口是这样的{ model: gpt-4o, messages: [ {role: system, content: 你是一个资深 Python 工程师……}, {role: user, content: 看一下这个项目的结构}, {role: assistant, content: 我先扫描一下目录。} ], tools: [ {type: function, function: {name: read_file, description: 读取文件, parameters: {type: object, properties: {path: {type: string}}}}} ] }差异点非常明显Anthropic 的system和messages是分开的两层OpenAI 则是一个平的messages数组。转换层要做的是把 system 字段抽出来unshift到数组最前面同时还要注意 Claude Code 在运行过程中可能多次更新 system 内容得保证每次都同步过来。另一个隐蔽差异是content的形态。Anthropic 的user消息 content 既可以是字符串也可以是块数组OpenAI 的 content 基本只接受字符串多模态才会有image_url之类的块。我在转换层里做了一个统一处理所有块数组都先序列化拼成纯文本图片块转成 data URL 形式传给 GPT-4o 的多模态接口这样 Claude Code 里那些看截图写样式的场景也能在 GPT-4o 上跑。2.3 工具调用两套完全不同的手和口工具调用是整个协议转换里最容易翻车的地方。Claude Code 之所以强很大程度是因为它的工具定义极其丰富——读文件、编辑文件、执行命令、搜索全部通过tool_use块完成。要让 GPT-4o 接住这些工具转换层必须把 Anthropic 的工具 schema 转成 OpenAI 的 function schema再把返回的 tool_calls 转回 tool_use 块。Anthropic 侧一个典型的工具调用响应片段{ content: [ {type: text, text: 我来读取这个文件}, {type: tool_use, id: toolu_01ABC, name: read_file, input: {path: /tmp/project/main.py}} ], stop_reason: tool_use }OpenAI 侧对应的响应{ choices: [{ message: { role: assistant, content: 我来读取这个文件, tool_calls: [{ id: call_01XYZ, type: function, function: {name: read_file, arguments: {\path\: \/tmp/project/main.py\}} }] }, finish_reason: tool_calls }] }注意几个关键转换点tool_use.id和tool_calls[i].id可以互相映射但必须保留原 ID因为工具执行结果要靠 ID 回填。arguments在 OpenAI 侧是JSON 字符串得JSON.parse一下再塞进 Anthropic 的input对象。OpenAI 的 assistant 消息在同一轮里只能有一套tool_calls而 Anthropic 可以在一个content数组里混合多个文本块和工具块转换时要把文本和工具调用拆开再重组。工具结果回传也一样。Anthropic 用tool_result块挂在同一条 user 消息里OpenAI 则要求每条工具结果独立成一条role: tool消息并且用tool_call_id关联。转换层要维护一个消息队列把工具结果的顺序和 ID 全部对齐错一条整个链路的工具调用就彻底乱套了。2.4 SSE 流式事件终端里的逐字跳动从哪来Claude Code 和 Codex 在终端里那种逐字渲染的效果全靠 SSE 流。如果转换层只处理非流式请求工具会卡到怀疑人生——模型输出全部积压到最后一次性返回体验极差。所以流式事件也必须逐条翻译。Anthropic 的流式事件序列是message_start→content_block_start→content_block_delta→content_block_stop→message_delta→message_stop其中文本增量在delta.text工具参数增量在delta.input_json_delta。OpenAI chat 的流式则是choices[0].delta.content一条条蹦出来工具参数增量在delta.tool_calls[0].function.arguments也是增量字符串。我做映射时的原则是以 Anthropic 格式为外围标准因为 Claude Code 是消费者它只认这套把 OpenAI 的事件翻译成 Anthropic 事件再往终端推。比如收到 OpenAI 的choices[0].delta.content就构造一个content_block_delta事件delta.type设为text_delta。收到delta.tool_calls就翻译成input_json_delta并且要自己维护一个当前工具块是否已经开始的状态机因为 Anthropic 要求先发content_block_start才能发content_block_delta而 OpenAI 那边没有这么细的块生命周期。3. 拆墙设计为什么必须靠一层本地协议转换器3.1 三条路线摆在我面前为什么选了转换器在动手之前我认真评估了三条路线。路线一分别原生使用不开源脑筋。老老实实用 Claude Code 配 Claude用 Codex 配 GPT-4o。这当然最简单但我就是不满意——工具和模型的排列组合是双选凭什么只能对角匹配。路线二直接改环境变量指向对方的官方地址。前面已经验证过完全走不通因为两边的请求格式根本不同官方接口不会做善意兼容。有些第三方聚合网关确实同时提供 Anthropic 风格和 OpenAI 风格的端点但它们内部还是固定模型没法解决Claude Code 跑 GPT-4o这种交叉需求。路线三本地起一个协议转换层。把 Claude Code 的ANTHROPIC_BASE_URL指向http://127.0.0.1:8080转换层收到 Anthropic 格式请求后转成 OpenAI 格式发给真实的 GPT-4o 端点同理让 Codex 走另一个监听端口把 OpenAI 格式转成 Anthropic 格式发给 Claude。这条路线完全可控还能顺手加日志、加熔断。我选了路线三同时也明确了边界这个转换层只负责协议翻译不负责模型路由决策。模型选谁、key 用谁的由上游工具配置决定。3.2 转换层的数据流与配置链路整个系统的数据流长这样Claude Code ──Anthropic 格式── 本地转换层(监听 8080) ──OpenAI 格式── GPT-4o Codex CLI ──OpenAI 格式── 本地转换层(监听 8081) ──Anthropic 格式── Claude这里有个讲究我只在一台机器上跑转换层所以两个方向的服务都绑127.0.0.1避免局域网暴露。如果你要多人共用记得加一层最简单的鉴权别让同事把整个转换层当公共接口用key 泄露了不好追溯。配置链路方面Claude Code 认的环境变量是ANTHROPIC_BASE_URL、ANTHROPIC_AUTH_TOKEN、ANTHROPIC_MODEL。你可以在启动 Claude Code 前 export也可以写进~/.claude/settings.json的env块里。Codex 认OPENAI_BASE_URL和OPENAI_API_KEY以及~/.codex/config.toml里的 model provider 配置。我建议统一用配置文件管理别散落在 shell 历史里不然换个项目就忘了自己配过什么。当时我正好用上了 cc switch 这个工具来管理这些配置。它本质上是 Claude Code 和 Codex 的配置切换器可以在多个 provider 的配置之间快速切换避免我手动去改环境变量。我原本以为 cc switch 只做配置管理后来发现它还带了一个 local proxy 模式能在本机做协议转发——也正是这个模式把我引到了后面那个/responses的坑里。3.3 cc switch 在链条里的定位配置管家不是万灵药用 cc switch 的好处是provider 配置、key、模型映射都能在界面上管理切换的时候一键生效。我配置了两套 Claude Code provider——一套指向 Anthropic 官方一套指向本地转换层Codex 那边也是一样。但这里必须打个预防针cc switch 只是把墙的管理变方便了拆墙本身还是得靠协议转换层。如果你只是把 provider 从官方切到某个第三方而第三方不兼容 Anthropic 格式照样报错。我踩的那个cc switch local proxy failed while handling codex endpoint /responses就是因为它内置的转发功能在处理 Codex 发出的/v1/responses请求时没处理好。后面我会专门讲这个排查过程。所以我的最终架构是cc switch 管配置切换本地转换层管协议翻译两者各司其职。这样即使 cc switch 升级换代我的协议转换层也不受影响。4. 第一拆实录让 Claude Code 跑 GPT-4o从配置到跑通4.1 前置准备你需要哪几样东西动手之前确认三件事一是有 OpenAI 的 API key要有 GPT-4o 模型访问权限二是有 Claude Code 的最新版本它通过ANTHROPIC_BASE_URL支持自定义端点三就是 cc switch用于管理 provider 配置。还建议装一个 Node.js 或 Python 运行时因为小型的协议转换服务用这两种语言写起来最快。我这次用的是 Python FastAPI理由就俩FastAPI 对 SSE 的支持直接异步处理不会阻塞Python 里解析 JSON 和字符串转换心智负担低。我的目录结构非常朴素~/protocol-wall-buster/ ├── translator_anthropic_to_openai.py # 8080 端口服务 Claude Code ├── translator_openai_to_anthropic.py # 8081 端口服务 Codex ├── providers.json # provider 与 key 的映射 └── run.sh # 一键启动4.2 配置 provider把 Claude Code 的出口指向本地在 cc switch 里新建一个 provider名字叫ClaudeCode - GPT4o然后做两件事第一设置本地转换层的地址和 key。我给 Claude Code 配置的环境变量如下export ANTHROPIC_BASE_URLhttp://127.0.0.1:8080 export ANTHROPIC_AUTH_TOKENsk-local-translator export ANTHROPIC_MODELgpt-4o这里的ANTHROPIC_AUTH_TOKEN可以随便设它只用于让转换层识别这个请求是从 Claude Code 来的。真正的 OpenAI key 放在转换层的providers.json里{ clients: { claude_code: { auth_token: sk-local-translator, upstream: { base_url: https://api.openai.com/v1, api_key: sk-你的真实key, model: gpt-4o } } } }注意一点ANTHROPIC_MODEL里写gpt-4o会不会让 Claude Code 错乱实测不会。模型名只是一个字符串Claude Code 本身不对名字做合法性校验真正消费模型名的是转换层——转换层收到请求后把model字段替换成 upstream 里的gpt-4o然后走 OpenAI 接口。也就是说你甚至可以写gpt-4o的任意别名只要转换层最后映射成有效模型名就行。第二把~/.claude/settings.json里的 env 块也同步一份这样每次启动 Claude Code 都会自动带上环境变量不用每次 export{ env: { ANTHROPIC_BASE_URL: http://127.0.0.1:8080, ANTHROPIC_AUTH_TOKEN: sk-local-translator, ANTHROPIC_MODEL: gpt-4o } }4.3 转换层核心代码Anthropic 请求转 OpenAI 请求转换层最核心的是请求翻译。贴一段我当时写的核心逻辑代码不追求完整重点是思路def translate_messages(anthropic_body: dict) - dict: openai_messages [] # 1. system 字段抽出来放到数组头部 if anthropic_body.get(system): openai_messages.append({role: system, content: anthropic_body[system]}) # 2. 逐条转换 messages for msg in anthropic_body.get(messages, []): role msg[role] content msg.get(content) if isinstance(content, str): openai_messages.append({role: role, content: content}) else: # 3. 处理块数组文本、图片、tool_result text_parts, image_parts, tool_results [], [], [] for block in content: if block[type] text: text_parts.append(block[text]) elif block[type] image: image_parts.append({type: image_url, image_url: {url: fdata:{block[source][media_type]};base64,{block[source][data]}}}) elif block[type] tool_result: tool_results.append({role: tool, tool_call_id: block[tool_use_id], content: extract_text(block[content])}) # 4. tool_result 作为独立 tool 消息追加 openai_messages.extend(tool_results) # 5. 文本/图片组装成普通消息 if text_parts or image_parts: content_parts [{type: text, text: \n.join(text_parts)}] if text_parts else [] content_parts.extend(image_parts) openai_messages.append({role: role, content: content_parts}) return openai_messages工具定义和工具调用的转换类似input_schema改成parameters外层包一层{type: function, function: {...}}。流式事件通过 FastAPI 的StreamingResponse逐条转发每收到 OpenAI 的一个 SSE chunk就按 2.4 节说的状态机翻译成 Anthropic 事件。4.4 跑通验证第一个命令就暴露了一个问题启动转换层后我在 Claude Code 里敲了第一句claude 看一下当前目录告诉我这个项目用了哪些技术栈结果它顺利读出了项目文件列出了技术栈全程终端渲染流畅——协议转换层基本工作正常。但紧接着我让它修改某个文件里的一段逻辑问题来了Claude Code 发了tool_result之后GPT-4o 的响应格式和 Claude 不一样转换层没有把finish_reason: tool_calls正确翻译成stop_reason: tool_use导致 Claude Code 认为工具没执行完卡在处理中。这个 bug 调了我一个多小时。根因在转换工具调用响应时我只转了内容和 arguments没有转finish_reason。修复很简单把 OpenAI 的finish_reason: tool_calls映射成 Anthropic 的stop_reason: tool_usefinish_reason: stop映射成end_turn。这个映射逻辑必须放在消息转换那一层不能放在流式事件层因为非流式请求的停止原因在 body 里流式请求在最后一个事件里两处都要处理。5. 第二拆实录让 Codex 跑 Claude以及 /responses 那个报错的完整排查5.1 反向转换OpenAI 请求转 Anthropic 请求搞定第一拆后第二拆其实就是反过来再来一遍。Codex CLI 默认走 OpenAI 的 Responses API请求体是input数组、工具在tools数组里、鉴权用Authorization: Bearer。要把这些翻译成 Anthropic 格式发给 Claude需要做一个反向的openai_to_anthropic转换。核心逻辑正好是 4.3 的逆过程input数组里的system角色消息抽出来放到 Anthropic 请求体的顶层systemtool角色消息转换成tool_result块assistant 消息里的tool_calls拆成tool_use块。我把两个转换脚本放在同一个目录下互相引用同一套类型定义避免字段名不一致。关键配置在 Codex 侧的~/.codex/config.tomlmodel claude-sonnet-4-20250514 model_provider local-anthropic [model_providers.local-anthropic] name Local Anthropic Translator base_url http://127.0.0.1:8081 wire_api responses env_key OPENAI_API_KEYCodex 会把请求发到http://127.0.0.1:8081然后由反向转换层转成 Anthropic 格式发给 Claude。wire_api字段会决定 Codex 用哪一个 API 风格我一开始用的是responses结果就是那个著名的报错现场。5.2 报错现场cc switch local proxy failed while handling codex endpoint /responses事情是这样的我图省事想直接用 cc switch 的 local proxy 功能来转发 Codex 的请求结果一启动Codex 那边直接抛了一行日志cc switch local proxy failed while handling codex endpoint /responses. provider ...后面 provider 的信息被截断了但从日志能看出两个信息一是 cc switch 确实把本地代理启动起来了二是它收到 Codex 发出的/v1/responses请求时没处理成功。这行报错一开始让我很懵——cc switch 既然宣称支持 Codex怎么会连 endpoints 都处理不了我先把错误日志级别调高抓到了完整堆栈。问题指向非常清晰cc switch 本地代理的实现里只注册了/v1/chat/completions这个路由的兼容处理逻辑但对新版 Codex 默认使用的/v1/responses端点只做了透传转发当目标 provider 不认 Responses API 格式时转换层没做降级直接把错误抛出来了。换句话说Codex 用 Responses API 说话cc switch 的本地代理用 Chat Completions 的翻译逻辑去接中间差了整整一代接口的映射。这不是 cc switch 的路径写错了是它内置转发能力落后于 Codex 的接口演进。5.3 完整排查链路从日志到修复我把排查和修复的完整链路列出来方便你复现第一步确认报错发生在哪一层。把 Codex 的日志级别调成 debug加上RUST_LOGdebugCodex 是 Rust 写的支持这个环境变量看到cc switch local proxy字样后基本锁定转发层出了问题。第二步手动 curl 本地代理。我直接模拟 Codex 的请求curl -X POST http://127.0.0.1:8081/v1/responses \ -H Authorization: Bearer sk-fake \ -H Content-Type: application/json \ -d {model:gpt-4o-mini,input:hello}返回的错误体给出了具体信息——后端 provider 返回 400说是不认识input字段。到这里问题就很明确了本地代理把 Responses API 的input原样转发给了 Anthropic而 Anthropic 只认messages不认input。第三步决定修的方向。两个选择改配置让 Codex 走 chat/completions 风格这样 cc switch 的兼容逻辑能接住或者自己写一个专门的 Responses API → Anthropic 转换层。我选了后者原因很实在Codex 新版对 Responses API 支持最完整包括并行工具调用、存储会话这些特性强制降级到 chat 会损失能力。第四步给转换层补上 Responses API 的解析逻辑。核心是把input数组里面的消息类型映射到 Anthropic 角色。Responses API 的消息类型比 chat 多比如message里面有role、contentfunction_call类型的消息要转成 tool_usefunction_call_output要转成 tool_result。我加了一个专门的分发函数按input[i].type做分支处理。第五步回归验证。先在 Codex 里跑一个无工具调用的简单问答再跑一个需要改文件的完整任务确认工具调用链路没断。同时把 cc switch 的 local proxy 关掉彻底换成自建转换层保留 cc switch 只做配置管理。这条排查链路走下来我最大的体会是报错文本里的endpoint往往不是根因根因是谁在处理这个 endpoint、处理得全不全。遇到这种报错先别急着找升级版本这种万能解先把请求链路拆开用 curl 一层层试问题定位会快得多。6. 拆完之后的实测延迟、流式、工具调用一个都别想省6.1 延迟损耗到底有多少协议转换不是零成本但延迟损耗完全在可接受范围内。我在同一台机器上做了对比测试直接调用 Claude 官方接口 vs 经过转换层调用 GPT-4o等一个中长回答约 500 token的耗时。场景首 token 延迟完整响应时间Claude Code 直接连 Claude约 1.2s约 6.5sClaude Code → 转换层 → GPT-4o约 1.5s约 7.2sCodex 直接连 GPT-4o约 1.1s约 6.1sCodex → 转换层 → Claude约 1.4s约 7.0s额外增加的时间主要是两次 HTTP 转发 字符串 JSON 序列化的开销对于终端交互来说体感差异很小。真正影响体验的是流式刷新频率——如果你的转换层是攒一批再推终端就会一卡一卡如果逐条转发就完全无感。我最后用了收到 OpenAI chunk 立即翻译立即推送的模式终端渲染稳稳的。6.2 工具调用这个真坑我替你们踩熟了工具调用兼容性是最容易出问题的环节我实测下来有三个高频坑。第一个坑是工具入参的类型校验。GPT-4o 生成的arguments是 JSON 字符串但格式偶尔不严格缺引号、多逗号JSON.parse会直接挂。我在转换层里加了一个 lenient JSON 解析函数先试标准 parse失败后用正则修常见错误再不行就返回一个带原始字符串的特殊错误给模型重试。第二个坑是并发工具调用的顺序。GPT-4o 一次可以返回多个tool_calls转换层把它们都拆成 tool_use 块之后工具执行结果回传时必须保持同一个 ID 映射。有些转换层偷懒用数组下标一旦并发乱序整个会话就废了。我用的是一个dict保存openai_call_id → anthropic_tool_use_id的映射回传时按映射查找稳得很。第三个坑是停止原因没映射干净。前面我提了finish_reason转stop_reason的事这里再补充一个细节Anthropic 的stop_reason有max_tokensOpenAI 的finish_reason是length。你要是不映射模型回答到一半被截断时Claude Code 会把它当成正常结束导致它继续执行不存在的后续步骤。这个映射必须放在响应体和流式事件两处都要做。6.3 上下文长度与限流两边的脾气不一样Claude 和 GPT-4o 的上下文窗口大小不一致工具使用习惯也不一样。Claude Code 默认会往上下文里塞很多系统提示和工具定义这些 token 在转换层原样转发给 GPT-4o 之后可能会把 GPT-4o 的上下文预算吃掉很多。我的应对是在转换层加了一个系统提示词压缩开关当原始 system 超过一定长度时用另一个小模型比如gpt-4o-mini做一次摘要压缩保住主模型的上下文空间。这个功能要谨慎用摘要会丢信息我只在确实超限时才启用。限流方面Anthropic 和 OpenAI 的限流策略都基于每分钟请求数 每分钟 token 数但报错格式完全不同。OpenAI 的 429 响应里有retry-after头Anthropic 的 429 响应里有retry-after-ms。转换层要把这些头原样传给调用方否则 Claude Code 和 Codex 的重试逻辑会失效表现为一限流就整个会话崩掉。这些细节不处理日常使用会频繁翻车。7. 同一套思路还能指哪打哪Ollama、DeepSeek 与其它兼容端点7.1 把 Claude Code 接到本地 Ollama拆完那两堵墙之后我发现这套本地协议转换层的思路可以泛化到任何模型端点。比如本地跑 Ollama它自己只暴露 OpenAI 兼容接口那我想让 Claude Code 用它怎么办同样的套路转换层把 Anthropic 格式翻译成 OpenAI 兼容格式只不过上游从api.openai.com换成了http://127.0.0.1:11434/v1。cc switch 对 Ollama 也做了专门适配很多人在热词里搜claude code cc switch ollama说明这是个高频需求。我的建议是本地模型的工具调用能力普遍弱于云端模型转换层里的工具 schema 一定要精简别把 Claude Code 那几十个工具全塞给本地小模型它根本处理不过来。我实测qwen2.5-coder:32b这种规模的模型跑简单文件操作还行复杂重构会乱适合做预算敏感型的轻量任务。7.2 接 DeepSeek 这类第三方模型的注意点第三方模型端点比如 DeepSeek通常都提供 OpenAI 兼容接口所以Claude Code 接 DeepSeek只要把转换层的 upstream 地址改成 DeepSeek 的 endpoint 就行几乎零成本。但要注意两个点一是 DeepSeek 官网有比较严格的并发限制Claude Code 经常并发调用工具转换层需要加一个简单的信号量做限流二是 DeepSeek 对超长上下文的处理策略和 OpenAI 不同Claude Code 的自动 diff、自动重试会频繁触发长上下文请求建议在转换层里把max_tokens掐小一点避免尾部长输出被截断。7.3 给转换层做的最后一点产品化建议折腾完这一圈我把这套转换层整理成了一个小工具供自己团队使用。所谓产品化其实就是加了三个东西请求日志每次转换都记录来源工具、目标模型、耗时、错误码有问题一眼定位。一键切换配置配合 cc switch预置了官方 ClaudeClaude Code GPT-4oCodex Claude本地 Ollama四套配置切换不再改环境变量。健康检查端点GET /health返回当前所有 upstream 的连通状态CI 脚本和同事的日常检查都能用。这三个东西看着简单实际使用中价值极大。比如上次同事反馈Codex 响应很慢我打开日志一看发现他配错了 key转换层每次都在等 401 超时后重试而不是立刻失败。日志一查就清楚了。回到最开始的问题为什么非要拆这堵墙我的个人体会是AI 编程工具生态现在还处于百家争鸣的阶段没有哪个模型的全部能力都碾压其他模型。Claude Code 的终端体验和 Codex 的沙盒执行各有拥趸但模型本身的差距每隔几个月就会换一次风向。如果工具和模型被协议死死绑定你就等于把自己锁死在一个厂商的技术路线上想切换的代价会越来越高。拆掉协议这堵墙之后你手里的工具和模型就像积木一样可以自由组合。今天喜欢让 Claude Code 跑 GPT-4o明天想让 Codex 用 Claude 做复杂推理后天想接本地模型省点钱——改一下配置就行。最后再分享一个实际操作中的小技巧如果你只是临时想试试某种组合直接在启动命令前 export 环境变量就行不用大动干戈部署转换层一旦发现某个组合是长期需求再把它固化成配置文件、交给 cc switch 统一管理。这套先临时验证、后固化配置的节奏能帮你少走很多弯路。
返回列表