1. vLLM架构概览:理解核心组件与自定义切入点
大家好,我是你们的老朋友。今天咱们来聊聊vLLM的架构。说实话,我第一次看vLLM源码的时候,也被它那一堆组件绕得有点晕。但后来我发现,只要抓住三个核心角色——LLMEngine、ModelRunner、Worker——整个架构就清晰了。
嗯,咱们先别急着看代码。先想一个问题:vLLM到底是怎么把一个大模型跑起来的?
说白了,它就是一个调度器+执行器的组合。你给它一个请求,它负责分配GPU资源、管理显存、调度批次、执行推理。而这三个核心组件,各司其职。
1.1 LLMEngine:大脑与调度中心
LLMEngine是整个系统的大脑。它负责接收请求、管理调度、控制推理流程。我个人习惯把它理解成「总指挥」。
- 请求管理:接收用户的输入,维护一个请求队列。
- 调度策略:决定哪些请求可以一起跑(batching),怎么调度最省显存。
- 生命周期管理:从请求进入到输出完成,全程跟踪。
关键点:LLMEngine不直接执行模型推理。它只负责「安排谁什么时候干活」。真正的苦力活,交给下面两位。
我在项目中遇到过一个问题:自定义模型注册后,LLMEngine死活不认。后来发现是模型配置的键名没对上。LLMEngine在初始化时会读取一个配置字典,如果你的模型名字不在里面,它就直接跳过。嗯,这个坑我踩过,后面会细说。
1.2 ModelRunner:模型执行的核心
ModelRunner是真正干活的人。它负责加载模型权重、执行前向推理、管理KV Cache。
你想想看,LLMEngine说「现在跑这批请求」,ModelRunner就接过数据,调用模型做forward。它需要知道:
- 模型的结构是什么(比如是LLaMA还是ChatGLM)
- 权重怎么加载(从HuggingFace还是本地)
- KV Cache怎么管理(PagedAttention的核心逻辑就在这里)
自定义模型的切入点就在这里。因为vLLM默认支持的是主流架构(LLaMA、Falcon等)。如果你的模型结构不一样,就需要告诉ModelRunner:「嘿,我的模型长这样,forward的时候走这个逻辑」。
我的经验:自定义模型时,80%的工作量都在ModelRunner这一层。你需要实现一个XXXModelForCausalLM类,继承自PretrainedModel,然后注册进去。
1.3 Worker:真正的GPU执行者
Worker是最底层的执行单元。每个GPU对应一个Worker。它负责:
- 把数据搬到GPU上
- 调用ModelRunner执行推理
- 把结果拿回来
说白了,Worker就是ModelRunner的「手」。ModelRunner说「我要forward」,Worker就负责把tensor放到cuda设备上,然后调用forward函数。
我记得有一次调试自定义模型,模型在CPU上跑得好好的,一上GPU就报错。查了半天,发现是Worker初始化时没有正确设置设备。嗯,这种低级错误,犯过一次就记住了。
1.4 三者关系:一张图说清楚
下面这张SVG图,是我自己画的。它展示了这三个组件怎么配合工作:
1.5 自定义模型的切入点在哪里?
好,现在咱们知道了三个组件的关系。那问题来了:我要接入一个自定义模型,该动哪个部分?
答案是:ModelRunner。更准确地说,是ModelRunner内部的模型定义和加载逻辑。
具体来说,你需要做三件事:
- 定义模型类:实现一个继承自
PreTrainedModel的类,包含forward方法。 - 注册模型:把模型名字和类映射告诉vLLM,让它知道「哦,这个模型叫my_model,用这个类来加载」。
- 配置加载逻辑:告诉ModelRunner怎么加载你的权重(比如从HuggingFace还是本地路径)。
注意:千万不要去改LLMEngine的代码!那是调度逻辑,跟模型结构无关。我曾经见过有人为了适配模型,直接改了LLMEngine的调度策略,结果整个系统崩了。嗯,血的教训。
1.6 一张表总结核心组件
| 组件 | 职责 | 自定义切入点 | 我的建议 |
|---|---|---|---|
| LLMEngine | 调度、请求管理 | 一般不改 | 别动它,除非你懂调度算法 |
| ModelRunner | 模型加载、forward | 核心切入点 | 80%的工作在这里 |
| Worker | GPU执行、设备管理 | 偶尔需要改 | 注意设备设置,别踩坑 |
1.7 一个小例子:注册流程预览
虽然咱们还没到实战环节,但我先给你看个大概。注册一个自定义模型,代码大概长这样:
# 1. 定义你的模型类
class MyCustomModelForCausalLM(PreTrainedModel):
def __init__(self, config):
super().__init__(config)
# 你的模型结构定义
def forward(self, input_ids, **kwargs):
# 你的前向逻辑
return outputs
# 2. 注册到vLLM
from vllm.model_executor.models import ModelRegistry
ModelRegistry.register_model("my_custom_model", MyCustomModelForCausalLM)
# 3. 使用
llm = LLM(model="my_custom_model", ...)
你看,核心就是注册这一步。vLLM内部维护了一个全局的模型字典,你注册进去,它就能找到。
小技巧:注册时模型名字要跟HuggingFace的config.json里的model_type字段一致。不然vLLM会找不到。我刚开始就栽在这个细节上。
1.8 本章小结
好了,咱们把vLLM的架构捋了一遍。核心就三句话:
- LLMEngine是大脑,负责调度。
- ModelRunner是核心,负责模型执行。
- Worker是手,负责GPU操作。
自定义模型的切入点,就在ModelRunner这一层。你需要定义模型类、注册进去、配置加载逻辑。别去动LLMEngine,那是自找麻烦。
下一章,咱们会手把手带你走一遍完整的注册流程。到时候你会看到,其实没那么复杂。嗯,今天就到这儿,咱们下次见。