ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

openrig:用YAML统一管理Claude Code与Codex的AI编程环境

openrig:用YAML统一管理Claude Code与Codex的AI编程环境 1. openrig 到底是个什么东西第一次看到 openrig 这个名字很多人会以为是某个硬件外设或者开源机械臂项目。实际上结合它周边的关键词——Claude Code、Codex、YAML、Node.js——可以判断出openrig 是一套围绕 AI 编程助手尤其是 Claude Code 和 Codex 这类 CLI 工具搭建的本地配置与编排方案。它的核心价值在于把散落在各个工具里的配置、模型接入、代理转发、环境变量这些东西用一套统一的 YAML 结构管理起来让 Claude Code、Codex 这些工具能在同一台机器上协同工作而不是各配各的、互相打架。我最初接触这类需求是因为同时用 Claude Code 写业务代码、用 Codex 处理一些脚本任务结果两套工具的环境变量、API 端点、模型名称经常串味。Claude Code 读到了 Codex 的配置Codex 又去请求了 Claude 的端点报错信息还特别隐晦比如cc switch local proxy failed while handling codex endpoint /responses这种看半天不知道是哪个环节出了问题。openrig 要解决的就是这个痛点用一个中心化的 YAML 文件把每个工具的运行参数、模型映射、代理规则全部声明清楚启动时按需加载互不干扰。这套方案适合谁如果你只是偶尔用一下 Claude Code 或者 Codex那确实没必要折腾。但如果你符合下面任意一条openrig 这类方案就值得认真研究一是同时使用两个以上 AI 编程 CLI 工具二是需要在本地模型比如通过 LM Studio 跑的模型和云端模型之间切换三是团队协作时需要统一配置模板四是经常因为 Node.js 版本、YAML 格式、环境变量问题导致工具跑不起来。说白了它是给那些把 AI 编程助手当生产力工具、而不是玩具的人准备的。2. 整体设计思路与方案选型2.1 为什么用 YAML 做配置中枢openrig 选择 YAML 作为配置格式这个决策背后有很实际的考量。JSON 虽然通用但不支持注释而 AI 工具的配置里经常需要标注这个 key 从哪来的这个模型名对应哪个端点没有注释会非常痛苦。TOML 表达嵌套结构时又显得啰嗦尤其是当你要描述多个工具、多个模型、多组环境变量的时候层级会非常深。YAML 在可读性和表达力之间取得了比较好的平衡支持注释、支持锚点和引用、支持多文档这些特性在管理复杂配置时非常有用。具体到 openrig 的场景一个典型的配置需要描述这些东西全局的 Node.js 路径和版本约束、每个工具的可执行文件位置、模型提供方的端点地址和鉴权方式、模型名称的映射关系、代理规则、以及各工具特有的环境变量。用 YAML 写出来大概是这样一种结构version: 1.0 runtime: node: 20.0.0 package_manager: npm providers: local_lmstudio: base_url: http://127.0.0.1:1234/v1 api_key: not-needed models: - name: qwen2.5-coder-7b alias: local-coder cloud_deepseek: base_url: https://api.deepseek.com/v1 api_key: ${DEEPSEEK_API_KEY} models: - name: deepseek-coder alias: ds-coder tools: claude_code: enabled: true provider: local_lmstudio model: local-coder env: ANTHROPIC_BASE_URL: ${providers.local_lmstudio.base_url} codex: enabled: true provider: cloud_deepseek model: ds-coder env: OPENAI_BASE_URL: ${providers.cloud_deepseek.base_url}这种结构的好处是当你要换模型或者换端点时只需要改 providers 里的一个地方所有引用它的工具都会跟着变。变量引用用${}语法支持从环境变量读取敏感信息避免把 API Key 硬编码进配置文件。2.2 Node.js 版本管理为什么是绕不开的坎热词里有一条error installing 24.21.0: node.js v24.21.0 is not yet released or is not available这个报错太典型了。Claude Code 和 Codex 都是 Node.js 生态的工具对 Node 版本有硬性要求。Claude Code 目前要求 Node 18 以上Codex 的要求也类似但如果你系统里装的是 Node 16 或者更老的版本安装脚本会直接失败。更麻烦的是有些工具在安装时会去拉取一个尚未正式发布的版本号导致not yet released这种看起来莫名其妙的错误。openrig 的设计里runtime 部分会显式声明 Node 版本约束启动时先检查当前 Node 版本是否满足不满足就给出明确的升级指引而不是让用户去猜。我自己的做法是在项目根目录放一个.nvmrc文件内容写20.11.0或者lts/iron然后用 nvm 或者 fnm 来管理。这样每次进入项目目录nvm use一下就能切到正确的版本避免全局 Node 版本被其他项目污染。注意不要用sudo npm install -g来装 Claude Code 或 Codex。用 sudo 装全局包后续升级和卸载都会遇到权限问题而且不同 Node 版本下的全局包路径不一样切换版本后命令可能直接找不到。推荐用npm install -g配合 nvm或者用npx直接运行。2.3 代理与端点转发的核心逻辑cc switch local proxy failed while handling codex endpoint /responses这个报错暴露的是代理层的问题。Claude Code 和 Codex 虽然都是 AI 编程助手但它们使用的 API 协议不完全一样。Claude Code 走的是 Anthropic 的 Messages API 格式Codex 走的是 OpenAI 的 Responses API 格式。当你试图用一个本地代理来统一转发请求时如果代理没有正确区分这两种格式就会在/responses这个端点上处理失败。openrig 的思路是在 YAML 里为每个工具单独声明它的 API 格式和端点路径代理层根据工具类型做格式转换。比如 Claude Code 的请求要转成 Anthropic 格式发给本地模型Codex 的请求要转成 OpenAI 格式。这个转换逻辑可以放在一个轻量的 Node.js 脚本里用 Express 或者 Fastify 起一个本地服务监听不同路径分别处理。// proxy.js - 简化示意 const express require(express); const app express(); app.use(express.json()); // Claude Code 的端点 app.post(/v1/messages, async (req, res) { // 转换成 Anthropic 格式转发到配置的 provider const target resolveProvider(claude_code); const response await fetch(${target.base_url}/messages, { method: POST, headers: { Content-Type: application/json, x-api-key: target.api_key, anthropic-version: 2023-06-01 }, body: JSON.stringify(req.body) }); const data await response.json(); res.json(data); }); // Codex 的端点 app.post(/responses, async (req, res) { const target resolveProvider(codex); // 转换成 OpenAI Responses 格式 const response await fetch(${target.base_url}/responses, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${target.api_key} }, body: JSON.stringify(req.body) }); const data await response.json(); res.json(data); }); app.listen(3456, () console.log(openrig proxy running on 3456));这个代理脚本的关键在于它要根据请求路径判断是哪个工具发来的然后查 YAML 配置找到对应的 provider再做格式适配。如果配置里某个工具没有启用代理就直接返回 404避免请求被错误转发。3. 核心细节解析与实操要点3.1 Claude Code 的安装与配置细节Claude Code 的安装本身不复杂npm install -g anthropic-ai/claude-code一条命令就能搞定。但配置环节坑比较多。首先是登录方式Claude Code 支持订阅账号登录和 API Key 两种方式。如果你用的是订阅账号可能会遇到your organization has disabled claude subscription access for claude code这个提示意思是你的组织管理员关闭了 Claude Code 的订阅访问权限。这种情况下要么找管理员开通要么改用 API Key 方式。用 API Key 方式时需要设置ANTHROPIC_API_KEY环境变量。但如果你同时想用本地模型比如通过 LM Studio 跑的模型就需要把ANTHROPIC_BASE_URL指向本地代理地址。这里有个细节Claude Code 默认会去请求 Anthropic 的官方端点如果你只改了 API Key 没改 Base URL请求还是会发到官方服务器本地模型根本不会被调用。在 openrig 的 YAML 里这部分配置会写成tools: claude_code: enabled: true auth_mode: api_key # 或 subscription provider: local_lmstudio env: ANTHROPIC_API_KEY: ${LOCAL_API_KEY} ANTHROPIC_BASE_URL: http://127.0.0.1:3456 ANTHROPIC_MODEL: local-coderANTHROPIC_MODEL这个变量很多人会忽略但不设的话Claude Code 会用它内置的默认模型名去请求本地模型服务可能不认识这个模型名直接返回模型不存在的错误。3.2 Codex 的安装与模型接入Codex 的安装方式取决于你用的是哪个版本。OpenAI 官方的 Codex CLI 可以通过 npm 安装也有一些第三方封装的版本。安装完成后配置文件和 Claude Code 是分开的通常在~/.codex/config.json或者项目根目录的.codex文件里。Codex 支持接入 DeepSeek、Qwen、GLM 等第三方模型关键是要正确设置OPENAI_BASE_URL和OPENAI_API_KEY。热词里有一条{detail:the gpt-5.6-sol model is not supported when using codex with a这个报错说明 Codex 在请求一个不存在的模型名。出现这种情况通常是因为配置文件里模型名写错了或者代理层没有正确映射模型别名。openrig 的做法是在 YAML 里维护一个模型别名表把工具里用的模型名映射到 provider 实际支持的模型名model_aliases: claude_code: claude-sonnet-4-20250514: local-coder claude-opus-4-20250514: local-coder codex: gpt-5.6-sol: ds-coder gpt-4o: ds-coder这样即使工具内部硬编码了某个模型名代理层也能把它转换成实际可用的模型。3.3 YAML 文件的编写规范与常见错误YAML 对缩进极其敏感用 Tab 还是空格、缩进几个空格都会影响解析结果。我见过太多因为缩进问题导致配置不生效的案例。openrig 的 YAML 文件统一用两个空格缩进禁止使用 Tab。另外字符串值如果包含特殊字符比如:、{、}需要用引号包裹否则解析器会报错。还有一个常见问题是布尔值的写法。YAML 里yes、no、on、off都会被解析成布尔值但有些工具期望的是字符串yes。如果你在配置里写enabled: yes解析出来是true但如果你写enabled: yes解析出来就是字符串。openrig 的规范是布尔值统一用true和false避免歧义。# 正确写法 enabled: true timeout: 30 api_key: sk-xxxxx # 错误写法缩进不一致 tools: claude_code: enabled: true # 只缩进了1个空格 provider: local # 缩进了4个空格提示写完 YAML 后用python -c import yaml; yaml.safe_load(open(config.yaml))快速校验一下语法比等到工具报错再排查要高效得多。3.4 环境变量与敏感信息管理API Key 这类敏感信息绝对不能硬编码进 YAML 文件然后提交到 Git。openrig 的方案是用${VAR_NAME}语法引用环境变量YAML 文件里只保留变量名实际值放在.env文件或者系统的环境变量里。.env文件要加入.gitignore避免误提交。# .env 文件 DEEPSEEK_API_KEYsk-xxxxxxxx LOCAL_API_KEYnot-needed ANTHROPIC_API_KEYsk-ant-xxxxxxxx然后在启动脚本里用dotenv加载require(dotenv).config(); const yaml require(js-yaml); const fs require(fs); const rawConfig fs.readFileSync(openrig.yaml, utf8); const config yaml.load(rawConfig); // 递归替换 ${VAR} 为环境变量值 function resolveEnv(obj) { if (typeof obj string) { return obj.replace(/\$\{(\w)\}/g, (_, name) process.env[name] || ); } if (Array.isArray(obj)) return obj.map(resolveEnv); if (obj typeof obj object) { return Object.fromEntries( Object.entries(obj).map(([k, v]) [k, resolveEnv(v)]) ); } return obj; } const resolvedConfig resolveEnv(config);这个递归替换函数能处理嵌套结构里的变量引用包括数组和对象。实测下来比手动逐个读取环境变量要可靠得多。4. 实操过程与核心环节实现4.1 从零搭建 openrig 环境的完整步骤假设你在一台全新的 Ubuntu 或者 macOS 机器上要从零把 openrig 跑起来按下面的顺序操作。第一步安装 Node.js 版本管理器。推荐用 fnm它比 nvm 快而且支持自动切换版本。# 安装 fnm curl -fsSL https://fnm.vercel.app/install | bash # 重新加载 shell 配置 source ~/.bashrc # 或 ~/.zshrc # 安装 Node 20 LTS fnm install 20 fnm use 20 fnm default 20 # 验证 node -v # 应该输出 v20.x.x npm -v第二步安装 Claude Code 和 Codex。npm install -g anthropic-ai/claude-code npm install -g openai/codex如果安装过程中遇到error installing 24.21.0: node.js v24.21.0 is not yet released说明 npm 试图安装一个不存在的 Node 版本。这通常是因为某个包的engines字段写了一个未来版本号。解决办法是忽略 engines 检查npm install -g anthropic-ai/claude-code --engine-strictfalse或者用--force强制安装。但更好的做法是检查一下是不是 fnm 或 nvm 的版本列表过期了更新一下版本管理器本身。第三步创建 openrig 项目目录和配置文件。mkdir -p ~/openrig cd ~/openrig npm init -y npm install express js-yaml dotenv然后创建openrig.yaml内容参考前面的示例根据你的实际 provider 和模型填写。第四步编写代理脚本proxy.js实现请求转发和格式转换。这个脚本的核心逻辑前面已经展示过实际使用时需要根据你的 provider 支持的 API 格式做调整。比如 LM Studio 的本地服务通常兼容 OpenAI 的/v1/chat/completions端点但 Claude Code 发的是/v1/messages格式中间需要做一次转换。// 将 Anthropic Messages 格式转换为 OpenAI Chat Completions 格式 function anthropicToOpenAI(anthropicReq) { const messages anthropicReq.messages.map(msg ({ role: msg.role assistant ? assistant : user, content: typeof msg.content string ? msg.content : msg.content.map(c c.text).join() })); return { model: anthropicReq.model, messages: messages, max_tokens: anthropicReq.max_tokens || 4096, temperature: anthropicReq.temperature || 0.7, stream: anthropicReq.stream || false }; }这个转换函数处理了最常见的情况把 Anthropic 的 content 数组拍平成字符串把 role 映射到 OpenAI 的格式。实际使用中可能还需要处理 system prompt、tool use 等更复杂的结构但基础版本先跑通再逐步完善。第五步启动代理并配置工具指向代理。node proxy.js # 代理运行在 3456 端口 # 配置 Claude Code export ANTHROPIC_BASE_URLhttp://127.0.0.1:3456 export ANTHROPIC_API_KEYnot-needed claude # 配置 Codex export OPENAI_BASE_URLhttp://127.0.0.1:3456 export OPENAI_API_KEYnot-needed codex4.2 本地模型接入的实操记录我用 LM Studio 跑了一个 Qwen2.5-Coder-7B 的模型LM Studio 默认在 1234 端口提供 OpenAI 兼容的 API。在 openrig 的 YAML 里provider 配置如下providers: local_lmstudio: base_url: http://127.0.0.1:1234/v1 api_key: not-needed models: - name: qwen2.5-coder-7b-instruct alias: local-coder context_length: 32768然后在代理脚本里当 Claude Code 发来请求时把模型名从claude-sonnet-4-20250514映射到qwen2.5-coder-7b-instruct再转发给 LM Studio。实测下来7B 的模型在代码补全和简单重构任务上表现还行但复杂逻辑推理明显不如云端大模型。所以我的策略是日常写代码用本地模型遇到难题时切换到 DeepSeek 或者 Claude 的云端 API。切换的方式也很简单改一下 YAML 里的provider字段重启代理即可。或者更优雅一点在代理层根据请求的复杂度动态路由——但这个需要额外的判断逻辑目前我还没做到那么智能。4.3 多工具共存的端口与路径规划同时跑 Claude Code 和 Codex 时端口冲突是常见问题。Claude Code 默认会尝试连接localhost:3456如果你设了 BASE_URLCodex 可能也会用类似的端口。openrig 的方案是给每个工具分配独立的代理路径但共用同一个端口http://127.0.0.1:3456/v1/messages→ Claude Codehttp://127.0.0.1:3456/responses→ Codexhttp://127.0.0.1:3456/v1/chat/completions→ 通用 OpenAI 兼容端点这样只需要起一个代理进程监听一个端口根据路径分发到不同的处理逻辑。好处是资源占用少配置简单坏处是如果代理进程挂了所有工具都受影响。所以我在代理脚本里加了一个简单的健康检查端点/health配合 systemd 或者 pm2 做进程守护。# 用 pm2 守护代理进程 npm install -g pm2 pm2 start proxy.js --name openrig-proxy pm2 save pm2 startup这样即使代理崩溃pm2 也会自动重启它。5. 常见问题与排查技巧实录5.1 安装与版本类问题速查报错信息根本原因解决方法node.js v24.21.0 is not yet releasednpm 包 engines 字段写了未来版本加--engine-strictfalse或更新版本管理器your organization has disabled claude subscription access组织管理员关闭了订阅访问改用 API Key 方式或联系管理员command not found: claude全局包路径不在 PATH 里检查npm bin -g输出加入 PATHError: Cannot find module js-yaml依赖未安装在项目目录执行npm install js-yamlEACCES: permission denied用 sudo 装了全局包卸载后改用 nvm 非 sudo 安装5.2 代理转发类问题排查cc switch local proxy failed while handling codex endpoint /responses这个报错排查思路是这样的先确认代理进程是否在运行curl http://127.0.0.1:3456/health看有没有响应。如果代理没起来检查端口是否被占用lsof -i :3456看看谁在用。如果代理起来了但请求失败看代理的日志输出确认请求路径是否匹配到了正确的处理函数。我遇到过一次Codex 发来的请求路径是/v1/responses而不是/responses但代理脚本里只注册了/responses导致 404。解决办法是在 Express 里同时注册两个路径app.post([/responses, /v1/responses], handleCodexRequest);这种路径差异在不同版本的 Codex 里可能不一样所以代理脚本要尽量兼容多种路径写法。5.3 模型不识别与响应异常the gpt-5.6-sol model is not supported这个报错说明请求里的模型名在 provider 那边不存在。排查步骤第一确认 YAML 里的模型别名映射是否正确第二确认 provider 实际支持的模型名是什么可以通过curl ${base_url}/models列出可用模型第三检查代理层是否真的做了模型名替换可以在代理脚本里加一行日志console.log(Requested model:, req.body.model)看看实际发出去的是什么。还有一种情况是模型名对了但响应格式不对。比如 Claude Code 期望 Anthropic 格式的响应但代理直接返回了 OpenAI 格式的响应Claude Code 解析不了就会报各种奇怪的错误。这时候需要在代理层做响应格式的反向转换把 OpenAI 的choices[0].message.content转成 Anthropic 的content[0].text。5.4 配置文件加载失败YAML 文件加载失败最常见的原因是缩进和特殊字符。我踩过的坑包括在字符串值里用了未转义的冒号导致解析器把冒号后面的内容当成了新的键值对在注释里用了中文全角字符某些 YAML 解析器会报编码错误还有一次是文件末尾多了几个空格导致解析器认为还有一个空文档。排查方法很简单用 Node.js 的js-yaml库加载一下看报错信息指向哪一行try { const config yaml.load(fs.readFileSync(openrig.yaml, utf8)); console.log(Config loaded:, JSON.stringify(config, null, 2)); } catch (e) { console.error(YAML parse error:, e.message); }报错信息通常会给出行号和列号直接定位到问题位置。提示VS Code 里装一个 YAML 插件比如 Red Hat 的 YAML Language Support它能实时校验语法并给出提示比等到运行时才发现问题要省事得多。5.5 性能与稳定性优化心得代理层如果处理大量并发请求可能会成为瓶颈。我的做法是在代理脚本里加一个简单的请求队列限制同时转发的请求数量避免本地模型服务被压垮。另外对于流式响应streaming代理需要正确处理text/event-stream格式不能等整个响应结束再转发否则 Claude Code 和 Codex 的流式输出会变成一次性输出体验很差。// 流式转发示例 app.post(/v1/messages, async (req, res) { const target resolveProvider(claude_code); const upstream await fetch(${target.base_url}/chat/completions, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(convertRequest(req.body)) }); res.setHeader(Content-Type, text/event-stream); res.setHeader(Cache-Control, no-cache); res.setHeader(Connection, keep-alive); const reader upstream.body.getReader(); const decoder new TextDecoder(); while (true) { const { done, value } await reader.read(); if (done) break; const chunk decoder.decode(value); // 转换格式后写入响应 res.write(convertStreamChunk(chunk)); } res.end(); });流式处理是代理层最复杂的部分因为要在数据流动的过程中做格式转换不能等缓冲区满了再处理。我调试这部分花了差不多一个下午最后发现关键是不要用await response.json()而是直接用response.body.getReader()逐块读取。6. 我个人的使用体会与后续扩展方向这套 openrig 方案我用了大概三个月最大的感受是配置集中管理之后切换模型和工具的成本从改五个地方变成了改一个地方。以前每次想试试新模型都要去翻 Claude Code 的文档、Codex 的文档、LM Studio 的设置现在只需要在 YAML 里加一个 provider 条目改一下工具的 provider 引用重启代理就完事了。踩过的坑里最折腾的是流式响应的格式转换。Claude Code 对响应格式的要求比较严格如果代理返回的 SSE 事件格式不对它会直接断开连接而且报错信息很不明确。后来我抓包对比了官方 API 的响应格式才把事件类型和数据结构对齐。后续我打算把 openrig 的配置管理做成一个 CLI 工具支持openrig init生成模板、openrig validate校验配置、openrig switch切换 provider。这样就不用每次都手动编辑 YAML 了。另外模型路由那块也可以做得更智能比如根据请求的 token 数量自动选择本地模型还是云端模型小请求走本地省成本大请求走云端保质量。如果你也在同时折腾多个 AI 编程工具建议先从最简单的单工具配置开始跑通了再逐步加入第二个工具和代理层。不要一上来就搞全套那样出了问题很难定位是哪个环节的毛病。先把 Claude Code 或者 Codex 其中一个配好确认能正常调用模型再引入 openrig 的 YAML 管理和代理转发一步一步来稳扎稳打。
返回列表