第 06 课 | 高性能推理:vLLM 部署与 OpenAI 兼容接口
Ollama 好用,但它是「单线程」的——一次只能处理一个请求。当你跑多 Agent 系统的时候,4 个 Agent 同时发请求,Ollama 就只能排队。vLLM 就是来解决这个问题的:高并发、高吞吐、生产级推理引擎。
为什么需要 vLLM
前两节课我们用 Ollama 跑起了本地模型,体验不错。但 Ollama 有一个明显的局限:它是一次只处理一个请求的。
当你跑一个简单的对话时,这没问题。但当你在后面构建多 Agent 系统时,问题就来了——4 个 Agent 同时向模型发请求,Ollama 只能一个一个处理,其他 3 个得排队等着。
这就好比一个餐厅只有一个厨师,不管来了多少客人,菜只能一道一道做。客人少的时候没问题,客人一多就全堵住了。
vLLM就是来解决这个问题的。它是一个高性能的 LLM 推理引擎,核心特性包括:
- 连续批处理(Continuous Batching):动态合并多个请求,一起处理
- PagedAttention:高效利用显存,支持更长的上下文
- 高吞吐量:比 Ollama 快 2-4 倍(特别是在并发场景下)
- OpenAI 兼容 API:代码几乎不用改,切换后端就行
打个比方:如果 Ollama 是一个厨师,vLLM 就是一个厨房——多个厨师同时做菜,还能动态调整谁先做谁后做。
对于我们的教程来说,前期开发用 Ollama 就够了。但当你开始跑多 Agent 系统(场景三)的时候,vLLM 的高并发能力就派上用场了。
vLLM 核心特性
先简单了解 vLLM 的两个核心技术。你不需要成为专家,但理解它们有助于你做出正确的架构选择。
PagedAttention
传统的 LLM 推理会把整个 KV Cache(注意力机制的中间结果)放在连续的显存空间里。但显存是稀缺资源,连续的显存空间更难找。
PagedAttention 借鉴了操作系统虚拟内存的思想:把 KV Cache 分成固定大小的「页」,不需要连续存储。这样显存利用率大幅提升,能支持更长的上下文和更大的批处理。
连续批处理
传统批处理是等一个批次的所有请求都完成后,才开始处理下一个批次。如果批次里有一个请求特别长,其他请求就得等它完成。
连续批处理更聪明:它动态地把新请求加入正在处理的批次,把已完成的请求移出。这样 GPU 始终在满负荷工作,吞吐量大幅提升。
图 1:Ollama 单请求 vs vLLM 连续批处理
安装 vLLM
vLLM 的安装比 Ollama 稍微复杂一点,因为它需要 CUDA 环境。
首先确认你的 NVIDIA 驱动和 CUDA 版本:
nvidia-smi看输出里的CUDA Version字段,应该是 12.x 以上。
然后安装 vLLM:
uv pip install vllm如果你遇到安装问题,可能是因为 Windows 上 vLLM 的支持不如 Linux 完善。vLLM 官方主要支持 Linux,Windows 上可能需要通过 WSL2 来运行。
如果你的 Windows 环境安装 vLLM 有困难,不用担心——Ollama 已经足够支撑我们学完大部分课程。vLLM 主要在后期的多 Agent 高并发场景才体现出优势。你可以先把 Ollama 用熟,后面需要 vLLM 的时候再装。
启动 vLLM 服务
安装成功后,启动 vLLM 服务:
python-m vllm.entrypoints.openai.api_server \--model Qwen/Qwen2-7B-Instruct \--host 0.0.0.0 \--port 8000 \--max-model-len 8192 \--gpu-memory-utilization 0.85参数说明:
--model:模型名称(vLLM 会自动从 HuggingFace 下载)--host 0.0.0.0:监听所有网络接口--port 8000:服务端口--max-model-len 8192:最大上下文长度--gpu-memory-utilization 0.85:显存使用率上限(留 15% 给系统)
服务启动后,vLLM 会提供一个 OpenAI 兼容的 API,地址是http://localhost:8000。
验证一下:
curl http://localhost:8000/v1/models如果返回模型列表,就说明服务启动成功了。
用 Python 调用 vLLM
vLLM 的 API 和 Ollama 几乎一样——都是 OpenAI 兼容格式。这意味着你之前写的调用代码,几乎不用改就能用。
# vllm_chat.py - 用 Python 调用 vLLM# AI Agent 全栈开发 50 讲 - 第 06 课importrequestsdefchat_vllm(prompt:str,model:str="Qwen/Qwen2-7B-Instruct")->str:"""调用 vLLM 的 OpenAI 兼容 API"""url="http://localhost:8000/v1/chat/completions"payload={"model":model,"messages":[{"role":"user","content":prompt}],"temperature":0.7,}response=requests.post(url,json=payload,timeout=120)response.raise_for_status()result=response.json()returnresult["choices"][0]["message"]["content"]defmain():reply=chat_vllm("用 3 句话总结一下 AI Agent 的核心价值。")print(f"vLLM 回复:\n{reply}")if__name__=="__main__":main()看到了吗?代码和第 4 课的ollama_chat.py几乎一模一样,只是 URL 从localhost:11434变成了localhost:8000。这就是 OpenAI 兼容 API 的好处——切换后端几乎零成本。
性能对比:vLLM vs Ollama
我们用脚本测试一下 vLLM 和 Ollama 的性能差异。
# vllm_benchmark.py - vLLM vs Ollama 性能对比# AI Agent 全栈开发 50 讲 - 第 06 课importrequestsimporttimeimportconcurrent.futuresdefsingle_request(url:str,model:str,prompt:str)->dict:"""单次请求测试"""payload={"model":model,"messages":[{"role":"user","content":prompt}],"temperature":0.7,}start=time.time()response=requests.post(url,json=payload,timeout=120)elapsed=time.time()-start content=response.json()["choices"][0]["message"]["content"]tokens=len(content)/2return{"elapsed":round(elapsed,2),"tokens_per_second":round(tokens/elapsed,1),}defconcurrent_requests(url:str,model:str,prompt:str,n:int=5)->dict:"""并发请求测试"""start=time.time()withconcurrent.futures.ThreadPoolExecutor(max_workers=n)asexecutor:futures=[executor.submit(single_request,url,model,prompt)for_inrange(n)]results=[f.result()forfinfutures]total_elapsed=time.time()-start avg_tps=sum(r["tokens_per_second"]forrinresults)/len(results)return{"total_elapsed":round(total_elapsed,2),"avg_tokens_per_second":round(avg_tps,1),"requests":n,}defmain():prompt="用 200 字介绍一下人工智能的发展历程。"print("="*60)print(" vLLM vs Ollama 性能对比")print("="*60)# 测试配置configs=[{"name":"Ollama (Qwen2 7B)","url":"http://localhost:11434/v1/chat/completions","model":"qwen2:7b"},{"name":"vLLM (Qwen2 7B)","url":"http://localhost:8000/v1/chat/completions","model":"Qwen/Qwen2-7B-Instruct"},]forconfiginconfigs:print(f"\n[{config['name']}]")# 单请求测试try:result=single_request(config["url"],config["model"],prompt)print(f" 单请求:{result['elapsed']}s,{result['tokens_per_second']}tokens/s")exceptExceptionase:print(f" 单请求: [错误]{e}")# 并发测试(5 个并发)try:result=concurrent_requests(config["url"],config["model"],prompt,n=5)print(f" 5 并发: 总耗时{result['total_elapsed']}s, 平均{result['avg_tokens_per_second']}tokens/s")exceptExceptionase:print(f" 5 并发: [错误]{e}")if__name__=="__main__":main()在我的 RTX 3090 上,测试结果如下:
| 测试项 | Ollama | vLLM | vLLM 提升 |
|---|---|---|---|
| 单请求速度 | ~45 tokens/s | ~50 tokens/s | +11% |
| 5 并发总耗时 | ~25 秒 | ~7 秒 | 3.6x |
| 5 并发平均速度 | ~9 tokens/s | ~36 tokens/s | 4x |
关键发现:
- 单请求速度:vLLM 比 Ollama 略快,但差距不大(+11%)。
- 并发场景:vLLM 的优势非常明显。5 个并发请求,vLLM 的总耗时只有 Ollama 的 1/3.6,平均速度是 Ollama 的 4 倍。
这就是连续批处理的威力。当多个请求同时到达时,vLLM 能把它们合并成一个批次一起处理,GPU 利用率大幅提升。
Ollama vs vLLM:如何选择
图 2:Ollama vs vLLM 选择决策树
| 维度 | Ollama | vLLM |
|---|---|---|
| 安装难度 | 简单(一行命令) | 中等(需要 CUDA) |
| 使用难度 | 简单 | 中等 |
| 单请求速度 | 好 | 略好 |
| 并发能力 | 弱(串行处理) | 强(连续批处理) |
| 适用场景 | 本地开发、个人使用 | 生产部署、多 Agent |
| Windows 支持 | 好 | 一般(建议 WSL2) |
我的推荐:
- 开发阶段:用 Ollama。简单、快速、够用。
- 生产部署:用 vLLM。高并发、高吞吐、更稳定。
- 教程学习:前期用 Ollama,后面多 Agent 场景再切换到 vLLM。
统一 LLM 客户端设计
为了让代码能在 Ollama 和 vLLM 之间无缝切换,我们设计一个统一的 LLM 客户端。
# llm_client.py - 统一 LLM 客户端# AI Agent 全栈开发 50 讲 - 第 06 课importrequestsfromconfigimportLLM_BACKEND,LLM_MODEL,LLM_BASE_URL,LLM_API_KEYclassLLMClient:"""统一的 LLM 客户端,支持 Ollama 和 vLLM"""def__init__(self,backend:str=None,model:str=None,base_url:str=None):self.backend=backendorLLM_BACKEND self.model=modelorLLM_MODEL self.base_url=base_urlorLLM_BASE_URL self.api_key=LLM_API_KEYdefchat(self,messages:list,temperature:float=0.7)->str:"""发送聊天请求 Args: messages: 消息列表 [{"role": "user", "content": "..."}] temperature: 温度参数 Returns: 模型回复文本 """url=f"{self.base_url}/chat/completions"headers={}ifself.api_keyandself.api_key!="ollama":headers["Authorization"]=f"Bearer{self.api_key}"payload={"model":self.model,"messages":messages,"temperature":temperature,}response=requests.post(url,json=payload,headers=headers,timeout=120)response.raise_for_status()result=response.json()returnresult["choices"][0]["message"]["content"]def__repr__(self):returnf"LLMClient(backend={self.backend}, model={self.model})"defmain():# 创建客户端(自动读取配置)client=LLMClient()print(f"客户端:{client}")# 测试对话messages=[{"role":"system","content":"你是一个专业的市场分析师。"},{"role":"user","content":"用 2 句话总结 AI Agent 的核心价值。"}]reply=client.chat(messages)print(f"\n回复:\n{reply}")if__name__=="__main__":main()这个客户端通过配置文件自动选择后端。想切换 Ollama 和 vLLM?只需要改.env文件里的LLM_BASE_URL:
# 用 OllamaLLM_BASE_URL=http://localhost:11434/v1# 用 vLLMLLM_BASE_URL=http://localhost:8000/v1代码完全不用改。这就是统一接口设计的好处。
小结与预告
这节课我们学习了 vLLM——一个生产级的高性能推理引擎。
核心收获:
- vLLM 的优势:连续批处理、PagedAttention、高并发
- 性能对比:并发场景下 vLLM 比 Ollama 快 3-4 倍
- 统一客户端:封装了 LLMClient,支持 Ollama 和 vLLM 无缝切换
现在你有了两个推理引擎可选:Ollama 适合开发,vLLM 适合生产。后面的课程中,我们会根据场景灵活切换。
下一节课,我们会把 Ollama、vLLM、OpenAI API 统一封装成一个客户端,通过配置文件一键切换。这是整个教程的基础设施——后面所有的 Agent 调用都通过这个客户端完成。
我们下一课见。
系列教程导航
上一篇:第 05 课 | 模型量化与优化:让 14B 模型也能流畅运行
下一篇:第 07 课 | 统一 LLM 客户端:本地与云端 API 无缝切换
本系列共 50 课,持续更新中。关注我不迷路。