☰
openrig:让Claude Code与Codex自由接入任意模型的代理层实战
2026/10/3 18:11:31 网站建设 项目流程

1. 从“openrig”这个名字说起:它到底想解决什么问题

第一次看到openrig这个词,我脑子里蹦出来的第一反应是“open”加“rig”——一个开放的、可拼装的“机架”或者“装置”。放到当下这个 AI 编程助手满天飞的环境里,这个名字其实指向了一个非常具体的痛点:把 Claude Code、Codex 这类命令行 AI 编程工具,从“官方绑定”里解放出来,让它们能自由接入任意模型、任意端点、任意本地服务。

我接触 Claude Code 和 Codex 的时间不算短,踩过的坑也足够多。最开始用 Claude Code 的时候,最让人头疼的就是它默认只认官方那套订阅体系,一旦你的账号权限、组织策略、地区策略出点问题,直接给你甩一句your organization has disabled claude subscription access for claude code,然后你就卡在那里了。Codex 那边也差不多,codex无法加载组织设置、the 'gpt-5.6-sol' model is not supported when using codex这类报错,几乎每个想折腾的人都遇到过。

openrig这类项目的核心价值,就是在这两个工具和它们背后的模型服务之间,插一层可配置的中间层。你可以把它理解成一个“转接头”:Claude Code 和 Codex 各自说自己的方言(Anthropic 的 Messages API、OpenAI 的 Responses API),而 openrig 负责把这些方言翻译成目标模型能听懂的话,再把结果翻译回来。这样一来,你就能用 Claude Code 去调 LM Studio 里的本地模型,也能用 Codex 去接 DeepSeek、Qwen、GLM 这些第三方服务。

这篇文章我打算把 openrig 这套思路从头到尾拆一遍:它依赖哪些基础组件(Node.js、YAML 配置)、核心的代理转发逻辑是怎么设计的、Claude Code 和 Codex 分别怎么接、常见的报错怎么排查。内容会偏实操,代码和配置都会给到能直接抄的程度。适合两类人看:一类是刚装完 Claude Code 或 Codex、被各种报错卡住的新手;另一类是想把 AI 编程工具接到自己私有模型上的进阶玩家。

2. 整体设计思路:为什么是“代理层 + YAML 配置”这套组合

2.1 核心矛盾:工具协议和模型协议对不上

要理解 openrig 为什么要做成一个代理层,得先搞清楚 Claude Code 和 Codex 各自在跟谁说话。

Claude Code 是 Anthropic 出的命令行工具,它内部走的是 Anthropic 的 Messages API 格式,请求体长这样:messages数组里每个元素带role和content,content还可以是块状结构(text block、tool_use block、tool_result block)。而 Codex 是 OpenAI 系的,它走的是 Responses API,请求结构、工具调用(tool call)的字段命名、流式返回的事件类型,跟 Anthropic 那套完全不是一回事。

问题就来了:如果你想用 Claude Code 去调一个只支持 OpenAI 格式的模型服务,两边根本对不上话。反过来,用 Codex 去调一个只认 Anthropic 格式的端点,也一样抓瞎。这就是为什么需要一层代理——它站在中间,左边收 Claude Code 或 Codex 的请求,右边按目标服务的格式发出去,回来的时候再翻译一遍。

提示:很多人以为“接入第三方模型”就是改个 base_url 那么简单,实际上协议不兼容才是真正的拦路虎。base_url 只是地址,协议才是语言。

2.2 为什么选 YAML 做配置,而不是 JSON 或环境变量

openrig 这类项目普遍用 YAML 来写配置,这个选择不是随便定的。我对比过三种方案:

配置方式优点缺点适用场景
环境变量简单、容器友好复杂嵌套结构表达困难,多模型配置会爆炸单一模型、简单场景
JSON结构清晰、机器友好不支持注释,手写容易漏逗号,长配置难维护程序生成、API 交互
YAML支持注释、层级直观、多文档缩进敏感,tab 和空格混用会报错多模型、多端点、需要注释说明

openrig 要管理的是“多个模型 + 多个端点 + 各自的鉴权 + 各自的协议映射”,这种嵌套结构用 YAML 写出来最舒服。你可以给每个 provider 单独写一段,注释清楚它是干嘛的,改的时候一眼就能找到。JSON 做不到注释,环境变量更是没法表达嵌套。

YAML 的坑也很典型:缩进必须用空格不能用 tab,冒号后面要留一个空格,字符串里有特殊字符要加引号。我见过太多人yaml文件写错一个缩进,整个服务起不来,报错还特别隐晦。后面排查章节我会专门讲这个。

2.3 Node.js 在整个链路里的角色

openrig 跑在 Node.js 上,这不是偶然。Claude Code 本身就是 Node.js 写的(通过 npm 分发),Codex CLI 也是 Node.js 生态的产物。用 Node.js 做代理层有几个实打实的好处:

第一,事件循环天然适合做流式转发。AI 编程工具的响应基本都是 SSE(Server-Sent Events)流式的,Node.js 的 stream 和 pipe 处理这种场景非常顺手,不用像某些语言那样开线程池。

第二,和工具本身同源。你装 Claude Code 的时候已经装了 Node.js,再跑一个 Node.js 写的代理,环境依赖是复用的,不用额外装 Python 或 Go 运行时。

第三,npm 生态里有现成的 HTTP 框架。Express、Fastify、Hono 这些都能快速搭起一个转发服务,中间件机制处理鉴权、日志、错误也方便。

所以node.js安装是整条链路的第一步,这个后面会详细讲。node.js是干什么的这个问题,放到这个场景里答案很明确:它是 Claude Code、Codex、openrig 三者共同的运行时底座。

3. 环境准备:Node.js 安装与版本选择的那些坑

3.1 Node.js 版本怎么选,LTS 还是 Current

装 Node.js 第一件事就是选版本。官网(node.js官网下载)上永远摆着两个选项:LTS 和 Current。我的建议很明确——无脑选 LTS。

LTS 是 Long Term Support,长期支持版,稳定、bug 少、生态兼容性好。Current 是最新特性版,可能带着还没被生态消化完的改动。AI 编程工具这条链路上,任何一个环节用了 Current 版,都可能遇到某个依赖还没适配的情况。

我印象很深的一次,有人图新鲜装了 Current 版,结果error installing 24.21.0: node.js v24.21.0 is not yet released or is not ava这种报错就来了——版本号根本不存在,或者某个包还没发布对应版本。这种错误看着吓人,其实就是版本选错了。

具体操作上,我更推荐用版本管理器而不是直接装官网安装包:

# 用 nvm 管理 Node.js 版本(Linux/macOS) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc nvm install --lts nvm use --lts node -v

Windows 用户可以用 nvm-windows,或者直接去node.js官网下载页面拿 LTS 的 msi 安装包。装完之后node -v和npm -v都要能正常输出,这是最基本的验证。

注意:如果你之前装过旧版本,装新版本前最好先卸干净,尤其是 Windows 上,残留的 PATH 会导致node -v显示的还是旧版本。

3.2 npm 镜像与全局安装权限

国内环境下 npm 装包慢是常态,配个镜像能省不少时间:

npm config set registry https://registry.npmmirror.com npm config get registry

全局安装权限这块,Linux/macOS 上如果不用 nvm 而是系统级安装,npm install -g经常会报 EACCES 权限错误。用 nvm 就天然避开了这个问题,因为包都装在用户目录下。Windows 上一般不会有这个问题,但如果遇到,用管理员权限开终端即可。

3.3 验证环境是否就绪

装完 Node.js 之后,跑一遍这个检查清单:

node -v # 应输出 v20.x 或 v22.x 这类 LTS 版本号 npm -v # 应输出对应 npm 版本 npm config get registry # 确认镜像地址 which node # Linux/macOS 确认路径

这四步都过了,环境底座就算搭好了。接下来才是 Claude Code、Codex 和 openrig 的安装。

4. Claude Code 与 Codex 的安装和基础配置

4.1 Claude Code 安装:npm 全局装最省心

Claude Code 的安装方式,官方推荐 npm 全局安装:

npm install -g @anthropic-ai/claude-code claude --version

装完之后第一次运行claude,它会引导你做登录或者配置 API key。这里就是很多人卡住的地方——your organization has disabled claude subscription access for claude code这个报错,本质上是账号层面的订阅权限问题,不是安装问题。遇到这个,要么换一个有权限的账号,要么走 API key 模式,要么就是本文要讲的——通过代理层接到别的模型上。

claude code安装在 Windows 上稍微麻烦一点,因为 Claude Code 早期对 Windows 原生支持一般,很多人是在 WSL 里跑的。现在原生支持好多了,但如果你在 Windows 上遇到奇怪的路径问题,WSL 仍然是个稳妥选择。

vscode配置claude code和claude code for vs code是另一条路——在 VS Code 里装 Claude Code 扩展,这样能在编辑器内直接用。配置方式跟命令行版基本一致,只是入口不同。

4.2 Codex 安装:注意 CLI 和桌面版的区别

Codex 这边有 CLI 版和桌面版两条线。codex cli是命令行工具,codex安装 windows桌面版则是带界面的。安装 CLI 版一般也是 npm:

npm install -g @openai/codex codex --version

codex登录之后,同样会遇到组织策略、模型支持的问题。codex无法加载组织设置这个报错,通常是网络请求没通,或者账号配置有问题。the 'gpt-5.6-sol' model is not supported when using codex这种,则是模型名对不上——你配置里写的模型名,目标服务不认。

codex接入deepseek是很多人关心的场景,因为 DeepSeek 性价比高。但 Codex 默认走 OpenAI 的 Responses API,DeepSeek 的接口格式未必完全一致,这就需要代理层来做协议转换。这正是 openrig 这类项目的用武之地。

4.3 两个工具共存的注意事项

Claude Code 和 Codex 装在同一台机器上,一般不会冲突,因为它们各自的配置目录、环境变量前缀都不一样。但有两个点要注意:

一是环境变量污染。有些第三方接入方案会让你设ANTHROPIC_BASE_URL、OPENAI_BASE_URL这类变量,如果两个工具都读同名变量,就会互相干扰。解决办法是给每个工具用独立的配置文件,而不是全局环境变量。

二是端口占用。如果你同时跑多个代理服务,端口要错开。openrig 默认端口如果跟别的服务撞了,改配置里的端口号即可。

5. openrig 的核心:代理转发与协议映射怎么实现

5.1 代理层的基本骨架

openrig 的核心是一个 HTTP 服务,它至少要做三件事:接收请求、转换协议、转发并回传。用 Node.js 写一个最小骨架大概是这样:

// proxy.js - 最小代理骨架 const http = require('http'); const https = require('https'); const TARGET_BASE = process.env.TARGET_BASE || 'http://localhost:1234'; const PORT = process.env.PORT || 8787; const server = http.createServer(async (req, res) => { // 1. 收集请求体 let body = ''; req.on('data', chunk => body += chunk); req.on('end', async () => { // 2. 协议转换(这里是最关键的部分) const transformed = transformRequest(req.url, body); // 3. 转发到目标服务 const targetUrl = new URL(req.url, TARGET_BASE); const proxyReq = (targetUrl.protocol === 'https:' ? https : http).request({ hostname: targetUrl.hostname, port: targetUrl.port, path: targetUrl.pathname + targetUrl.search, method: req.method, headers: buildHeaders(req.headers), }, proxyRes => { res.writeHead(proxyRes.statusCode, proxyRes.headers); proxyRes.pipe(res); // 流式回传 }); proxyReq.on('error', err => { res.writeHead(502); res.end(JSON.stringify({ error: err.message })); }); proxyReq.write(transformed); proxyReq.end(); }); }); server.listen(PORT, () => console.log(`openrig proxy on ${PORT}`));

这个骨架的关键点在于transformRequest和buildHeaders两个函数——它们负责把 Claude Code 或 Codex 发来的请求,翻译成目标服务能懂的格式。proxyRes.pipe(res)这一行保证了流式响应能原样透传,不会因为缓冲导致打字机效果卡顿。

5.2 协议映射:Anthropic Messages 与 OpenAI Responses 的差异

这是整个项目最硬核的部分。我拿两个典型请求对比一下。

Claude Code 发出来的请求(Anthropic Messages 格式):

{ "model": "claude-sonnet-4", "max_tokens": 4096, "messages": [ { "role": "user", "content": "帮我写个快排" } ], "stream": true }

Codex 发出来的请求(OpenAI Responses 格式):

{ "model": "gpt-5-codex", "input": "帮我写个快排", "stream": true }

注意差异:Anthropic 用messages数组,OpenAI Responses 用input;Anthropic 的content可以是字符串也可以是块数组,Responses 的input结构又不一样。工具调用(tool use)的字段差异更大——Anthropic 叫tool_use/tool_result,OpenAI 叫function_call/tool_calls。

代理层要做的就是把这些字段一一对应起来。写映射逻辑的时候,我建议用一个显式的映射表,而不是一堆 if-else:

function anthropicToOpenAI(anthropicReq) { return { model: mapModelName(anthropicReq.model), input: anthropicReq.messages.map(m => ({ role: m.role, content: typeof m.content === 'string' ? m.content : m.content.map(block => blockToText(block)).join('') })), stream: anthropicReq.stream, max_output_tokens: anthropicReq.max_tokens, }; }

mapModelName这个函数很重要——它负责把 Claude Code 里写的模型名,映射到目标服务实际支持的模型名。the 'gpt-5.6-sol' model is not supported这类报错,很多时候就是映射没配对。

5.3 YAML 配置文件怎么写

openrig 的配置用 YAML 组织,一个典型的多模型配置长这样:

# openrig.yaml server: port: 8787 host: 127.0.0.1 providers: local-lmstudio: type: openai base_url: http://localhost:1234/v1 api_key: not-needed models: - name: qwen2.5-coder-7b alias: claude-sonnet-4 # 把本地模型伪装成 Claude 模型名 deepseek: type: openai base_url: https://api.deepseek.com/v1 api_key: ${DEEPSEEK_API_KEY} models: - name: deepseek-chat alias: gpt-5-codex routes: - match: /v1/messages provider: local-lmstudio protocol: anthropic-to-openai - match: /responses provider: deepseek protocol: openai-responses-to-chat

这份配置里几个关键点:

alias字段是精髓。Claude Code 内部会写死一些模型名,你没法直接改,但通过 alias 映射,可以让它以为自己在调claude-sonnet-4,实际上请求被转发到了本地 Qwen 模型上。

${DEEPSEEK_API_KEY}是环境变量插值,避免把密钥硬编码进配置文件。这个语法不是 YAML 原生的,是 openrig 在加载配置时自己解析的。

routes段定义了路由规则:什么样的请求路径,走哪个 provider,用哪种协议转换。/v1/messages是 Anthropic 的端点,/responses是 OpenAI Responses 的端点。

注意:YAML 里alias后面的冒号一定要跟一个空格,alias:claude-sonnet-4这种写法会被解析成字符串而不是键值对,服务起不来还不报明确错误。

5.4 流式响应的处理细节

流式这块是最容易出问题的地方。Claude Code 和 Codex 都依赖 SSE 流式返回来实现“打字机”效果,如果代理层把流缓冲了,用户就会看到响应卡半天然后一次性蹦出来。

处理 SSE 流的时候,有几个细节必须注意:

第一,不要用res.json()或res.send(),那会把整个响应缓冲起来。要用pipe或者手动res.write()。

第二,SSE 的事件边界要保留。Anthropic 和 OpenAI 的 SSE 事件格式不同,转换的时候要保证data:前缀、事件类型、[DONE]结束标记都正确。

第三,超时设置要合理。AI 生成长文本可能几十秒,代理层的超时如果设太短,会中途断开。Node.js 默认的 socket 超时是 2 分钟,一般够用,但如果你接的模型特别慢,要手动调大。

// 流式转发时保留 SSE 格式 proxyRes.on('data', chunk => { // 如果需要在流中间做协议转换,在这里处理 chunk res.write(chunk); }); proxyRes.on('end', () => res.end());

6. 常见报错排查与避坑实录

6.1 报错速查表

我把这条链路上最常见的报错整理成了一张表,遇到问题先对号入座:

报错信息根本原因解决方向
your organization has disabled claude subscription access账号订阅权限被组织策略限制换账号、走 API key、或接第三方模型
codex无法加载组织设置网络请求未通或账号配置异常检查网络、重新登录、检查配置文件
the 'gpt-5.6-sol' model is not supported模型名映射错误检查 YAML 里的 alias 和实际模型名
cc switch local proxy failed while handling codex endpoint /responses代理层处理 Responses 端点时出错检查协议转换逻辑、看代理日志
error installing 24.21.0: node.js v24.21.0 is not yet releasedNode.js 版本号不存在或未发布改用 LTS 版本
YAML 解析失败缩进用了 tab、冒号后缺空格用空格缩进、冒号后加空格
端口被占用多个服务抢同一端口改配置里的端口号

6.2 代理层调试的三个实用技巧

技巧一:先关流式,用非流式请求验证协议转换。流式请求出问题时,很难判断是协议转换错了还是流处理错了。把stream设成false发一次请求,如果非流式能通,说明协议转换没问题,问题在流处理;如果非流式也不通,那就是协议映射本身错了。

技巧二:把代理层的请求和响应都打日志。在transformRequest前后各打一次,在转发前后各打一次。这样能清楚看到“进来的长什么样、转换后长什么样、目标服务返回什么”。很多问题看一眼日志就明白了。

console.log('[IN]', req.url, body.slice(0, 500)); console.log('[OUT]', JSON.stringify(transformed).slice(0, 500));

技巧三:用 curl 直接打代理端点,绕过 Claude Code 和 Codex。这样能排除工具本身的干扰,确认代理服务本身是好的:

curl -X POST http://127.0.0.1:8787/v1/messages \ -H "Content-Type: application/json" \ -d '{"model":"claude-sonnet-4","max_tokens":100,"messages":[{"role":"user","content":"hi"}]}'

如果 curl 能通,但 Claude Code 不通,那问题就在 Claude Code 的配置上,不在代理。

6.3 我踩过的几个坑

坑一:YAML 里的 tab。这个坑我踩过不止一次。从别处复制配置过来,看着缩进是对的,实际上混了 tab 和空格,解析直接失败。解决办法是编辑器里开启“显示空白字符”,一眼就能看出 tab。VS Code 里editor.renderWhitespace设成all就行。

坑二:环境变量没生效。${DEEPSEEK_API_KEY}这种插值,如果环境变量没 export,加载配置时会变成空字符串,然后请求目标服务就 401。排查的时候先echo $DEEPSEEK_API_KEY确认一下。

坑三:模型名大小写。有些服务的模型名是大小写敏感的,DeepSeek-Chat和deepseek-chat可能一个通一个不通。映射表里的名字最好从目标服务的文档里直接复制,别手打。

坑四:本地模型服务没起。接 LM Studio 的时候,忘了在 LM Studio 里点“Start Server”,代理转发过去直接 connection refused。这种低级错误排查起来反而费时间,因为你会一直怀疑是代理的问题。

7. 把 Claude Code 和 Codex 接到本地模型上的完整流程

7.1 本地模型服务准备

以 LM Studio 为例,先在 LM Studio 里加载一个模型(比如 Qwen2.5-Coder),然后在 Developer 标签页里启动本地服务,默认端口 1234。启动后用 curl 验证一下:

curl http://localhost:1234/v1/models

能返回模型列表,说明本地服务是好的。

7.2 启动 openrig 代理

把前面写的 YAML 配置存成openrig.yaml,然后启动代理:

export DEEPSEEK_API_KEY=sk-xxxx node proxy.js --config openrig.yaml

启动后看到openrig proxy on 8787就说明起来了。

7.3 配置 Claude Code 指向代理

Claude Code 通过环境变量指定 base URL:

export ANTHROPIC_BASE_URL=http://127.0.0.1:8787 export ANTHROPIC_API_KEY=dummy-key claude

这里的ANTHROPIC_API_KEY随便填一个非空值就行,因为真正的鉴权在代理层到目标服务那一段。Claude Code 只要求这个变量存在。

7.4 配置 Codex 指向代理

Codex 的配置类似,但变量名不同:

export OPENAI_BASE_URL=http://127.0.0.1:8787/v1 export OPENAI_API_KEY=dummy-key codex

注意 Codex 的 base URL 通常要带/v1后缀,具体看你的代理路由怎么配的。

7.5 验证端到端链路

配置完之后,在 Claude Code 里随便问一句,看响应是否正常。如果响应回来了,但内容不对(比如模型答非所问),那可能是协议转换里某个字段映射错了。如果完全没响应,看代理日志,确认请求有没有到代理、有没有转发出去。

8. 这套方案还能怎么扩展

openrig 这套代理思路,本质上是一个“协议适配 + 路由分发”的中间层,它的扩展空间比想象中大。

一个方向是多模型负载均衡。在 YAML 里给同一个 alias 配多个 provider,代理层按轮询或按延迟选一个转发。这样本地模型和云端模型可以混用,本地忙的时候自动切云端。

另一个方向是请求改写和增强。比如在代理层统一注入 system prompt,或者对请求做敏感词过滤、token 计数、成本统计。这些逻辑放在代理层,Claude Code 和 Codex 本身不用改。

还有一个方向是缓存。相同的请求直接返回缓存结果,省 token 也省时间。对于调试阶段反复问同一个问题的场景特别有用。

我自己在实际操作中的体会是,代理层这东西一旦搭起来,后面想接什么模型、想加什么功能,都是改配置和加中间件的事,不用动 Claude Code 和 Codex 本身。这种“把变化隔离在一层”的设计,才是 openrig 这类项目真正的价值所在。最后再分享一个小技巧:代理层的日志一定要带请求 ID,这样在 Claude Code、代理、目标服务三处日志之间对账的时候,能快速定位问题出在哪一段。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询