「AI Agent 全栈开发 50 讲」——从本地模型部署到多智能体系统,一年省 87 万 第6节
第 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 课
import requests
def chat_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()
return result["choices"][0]["message"]["content"]
def main():
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 课
import requests
import time
import concurrent.futures
def single_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) / 2
return {
"elapsed": round(elapsed, 2),
"tokens_per_second": round(tokens / elapsed, 1),
}
def concurrent_requests(url: str, model: str, prompt: str, n: int = 5) -> dict:
"""并发请求测试"""
start = time.time()
with concurrent.futures.ThreadPoolExecutor(max_workers=n) as executor:
futures = [
executor.submit(single_request, url, model, prompt)
for _ in range(n)
]
results = [f.result() for f in futures]
total_elapsed = time.time() - start
avg_tps = sum(r["tokens_per_second"] for r in results) / len(results)
return {
"total_elapsed": round(total_elapsed, 2),
"avg_tokens_per_second": round(avg_tps, 1),
"requests": n,
}
def main():
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"},
]
for config in configs:
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")
except Exception as e:
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")
except Exception as e:
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 课
import requests
from config import LLM_BACKEND, LLM_MODEL, LLM_BASE_URL, LLM_API_KEY
class LLMClient:
"""统一的 LLM 客户端,支持 Ollama 和 vLLM"""
def __init__(self, backend: str = None, model: str = None, base_url: str = None):
self.backend = backend or LLM_BACKEND
self.model = model or LLM_MODEL
self.base_url = base_url or LLM_BASE_URL
self.api_key = LLM_API_KEY
def chat(self, messages: list, temperature: float = 0.7) -> str:
"""发送聊天请求
Args:
messages: 消息列表 [{"role": "user", "content": "..."}]
temperature: 温度参数
Returns:
模型回复文本
"""
url = f"{self.base_url}/chat/completions"
headers = {}
if self.api_key and self.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()
return result["choices"][0]["message"]["content"]
def __repr__(self):
return f"LLMClient(backend={self.backend}, model={self.model})"
def main():
# 创建客户端(自动读取配置)
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:
# 用 Ollama
LLM_BASE_URL=http://localhost:11434/v1
# 用 vLLM
LLM_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 课,持续更新中。关注我不迷路。
更多推荐
所有评论(0)