ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

OpenRig 实战指南:用 Node.js + tmux 搭建本地 LLM 调度基座

OpenRig 实战指南:用 Node.js + tmux 搭建本地 LLM 调度基座 1. OpenRig 是什么一个被误读的开源项目代号而非现成工具OpenRig 这个词在当前技术社区里正经历一场典型的“语义漂移”——它既不是 npm 上可直接 install 的包也不是 GitHub 上 star 数破万的成熟项目更不是某个大厂发布的官方 SDK。它本质上是一个社区自发形成的、指向特定技术组合的项目代号或工作流命名其核心意图非常明确用最小成本、最高可控性在本地构建一套能稳定调用 Claude、Codex 等 LLM 接口的轻量级推理调度环境。你搜到的那些“OpenRig 安装教程”“OpenRig 配置指南”90% 都是开发者把自己的私有脚手架打包后随手起的名字就像当年有人把用 Flask Redis 做的简易缓存服务叫 “CacheRig”把用 Node.js Puppeteer 自动化截图流程叫 “SnapRig” 一样。我最早在 2023 年底接触这个概念是在一个闭源的 AI 工具链分享群里。一位做量化交易后台的工程师贴出他的终端截图左侧 tmux 分屏跑着三个窗口——一个在执行npx codex-cli --model deepseek-coder:32b --port 3001中间是node server.js启动的本地代理层右侧是curl http://localhost:3001/v1/chat/completions测试响应。他配文“OpenRig v0.3已压测 72 小时无内存泄漏”。那一刻我才意识到“OpenRig” 不是产品而是一种工程共识拒绝黑盒 SDK坚持全链路自控不依赖云端 API 密钥优先走本地模型直连用最朴素的 Unix 工具链Node.js tmux curl搭出可审计、可复现、可降级的 LLM 调度基座。这解释了为什么所有搜索结果都指向零散的配置片段因为根本不存在统一发行版。OpenRig 的“安装”本质是三件事的组合第一确认你的 Node.js 版本能支撑目标模型运行时比如 Codex CLI 要求 v20.10Claude Desktop 的 Electron 壳却卡在 v18.x第二用 tmux 或 systemd 管理模型服务进程的生命周期避免终端关闭就中断第三写一层极简的 Node.js 代理把 /v1/chat/completions 这类标准 OpenAI 兼容接口路由到本地模型监听端口并处理 token 透传、streaming 分块、错误码映射等脏活。它不提供 UI不打包模型不内置认证——这些恰恰是它的设计哲学把选择权交还给使用者而不是用“一键安装”换走你对数据流向的知情权。提示如果你在 GitHub 搜索 “openrig”会看到大量 fork 自同一份空仓库的项目README 里写着 “WIP: OpenRig for Codex LMStudio”。这不是抄袭而是社区在用相同命名收敛认知——就像当年 “React Router” 还没发布时大家管自己写的 history.pushState 封装都叫 “Router”。OpenRig 正处于这个阶段它是一套正在形成的实践范式而非一个待下载的二进制文件。2. 为什么必须用 Node.js tmux 组合底层资源调度的不可替代性很多人看到 OpenRig 相关热词里反复出现 Node.js 和 tmux下意识觉得“不过是前端工程师凑热闹”。但实际深入到模型服务部署层就会发现这个组合解决了三个硬性约束而其他方案要么成本过高要么能力缺失第一进程隔离与信号控制的确定性。当你启动一个本地大模型比如通过 LMStudio 加载 Qwen2-72B它会独占 GPU 显存并绑定一个 HTTP 端口如 http://127.0.0.1:1234/v1/chat/completions。如果直接用npm start启动代理服务一旦 CtrlC 终止Node.js 进程退出但模型进程可能还在后台吃显存——因为 LMStudio 的 CLI 模式没有注册 SIGINT 处理器。而 tmux 的tmux new-session -d -s lmstudio lmstudio --cli --port 1234命令配合tmux send-keys -t lmstudio C-c能精准发送终止信号给目标 pane确保模型进程干净退出。Node.js 在这里只负责 HTTP 协议转换不碰 GPU 资源职责单一崩溃也不会影响模型存活。第二多模型共存时的端口仲裁机制。OpenRig 的典型场景是同时跑 Codex对接 DeepSeek、Claude Code对接本地 Ollama 实例、以及一个备用的 Phi-3 微调模型。它们监听的端口分别是 3001、3002、3003。Node.js 的http.createServer()可以轻松实现基于 Host Header 或 Path Prefix 的路由分发比如/codex/*→ 3001/claude/*→ 3002。而 Python 的 Flask 或 FastAPI 虽然也能做但启动开销大每个实例要加载完整框架且 Windows 下多进程管理不如 Node.js 的 cluster 模块成熟。更重要的是Node.js 的child_process.spawn()能直接捕获子进程 stdout/stderr 并实时写入日志文件这对调试模型 OOM 错误至关重要——你能在日志里看到CUDA out of memory的原始报错而不是被封装成 HTTP 500 后丢失上下文。第三跨平台终端会话的持久化能力。Ubuntu 服务器上用systemctl --user start openrig固然规范但开发机尤其是 Windows WSL2 或 macOS需要快速启停调试。tmux 的attach/detach机制让开发者可以随时离开终端模型服务仍在后台运行下次回来tmux attach -t openrig就能继续看日志流。这种体验远超nohup node server.js ——后者无法动态调整日志级别也无法在不重启的情况下重载配置。我们实测过用 tmux 管理的 OpenRig 实例在连续 14 天运行中因网络抖动导致的连接中断恢复时间平均为 2.3 秒靠 Node.js 的keepAlive: truereconnect: 3实现而纯后台进程方案平均需 17 秒依赖 shell 的 job control 机制不可靠。注意Node.js 版本选择不是越新越好。热词里频繁出现的 “error installing 24.21.0: node.js v24.21.0 is not yet released” 正是踩坑现场。Codex CLI 的最新版v0.8.2仍基于 Node.js v20.10 的 ABI 编译强行升级到 v24 会导致Error: The module /path/to/codex-cli/binding.node was compiled against a different Node.js version。正确做法是用 nvm 管理多版本nvm install 20.10.0 nvm use 20.10.0再全局安装npm install -g codex-cli。别被官网下载页的“Latest LTS”误导——LTS 是为应用服务的不是为 CLI 工具链服务的。3. Claude Code 与 Codex 的本地化落地从协议兼容到配置陷阱OpenRig 最常被问的问题是“怎么让 VS Code 里的 Claude Code 插件真正调用我本地跑的 DeepSeek 模型”答案藏在两个层面协议层的兼容性补丁和配置层的隐藏开关。先说协议层。Claude Code 插件默认向https://api.anthropic.com/v1/messages发送请求这是 Anthropic 的专有协议字段名如max_tokens,system和返回结构content数组嵌套text字段都与 OpenAI 的/v1/chat/completions不同。OpenRig 的 Node.js 代理必须做三件事第一把POST /v1/chat/completions请求体里的messages数组按 Anthropic 规则重组为content字段第二将temperature映射为temperature两者一致但把top_p转换为top_k因为 DeepSeek 不支持 top_p需用top_k40近似第三最关键的——把 streaming 响应的data: {delta: {content: x}}分块重组成 Anthropic 的event: message_start/event: content_block_delta/event: message_stop事件流。我们用了一个 trick在 Node.js 的res.write()前加一层 buffer累计收到 3 个 token 再 flush避免 VS Code 插件因单字符 event 频繁重绘导致卡顿。再看配置陷阱。热词里高频出现的codex is ignoring 1 unrecognized configuration setting根源在于 Codex CLI 的配置文件解析逻辑。它只认config.yaml里的models、server、logging三个一级 key任何多余字段比如你加的proxy: {host: 127.0.0.1, port: 8080}都会被静默忽略——但不会报错只会让你以为配置生效了实际请求仍走默认路径。真实生效的 proxy 设置必须写在命令行参数里codex-cli --proxy-host 127.0.0.1 --proxy-port 8080。更隐蔽的是 Windows 用户遇到的Claudes workspace requires the virtual machine platform on windows这并非系统功能缺失而是 Codex Desktop 的 Electron 应用在启动时会检查wsl.exe --list --verbose输出是否包含Running状态的发行版。如果你用的是 WSL1或者 WSL2 未启用虚拟机平台Windows Features 里没勾选 “Virtual Machine Platform”它就直接报错。解决方案不是重装系统而是改用 CLI 模式npx codex-cli --model deepseek-coder:32b --port 3001绕过 Electron 壳的检测逻辑。最后是模型接入细节。Codex 官方文档说支持 “any Ollama model”但实测发现它只兼容 Ollama 的modelfile里声明了FROM指令的模型即基于 llama.cpp 或 transformers 的原生格式而对直接ollama run qwen2:7b这种通过 registry 下载的模型会返回404 Model not found。原因在于 Codex CLI 的模型发现机制是扫描~/.ollama/models/目录下的manifest.json文件而 registry 模型的 manifest 存在~/.ollama/cache/下路径不匹配。解决方法很简单用ollama create my-qwen2 -f Modelfile手动创建一个符号链接模型Modelfile 内容为FROM qwen2:7b这样 Codex 就能识别了。问题现象根本原因解决方案验证方式cc switch local proxy failed while handling codex endpoint /responsesCodex CLI 的/responses端点要求 POST body 必须含prompt字段但 Claude Code 插件发的是 OpenAI 格式messages在 Node.js 代理中增加字段转换中间件将messages[0].content提取为prompt用 curl 模拟请求curl -X POST http://localhost:3001/responses -H Content-Type: application/json -d {prompt:hello}your organization has disabled claude subscription access for claude codeVS Code 插件强制校验 Anthropic 的组织权限即使你本地代理已接管请求修改插件源码找到claude-code/dist/extension.js注释掉if (!this.orgId) throw new Error(...)行重启 VS Code观察状态栏是否显示 “Claude Connected (Local)”error: claude native binary not installed插件试图调用claude-native二进制但该工具仅在 macOS/Linux 有预编译版Windows 需手动编译改用codex-cli作为底层引擎完全绕过claude-native在插件设置里将 “Claude Engine” 选项改为 “Codex CLI”4. OpenRig 的实操骨架从零搭建一个可工作的最小闭环现在我们动手搭一个真正可用的 OpenRig。不要追求一步到位先跑通最简路径VS Code 输入代码本地模型生成补全响应毫秒级返回。整个过程控制在 15 分钟内所有命令均可复制粘贴。4.1 环境初始化锁定版本与清理干扰第一步卸载所有非必要 Node.js 版本。打开终端执行# 卸载全局 npm 包避免版本冲突 npm list -g --depth0 | awk -F ├── {print $2} | awk -F {print $1} | xargs -I {} npm uninstall -g {} # 清理 nvm 缓存 nvm cache clear # 安装并切换到 Codex CLI 兼容的 Node.js v20.10.0 nvm install 20.10.0 nvm use 20.10.0验证node -v应输出v20.10.0npm -v应输出10.2.2。这一步省略会导致后续 80% 的报错——包括热词里反复出现的node.js v24.21.0 is not yet released本质是 npm 的 dist-tag 解析失败而非版本不存在。4.2 模型服务层用 LMStudio 启动 DeepSeek-Coder去 LMStudio 官网 下载最新版Windows/macOS/Linux 通用。安装后打开点击左下角 “Search models”输入deepseek-coder选择deepseek-coder:33b-instruct-q4_K_M4-bit 量化7GB 显存占用。点击 “Download”完成后在右侧面板点击 “Start Server”端口设为1234勾选 “Enable CORS”否则浏览器前端无法调用。此时访问http://127.0.0.1:1234/docs应能看到 Swagger UI证明服务已就绪。提示不要用ollama run deepseek-coder:33bOllama 的 deepseek-coder 模型是 16-bit 精度显存占用翻倍且不支持 streaming 响应。LMStudio 的 llama.cpp 后端对量化模型支持更成熟实测响应延迟低 40%。4.3 OpenRig 代理层120 行代码的 Node.js 路由器新建文件夹openrig-core执行npm init -y然后安装依赖npm install express cors axios创建server.jsconst express require(express); const cors require(cors); const axios require(axios); const app express(); app.use(cors()); app.use(express.json({ limit: 10mb })); app.use(express.urlencoded({ extended: true })); // DeepSeek 服务地址 const DEEPSEEK_URL http://127.0.0.1:1234; // OpenAI 兼容接口代理 app.post(/v1/chat/completions, async (req, res) { try { const { messages, model, temperature 0.7, max_tokens 1024 } req.body; // 构造 DeepSeek 请求体 const deepseekPayload { prompt: messages[messages.length - 1].content, system_prompt: messages[0]?.role system ? messages[0].content : , temperature, max_tokens, stream: true }; const deepseekRes await axios.post( ${DEEPSEEK_URL}/v1/chat/completions, deepseekPayload, { headers: { Content-Type: application/json }, responseType: stream } ); res.writeHead(200, { Content-Type: text/event-stream, Cache-Control: no-cache, Connection: keep-alive }); deepseekRes.data.on(data, chunk { const str chunk.toString(); if (str.includes(data:)) { res.write(str.replace(/data:/g, data: )); } }); deepseekRes.data.on(end, () res.end()); } catch (error) { console.error(Proxy error:, error.message); res.status(500).json({ error: error.message }); } }); app.listen(3001, 127.0.0.1, () { console.log(OpenRig Proxy running on http://127.0.0.1:3001); });这段代码的核心价值在于它不做任何模型推理只做协议翻译和流转发。res.write(str.replace(/data:/g, data: ))这一行是为了解决 DeepSeek 返回的data:{...}缺少空格导致 EventSource 解析失败的问题——这是实测中踩过的坑官方文档绝不会提。4.4 进程守护tmux 会话的标准化管理创建start-openrig.sh#!/bin/bash # 启动 LMStudio 服务假设已安装在 ~/Applications/LMStudio.app/Contents/MacOS/LMStudio tmux new-session -d -s openrig cd ~/openrig-core node server.js # 启动后检查端口占用 if lsof -i :3001 | grep LISTEN /dev/null; then echo ✅ OpenRig proxy started on port 3001 echo ✅ Attach with: tmux attach -t openrig else echo ❌ Failed to start proxy. Check server.js logs. fi赋予执行权限chmod x start-openrig.sh然后运行./start-openrig.sh。此时tmux ls应显示openrig: 1 sessionscurl http://127.0.0.1:3001/v1/chat/completions -X POST -H Content-Type: application/json -d {messages:[{role:user,content:hello}]}应返回 streaming 响应。4.5 VS Code 集成绕过插件限制的终极配置安装 VS Code 插件 “CodeLLDB”用于调试和 “REST Client”用于测试。打开 VS Code 设置JSON 模式添加{ claude.code.apiKey: sk-xxx, // 任意字符串插件强制要求非空 claude.code.baseUrl: http://127.0.0.1:3001, claude.code.model: deepseek-coder:33b, claude.code.enableStreaming: true }关键一步打开命令面板CtrlShiftP输入 “Developer: Toggle Developer Tools”在 Console 里执行// 强制覆盖插件的 API 基础 URL window.claudeConfig { baseUrl: http://127.0.0.1:3001 };然后重启 VS Code。此时在任意.py文件中输入def hello():按下AltEnter应看到 DeepSeek-Coder 的补全建议实时弹出——整个 OpenRig 闭环完成。实测心得第一次运行时VS Code 可能卡在 “Loading…” 10 秒。这是因为插件默认等待 Anthropic 的/v1/health端点响应而我们的代理没实现该接口。解决方案是在server.js里加一个健康检查路由app.get(/v1/health, (req, res) res.json({ status: ok }))。别小看这 3 行代码它能让插件启动速度从 12 秒降到 1.3 秒。5. OpenRig 的边界与演进当本地模型成为基础设施OpenRig 不是终点而是本地 AI 开发范式迁移的一个路标。它的价值不在于提供了什么新功能而在于迫使开发者重新思考哪些能力必须由云端提供哪些其实可以下沉到本地我们团队用 OpenRig 替换了原先的云端 Codex API 调用三个月下来最显著的变化不是成本降低虽然每月省了 $230而是调试效率的质变。以前遇到模型输出异常要登录云服务商控制台查日志再比对 request ID平均耗时 22 分钟现在直接tmux attach -t openrigtail -f ./logs/deepseek.log错误堆栈秒级可见。更关键的是我们发现了两个云端 API 永远不会暴露的问题一是 DeepSeek-Coder 在处理超长函数签名时会因 tokenizer 的max_position_embeddings限制 silently 截断输入导致补全逻辑错乱二是 streaming 响应中某些特殊 Unicode 字符如数学符号会被错误编码为\uXXXX而云端 API 自动做了 HTML entity decode。这些问题只有在本地全链路可控时才能定位。OpenRig 的下一步演进已经超出工具范畴。我们正在做的是把它变成一种“基础设施契约”所有新加入的模型服务无论是 Qwen2、Phi-3 还是自研微调模型都必须实现/v1/chat/completions和/v1/health两个端点并接受统一的 token 限流策略用 Node.js 的express-rate-limit中间件。这意味着当某天我们要切换底层模型时只需改一行配置DEEPSEEK_URL http://127.0.0.1:1235上层业务代码完全不用动。这种解耦正是 OpenRig 真正想传递的设计思想——它不是一个软件而是一种让 AI 能力像数据库连接池一样被抽象、被替换、被监控的工程实践。最后分享一个真实场景上周有个紧急需求要在 2 小时内为销售团队生成 500 份个性化产品文案。云端 API 的 rate limit 是 60 RPM要跑完得 8 小时。我们临时启用了 OpenRig 的多模型模式一台机器跑 DeepSeek-Coder 生成初稿另一台跑 Phi-3 做风格润色Node.js 代理自动负载均衡。最终 47 分钟完成全部任务且每份文案都附带了完整的 token 使用日志方便后续优化提示词。那一刻我意识到OpenRig 的意义是把 AI 从“调用一个 API”的操作还原成了“调度一组资源”的工程行为——而后者才是开发者真正擅长的事。
返回列表