第 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 始终在满负荷工作,吞吐量大幅提升。

vLLM 连续批处理

请求 1 + 2 + 3
一起处理
总耗时 6s

Ollama 单请求

请求 1
处理 5s

请求 2
处理 5s

请求 3
处理 5s

图 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 上,测试结果如下:

测试项OllamavLLMvLLM 提升
单请求速度~45 tokens/s~50 tokens/s+11%
5 并发总耗时~25 秒~7 秒3.6x
5 并发平均速度~9 tokens/s~36 tokens/s4x

关键发现:

  1. 单请求速度:vLLM 比 Ollama 略快,但差距不大(+11%)。
  2. 并发场景:vLLM 的优势非常明显。5 个并发请求,vLLM 的总耗时只有 Ollama 的 1/3.6,平均速度是 Ollama 的 4 倍。

这就是连续批处理的威力。当多个请求同时到达时,vLLM 能把它们合并成一个批次一起处理,GPU 利用率大幅提升。


Ollama vs vLLM:如何选择

是

否

你的场景是什么?

本地开发/个人使用?

生产部署/多 Agent?

用 Ollama
简单、快速、够用

需要高并发?

用 vLLM
高吞吐、低延迟

Ollama 也行
简单优先

图 2:Ollama vs vLLM 选择决策树

维度OllamavLLM
安装难度简单(一行命令)中等(需要 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——一个生产级的高性能推理引擎。

核心收获:

  1. vLLM 的优势:连续批处理、PagedAttention、高并发
  2. 性能对比:并发场景下 vLLM 比 Ollama 快 3-4 倍
  3. 统一客户端:封装了 LLMClient,支持 Ollama 和 vLLM 无缝切换

现在你有了两个推理引擎可选:Ollama 适合开发,vLLM 适合生产。后面的课程中,我们会根据场景灵活切换。

下一节课,我们会把 Ollama、vLLM、OpenAI API 统一封装成一个客户端,通过配置文件一键切换。这是整个教程的基础设施——后面所有的 Agent 调用都通过这个客户端完成。

我们下一课见。


系列教程导航

上一篇:第 05 课 | 模型量化与优化:让 14B 模型也能流畅运行

下一篇:第 07 课 | 统一 LLM 客户端:本地与云端 API 无缝切换

本系列共 50 课,持续更新中。关注我不迷路。

Logo

聚焦RDK与具身智能,汇集机器人开发教程、开源案例和技术交流

更多推荐