vLLM Multi-LoRA动态部署实战:共享基座省显存,业务切换零重启
2026/9/23 7:08:17 网站建设 项目流程

上个月我们把推理平台里六个业务线的定制模型全部收编到一套 vLLM 服务里,基座是 Qwen3.5-9B,六个业务各挂一个 LoRA,支持动态加载、动态卸载。折腾完回头看,这个方案本身不神秘,但真正把它用到生产环境,中间有不少文档上不会写的细节。这篇就把部署思路、技术机制、完整操作和踩坑过程都盘一遍,给准备做 Multi-LoRA 动态部署的同学当个参考。

先说下背景。我们当时同时接了客服、营销文案、代码问答、法务、医疗科普、报表解释六个业务线的定制需求,每条业务线都有自己基于 Qwen3.5-9B 微调出来的 LoRA。最开始的部署方式非常粗暴:每个业务线起一个独立的 vLLM 服务,每个服务加载一份完整的 Qwen3.5-9B 权重,然后挂各自的 LoRA。GPU 很快就顶不住了,A100 八卡都不够分,而且大部分时间利用率不到 15%。这种局面逼着我重新思考部署架构,最终选择了 vLLM 官方支持的多 LoRA 方案,也就是一个基座进程内同时管理多个 LoRA adapter,按请求动态切换。

这篇文章适合三类人:一是已经用 LoRA 微调了多个模型、想省显存的人;二是想搞懂 vLLM 的 LoRA 调度原理、避免被缓存问题坑到的人;三是正在把多 LoRA 服务推向生产、需要处理性能调优和稳定性问题的同学。我会把原理、参数、API、坑全部串起来讲,尽量做到看完就能直接动手。

1. 多业务共存时的部署困境与 LoRA 共享基座的账

1.1 三种方案摆在一起对比

在动手之前,我先把候选方案列了一遍。需要注意的是,这里讨论的是“多个 LoRA 服务多个场景”的问题,不是单 LoRA 微调上线。很多团队第一次遇到多条业务线同时要模型时,会本能地选择最直接的方案:一个业务,一套模型服务。

方案 A 是每个 LoRA 各部署一个完整模型服务。这个方案最省事,业务隔离也最干净,但资源消耗几乎是线性增长。Qwen3.5-9B 的 BF16 权重就有 18GB 左右,再加上 KV cache、CUDA context、激活值,一张 24GB 的卡跑一个服务都紧巴巴。六个业务就是六张卡起步,如果还要做多副本保障,GPU 数量直接爆炸。而且大部分业务线流量并不高,很多时间的算力都在空转。

方案 B 是多个 LoRA 合并在同一个模型备份里,每个服务加载一个 LoRA。这种做法比方案 A 省一点模型加载内存,因为几个 LoRA 可以共用同一份基座权重副本,但本质上还是一个进程服务一个业务,并没有解决多业务共存的问题。而且如果想要动态切换业务,还得重启服务或者靠额外的路由层转发,工程复杂度一点没少。

方案 C 就是最终采用的 vLLM Multi-LoRA 方案。一个 vLLM 进程里加载一份 Qwen3.5-9B 权重,同时注册多个 LoRA adapter,请求进来时通过 LoRA 标识选择用哪个 adapter。adapter 可以启动时预加载,也可以在运行过程中动态加载和卸载,整个过程不需要重启服务。业务之间的显存开销只是多几个 LoRA 权重的体积,几乎可以忽略。

三个方案放在一起看,差异非常明显:

维度方案 A:每 LoRA 独立服务方案 B:每 LoRA 单独进程方案 C:Multi-LoRA 共享基座
GPU 显存需求约 N 份完整模型资源约 N 份完整模型资源1 份基座 + N 个 adapter
业务隔离最好中等,靠路由和权限隔离
新 LoRA 上线重新部署服务重新部署服务动态加载 API 即可
切换 LoRA改路由/重启改路由/重启请求级切换
运维复杂度高,服务数量多低,集中管理
适合场景大型独立业务、强隔离要求中型团队批量管理中小业务线多,GPU 预算有限

1.2 算清楚显存和切换成本

很多第一次接触 Multi-LoRA 的人会问:一个 LoRA adapter 那么小,为什么需要单独部署一个服务?答案是问题不在 adapter 本身,而在“完整模型权重 + KV cache”的开销。一个 rank=16 的 LoRA adapter 权重文件通常只有几十 MB,但陪它一起运行的基座模型是 9B 参数的完整权重,这才是显存大头。

拿我们当时的情况算一笔账:Qwen3.5-9B 的 BF16 权重约 18GB,KV cache 按 4K 上下文、并发 16 估算预留 6GB,再加上 CUDA context 和碎片,单份模型服务至少吃掉 25GB 显存。六个业务独立部署时,显存需求是 6 × 25GB ≈ 150GB,四张 A100 都不太够舒服地跑。换成 Multi-LoRA 共享基座,基座加 KV cache 还是 25GB 左右,每个 LoRA adapter 运行时代价大约 100MB 到 300MB(取决于 rank 和 target modules 数量),就算六个 adapter 全部加载,总量也就 27GB 上下,一张 32GB 的卡就能扛下来。

但这里要泼一盆冷水:共享基座省的是显存,不是算力。六个业务请求都打到一个服务上,GPU 计算压力会叠加,如果总并发上去了,单卡依然扛不住。所以 Multi-LoRA 适合的是“业务多、单业务流量不高”的场景,而不是把高流量业务硬塞到一个进程里。我们后来给高流量业务单独留了副本,低流量业务共享一套服务,这样就平衡了成本和稳定性。

切换成本的账也要算清楚。传统方案里,业务从模型 A 切到模型 B,要么改网关转发,要么重启服务加载新权重,冷启动时间从几十秒到几分钟不等。Multi-LoRA 方案里,切换只是一个请求级的 LoRA 标识变化,adapter 已经在 GPU 缓存里的话,切换代价基本为零;如果 adapter 不在缓存里,需要从磁盘或 CPU 内存换入,这个代价我会在第 2 节详细讲。

2. vLLM 的动态 LoRA 机制:EngineCore、Scheduler 与 Executor 的配合

2.1 一次带 LoRA 的请求在 vLLM 内部怎么走

理解了“为什么要共享基座”,接下来要搞明白 vLLM 到底怎么实现“按请求切换 LoRA”的。这部分如果不了解,后面遇到缓存性能问题会一头雾水。

vLLM 在收到一个带 LoRA 标识的请求后,会经过这么一条链路:HTTP 入口把请求解析成带 LoRA Request 的调度任务,交给 EngineCore。EngineCore 是 vLLM 的核心控制组件,相当于推理服务的大脑,它维护模型状态、请求队列和 LoRA 管理器。EngineCore 不会直接执行计算,而是把任务交给 Scheduler 去安排。

Scheduler 是整个调度流程里和 LoRA 关系最密切的部分。它会检查请求使用的 LoRA adapter 当前是否已经加载在 GPU 缓存中。如果已经加载,这个请求直接进入正常的 prefill 和 decode 调度;如果没有加载,Scheduler 需要先把目标 adapter 换入显存,如果有必要还会驱逐一部分暂时不用的 adapter。这个换入换出的过程发生在每次调度之前,所以频繁切换 LoRA 会直接影响调度效率和吞吐。

调度完成后,真正的矩阵计算发生在 Executor 层。Executor 负责驱动 GPU kernel,把基座模型的权重和 LoRA 的增量权重组合起来执行 attention 和 MLP 计算。在单卡环境下,这个过程相对简单;在多卡张量并行环境下,LoRA 权重还要考虑分片问题,我后面会单独讲。

2.2 LoRA 缓存:为什么“动态”不是免费的

vLLM 实现多 LoRA 动态加载的核心是 LoRA 管理器,本质上是一个带容量上限的缓存系统。启动时注册的所有 LoRA 是“已知 adapter”,但“知道”不代表“常驻显存”。vLLM 会按最近使用情况维护一个 LRU 风格的缓存,把当前最热的 adapter 保留在 GPU 显存里,冷门的 adapter 要么不加载、要么被换出到 CPU 内存或磁盘。

这个设计非常聪明,但也有代价。第一个代价是首次访问某个冷 LoRA 时,请求需要等待 adapter 从磁盘或 CPU 内存加载到 GPU,延迟会从几十毫秒级别跳到几百毫秒甚至更高,这就是典型的“冷启动延迟尖刺”。第二个代价是当缓存容量不够时,Scheduler 要执行驱逐操作,如果被驱逐的 adapter 马上又被访问,就会出现反复换入换出的“抖动”,整卡吞吐都会受到影响。

所以 vLLM 的参数里才会有--max-loras--max-cpu-loras两个维度。--max-loras控制 GPU 上最多同时保存多少个 LoRA 适配器,--max-cpu-loras控制 CPU 内存里最多缓存多少个 adapter 作为快速换入的后备。CPU 内存的加载速度虽然不如显存,但比磁盘快很多,把冷门 adapter 放 CPU 缓存是一个比每次都读盘稳妥得多的策略。

我在生产里遇到的一个教训是:一开始把--max-loras设得很小,以为动态加载功能会自动处理一切,结果热门 adapter 被频繁驱逐,服务吞吐忽高忽低。后来把热门业务的 adapter 数量调大,同时把冷门业务放到 CPU 缓存,抖动才消失。

2.3 加载和卸载 API 进入控制流的路径

动态部署里最吸引人的能力是“运行时不重启地加载新 LoRA”。vLLM 从某个版本开始提供了两个专门接口:POST /v1/load_lora_adapterPOST /v1/unload_lora_adapter。这两个接口的本质是把 LoRA 管理操作注入 EngineCore 的控制流,而不是简单地在文件系统里复制文件。

调用 load 接口时,EngineCore 会读取请求体里的 adapter 路径,检查 PEFT config 与基座模型的兼容性,然后把 adapter 信息加入 LoRA 管理器的注册列表。此时如果 GPU 缓存还有空间,vLLM 可以立即把 adapter 加载进显存;如果没有空间,它会先把 adapter 放到 CPU 缓存,等待后续请求触发换入。unload 接口则相反,它会把指定 adapter 从注册列表和缓存里移除。这个移除是幂等的,重复卸载不会报错,所以脚本里可以大胆调用。

这个机制让我在部署新业务时轻松很多。以前新业务上线要排期、改配置、重启服务,现在只要把训练好的 adapter 目录放到约定路径,调用一次 load 接口,然后让请求带上新的lora_name就能立刻切换。

3. 搭建部署环境:模型目录、LoRA 产物与 vLLM 安装

3.1 GPU、驱动与 vLLM 版本怎么选

先说硬件。Qwen3.5-9B 这个规模的模型,如果要跑 4K 上下文和正常并发,建议单卡显存不低于 24GB。A10、A30、L20、A100、H100 都可以,我们实际用的是 32GB 卡。如果显存只有 16GB,可以上 AWQ 量化版本,但要注意量化模型搭配 LoRA 时需要确认 vLLM 对量化 + LoRA 的支持情况,不同版本差异很大,不要想当然。

vLLM 版本选择是我最想强调的一点。这个项目我们最开始图新鲜,装了当时最新的 vLLM,结果跑起来性能还不如老版本,后来回退到稳定的 0.8.x 系列才恢复正常。在生产环境里,vLLM 的版本策略应该是“选一个社区验证过的稳定版,固定住,不要随意升级”。每次升级前要在测试环境把多 LoRA 场景完整回归一遍,特别是动态加载接口和缓存行为,否则很容易踩到“升级后性能反而下降”的坑。

Windows 用户要注意,vLLM 对原生 Windows 的支持非常有限,很多 CUDA kernel 编译和依赖链在 Windows 上容易出问题。我们内部统一用 Linux 容器,Windows 开发机一般通过 WSL2 跑。如果你必须在 Windows 环境试验,建议直接用 Docker 镜像或者 WSL2,别在原生环境硬刚。

3.2 Qwen3.5-9B 基座与 LoRA adapter 的目录规范

模型和 adapter 的目录规划看起来是小事,但多业务协作时,一个混乱的目录结构会引发一堆问题。我们的规范是:

/data/models/ Qwen3.5-9B/ # 基座模型,HF 格式 config.json model.safetensors.index.json model-00001-of-0000X.safetensors tokenizer.json tokenizer_config.json ... /data/loras/ customer-service/ # 业务线一 adapter_config.json adapter_model.safetensors marketing-copy/ # 业务线二 adapter_config.json adapter_model.safetensors code-assistant/ legal/ medical-science/ report-analysis/

LoRA adapter 的产物格式必须保持标准 PEFT 格式,也就是至少包含adapter_config.jsonadapter_model.safetensorsadapter_config.json里的base_model_name_or_path字段要特别注意,我见过不少同事训练完 adapter 后换了机器部署,base model 路径对不上,vLLM 加载时直接报错。这种情况可以核对模型路径,或者编辑 config 文件里的base_model_name_or_path字段,让它和实际基座路径一致。

另外,adapter 的ranktarget_modules在部署时无法随意更改。比如训练时用的 rank=32,部署时 vLLM 会自动读取 config,不需要手动指定,但如果文件损坏或者字段缺失,加载就会失败。建议所有 adapter 在上传前跑一个简单的校验脚本,用peft库加载一遍确保文件完整。

3.3 安装验证这条链路

安装 vLLM 本身不复杂,但我建议在干净的 Python 3.10+ 环境里安装,避免依赖冲突。基础命令是:

pip install vllm

如果需要固定版本,可以指定版本号安装。装完后先跑一个最小的加载验证,确认 CUDA、模型和 LoRA 链路是通的:

python -c "import vllm; print(vllm.__version__)"

接下来用官方 OpenAI 兼容接口做一次最简单的推理,不带 LoRA,确认基座服务正常:

curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3.5-9b", "messages": [{"role": "user", "content": "你好"}], "max_tokens": 32 }'

能正常返回内容,说明环境基本没问题。然后再进入多 LoRA 配置阶段。

4. 实操:启动服务、动态加载与按请求切换 LoRA

4.1 启动命令和每个参数真实含义

先给一个可以被直接复用的启动命令,然后逐个参数解释它的意义:

vllm serve /data/models/Qwen3.5-9B \ --served-model-name qwen3.5-9b \ --tensor-parallel-size 1 \ --enable-lora \ --max-loras 12 \ --max-cpu-loras 24 \ --gpu-memory-utilization 0.92 \ --lora-modules \ customer-service=/data/loras/customer-service \ marketing-copy=/data/loras/marketing-copy \ code-assistant=/data/loras/code-assistant

--enable-lora是总开关,不加这个参数,后面所有 LoRA 相关配置都不会生效。--lora-modules用来指定启动时预注册的 adapter,格式是“名称=路径”,可以有多个。预注册的 adapter 会立即变得可用,但未必全部常驻显存,加载策略还是交给缓存管理器决定。

--max-loras表示 GPU 上最多同时缓存多少个 LoRA adapter。这个值不是越大越好,每个 adapter 都会占用显存,设太大会挤占 KV cache 的空间,反而降低并发能力。--max-cpu-loras则表示在 CPU 内存中缓存多少个 adapter 作为后备,用于快速换入。

--gpu-memory-utilization控制给 vLLM 预留的显存比例。多 LoRA 场景下我建议留一点余量,不要设到 0.98,否则 LoRA 换入时可能因为显存不足出现驱逐困难或者 OOM。实测 0.9 到 0.94 之间比较舒服。

如果你的服务是多卡张量并行,还要注意--fully-sharded-loras参数。它在多 GPU 场景下会把 LoRA 权重也做分片,避免每张卡都保存一份完整 adapter。单卡场景不需要这个参数。

4.2 运行时加载/卸载 LoRA 的接口调用

服务启动后,新增一个业务线的 LoRA 不需要重启。把 adapter 目录放到约定位置,然后调用 vLLM 的动态加载接口:

curl -X POST http://localhost:8000/v1/load_lora_adapter \ -H "Content-Type: application/json" \ -d '{ "lora_name": "medical-science", "lora_path": "/data/loras/medical-science" }'

返回成功后就注册到当前服务了。卸载同理:

curl -X POST http://localhost:8000/v1/unload_lora_adapter \ -H "Content-Type: application/json" \ -d '{ "lora_name": "report-analysis" }'

这里要强调一个容易误解的点:动态加载接口只是把 adapter 加入注册表和缓存,如果 GPU 缓存空间不足,新加载的 adapter 可能处于“已注册但未换入”的状态,第一次实际请求它时依然有加载延迟。所以不要以为调用完 load 接口就万事大吉,新业务上线最好先发一两个预热请求,让 adapter 真正进入显存。

请求时指定使用哪个 LoRA,在 vLLM 的 OpenAI 兼容接口里通过在请求体中携带 LoRA 名称字段来实现。具体字段名在不同版本里略有差异,比较通用的是lora_name。示例:

curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3.5-9b", "lora_name": "customer-service", "messages": [{"role": "user", "content": "我的订单两天了还没发货"}], "max_tokens": 128 }'

如果是 Python 客户端,用 OpenAI SDK 也能传扩展字段,因为 vLLM 的接口兼容 OpenAI 协议,但额外字段在标准 SDK 里可能不直接暴露。我们实际用的是自定义 httpx 请求来带lora_name,这样最可控。

4.3 从“冷启动”到“业务可用”的一次完整演练

最后把完整流程串一遍。假设我现在要上线一个新业务“留学咨询”,adapter 已经训练好放在/data/loras/study-abroad

第一步,先确认服务健康状况和 GPU 余量:

curl http://localhost:8000/health

第二步,加载新 adapter:

curl -X POST http://localhost:8000/v1/load_lora_adapter \ -H "Content-Type: application/json" \ -d '{"lora_name": "study-abroad", "lora_path": "/data/loras/study-abroad"}'

第三步,发两个预热请求让 adapter 换入显存:

curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3.5-9b", "lora_name": "study-abroad", "messages": [{"role": "user", "content": "hi"}], "max_tokens": 1 }'

第四步,做一轮自动化冒烟测试,用几条真实业务语料验证生成质量,确认没问题后把流量切过来。第五步,如果有旧业务下线,调用 unload 接口把不用的 adapter 卸掉,释放显存和缓存槽位。

这个流程看起来平淡无奇,但实际价值很大:从拿到新 adapter 到业务全量可用,只需要十几分钟,而且全程不需要重启推理服务,其他业务的请求完全不受影响。

5. 性能调优与生产环境踩坑记录

5.1 显存、并发和缓存参数到底怎么调

多 LoRA 服务调优的核心不是单次推理速度,而是缓存命中率和请求调度效率。我在 vLLM 的 metrics 里主要盯三类指标:总吞吐、排队时延、以及和 LoRA 相关的缓存命中情况。

如果发现大量请求都落在同一个热门 LoRA 上,说明缓存策略压力很小,核心瓶颈在模型计算本身,这时候应该考虑加副本或者换更强 GPU。如果发现请求分散在多个 LoRA 上,而且频繁出现冷 adapter 访问,那就该调大--max-loras给热门 adapter 腾出更多常驻空间,或者调大--max-cpu-loras加快冷 adapter 的换入速度。如果显存还有富余,适当调高--max-loras通常能显著减少延迟尖刺。

并发参数也要配合调整。多 LoRA 场景下,服务可能同时收到来自多个业务的请求,相同 LoRA 的请求可以高效批处理,不同 LoRA 的请求则要看缓存是否命中。如果同时发起大量不同 LoRA 的并发请求,Scheduler 会被迫做大量换入换出,反而拖累整体吞吐。我们在生产里给低优先级业务做了限流,避免它们一次涌入太多不同 LoRA 的请求把缓存冲垮。

5.2 冷 LoRA 触发加载时出现的延迟尖刺

这是多 LoRA 部署里最典型的问题。我遇到过的情况是:服务跑得很稳,突然某个请求延迟从 100ms 涨到 800ms,然后再恢复。排了很久发现不是网络问题,而是这个请求访问了一个冷门的 LoRA,vLLM 正在从磁盘加载 adapter。

这种延迟尖刺对普通聊天场景还能接受,但对要求低延迟的业务是致命的。解决思路有几个:一是给所有 adapter 做启动预热,让它们在服务刚启动时就进入 CPU 或 GPU 缓存;二是对低延迟业务单独部署副本,不和其他冷门业务混跑;三是把 adapter 文件放到高速 SSD 上,至少别用网络存储来加载 adapter,IO 延迟会被放大很多。

我后来写了一个简单的预热脚本,遍历所有已注册 LoRA,各发一个max_tokens=1的请求,确保 adapter 进入缓存。每次服务启动、每次批量加载新 adapter 后都跑一遍,延迟尖刺基本消失。

5.3 升级 vLLM 后发现性能不升反降

这个坑必须单独拎出来说。有一次我们发现升级到某个新版本后,同样负载下吞吐量下降了 30% 左右。一开始怀疑是参数配置变化,反复对比后发现新版本在多 LoRA 场景下的缓存策略和调度行为有调整,而我们的 adapter 数量比较多,正好踩中了性能回退区间。

最后我们做的不是去深挖源码,而是立即回退到稳定版本,然后在测试环境把升级验证流程固定下来:统一压测脚本、固定并发模型、对比升级前后 P99 延迟和吞吐。以后再升级版本,第一步先跑这套基线,不达标就直接放弃。多 LoRA 场景对版本非常敏感,不建议在生产环境尝鲜。

5.4 几个折腾过的报错和排查方法

这里列几个实际遇到过的报错,都是比较有共性的:

第一个是 adapter 加载时报 PEFT config 解析错误。原因通常是adapter_config.json里字段缺失或者格式不对,比如缺少base_model_name_or_path。排查方法是用peft库直接加载 adapter,看报错信息是不是一致,然后把 config 文件补全。

第二个是加载 adapter 时提示 rank 或 target modules 不匹配。这类报错属于训练和部署环境不一致,比如训练代码里用了自定义 target modules,部署配置里却按默认 8 个线性层去算。解决方法是保证训练脚本和部署环境使用同一份 PEFT 配置,训练完把adapter_config.json原封不动地作为部署依据。

第三个是某些新模型在 vLLM 启动时报类似ValueError: model class ... not found的错误。这个报错本质是 vLLM 内置的模型注册表里没有这个新模型类,需要升级 vLLM 版本,或者通过模型实现扩展机制注册。Qwen3.5-9B 本身没有问题,但如果你换了更新的模型,一定要先确认 vLLM 版本是否支持,否则启动阶段就会直接挂掉。

第四个是动态加载接口返回成功,但请求时提示 LoRA 未找到。这种情况多半是请求体里的lora_name和加载时注册的名称不一致,或者是请求没有正确传 LoRA 标识字段。检查日志里的 LoRA 解析过程,通常一眼就能定位。

6. 落地到多个业务线的路由、监控与后续扩展

6.1 用一层简单网关做按业务路由

vLLM 本身只负责“请求带 LoRA 标识”,不负责“业务流量怎么路由到对应 LoRA”。如果让业务方直接构造带lora_name的请求,容易出错,也容易泄露内部目录命名。我们在前面加了一层非常轻的网关服务,负责从业务请求里识别业务线 ID,然后自动加上对应的 LoRA 标识转发给 vLLM。

网关逻辑很简单,核心就是一张配置表:

LORA_ROUTE = { "customer_service": "customer-service", "marketing": "marketing-copy", "code": "code-assistant", "legal": "legal", "medical": "medical-science", "report": "report-analysis", } def route_request(biz_id: str, payload: dict): lora_name = LORA_ROUTE[biz_id] payload["lora_name"] = lora_name return forward_to_vllm(payload)

这样业务方只需要使用自己熟悉的业务 ID,完全不用关心 LoRA 的内部名称,切换配置也只改网关上的映射表,不需要动业务代码。

6.2 监控该盯哪些指标

多 LoRA 服务比单模型服务多了一类需要重点监控的指标:LoRA 缓存的命中与驱逐情况。如果命中率低,说明 adapter 换入换出太频繁,用户体验会明显变差。我们会在监控面板里同时展示 GPU 利用率、请求排队时延、平均吞吐、以及 LoRA 相关计数器的变化趋势。

另外要监控每个 LoRA 单独的请求量和错误率。一个业务线的异常请求不应该影响其他业务线,但共享一个进程意味着一旦 GPU OOM 或 Scheduler 卡死,所有业务都会受影响。所以在网关层做限流和熔断,比在 vLLM 层做更有效。

6.3 从 9B 迁到更大模型,以及为什么仍用 vLLM

这套多 LoRA 架构并不绑定 Qwen3.5-9B 这个具体模型。如果后续要换更大的基座,比如 Qwen3.6-27B 或者类似的 27B 级别模型,部署流程几乎可以平移,主要变化是显存预算和量化选择。27B 的 BF16 权重接近 54GB,单卡搞不定,需要走多卡张量并行,这时要把--tensor-parallel-size调大,并开启--fully-sharded-loras,让 LoRA 权重也跟着分片。

有人问过为什么不用 sglang 或者其他推理框架。sglang 的调度性能也很强,对 LoRA 也有支持,但 vLLM 在多 LoRA 场景下胜在生态最完整:动态加载/卸载接口是现成的,OpenAI 兼容接口直接可用,社区踩坑资料多,团队内部也更容易维护。另外一个现实原因是我们的网关、压测脚本、监控告警都已经基于 vLLM 的接口形态做好了,迁移其他框架意味着整套工具链重写,收益不大。框架选型最终还是要看团队对现有体系的掌控力和实际业务需求。

最后分享一个小技巧:如果业务方改 LoRA 很频繁,别每次都手动调 load/unload 接口,可以把 adapter 的“名称、路径、服务实例、上线状态”存在一张配置表里,写一个几十行的同步脚本监听配置表变化,自动完成加载、预热、卸载。我们的新业务上线从十几分钟缩短到了几分钟,而且人工操作带来的错误率几乎降为零。这个思路回头可以单独写一篇,这里先埋个伏笔。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询