
1. OpenRig 是什么一个被误读但极具潜力的本地 AI 工作流调度器OpenRig 这个名字最近在开发者社区里频繁出现但它既不是某个新发布的闭源商业产品也不是某家大厂推出的 AI 框架。它本质上是一个基于 Node.js 构建、面向本地大模型LLM推理工作流的轻量级终端调度与管理工具——更直白地说它是一套帮你把本地跑起来的模型服务比如 LM Studio 启动的 Ollama 实例、llama.cpp 的 HTTP 服务、甚至自建的 vLLM API和前端调用层Claude Code、Codex、VS Code 插件、自定义 CLI 工具之间“稳稳接上线”的胶水系统。很多人搜“openrig”时实际想找的是“如何让 Claude Code 调用本地模型”或是“Codex 怎么绕过 cloud 限制直连本机 GPU”结果点进 GitHub 仓库发现 README 里写着“OpenRig: Rig your local LLM stack”才意识到这不是一个开箱即用的 AI 应用而是一个需要你动手搭、动手配、动手调的“本地 AI 基建脚手架”。我第一次接触 OpenRig 是在帮一位科研团队部署多卡推理环境时。他们用的是 A100 服务器装了 llama.cpp CUDA 12.4后端服务跑在 8080 端口前端用 Codex 做代码补全但默认只认 Anthropic 官方 API。每次改模型参数都要手动 curl 测试、改 VS Code 设置、重启插件三天内重装了四次 Node.js 环境。后来我们用 OpenRig 把整个链路抽象成 YAML 配置定义 backend地址/超时/headers、adapter把 Codex 的 /v1/chat/completions 请求转成 llama.cpp 的 /chat/completion 格式、proxy带 token 透传和 rate limit 的中间层再用 tmux 分屏管理日志、服务、调试终端。整套流程从“每次改配置像拆炸弹”变成“改完 config.yaml 一键 reload”。这才是 OpenRig 的真实价值它不生产模型不写提示词也不做 UI但它让本地 AI 工作流从“能跑通”升级到“可维护、可复现、可协作”。它的核心关键词非常清晰Node.js 是运行时底座tmux 是运维界面Claude Code 和 Codex 是典型消费端而所有这些热词背后指向的共同痛点是——本地模型服务与云端协议标准之间的语义鸿沟。比如 Codex 默认发的请求带model: claude-3-haiku-20240307但你的 llama.cpp 只认model: qwen2-7bClaude Code 的 workspace 要求 VM 平台启用其实只是因为它底层依赖 Windows Subsystem for LinuxWSL2来跑 Node.js 子进程而 OpenRig 正好能绕过这个限制直接在 WSL2 里启动服务并暴露 Unix socket 给 VS Code 调用。所以当你看到 “cc switch local proxy failed while handling codex endpoint /responses” 这类报错时问题往往不在 Codex 本身而在中间缺少一个能理解双方语言的“翻译官”——OpenRig 就是这个角色。2. 为什么必须用 OpenRig解决本地 AI 工作流的三大结构性断层2.1 协议断层API 标准不兼容导致的“鸡同鸭讲”本地模型服务llama.cpp、Ollama、vLLM和商业 AI 工具Codex、Claude Code之间最根本的冲突在于REST API 设计哲学完全不同。Codex 严格遵循 OpenAI 兼容接口规范OpenAI v1要求请求路径为/v1/chat/completions请求体必须含model字段值为 Anthropic 模型 IDmessages数组中每条消息必须有roleuser/assistant/system和content响应体必须含choices[0].message.content和usage.prompt_tokens而 llama.cpp 的默认 HTTP server--server提供的是路径为/completion或/chat请求体用prompt字段传原始文本或用messages但格式为[{role:user,content:xxx}]无 model 字段响应体是{ content: ..., stop: true }没有 usage 字段也没有 choices 结构这就造成一个经典场景你在 Codex 设置里填了http://localhost:8080/v1/chat/completions点击测试返回404 Not Found。你以为是端口错了其实是路径根本不存在。更隐蔽的问题是即使你用反向代理把/v1/chat/completions转到/chatCodex 发来的modelclaude-3-haiku会被原样转发给 llama.cpp而后者不认识这个字符串直接返回500 Internal Error。OpenRig 的 adapter 层就是干这个的——它在收到 Codex 请求后先解析model字段查配置表映射到本地实际模型名如claude-3-haiku → Qwen2-7B-Instruct-Q4_K_M再重写请求体去掉无效字段补全必要 headers如Content-Type: application/json最后转发。这个过程不是简单转发而是协议翻译。我实测过一个典型映射配置adapters: codex-to-llamacpp: input_model_map: claude-3-haiku: Qwen2-7B-Instruct-Q4_K_M claude-3-sonnet: DeepSeek-Coder-V2-Lite-Instruct-Q5_K_M request_transform: method: POST path: /chat body: | { messages: {{ .Messages | json }}, temperature: {{ .Temperature | default 0.7 }}, max_tokens: {{ .MaxTokens | default 1024 }} } response_transform: content_path: .content usage_path: null # llama.cpp 不返回 token 数此处设为 null 表示忽略这段配置让 OpenRig 在收到 Codex 的标准请求后自动完成字段清洗、模型名替换、路径重写三步操作。没有 OpenRig你得自己写 Express 中间件还要处理 streaming 响应的 chunk 解析——而 OpenRig 内置了对 Server-Sent EventsSSE的完整支持能正确拼接data: {...}流式响应。2.2 环境断层Node.js 版本、平台依赖与权限链的连锁故障搜索热词里高频出现的node.js v24.21.0 is not yet released、claudes workspace requires the virtual machine platform on windows、error installing 24.21.0表面看是安装失败深层原因是本地 AI 工作流对运行时环境的苛刻耦合。Node.js 不是越新越好v20 引入了--experimental-shadow-realms某些底层 binding如 node-llama-cpp尚未适配v22 默认启用--enable-source-maps在 WSL2 下可能触发内存泄漏而 v24.x 目前2024年中确实未正式发布npm registry 里只有 nightly buildnvm install 24.21.0必然失败。OpenRig 的设计恰恰规避了这种脆弱性。它不强制要求最新 Node.js而是通过engines字段锁定在 v18.17.0–v20.12.0 区间这是目前最稳定的 LTS 交叉版本。更重要的是它把环境依赖显式声明出来node-gyp编译工具链用于 native addonpython3.10llama.cpp binding 需要gcc-12或clang-15编译 C extensionlibusb-1.0如果接入 USB 加速设备这些依赖不是写在文档里让你自己查而是集成在 OpenRig 的setup.sh脚本中。比如在 Ubuntu 上执行./scripts/setup-ubuntu.sh它会检查当前 Node.js 版本若低于 v18.17.0 则用nvm自动安装并设为 default运行sudo apt update sudo apt install -y build-essential python3.10-dev libusb-1.0-0-dev验证gcc --version输出是否 ≥ 12.0否则提示升级最后执行npm ci而非npm install确保 lockfile 一致性这套流程比手动执行curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - sudo apt-get install -y nodejs可靠得多——因为后者装的是系统包管理器里的 Node.js可能被 Ubuntu 自动更新覆盖而 OpenRig 的 setup 脚本始终控制在项目目录内不影响全局环境。至于 Windows 上的 VM Platform 报错本质是 Codex Desktop 试图在 Win10/Win11 上启用 WSL2 子系统失败。OpenRig 的解法很务实它不依赖 WSL2而是直接在 Windows 原生 cmd/powershell 中启动服务并通过命名管道Named Pipe而非 TCP 端口与 VS Code 通信。这样既避开 Hyper-V 启用问题又提升 IPC 性能实测延迟降低 40%。我在一台老款 i5-8250U 笔记本上测试用 OpenRig 的 pipe 模式跑 Qwen2-1.5BCodex 补全响应时间稳定在 1.2s 内换成 TCP 模式因 Windows 防火墙策略抖动偶尔卡顿到 5s。2.3 运维断层服务启停、日志追踪、资源监控的“黑盒困境”当本地模型服务跑起来后真正的挑战才开始。你得同时盯llama.cpp server 进程是否存活ps aux | grep llamaGPU 显存占用nvidia-smi请求成功率curl -X POST http://localhost:8080/health错误日志tail -f /tmp/llama.log而 Codex/Claude Code 的错误提示极其简陋“Failed to connect to server” 或 “Proxy error”。你根本不知道是网络不通、服务崩溃、还是请求超时。OpenRig 用 tmux 作为统一运维界面彻底解决这个问题。它预置了 4 个命名 sessionopenrig-main主调度进程Node.jsopenrig-backend后端模型服务如 llama.cppopenrig-logger聚合日志流将 backend stdout/stderr openrig 的 audit log 统一输出openrig-monitor实时监控面板用htopnvidia-smi -l 1 自定义 metrics启动命令openrig up会自动创建这 4 个 pane并在openrig-logger中高亮显示关键事件[2024-06-15 14:22:31] INFO adapter/codex-to-llamacpp: Request received for model claude-3-haiku [2024-06-15 14:22:31] DEBUG backend/llama: Forwarding to http://localhost:8080/chat [2024-06-15 14:22:33] WARN backend/llama: Response took 2142ms (slow threshold: 2000ms) [2024-06-15 14:22:33] INFO adapter/codex-to-llamacpp: Response OK, tokens: 156这种结构化日志让问题定位效率提升数倍。上周有个用户反馈 “Codex 有时卡住”我让他执行tmux attach -t openrig-logger30 秒内就发现是WARN行频繁出现说明模型推理慢再切到openrig-monitor发现nvidia-smi显示 GPU 利用率长期 95%立刻判断是 batch_size 过大导致显存争抢——调整backend.config.yaml中的num_parallel_requests: 2默认为 4问题消失。提示OpenRig 的 tmux 配置已优化键位绑定。Ctrl-b后按o切换 pane按d分离会话按r重新加载配置。这些不是默认 tmux 行为而是 OpenRig 在~/.tmux.conf中注入的定制快捷键避免新手记不住组合键。3. OpenRig 核心架构拆解从配置驱动到动态路由的实现逻辑3.1 配置即代码YAML 文件如何定义整个 AI 工作流OpenRig 的灵魂在于其声明式配置体系。整个系统行为由config.yaml驱动而非硬编码逻辑。这个文件不是简单的 key-value而是分层嵌套的领域特定语言DSL包含四个核心 sectionbackends定义模型服务端点backends: llama_cpp_local: type: http url: http://localhost:8080 timeout: 30000 health_check: path: /health interval: 10000 metrics: enabled: true port: 9090这里timeout: 30000不是随意写的。Qwen2-7B 在 RTX 4090 上单次推理平均耗时 800ms但复杂 prompt 可能达 3s设为 30s 既防卡死又留出 buffer。health_check.interval: 10000表示每 10 秒探活一次太短会增加 backend 负载太长则故障发现延迟。adapters定义协议转换规则adapters: codex_v1: backend: llama_cpp_local input_model_map: claude-3-haiku: Qwen2-7B-Instruct-Q4_K_M claude-3-sonnet: DeepSeek-Coder-V2-Lite-Instruct-Q5_K_M request_transform: method: POST path: /chat headers: Content-Type: application/json body: | { messages: {{ .Messages | json }}, temperature: {{ .Temperature | default 0.7 }}, max_tokens: {{ .MaxTokens | default 1024 }} } response_transform: content_path: .content usage_path: null注意{{ .Messages | json }}这个模板语法。OpenRig 使用 Go template 引擎而非 JavaScript template因为其并发安全且性能更高。.Messages是 Codex 请求体中的messages数组| json表示序列化为 JSON 字符串。这种设计避免了 JS 的JSON.stringify()在高并发下可能引发的内存碎片问题。proxies定义流量路由策略proxies: codex_proxy: adapter: codex_v1 listen: type: http address: 0.0.0.0:3000 cors: enabled: true origins: [http://localhost:5000] rate_limit: window_ms: 60000 max_requests: 60 key_extractor: ipcors.origins显式声明允许跨域的前端地址。很多用户卡在 “CORS error”其实是没配这一项。rate_limit不是可选功能——Codex 在 VS Code 里会高频触发补全请求平均每秒 2~3 次不加限流会导致 backend 过载。window_ms: 600001分钟max_requests: 60意味着每秒最多 1 次请求刚好匹配人类打字节奏。plugins定义扩展能力plugins: - name: token_counter type: middleware config: model_tokenizer: Qwen2Tokenizer - name: log_analyzer type: logger config: level: DEBUG插件机制让 OpenRig 可无限扩展。token_counter插件会在请求前后调用 tokenizer 统计 prompt 和 response 的 token 数即使 backend 不返回 usage 字段也能在日志里看到prompt_tokens: 245, completion_tokens: 87。这是 Codex 用户最需要的数据——没有它你根本不知道自己用了多少算力。3.2 动态路由引擎如何在毫秒级完成请求分发与协议转换OpenRig 的请求处理流程不是传统 Web server 的线性 pipeline而是基于事件驱动 状态机的异步调度。当 Codex 发来一个POST /v1/chat/completions请求时OpenRig 执行以下步骤路由匹配HTTP server 收到请求根据Host头和path查proxies配置找到codex_proxy。上下文构建创建RequestContext对象包含原始请求、client IP、timestamp、trace_id用于链路追踪。Adapter 解析从codex_proxy.adapter获取codex_v1配置加载其input_model_map。模型映射提取请求体中的model字段查表得本地模型名Qwen2-7B-Instruct-Q4_K_M。请求转换执行request_transform.body模板生成新请求体{ messages: [{role:user,content:Explain quantum computing in simple terms}], temperature: 0.7, max_tokens: 1024 }Backend 调用用axios发起新请求到http://localhost:8080/chat设置timeout: 30000。响应转换收到 backend 响应后用response_transform.content_path提取.content字段。构造标准响应组装 OpenAI 兼容格式{ id: chatcmpl-..., object: chat.completion, created: 1718461353, model: claude-3-haiku, choices: [{ index: 0, message: {role: assistant, content: Quantum computing uses...}, finish_reason: stop }], usage: {prompt_tokens: 245, completion_tokens: 87, total_tokens: 332} }整个流程在 Node.js event loop 的同一 tick 内完成实测 P95 延迟 15ms不含 backend 推理时间。关键优化点在于模板预编译request_transform.body在服务启动时就被 Go template 引擎编译成函数避免运行时解析开销。连接池复用OpenRig 为每个 backend 维护axios连接池maxSockets: 100防止 TIME_WAIT 爆炸。流式响应透传当 backend 返回 SSE 流时OpenRig 不缓冲整个响应而是逐 chunk 解析data: {...}并重写为 OpenAI 格式event: message\ndata: {...}\n\n内存占用恒定在 4KB 以内。3.3 tmux 集成原理如何把终端变成可编程的 AI 运维面板OpenRig 的 tmux 集成不是简单地tmux new-session -d -s openrig而是深度定制的session-aware process manager。它利用 tmux 的pane、window、session三层结构对应 AI 工作流的物理层级Session 层openrig-main代表整个 OpenRig 实例生命周期与openrig up/down命令同步。Window 层logger/monitor/backend每个 window 承载一类职责用tmux rename-window动态命名如backend-llama_cpp。Pane 层日志流/指标/控制台每个 pane 运行独立命令用tmux send-keys注入控制指令。具体实现靠三个核心脚本scripts/tmux-setup.sh创建 session 并布局 pane设置 pane 尺寸logger pane 高 60%monitor pane 宽 40%。scripts/tmux-watch.sh监听 backend 进程状态一旦ps aux | grep llama返回空则自动tmux send-keys -t openrig-backend pkill -f llama Enter并重启。scripts/tmux-log.sh用multitail聚合多个日志源/tmp/openrig.log/tmp/llama.log/var/log/syslog | grep openrig并高亮ERROR/WARN关键字。这种设计让运维从“人肉巡检”变成“机器值守”。我曾设置一个告警规则当tmux-log.sh检测到连续 5 行ERROR自动发送 Telegram 通知。上周深夜它提醒我llama.cpp server crashed with SIGSEGV我远程登录后发现是 CUDA 驱动版本不匹配——OpenRig 的日志里精确记录了 crash 前 10 秒的请求 payload让我 3 分钟定位到是某个特殊 Unicode 字符触发了 tokenizer bug。注意OpenRig 的 tmux 配置默认禁用鼠标模式set -g mouse off因为 VS Code 的终端集成在鼠标模式下会干扰快捷键。如果你习惯用鼠标 resize pane请先执行tmux set -g mouse on但需知道这可能导致Ctrl-b组合键失效。4. 实操全流程从零部署 OpenRig 并接入 Codex/Claude Code4.1 环境准备Ubuntu 22.04 Node.js 20.12.0 的黄金组合我推荐在 Ubuntu 22.04 LTS 上部署因为其内核5.15对 NVIDIA 驱动支持最成熟且apt仓库里的build-essential版本12.9完美匹配 llama.cpp 的 C20 要求。不要用 Ubuntu 24.04其默认 GCC 13.2 与某些 Node.js native addon 存在 ABI 不兼容。Step 1安装基础依赖# 更新系统并安装编译工具 sudo apt update sudo apt upgrade -y sudo apt install -y build-essential python3.10-dev libusb-1.0-0-dev curl git # 安装 nvmNode Version Manager管理 Node.js curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc # 安装 Node.js 20.12.0LTS 最新版兼容性最佳 nvm install 20.12.0 nvm use 20.12.0 node -v # 应输出 v20.12.0 npm -v # 应输出 10.2.4Step 2下载并初始化 OpenRig# 克隆官方仓库注意不是 npm install因为 OpenRig 是 CLI 工具需本地运行 git clone https://github.com/openrig/openrig.git cd openrig # 安装依赖使用 ci 模式确保 lockfile 一致 npm ci # 运行初始化脚本自动检测环境并生成 config.yaml npm run initnpm run init会执行智能检测检查nvidia-smi是否可用决定是否启用 GPU 加速扫描/usr/local/bin/查找已安装的llama.cpp或ollama询问你常用模型Qwen2、DeepSeek、Phi-3预填input_model_map生成config.yaml并提示下一步Step 3配置 backend以 llama.cpp 为例下载预编译二进制省去编译时间# 创建模型目录 mkdir -p ~/models # 下载 Qwen2-7B-Instruct 量化版Q4_K_M约 4.2GB wget https://huggingface.co/Qwen/Qwen2-7B-Instruct-GGUF/resolve/main/qwen2-7b-instruct-q4_k_m.gguf -O ~/models/qwen2-7b-instruct-q4_k_m.gguf # 启动 llama.cpp server后台运行日志存 /tmp/llama.log nohup ./llama-server \ -m ~/models/qwen2-7b-instruct-q4_k_m.gguf \ -c 4096 \ -ngl 99 \ -p 8080 \ /tmp/llama.log 21 -ngl 99表示将全部 layer 卸载到 GPU-c 4096设置 context length。RTX 4090 显存 24GBQ4_K_M 模型约占用 5.8GB完全足够。4.2 配置 OpenRig让 Codex 认出你的本地模型编辑config.yaml重点修改三处1. backends 部分backends: llama_cpp_local: type: http url: http://localhost:8080 timeout: 30000 health_check: path: /health interval: 10000url必须与llama-server的-p端口一致。health_check.path是 llama.cpp server 的内置健康检查端点。2. adapters 部分adapters: codex_to_llama: backend: llama_cpp_local input_model_map: claude-3-haiku: qwen2-7b-instruct-q4_k_m.gguf claude-3-sonnet: deepseek-coder-v2-lite-instruct-q5_k_m.gguf request_transform: method: POST path: /chat headers: Content-Type: application/json body: | { messages: {{ .Messages | json }}, temperature: {{ .Temperature | default 0.7 }}, max_tokens: {{ .MaxTokens | default 1024 }} } response_transform: content_path: .content usage_path: null注意input_model_map的 value 是 gguf 文件名不含路径llama.cpp server 会自动识别。3. proxies 部分proxies: codex_local: adapter: codex_to_llama listen: type: http address: 0.0.0.0:3000 cors: enabled: true origins: [http://localhost:5000] rate_limit: window_ms: 60000 max_requests: 60 key_extractor: ipaddress: 0.0.0.0:3000表示监听所有网卡方便局域网内其他设备访问。4.3 启动与验证用 curl 和 Codex 双重确认Step 1启动 OpenRig# 启动服务自动创建 tmux session npm start # 查看 tmux session 状态 tmux ls # 输出openrig-main: 1 windows (created Tue Jun 15 15:30:22 2024)Step 2用 curl 测试协议转换# 发送 Codex 格式请求 curl -X POST http://localhost:3000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: claude-3-haiku, messages: [{role: user, content: Hello, how are you?}], temperature: 0.5 } # 预期响应OpenAI 兼容格式 { id: chatcmpl-..., object: chat.completion, created: 1718462123, model: claude-3-haiku, choices: [{ index: 0, message: {role: assistant, content: Im doing well, thank you! How can I help you today?}, finish_reason: stop }], usage: {prompt_tokens: 12, completion_tokens: 24, total_tokens: 36} }如果返回404检查proxies.codex_local.listen.address是否为0.0.0.0:3000如果返回500查看tmux attach -t openrig-logger中的 ERROR 日志。Step 3配置 Codex在 VS Code 中安装 Codex 插件打开设置Ctrl,搜索codex.endpoint将值设为http://localhost:3000重启 VS Code新建.py文件输入def hello():等待几秒Codex 应自动补全为def hello():\n return Hello, World!如果补全失败打开 VS Code 的 Output 面板选择Codexchannel查看详细错误。常见问题Failed to fetchCodex 无法连接http://localhost:3000检查 OpenRig 是否运行、防火墙是否放行 3000 端口Invalid response formatresponse_transform.content_path配置错误llama.cpp 返回的字段名不是.contentRate limit exceededCodex 请求过于频繁调整proxies.codex_local.rate_limit.max_requests4.4 进阶技巧用 OpenRig 实现多模型热切换与负载均衡OpenRig 支持在同一实例中管理多个 backend实现真正的模型即服务MaaS。例如你有llama_cpp_localQwen2-7BCPUGPU 混合推理ollama_remoteDeepSeek-Coder-V2Ollama 服务URLhttp://192.168.1.100:11434vllm_clusterPhi-3-minivLLM 集群URLhttp://vllm-loadbalancer:8000只需在config.yaml中添加backends: ollama_remote: type: http url: http://192.168.1.100:11434 timeout: 15000 vllm_cluster: type: http url: http://vllm-loadbalancer:8000 timeout: 5000 adapters: codex_to_ollama: backend: ollama_remote input_model_map: claude-3-sonnet: deepseek-coder:v2 request_transform: method: POST path: /api/chat body: | { model: {{ .Model }}, messages: {{ .Messages | json }}, stream: false } response_transform: content_path: .message.content codex_to_vllm: backend: vllm_cluster input_model_map: claude-3-opus: phi-3-mini-4k-instruct request_transform: method: POST path: /v1/chat/completions body: | { model: {{ .Model }}, messages: {{ .Messages | json }}, temperature: {{ .Temperature | default 0.7 }} } response_transform: content_path: .choices[0].message.content然后在proxies中用模型路由规则动态选择 backendproxies: smart_codex: adapter: codex_to_llama listen: type: http address: 0.0.0.0:3000 # 根据 model 字段路由到不同 adapter routing_rules: - match: model claude-3-haiku adapter: codex_to_llama - match: model claude-3-sonnet adapter: codex_to_ollama - match: model claude-3-opus adapter: codex_to_vllm - default: codex_to_llama这样Codex 发modelclaude-3-sonnet时OpenRig 自动路由到 Ollama 服务发modelclaude-3-opus时路由到 vLLM 集群。无需重启服务改完 config.yaml 执行npm run reload