不用多说,先讲故事。上个月我把一台退役下来的工作站翻出来,装上双卡,准备搭一套本地大模型部署环境。折腾到凌晨三点,发现真正让我头疼的不是显卡驱动,也不是vLLM启动失败,而是整个流程里缺一个“顺手”的壳:模型服务跑起来了,我却得手动写一堆curl去调接口;想测一下性能,又得临时拼脚本统计首token延迟;想换一个量化版本对比效果,配置文件散落各处。那天夜里我忽然意识到,我需要的东西其实就是一整套“开放的rig”——openrig。这个概念在国内讨论得不多,但我个人理解,它就是把本地模型部署、推理服务、性能评测、配置管理整合成一体的开源工作台。今天这篇文章,我打算用做项目的方式,把openrig这类方案从设计思路到实操落地完整拆一遍。代码会贴,命令会写,坑也会讲。适合谁看?正在做私有化部署的工程师、想本地跑模型做产品原型的开发者,以及被一堆推理框架折腾得想摔键盘的同学,这篇应该都对你有用。
2. 先聊聊openrig到底解决什么问题
2.1 我把“rig”理解成一套完整的部署栈
先解释一下这个词。Rig在英文里有“设备架”“试验台”的意思,摄影圈说camera rig是相机套件,矿圈说mining rig是矿机架子。到了模型部署领域,openrig的核心含义就是:把你跑模型需要的所有零部件——推理引擎、API网关、模型权重、评测脚本、监控面板——像搭积木一样装进同一个框架里,对外暴露一套统一的操作方式。
我见过的绝大多数本地部署场景,其实都长这样:先装Ollama或者llama.cpp,启动一个模型服务;然后为了暴露给上层应用,再套一层FastAPI或者Flask;接着要做性能验证,又去翻vLLM文档写压测脚本;最后想记录历史表现,只能靠Excel。这套流程不是不能跑,但它属于“手工作坊”:每个环节都靠人肉衔接,换个模型就要重新填一遍参数,换台机器就全部重来。openrig这类项目的目标,就是把这条链路“流水线化”。
再往深了说,我认为openrig解决的痛点有三个。第一个是可复现性:模型版本、推理参数、评测数据集、环境依赖,如果能全部固化成一个可分享的配置,那你做过的实验就不会丢失;第二个是可观测性:不只是看日志里的报错,而是能拿到每轮请求的耗时、吞吐、显存占用这些量化指标;第三个是多模型管理:同时跑多个模型、多套量化版本,随时切换对比。
2.2 什么人最适合用openrig
我按用户画像分三类说。第一类是私有化部署工程师。你给企业客户搭内部知识库问答,客户要求所有数据不出内网,那你必须把模型跑在自己的服务器上。openrig的价值是帮你把部署过程标准化:拿到新机器的第一件事,不再是翻博客找教程,而是直接加载一份rig配置,一键拉起整套服务。第二类是做 RAG 或 Agent 的应用开发者。这类人最烦的不是推理引擎本身,而是“怎么稳定地调接口”。openrig把模型服务封装成统一API,你只需要关心业务逻辑,不用关心背后是vLLM、TensorRT-LLM还是llama.cpp。第三类是算法工程师和评测同学。你们的目标不是上线,而是对比不同模型、不同量化方案的差异。openrig这类平台可以把评测任务批量编排,最后输出结构化报告。
如果你完全没接触过命令行、没装过Docker,那说实话,openrig类项目现在还不适合你。但如果“docker run”对你来说不陌生,那这个工具我建议你花一个周末试试。
2.3 不吹不黑:openrig和“自己写脚本”的差别
有朋友可能会问:我自己用Python写个调度脚本,再挂个Grafana看监控,不也是一个rig吗?没错,能,但代价不同。
我自己也走过这条路。第一版脚本大概分了几个模块:启动模型服务的Shell脚本、转发请求的FastAPI代码、采集指标的子进程、一个简单的SQLite存结果。听起来没多复杂,但实际用起来问题很多:换一台新机器,CUDA版本、驱动版本、Python依赖全部要重新排雷;想并行跑两个模型,端口配置简直是灾难;评测的prompt稍有改动,之前的结果全作废。这就是“自研平台”的通病——写脚本花一天,维护脚本花一周,最后发现问题全在脚本本身。
相比之下,openrig这类项目强调整体性和组合性。你可以把整个rig当作一套可移植装配,配置一次,到处运行。这里我也不是说一定要用某个具体产品,而是提倡“rig思维”:把模型部署环境视作一个可复现、可评测、可分享的独立单元。
3. 核心设计思路:一个开放的rig应该怎么拆
3.1 控制平面和数据平面分离
我见过很多失败的部署项目,崩就崩在“整成了一坨”。模型服务、评测任务、监控采集,全在一个进程里。一个评测任务把显存打满了,API也跟着挂掉;或者模型服务重启一下,整个管理端都连不上。这属于典型的控制平面和数据平面没有分离。
在openrig的架构里,我的理解是两层结构。数据平面负责“真正干活”:推理引擎(就是被拉起的模型服务)、存储、监控采集器。控制平面负责“安排干活”:启停服务、下发评测任务、收集结果。两层之间通过API通信。这么设计的好处非常直观:模型服务可以独立横向扩展,评测任务压死一个实例并不影响其他服务;管理端被重启时,正在跑的推理任务不会中断。
打个比方,这就像餐厅后厨和前台的关系。后厨只管炒菜,前台只管接单。顾客再多,前台可以加人排队;后厨压力太大,也是加灶台解决,而不是让前台跑到厨房里帮忙炒菜。模型部署也是这样,推理负载高就扩展推理节点,评测任务重就单独起评测执行器,互不干扰。如果你自己写代码,请务必把调度逻辑和推理进程拆开,否则后面维护成本会高到让你怀疑人生。
3.2 用插件协议统一模型接入
openrig这类平台的第二个设计要点,是模型接入方式的协议化。什么叫协议化?就是说,我不针对Ollama写一套代码,再针对vLLM写一套代码,而是定一个抽象的“模型服务协议”,然后给每个推理引擎写一个适配器,把各自的差异挡在适配器内部。上层只看到一个统一的标准接口。
具体到实操层面,标准接口大概长这样几个维度:
- 加载模型:指定模型名、量化方式、上下文长度、GPU编号
- 推理请求:输入prompt、温度、max tokens,输出回复文本
- 健康检查:模型是否就绪、当前排队长度、是否在加载中
- 指标上报:显存占用、平均延迟、吞吐量
我自己在配置openrig时,比较喜欢把模型注册表写成YAML。每一份模型配置就是一个固定结构的描述文件,包括模型来源、精度、所需显存、引擎类型。下面是当时写的示例配置,你们可以直接参考:
models: - name: qwen2.5-7b-instruct engine: vllm path: /models/Qwen2.5-7B-Instruct precision: bf16 max_model_len: 8192 gpu_memory_utilization: 0.45 serving_port: 8101 health_path: /health - name: qwen2.5-7b-instruct-gptq engine: vllm path: /models/Qwen2.5-7B-Instruct-GPTQ-Int4 precision: int4 max_model_len: 8192 gpu_memory_utilization: 0.30 serving_port: 8102 health_path: /health这两份配置放在一起,体现出rig思维的核心:同一个基座模型,一个bf16原版、一个int4量化版,并排注册。我想做对比评测时,只需要一次切换,剩下的推理、评测逻辑完全复用。没有这层注册抽象,你就得手动记端口、手动改地址、手动维护两套评测脚本。
3.3 评测与指标定义:让结果可对比
一个真正的openrig,不能只负责跑模型,还得管“跑得好不好”。评测模块是我认为整个项目里含金量最高的部分,因为它直接关系到模型选型决策。
我在实践中把评测任务设计成三种基本类型。第一种是单轮问答评测:给定一组问题和标准答案,调用模型接口,比对生成内容与标准答案的相关性。适合快速验证模型的基本能力。第二种是性能压测:模拟指定并发数,持续发送请求,统计吞吐量和延迟分布。适合判断当前硬件能支撑多少并发。第三种是长稳回归:用固定的prompt集,每隔一段时间跑一遍,看生成结果是否稳定、显存是否泄漏。适合上线前的最终检查。
评测任务本身也应该是配置化的。我在openrig里会为每个任务定义一个“评测场景”,里面包含模型名、数据集路径、超时时间、并发数、采样温度、最大生成长度。比如这样:
evaluation: name: qwen7b-bf16-vs-int4 dataset: ./datasets/rag_qa_200.jsonl concurrency: 8 max_retries: 3 timeout_per_request: 120 sampling_params: temperature: 0.2 top_p: 0.8 max_tokens: 512 metrics: - tps - first_token_latency - avg_latency - gpu_memory_peak这里有一个我踩了很多次才明白的细节:评测时温度参数一定要固定,而且最好设得很低,比如0.1或0.2。不然你每次跑同一道题,模型回答都不同,你很难判断是模型能力问题还是随机性问题。后面问题排查部分我会再展开讲。
4. 从零搭建openrig的实操记录
4.1 硬件准备和环境初始化
先说硬件。我推荐的最低门槛是“能跑得动你目标模型的机器”。以7B量级模型为例,bf16精度下模型权重本身约14GB,加上KV Cache、CUDA上下文、推理引擎的额外开销,单卡建议至少24GB显存。如果你想跑13B到14B模型,那32GB甚至双卡48GB会更舒服。如果你的目标模型是3B或者以下,一张12GB的卡也能玩,但别指望并行跑多个实例。
我实际用的环境是双RTX 3090(24GB*2),CPU是AMD 5900X,内存64GB,系统Ubuntu 22.04。第一步是把基础环境准备好,这里直接给你命令序列:
# 更新系统基础组件 sudo apt update && sudo apt upgrade -y # 安装NVIDIA驱动(以550系列为例) ubuntu-drivers devices sudo apt install -y nvidia-driver-550 # 安装完务必重启,然后检查驱动和CUDA可用性 nvidia-smi # 安装Docker Engine和Compose插件 curl -fsSL https://get.docker.com | sh sudo systemctl enable --now docker docker compose version # 给当前用户添加Docker权限,避免每次sudo sudo usermod -aG docker $USER newgrp docker这些操作我建议一步一步执行,不要一口气全跑完。尤其是NVIDIA驱动装完重启这一步,很多人跳过,后面跑容器时才发现英伟达容器工具包检测不到GPU,又回来返工。
接着安装NVIDIA Container Toolkit,这是让Docker容器内能够访问GPU的关键组件:
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \ sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \ sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list sudo apt update sudo apt install -y nvidia-container-toolkit安装完过后记得重启Docker守护进程:
sudo systemctl restart docker docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi最后一条命令如果能正常输出显卡信息,就说明环境跑通了。
4.2 部署第一个推理服务并注册到rig
环境准备好之后,我开始部署推理服务。在openrig这个框架里,推理服务不是“零零散散手动敲命令启动”,而是统一由rig的管理端拉起并注册。但为了让你理解底层发生了什么,我先把最核心的容器启动命令拆开看。
我推荐直接用vLLM作为第一个推理引擎,因为它的性能、生态成熟度都比较好。以Qwen2.5-7B-Instruct为例,启动命令大概是这样的:
docker run -d --name qwen7b-bf16 \ --gpus '"device=0"' \ --shm-size 16g \ -p 8101:8000 \ -v /models:/models \ -e HF_TOKEN=你的huggingface_token \ vllm/vllm-openai:latest \ --model /models/Qwen2.5-7B-Instruct \ --served-model-name qwen2.5-7b-instruct \ --tensor-parallel-size 1 \ --max-model-len 8192 \ --gpu-memory-utilization 0.45 \ --port 8000解释几个关键参数。--shm-size 16g是很多人忽略的:vLLM在并行处理时会用共享内存做数据交换,默认的64MB根本不够,模型一加载就报shared memory related错误。--tensor-parallel-size 1表示只用单卡;如果要把模型切到双卡,就设成2,并且--gpus要指定两张卡。--gpu-memory-utilization 0.45是我根据双卡环境设置的:让第一张卡只使用45%显存给这个大模型,留出另外的空间跑其他模型或评测任务。
服务起来之后,你可以先手动验证一下连通性:
curl http://127.0.0.1:8101/v1/models curl http://127.0.0.1:8101/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen2.5-7b-instruct", "messages": [{"role": "user", "content": "你好,用一句话介绍你自己"}], "max_tokens": 100, "temperature": 0.2 }'通了之后,再把它登记进rig的模型注册表,也就是前面第3节写的YAML。管理端会自动检测这个端口的健康状态,后续的评测任务就能直接引用qwen2.5-7b-instruct这个名字,而不是记一长串IP加端口。
为什么一定要先手动调通再注册?因为排查容器问题比排查rig问题容易得多。你直接把问题隔离在“模型服务层”,等它稳定了,再交给rig管理。这是我干活一直奉行的原则:先让零件转起来,再装配整机。
4.3 创建第一个评测任务并分析报告
第一个服务注册好之后,接下来干一件最能体现openrig价值的事:建评测任务。我准备了一个200条问答的测试集,内容来自我积累的内部RAG场景问题,单条格式是JSON Lines:
{"id": 1, "question": "公司今年第一季度的营收目标是多少?", "reference": "根据内部规划,第一季度营收目标为4800万元。"}评测任务配置文件之前也给了,这里再说一下跑评测时的实际输出。评测跑起来之后,管理端会定时轮询推理服务的指标,比如每个请求的首token延迟、平均生成速度、排队时间。一轮跑完后,我拿到的报告长这样:
| 指标 | 值 |
|---|---|
| 总请求数 | 200 |
| 并发数 | 8 |
| 平均首token延迟 | 约420ms |
| 平均单轮总延迟 | 约3.8s |
| 吞吐量 | 约42 tokens/s |
| 回答包含参考要点的比例 | 76.5% |
| 显存峰值 | 约11.2GB |
这个报告本身就能说明很多问题。比如平均首token延迟四百多毫秒,对于内部知识库问答来说,体感上会有一点点“迟钝”,如果是客户对面客系统,最好控制在200ms以内。于是我下一步就会压一下max_model_len,或者换一个量化版本,去优化这个指标。没有评测报告,这些决策全靠拍脑袋,有了报告,每一步都有数据支撑。
这里补充一句:我在双卡环境下又注册了GPTQ int4量化版的服务。对比之后发现,bf16版在RAG问答上的准确率确实更高(约76.5% vs 71.2%),但int4版在显存占用上省了近40%。如果你们公司只有单张24GB卡,又想跑一个大模型又跑一个向量库,那量化版大概是更稳的选择。这类结论,都是openrig搭好之后跑出来的,而不是靠纸面推算。
5. 常见问题与排查技巧实录
5.1 服务起来了,但评测一直超时
这个是我用openrig初期遇到频率最高的问题。具体症状是:模型服务手动curl能通,但一跑评测任务,大量请求都报超时。
排查思路十分关键,不是一上来就改配置。我当时的排查顺序是:
- 看最后一次评测的时间点,模型日志有没有异常;
- 看并发数是不是突然打满了排队队列;
- 看单次生成长度是不是远远超过预期。
最后定位到问题,是两个因素叠加:一是评测并发设了16,而单张卡跑7B模型时,实际吞吐并不足以支撑16个并发请求快速结束;二是测试集里有几道题触发了模型生成非常长的回答,最多的生成了上千tokens,直接把请求卡到了超时阈值之外。
解决方案很简单:并发降到8,超时时间从60秒调到120秒,同时把每个请求的max_tokens上限设成512。调完之后,评测稳定跑完,耗时还缩短了,因为排队不再互相阻塞。
这个问题的深层教训是:评测任务的参数设置,不是越大越好。并发高不代表效率高,反而可能因为推理引擎的排队机制,导致大量请求一起变慢。建议先小并发验证一轮,再逐步加压,不要一步到位。
5.2 显存OOM和显存碎片化
OOM这个问题,我在切换多个模型评测的时候踩得最深。第一个模型跑完,第二个模型启动时直接报CUDA out of memory。正常情况下,模型服务进程停掉后,显存应该被释放。但在vLLM这种高性能推理引擎里,显存的释放并不总是立即归还给系统,存在一定程度上的缓存和碎片化。
我当时用了最直接的办法来清理显存占用:
# 查看GPU上还有哪些进程占用内存 nvidia-smi --query-compute-apps=pid,used_memory,name --format=csv # 清理残留进程(谨慎操作,确认是已停止服务的残留) pkill -9 -f vllm # 或者从Docker层面强制清理所有已退出的容器 docker container prune -f但更根本的解法,还是在rig里做显存配额。就是我配置里gpu_memory_utilization字段。不要把每张卡的利用率都设成0.9,留一点余量给调度和碎片化开销。我自己跑双卡时,两个模型的利用率分别设0.45和0.4,这样能保证同时跑的稳定性。
另外一个很实用的技巧,在NVIDIA卡上可以开显存复用特性,不过不同卡支持程度不同,建议实测确认,这里不展开细说。
5.3 评测结果不稳定,同一道题答案总变
这个问题前面埋了伏笔,就是温度参数导致的随机性。很多评测平台没有把采样参数固化到任务配置里,导致不同时间跑出来的结果天差地别。你会以为是模型变笨了,其实是采样随机性在捣乱。
我的建议是:
- 评测场景一律设
temperature: 0.1,甚至0; - 如果引擎支持,固定随机种子
seed: 42; - 使用相同的prompt模板,不要混用“你是助手”和“你是一个AI助手”这类措辞。
别小看prompt模板的作用。我做过一次对比,同一个7B模型,只是把系统提示词从“请用中文回答问题”改成“你是企业内部助手,请用简洁中文回答”,准确率直接涨了4到5个百分点。评测不是只测模型权重,而是测“模型+提示词+参数”这个整体。所以评测配置里,这些必须是固化项。
5.4 日志太多看不到重点
openrig跑起来之后,日志是海量的。模型服务打印的每轮请求信息、管理端的调度日志、监控采集器的数据上报,全部混在一起。我前几次排查问题,全靠Ctrl+F搜关键词,效率极低。
后来我养成了几个好习惯,分享给大家:
- 跑评测前,先清空所有容器的日志文件;
- 评测结束后,优先看管理端的任务汇总,而不是逐行翻日志;
- 遇到报错,先确认是“推理引擎报错”还是“调度流程报错”——前者去模型服务日志找,后者去控制平面日志找,不要混在一起看。
如果要量化,我大概用了三成精力做推理服务的调优,剩下七成全是做可靠性和可观测性的活。如果你打算自己从零搭rig,请务必把日志规范当作一等公民来设计,不要到最后才想起来。
6. 后续可以怎么扩展,以及我的几点体会
openrig这套东西用顺手之后,我目前正在做三件事。第一件是把固定的评测数据集接入CI/CD,每次更新模型权重之后自动跑一轮回归,这样模型有没有“变笨”,数据说话。第二件是把多机部署纳入进来:一台机器专心跑推理,另一台机器跑评测和调度,控制平面和数据平面在物理上也拆开。第三件是尝试接入TensorRT-LLM这套引擎,看看在同样显存下能把吞吐再往上推多少。
这里再分享一个小技巧,算是我目前最想告诉你的经验。评测模型服务时,不要只盯着首token延迟和吞吐量,记得把端到端成功率也算进去。我第一轮跑评测时,发现虽然平均延迟看起来正常,但有一批请求因为排队时间过长被重试,最终成功率只有97.2%。对于内部工具来说,97%可能够用,但如果是面向外部客户的服务,这3%的失败率就可能是投诉来源。把成功率纳入评测指标之后,你会发现很多之前被“平均性能”掩盖的问题。
我在实际接触openrig这类项目之后,最大的体会是:模型本身只是方案的一半,另一半是把它“接好”——接上数据、接上评测、接上监控。很多团队买了好卡、跑起了大模型,最后却输在“没有一套稳定可复现的部署评测体系”。openrig这类思路正是补上这一环。我自己也是踩了无数坑才总结出上面这些套路,这篇分享里的配置基本都是可直接落地的,你可以拿去按自己的硬件情况改一改。如果跑通了,欢迎回来聊你的指标数字。