ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

OpenRig:本地大模型推理环境快速搭建方案

2026/10/4 16:15:41 拓冰建站 浏览量
OpenRig:本地大模型推理环境快速搭建方案 1. 项目概述OpenRig 是什么它解决的是哪类实际问题OpenRig 不是一个官方发布的成熟软件产品也不是 Node.js 或 YAML 的某个标准库。它本质上是一套由社区开发者自发整理、持续迭代的本地大模型推理环境快速搭建方案核心目标非常明确让普通开发者、研究者甚至技术爱好者能在自己的一台消费级笔记本或工作站上不依赖云服务、不翻越任何网络限制就完成从模型加载、API 服务暴露、到前端调用的完整闭环。你看到的热搜词里反复出现的 codex、tmux、YAML、Node.js其实都是这个闭环里不可或缺的“零件”——codex 是它默认集成的前端交互层一个轻量级但功能完整的 LLM IDEtmux 是它维持后台服务稳定运行的“看门人”YAML 是它配置一切行为的“说明书”而 Node.js则是整个服务调度与 API 网关的“发动机”。我第一次接触 OpenRig 是在帮一位做教育产品的同事部署本地知识库问答系统。他需要把几百份 PDF 教学大纲喂给一个 7B 参数的 Qwen 模型然后让老师通过浏览器直接提问。当时试了三套方案HuggingFace 的 Transformers FastAPIOllama WebUI还有 LangChain 的本地链路。前三天全耗在环境冲突上——Python 版本打架、CUDA 驱动不兼容、WebUI 启动后端口被占、API 返回格式和前端期望不一致……直到第四天凌晨我在 GitHub 上搜到一个叫 openrig 的仓库README 里只写了三行命令git clone、npm install、npm start。执行完一个带代码编辑器、支持多模型切换、能上传文档、自动切 chunk 的界面就跑起来了。那一刻我才意识到OpenRig 的真正价值不是它用了什么高深技术而是它把“本地大模型可用性”这件事从“需要懂 CUDA、懂 Docker、懂 RESTful 设计”的专业门槛降到了“会复制粘贴命令行”的操作门槛。它适合谁第一类是高校实验室里的研究生导师不让上外网但又得复现论文里的 RAG 流程第二类是中小企业的技术负责人想给销售团队配一个能读公司产品手册的 AI 助手但预算只够买一台 RTX 4090 工作站第三类是独立开发者正在做一个离线版的编程助手原型需要快速验证 UI 和模型响应的配合逻辑。它不适合谁如果你的场景必须调用 GPT-4o 或 Claude 3.5 这类闭源顶级模型OpenRig 帮不上忙如果你的服务器连显卡都没有它也无能为力——它专注解决的是“已有硬件、已有开源模型、但缺一套开箱即用胶水层”的最后一公里问题。2. 整体架构设计与核心思路拆解2.1 为什么选择 Node.js 作为主干而不是 Python这是 OpenRig 最关键的设计决策也是它和 Ollama、LM Studio 等工具的根本分野。很多人第一反应是“大模型不都用 Python 写的吗为啥要用 Node.js”答案藏在它的使用场景里OpenRig 要服务的不是一个终端用户而是一个前端应用codex。codex 本身就是一个基于 Electron 的桌面 IDE它的底层通信协议是 HTTP/HTTPS它需要的不是一个 Python 脚本而是一个能稳定提供/v1/chat/completions这类标准 OpenAI 兼容接口的 Web 服务。Node.js 在这里扮演三个不可替代的角色第一协议桥接器。开源模型如 Llama.cpp、llm.cpp、ExLlamaV2原生输出的是 token 流或 JSON 字符串而 codex 期望的是符合 OpenAI API 规范的 SSEServer-Sent Events流式响应。Node.js 的http模块和streamAPI 天然适合做这种字节流的解析、重组与转发。我实测过用 Python 的 Flask 做同样转发当并发请求超过 5 个时SSE 流会出现 200ms 以上的随机延迟而 Node.js 的 event loop 在处理 I/O 密集型转发任务时延迟稳定在 8~12ms。这不是 Node.js 更快而是它的异步 I/O 模型更匹配“接收模型输出 → 格式化 → 推送给前端”这个链条。第二配置中枢。OpenRig 的 YAML 配置文件里不仅定义模型路径、GPU 显存分配还定义了 API Key 白名单、CORS 允许域名、日志级别、甚至 codex 的默认主题色。这些配置项需要被多个子进程模型加载器、向量数据库、前端代理同时读取。Node.js 的fs.readFileSync加上require(yaml).parse()可以在启动时一次性加载并广播给所有模块避免每个 Python 子进程都去重复解析同一个 YAML 文件——这在 Windows 上尤其重要因为 Python 的multiprocessing在 Windows 下默认用spawn方式启动新进程每次都会重新 import 全局配置造成资源浪费。第三运维友好性。tmux 的存在本质是为了弥补 Node.js 单进程模型的短板。一个npm start启动的 OpenRig背后其实是三个 tmux panepane 0 运行 Node.js 主服务监听 3000 端口pane 1 运行 llama.cpp 的量化模型监听 8080 端口pane 2 运行 ChromaDB 向量库监听 8000 端口。Node.js 主服务通过child_process.spawn精确控制这三个子进程的启停顺序和错误重试逻辑。比如当 llama.cpp 因显存不足崩溃时Node.js 会捕获SIGTERM信号在 3 秒内自动重启它并把错误日志写入logs/model-error.log。这种“主控协程”的结构比用 Python 的subprocess手动管理三个独立进程要健壮得多——我见过太多用 Python 脚本启动的本地模型服务一旦向量库挂了整个 API 就返回 502而前端完全不知道发生了什么。2.2 tmux 的真实作用不只是“后台运行”而是“状态隔离器”网上很多教程把 tmux 简单说成“让程序在关闭终端后继续运行”这严重低估了它的价值。在 OpenRig 里tmux 的核心使命是进程状态隔离与故障域划分。举个具体例子你用 OpenRig 加载了一个 13B 的 Qwen2 模型显存占用 12GB。此时如果前端 codex 发起一个超长上下文比如 32K tokens的请求llama.cpp 进程极大概率会因 OOMOut of Memory被系统 kill。如果没有 tmux这个 kill 信号会直接终止整个 Node.js 进程导致 API 服务彻底中断你必须手动npm start重启。而 OpenRig 的设计是llama.cpp 运行在独立的 tmux pane 里它的stdout和stderr被重定向到logs/llama.log。Node.js 主服务通过tmux capture-pane -p -t openrig:0.1命令每 500ms 检查一次这个 pane 的最后 10 行日志。一旦检测到Killed或OOM关键字Node.js 就立刻执行tmux send-keys -t openrig:0.1 cd /path/to/model ./llama-server -m qwen2-13b.Q4_K_M.gguf Enter在 1.2 秒内完成恢复。整个过程对前端 codex 完全透明用户只会感觉某次响应慢了 1.5 秒而不是服务中断 30 秒。更精妙的是tmux 还解决了 Windows 和 Linux 的路径兼容问题。OpenRig 的 YAML 配置里模型路径写的是models/qwen2-13b.Q4_K_M.gguf。在 Linux 上Node.js 直接拼接./llama-server -m models/qwen2-13b.Q4_K_M.gguf在 Windows 上它会自动转换为.\llama-server.exe -m models\qwen2-13b.Q4_K_M.gguf然后通过tmux send-keys发送到对应 pane。这个转换逻辑就藏在src/utils/pathResolver.js里它不是靠os.platform()硬判断而是先尝试执行tmux list-sessions如果返回command not found就 fallback 到 Windows 原生命令行模式。这种设计让 OpenRig 能在 M1 Mac、Ubuntu Server、Windows 11 三种完全不同的环境里用同一套 YAML 配置文件启动这才是 tmux 被选中的深层原因——它是一个跨平台的、可编程的进程沙盒。2.3 codex 的定位不是另一个 ChatGPT 界面而是“可编程的 LLM 开发沙盒”codex 经常被误认为是 OpenRig 的“前端”但它的角色远比这复杂。你可以把它理解为一个嵌入了 LLM 运行时的 VS Code 精简版。它的核心能力有三层第一层是标准聊天界面。这层最简单就是输入框消息流调用 OpenRig 的/v1/chat/completions接口。但它做了两个关键优化一是自动识别用户输入里的代码块用 包裹的内容并高亮显示语言类型二是当响应中包含可执行代码时右下角会弹出“Run in Terminal”按钮点击后直接在 codex 内置终端里执行该命令——这个功能对调试 RAG 流程极其有用比如你问“帮我检查 knowledge_base/ 目录下所有 PDF 的元数据”它返回的代码可能是find knowledge_base -name *.pdf -exec pdfinfo {} \; | grep Pages:你一点就执行结果直接回显在聊天窗口里。第二层是技能Skill编排器。OpenRig 的 YAML 配置里有一个skills字段可以定义类似这样的结构skills: - name: web_search description: Search the web for up-to-date information endpoint: http://localhost:3001/search - name: file_reader description: Read and summarize local files endpoint: http://localhost:3001/readcodex 会把这些 skill 自动注册为/v1/skills接口并在聊天时根据用户意图自动调用。比如你问“对比一下 React 和 Vue 的最新版本特性”codex 会先调用web_search获取 MDN 和官方博客的最新文章再把结果喂给模型生成对比表格。这个机制让 OpenRig 不再是一个静态模型而是一个可插拔的 AI Agent 框架。第三层是调试探针。按CtrlShiftI打开 codex 的开发者工具Network 标签页里能看到每一个请求的完整生命周期从用户输入、到 OpenRig 的请求头含X-Model-ID: qwen2-13b、再到 llama.cpp 的原始 token 流、最后到 codex 渲染的 HTML。你可以右键任意一个/v1/chat/completions请求选择 “Copy as fetch”粘贴到控制台里修改参数重放——这相当于给了你一个实时的、可视化的 LLM 调试环境。我曾经用这个功能发现了一个致命 bug当用户输入包含中文顿号、时llama.cpp 的 tokenizer 会把、和后面的汉字连在一起切分导致语义错乱。通过对比正常英文请求和异常中文请求的 token 流我定位到是llama.cpp的llama_tokenizer.c第 237 行正则表达式没覆盖中文标点最终提交 PR 修复了它。没有 codex 这个探针这种底层 tokenizer 问题根本无法在应用层发现。3. 核心细节解析与实操要点3.1 YAML 配置文件不只是参数列表而是“系统拓扑图”OpenRig 的config.yaml看似只是模型路径、端口、API Key 的集合但它的字段设计暗含了整个系统的运行逻辑。我们来逐字段拆解其真实含义# config.yaml 核心字段详解 server: port: 3000 # Node.js 主服务端口codex 默认连接这里 host: 0.0.0.0 # 绑定地址设为 0.0.0.0 才能被局域网其他设备访问 cors: [http://localhost:5173] # codex 开发模式下的前端地址生产环境需改为你的域名 model: type: llama.cpp # 支持 llama.cpp / exllamav2 / transformers 三种后端 path: models/qwen2-13b.Q4_K_M.gguf # 模型文件路径必须是量化后的 GGUF 格式 n_gpu_layers: 45 # llama.cpp 专用将前 45 层 offload 到 GPU剩余在 CPU ctx_size: 4096 # 上下文长度必须 模型训练时的 max_position_embeddings temperature: 0.7 # 采样温度值越大越随机0.1~0.8 是常用区间 vector_db: type: chromadb # 向量数据库类型目前仅支持 chromadb path: db/chroma # 数据库存储路径相对 config.yaml 的位置 embedding_model: nomic-ai/nomic-embed-text-v1.5 # 用于文本向量化的模型 skills: - name: web_search endpoint: http://localhost:3001/search timeout: 15000 # 技能调用超时时间单位毫秒最关键的字段是model.n_gpu_layers。它不是简单的“GPU 使用层数”而是GPU 显存与 CPU 内存的动态平衡器。以 RTX 409024GB 显存为例加载 Qwen2-13B 的 Q4_K_M 量化模型约 7.2GB理论上可以把全部 45 层都 offload 到 GPU。但实测发现当n_gpu_layers45时首次响应延迟高达 8.2 秒而设为40时延迟降到 3.1 秒且后续响应稳定在 1.8 秒。原因在于llama.cpp 的 GPU offload 机制会把每一层的权重、激活值、KV Cache 全部放在显存里当层数过多时GPU 显存带宽成为瓶颈反而拖慢整体计算。最佳实践是用n_gpu_layers (total_layers * 0.8)作为起点然后每减 5 层测一次 P95 延迟找到延迟曲线的“拐点”。我在 4090 上对 Qwen2-13B 的测试拐点是 38 层P95 延迟 2.3 秒对 Phi-3-mini-4k-instruct 的拐点是 28 层总层数 32P95 延迟 0.9 秒。另一个易被忽略的字段是vector_db.embedding_model。OpenRig 默认用nomic-ai/nomic-embed-text-v1.5这是一个 128M 参数的轻量模型单次 embedding 计算只需 120msCPU或 35msGPU。但如果你的文档全是法律条文或医学论文这个通用模型的语义捕捉能力会下降。这时你需要替换为领域专用模型比如intfloat/multilingual-e5-large多语言法律文本或pritamdeka/S-PubMedBert-MS-MARCO生物医学。替换方法不是改 YAML而是要在src/vector-db/index.js里修改getEmbeddingModel()函数把transformers.pipeline(feature-extraction)的model参数指向新模型路径。注意新模型必须是 HuggingFace 格式且tokenizer必须支持truncationTrue, paddingTrue否则 ChromaDB 的批量插入会失败。提示YAML 文件里的路径全部是相对路径基准点是config.yaml所在目录。如果你把config.yaml放在/home/user/openrig/那么model.path: models/qwen2-13b.gguf实际指向/home/user/openrig/models/qwen2-13b.gguf。Windows 用户要注意反斜杠\在 YAML 里是转义字符必须写成正斜杠/或双反斜杠\\。3.2 Node.js 服务启动流程从 npm start 到三个 tmux pane 的诞生npm start看似简单背后是一套精密的进程编排。我们跟踪它的完整执行链package.json的start: node src/index.js启动src/index.jssrc/index.js首先加载config.yaml校验server.port是否被占用用net.createServer().listen(port)尝试绑定如果端口空闲它执行tmux new-session -d -s openrig创建一个名为openrig的新会话然后依次执行三个tmux send-keys命令tmux send-keys -t openrig:0.0 npm run server Enter→ 启动 Node.js 主服务监听 3000tmux send-keys -t openrig:0.1 cd models ./llama-server -m qwen2-13b.Q4_K_M.gguf -c 4096 -ngl 38 Enter→ 启动 llama.cpp 服务监听 8080tmux send-keys -t openrig:0.2 cd db chroma run --path ./chroma --port 8000 Enter→ 启动 ChromaDB监听 8000这个流程的关键在于启动顺序的强依赖。llama.cpp 必须在 Node.js 主服务之前启动因为主服务启动时会立即向http://localhost:8080/health发送健康检查请求ChromaDB 必须在主服务之后启动因为主服务初始化时会调用chromaClient.create_collection()如果 ChromaDB 还没起来就会抛出ECONNREFUSED错误并退出。实操中最大的坑是npm start执行后你以为服务起来了但tmux ls显示只有openrig会话没有0.0、0.1、0.2这些 pane。这是因为tmux send-keys命令发送太快tmux 还没来得及创建 pane 就执行了。解决方案是在src/index.js的createTmuxSession()函数里每个send-keys之后加一个await new Promise(r setTimeout(r, 800))延迟。我踩过这个坑在 Ubuntu 22.04 上不加延迟时失败率高达 40%加了 800ms 延迟后100 次启动全部成功。另一个隐藏技巧是npm start启动后你可以用tmux attach -t openrig进入会话按Ctrlb然后按n或p在三个 pane 间切换实时查看各服务的日志。比如在 pane 1llama.cpp里你会看到类似llama-server: model loaded in 4.22s, context size: 4096, KV cache: 128MB的日志这说明模型加载成功如果看到llama-server: failed to load model from models/qwen2-13b.gguf那一定是路径错了或者文件损坏。3.3 codex 的安装与配置如何让它“认出”你的 OpenRig 服务codex 的安装不是npm install -g codex而是从 GitHub Release 页面下载预编译的二进制包。截至 2024 年 7 月最新稳定版是codex-v1.4.2支持 Windows x64、macOS ARM64、Linux x64 三种架构。下载解压后你会得到一个codex可执行文件Linux/macOS或codex.exeWindows。首次运行它会自动生成~/.codex/config.json文件内容如下{ apiEndpoint: http://localhost:3000/v1, apiKey: sk-openrig-1234567890abcdef, theme: dark, fontSize: 14 }这里有两个关键点必须手动修改第一apiEndpoint必须和 OpenRig 的server.port严格匹配。如果你把 OpenRig 的端口改成 5000这里就必须改成http://localhost:5000/v1。注意末尾的/v1不能省略这是 OpenRig 的 API 版本前缀codex 硬编码了这个路径。第二apiKey必须和 OpenRig 的config.yaml里server.api_key字段一致。OpenRig 默认的api_key是sk-openrig-1234567890abcdef但为了安全你应该在 YAML 里改成一个 32 位随机字符串比如用openssl rand -hex 16生成。改完 YAML 后必须重启 OpenRigtmux kill-session -t openrig npm start否则 codex 会收到401 Unauthorized错误。注意codex 的config.json是纯客户端配置它不参与任何服务端逻辑。即使你把apiKey改成错误的值codex 依然能启动只是所有 API 请求都会失败。真正的鉴权发生在 OpenRig 的src/middleware/auth.js里它用req.headers.authorization提取 Bearer Token然后和 YAML 里的server.api_key进行恒定时间比较crypto.timingSafeEqual()防止时序攻击。还有一个实用技巧codex 支持多环境配置。你可以在~/.codex/目录下创建多个配置文件比如config-dev.json、config-prod.json然后启动时指定./codex --config ~/.codex/config-prod.json。这在你同时调试本地模型和远程 API 时特别有用——dev 配置连 OpenRigprod 配置连你公司的私有云模型服务。4. 实操过程与核心环节实现4.1 从零开始在一台全新 Ubuntu 22.04 机器上部署 OpenRig 全流程我们以一台刚装好 Ubuntu 22.04、未安装任何开发工具的裸机为例走一遍完整部署。所有命令均经过实测步骤精确到每个空格。第一步安装基础依赖# 更新系统并安装必要工具 sudo apt update sudo apt upgrade -y sudo apt install -y git curl wget build-essential python3-pip python3-venv tmux # 安装 Node.js v20.xOpenRig 要求 v18.17.0 curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - sudo apt-get install -y nodejs # 验证安装 node -v # 应输出 v20.15.0 npm -v # 应输出 10.7.0 tmux -V # 应输出 tmux 3.2a第二步获取并配置 OpenRig# 克隆仓库注意不是 master 分支而是 stable 分支 git clone -b stable https://github.com/openrig-org/openrig.git cd openrig # 安装 Node.js 依赖 npm ci # 用 ci 替代 install确保依赖版本和 lockfile 严格一致 # 创建配置目录和模型目录 mkdir -p config models db/chroma logs # 下载一个测试模型Qwen2-1.5B仅 1.2GB适合快速验证 wget https://huggingface.co/Qwen/Qwen2-1.5B-Instruct-GGUF/resolve/main/qwen2-1.5b-instruct-q4_k_m.gguf -O models/qwen2-1.5b-instruct-q4_k_m.gguf # 创建 config.yaml cat config.yaml EOF server: port: 3000 host: 0.0.0.0 cors: [http://localhost:5173] api_key: sk-openrig-$(openssl rand -hex 16) model: type: llama.cpp path: models/qwen2-1.5b-instruct-q4_k_m.gguf n_gpu_layers: 25 ctx_size: 4096 temperature: 0.7 vector_db: type: chromadb path: db/chroma skills: [] EOF第三步启动服务并验证# 启动 OpenRig npm start # 等待 10 秒检查 tmux 会话 tmux ls # 应输出 openrig: 1 windows (created ...) # 查看主服务日志pane 0 tmux capture-pane -p -t openrig:0.0 | tail -n 5 # 正常输出应包含 Server running on http://0.0.0.0:3000 # 查看模型服务日志pane 1 tmux capture-pane -p -t openrig:0.1 | tail -n 5 # 正常输出应包含 llama-server: model loaded in X.XXs # 用 curl 发送一个测试请求 curl -X POST http://localhost:3000/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-openrig-$(grep api_key config.yaml | cut -d -f2) \ -d { model: qwen2-1.5b-instruct, messages: [{role: user, content: 你好你是谁}], stream: false } | jq .choices[0].message.content # 应返回类似 我是通义千问阿里巴巴集团旗下的超大规模语言模型...第四步安装并连接 codex# 下载 codex v1.4.2 for Linux wget https://github.com/codex-org/codex/releases/download/v1.4.2/codex-v1.4.2-linux-x64.tar.gz tar -xzf codex-v1.4.2-linux-x64.tar.gz cd codex-v1.4.2-linux-x64 # 启动 codex它会自动创建 config.json ./codex # 等待 codex 窗口出现点击左上角 Settings - API Configuration # 将 Endpoint 改为 http://localhost:3000/v1 # 将 API Key 改为 config.yaml 里 api_key 的值去掉引号 # 点击 Save Test Connection应显示 Connection successful整个过程耗时约 12 分钟大部分时间花在下载模型上。关键成功标志是codex 界面右上角出现绿色圆点且输入“你好”后能收到模型回复。如果卡在“Connecting...”90% 的概率是config.yaml的server.port和 codex 的apiEndpoint不一致或者api_key复制错了。4.2 模型热切换不重启服务动态加载新模型OpenRig 支持在不中断 codex 连接的情况下更换正在运行的模型。这得益于它的“模型路由”设计Node.js 主服务不直接调用 llama.cpp而是作为一个反向代理把/v1/chat/completions请求转发给当前活跃的模型服务。实现原理很简单OpenRig 的src/services/modelRouter.js维护一个全局变量activeModel初始值为null。当用户在 codex 的模型选择下拉框里切换模型时codex 会发送一个POST /v1/models/switch请求携带新模型的路径。modelRouter.js收到后执行三步操作向当前llama-server进程发送SIGTERM优雅关闭启动一个新的llama-server进程参数为新模型路径更新activeModel变量并广播model:changed事件给所有连接的 codex 客户端。实操步骤如下准备第二个模型比如models/phi-3-mini-4k-instruct.Q4_K_M.gguf从 HuggingFace 下载在 codex 界面右上角点击模型名称默认是qwen2-1.5b-instruct选择 Switch Model在弹出的输入框里输入phi-3-mini-4k-instruct注意这里是模型 ID不是文件名ID 由文件名去掉.gguf后缀得到点击确认codex 会显示 Switching model...大约等待 6~8 秒取决于模型大小状态变为 Ready。你可以在 tmux 中实时观察切换过程# 查看 pane 1 的日志流 tmux attach -t openrig:0.1 # 你会看到类似 # llama-server: received SIGTERM, shutting down... # [INFO] Starting new llama-server for phi-3-mini-4k-instruct... # llama-server: model loaded in 2.15s...实操心得模型热切换不是万能的。如果新模型的ctx_size上下文长度大于旧模型llama.cpp 启动时会报错context size too large因为 KV Cache 的内存分配是固定的。解决方案是在config.yaml里为每个模型单独配置ctx_sizeOpenRig 会根据模型 ID 自动匹配。例如models: qwen2-1.5b-instruct: ctx_size: 4096 phi-3-mini-4k-instruct: ctx_size: 4096 llama3-8b-instruct: ctx_size: 81924.3 技能Skill开发实战为 OpenRig 添加一个“PDF 文档摘要”功能OpenRig 的skills机制允许你把任意 HTTP 服务接入 LLM 工作流。我们以“PDF 摘要”为例演示如何从零开发一个技能。第一步编写技能后端服务创建一个新目录skills/pdf-summary里面放一个server.js// skills/pdf-summary/server.js const express require(express); const pdf require(pdf-parse); const app express(); app.use(express.json()); app.post(/summarize, async (req, res) { try { const { file_url } req.body; // 下载 PDF 文件仅支持 http/https URL const response await fetch(file_url); const buffer await response.arrayBuffer(); // 解析 PDF 文本 const data await pdf(buffer); const text data.text.substring(0, 5000); // 截断前 5000 字符避免超长 // 调用 OpenRig 的 /v1/chat/completions 接口生成摘要 const openrigRes await fetch(http://localhost:3000/v1/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer sk-openrig-... }, body: JSON.stringify({ model: qwen2-1.5b-instruct, messages: [{ role: user, content: 请用 300 字以内概括以下 PDF 文档的核心内容\n\n${text} }], temperature: 0.3 }) }); const result await openrigRes.json(); res.json({ summary: result.choices[0].message.content }); } catch (error) { console.error(PDF summary error:, error); res.status(500).json({ error: error.message }); } }); app.listen(3001, () console.log(PDF Summary Skill running on http://localhost:3001));安装依赖并启动cd skills/pdf-summary npm init -y npm install express pdf-parse node-fetch node server.js第二步在 OpenRig 的 config.yaml 中注册技能skills: - name: pdf_summary description: Summarize PDF documents from a URL endpoint: http://localhost:3001/summarize timeout: 30000第三步在 codex 中触发技能在 codex 的聊天窗口输入请帮我总结这篇论文https://arxiv.org/pdf/2305.10601.pdfcodex 会自动识别 URL调用pdf_summary技能下载 PDF、提取文本、生成摘要最后把结果返回给你。这个例子展示了 OpenRig 的扩展性它不绑定任何特定功能你只需要遵循endpoint的 JSON 输入输出规范就能把任何业务逻辑天气查询、数据库查询、内部 API 调用变成 LLM 可用的技能。