首页 / 文章 / 如何用vLLM扩展AI智能体的LLM推理
← 返回
IT技术

如何用vLLM扩展AI智能体的LLM推理

✍️ zhirenhun 📅 2026/8/18 👁 140 阅读 ⏱ 30 分钟
如何用vLLM扩展AI智能体的LLM推理

在本教程中,我将向你展示如何使用 vLLM 为 AI 代理扩展 LLM 推理。我将帮助你建立对 LLM 推理工作原理的直觉,探讨为什么代理工作负载会造成 GPU 调度和内存压力,并研究 vLLM 如何设计来提高吞吐量。

然后,我们将运行一个本地 vLLM 服务器,并通过一个 AI 代理使用其兼容 OpenAI 的 API 连接到它。

目录

背景

一个简单的 AI 代理通常在一个用户、一个请求和一个模型响应的情况下工作得很好。但生产环境看起来截然不同。

想象一下数百个用户同时发送提示词。而用户请求很容易变成 10 到 30 次独立的 LLM 调用,用于规划、工具选择、总结、重试和最终响应生成。再乘以几十或几百个用户,推理层很快就会成为瓶颈。

前提条件

要学习本教程,你应该熟悉基本的 Python 和终端命令。你还应该安装 Python、一个包管理器(如 pipuv)以及一个代码编辑器。

对 LLM 提示词和 API 客户端有一些了解会有所帮助,但不需要具有 AI 代理、vLLM 或推理优化的先前经验。要了解更多关于 AI 代理的信息,你可以阅读这篇文章

本教程使用 vLLM-Metal,因此示例可以在 Apple Silicon 上本地运行。本教程适用于 macOS、Windows 和 Linux。我使用的是配备 32 GB 内存且没有外接 GPU 的 MacBook Pro,但通过使用较小的预训练模型,该工作流也可以在更有限的硬件上运行。

什么是 LLM 推理?

推理是使用训练好的模型从输入生成输出的过程。对于大型语言模型,这意味着处理提示词并逐个 token 预测输出。

推理与训练不同。在训练期间,模型通过调整其权重来学习。在推理期间,这些权重保持不变,模型使用它已经学到的知识来生成响应。

尽管模型不再学习,推理仍然可能代价高昂。更大的模型需要更多的内存和计算,更长的提示词需要更多处理工作,更长的响应需要更多的生成步骤。当许多用户同时提交请求时,推理层很快就会成为性能瓶颈。

LLM 推理如何使用 CPU 和 GPU

模型服务系统有两个主要职责:协调请求和执行模型。

在主机端,服务系统接受请求、对提示词进行分词、跟踪请求状态,并决定每个执行步骤应包含哪些请求。在加速器端(通常是 GPU),模型执行处理提示词和生成 token 所需的张量运算。

LLM 推理包含两个主要阶段:预填充解码

在预填充阶段,模型处理输入提示词中的所有 token。由于许多提示词 token 可以并行处理,预填充往往属于计算密集型。因此,包含对话历史、检索文档或工具指令的长提示词可能会增加第一个输出 token 出现之前的时间。

在解码阶段,模型一次生成一个输出 token。每个新 token 依赖于它之前的 token,这使得生成在解码步骤之间是顺序进行的。因此,长响应需要许多独立的模型执行步骤。

简单来说:

GPU 受到计算能力和内存的双重限制。它必须保存模型权重、临时执行数据以及与活动请求相关的状态。

请求状态中最重要的部分之一是KV 缓存。在注意力机制中,模型为先前处理的 token 创建键和值表示。存储这些表示允许模型在生成后续 token 时重用它们,而不是在每一步解码时重新计算整个序列。

KV 缓存使自回归生成变得实用,但同时也消耗内存。随着提示词和生成的响应增长,每个活动请求需要更多的 KV 缓存空间。这意味着可用的 KV 缓存内存可以直接影响服务器可以并发处理多少请求。

为什么 AI 代理工作负载难以服务

AI 代理放大了这些推理挑战,因为一个用户请求可能触发多次模型调用。

代理可能会调用模型来规划下一步行动、选择工具、解释工具结果、总结检索到的信息、从错误中恢复,或者决定是否需要更多工作或生成最终响应。

一次用户交互可能变成 10、20 甚至更多的推理请求。当几十或几百个用户活跃时,模型调用的数量会迅速增长。

代理请求也是不均匀的。一个请求可能包含一个简短的问题,而另一个请求则包含长的系统提示词、对话历史、检索到的文档和多个工具结果。它们生成的响应在长度上也可能有很大差异。

这创建了一个动态工作负载,其中请求在不同时间到达,消耗不同数量的内存,并在不同时间完成。高效地服务这些请求不仅仅需要将模型加载到 GPU 上。服务层必须持续调度工作、管理内存,并防止短请求被长请求不必要地延迟。

vLLM 如何服务代理工作负载

vLLM 是一个面向大语言模型的开源推理运行时和服务引擎。它暴露了兼容OpenAI的API,同时管理模型执行、请求调度、批处理和KV缓存内存。

应用不是直接在内部加载模型并调用诸如model.generate()的方法,而是向vLLM服务器发送HTTP请求。这将应用或智能体逻辑与其底层的推理基础设施分离开来。

当多个请求处于活动状态时,vLLM会将它们一起调度,而不是通过孤立的模型循环处理每个请求。这使得服务层能够更高效地利用可用的加速器。

vLLM的几项功能尤其与智能体工作负载相关:

普通的KV缓存是现代自回归推理的标准组成部分。vLLM的优势在于它如何在并发工作负载之间调度请求以及管理、分配和复用KV缓存内存。

前缀缓存特别减少了预填充阶段的重复工作。它不会使新输出token的生成更快,因此当请求共享长前缀时,其收益最大。

总的来说,这些优化使得vLLM在智能体应用超越单用户原型并开始处理并发、不均且内存密集的推理工作负载时非常有用。

动机与架构

一旦AI智能体开始处理并发流量,模型推理就可能成为其主要的性能瓶颈之一。智能体可能会花费大部分时间等待模型处理提示并生成token。

与其重写智能体逻辑,不如改进其底层的模型服务层。这正是vLLM的用武之地:它提供了一个兼容OpenAI的推理服务器,旨在通过连续批处理和KV缓存管理等功能高效处理并发请求。

请求流程如下:

User sends prompt
          ↓
Agent sends an OpenAI-compatible request
          ↓
vLLM receives request and schedules the request
          ↓
Prompt enters continuous batch
          ↓
Prefill processes the prompt and populates the KV cache
          ↓
Decode generates tokens while reusing the KV cache
          ↓
vLLM returns the generated response
          ↓
Agent receives final text

当多个请求并发到达时,vLLM 可以将兼容的工作合并为不断变化的批次。新请求可以在较早的请求完成后进入,有助于提高硬件利用率和整体吞吐量。

步骤 1:安装 vLLM

标准的 vLLM 安装主要面向带有受支持加速器(如 NVIDIA GPU)的 Linux 系统设计。在 Apple Silicon Mac 上,您可以使用 vLLM-Metal,这是一个由社区维护的 vLLM 硬件插件,它利用 MLX 和 Apple 的 Metal 框架。

$ curl -fsSL https://raw.githubusercontent.com/vllm-project/vllm-metal/main/install.sh | bash

$ source ~/.venv-vllm-metal/bin/activate

$ pip install openai

官方文档提供了针对特定平台和环境的安装说明,尤其是GPU和CUDA配置(更多详情请参阅文档)。

步骤2:启动vLLM服务器

现在使用一个模型启动兼容OpenAI的服务器:

vllm serve mlx-community/Qwen2.5-0.5B-Instruct-4bit --host 127.0.0.1 --port 8000

vllm serve 命令启动一个本地 OpenAI 兼容 API 服务器,用于模型推理。

vLLM 服务器在启动时会显示类似下面的输出:

...
(APIServer pid=35422) INFO 08-13 22:17:00 [launcher.py:99] API server: waiting for HTTP server to start
(APIServer pid=35422) INFO:     Started server process [35422]
(APIServer pid=35422) INFO:     Waiting for application startup.
(APIServer pid=35422) INFO:     Application startup complete.
(APIServer pid=35422) INFO 08-13 22:17:01 [launcher.py:105] API server: HTTP server started

一旦启动,您的服务器通常会监听一个本地端点,例如:

http://localhost:8000/v1

您可以验证服务器正在运行,并检查其暴露的模型名称:

$ curl http://localhost:8000/v1/models

{"object":"list","data":[{"id":"mlx-community/Qwen2.5-0.5B-Instruct-4bit","object":"model","created":1786685135,"owned_by":"vllm","root":"mlx-community/Qwen2.5-0.5B-Instruct-4bit","parent":null,"max_model_len":32768,"permission":[{"id":"modelperm-b05a3fc5dd824296","object":"model_permission","created":1786685135,"allow_create_engine":false,"allow_sampling":true,"allow_logprobs":true,"allow_search_indices":false,"allow_view":true,"allow_fine_tuning":false,"organization":"*","group":null,"is_blocking":false}]}]}%                               

步骤 3:将你的 AI 智能体连接到 vLLM

现在将你的智能体连接到 vLLM 服务器。由于 vLLM 兼容 OpenAI,你可以使用 OpenAI Python 客户端并将其指向你的本地服务器。将以下文件保存为 vllm_agent.py

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="NA",
)

def ask_model(user_input: str) -> str:
    response = client.chat.completions.create(
        model="mlx-community/Qwen2.5-0.5B-Instruct-4bit",
        messages=[
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": user_input},
        ],
        temperature=0,
    )

    return response.choices[0].message.content


print(ask_model("Why are automated tests useful?"))

这里不需要真正的 OpenAI API 密钥,因为请求会发送到你的本地 vLLM 服务器,而不是 OpenAI API。

步骤4:运行智能体

在新终端中运行智能体。确保 vLLM 服务器正在运行。

$ python vllm_agent.py

代理将向vLLM发送请求进行推理。vLLM将使用模型运行推理并生成响应。

示例输出

vLLM服务器日志显示:

(APIServer pid=35422) INFO:     127.0.0.1:59866 - "POST /v1/chat/completions HTTP/1.1" 200 OK
(APIServer pid=35422) INFO 08-13 22:36:11 [loggers.py:310] Engine 000: Avg prompt throughput: 2.5 tokens/s, Avg generation throughput: 20.4 tokens/s, Running: 0 reqs, Waiting: 0 reqs, GPU KV cache usage: 0.0%, Prefix cache hit rate: 33.7%

33.7%的prefix-cache命中率表明,33.7%符合条件的提示前缀令牌在vLLM的缓存中被找到并得以复用,而无需重新计算。这减少了冗余计算,节省了处理时间,充分体现了vLLM的关键性能优势之一。

代理输出:

Automated tests are useful for several reasons:

1. Efficiency: Automated tests can be run quickly and efficiently, allowing developers to focus on other aspects of the codebase.

...

Overall, automated tests are a valuable tool for ensuring that code is well-written and that it is tested thoroughly. They can help ensure that the code is well-written and that it is tested thoroughly, which can help ensure that the code is well-written and that it is tested thoroughly.
The main benefit is not just that the response works. The real benefit is that the same agent can now sit on top of a serving layer built for higher concurrency and better GPU utilization.

为什么KV缓存、PagedAttention、连续批处理和前缀缓存很重要

通过几个简单的计算,这些特性更容易理解。

KV缓存

在Transformer模型内部,注意力机制会创建通常被称为查询、键和值的内部表示。

在生成过程中,模型需要来自较早词元的键和值信息,以便能够关注之前的内容。模型不会每次都从头重新计算这些信息,而是将其存储在内存中。这种存储状态被称为KV缓存。

KV缓存使生成速度大大加快,但它也会占用GPU内存。请求包含的词元越多,所需的KV缓存内存就越多。这就是长提示词、长对话和检索上下文会使推理成本大幅增加的原因之一。

每个词元的KV缓存内存粗略估算为:

2 × number of layers × number of KV heads × head dimension × bytes per value

对于一个有32层、8个KV头、头维度为128且使用FP16精度的模型,每个token的KV缓存大约为128 KB。不同模型的KV缓存大小会有所不同,但总体趋势是一致的:更长的上下文会消耗更多的GPU内存。

PagedAttention

PagedAttention是vLLM管理KV缓存的内存方法。它不要求每个序列的KV缓存占用GPU内存中一个连续的区域,而是将其存储在较小的固定大小块中,这些块可以独立分配和重用。

为什么这样有帮助?在朴素的系统中,为长度不可预测的序列预留大型连续区域可能会因碎片化而浪费内存。PagedAttention将KV缓存划分为固定大小的块,按需分配,并且不需要物理上连续。当请求完成时,它们的块可以返回到空闲池中,并被其他请求重用。这提高了内存利用率,并允许服务器同时处理更多活动序列。

连续批处理

传统的批处理通常以固定轮次进行。服务器收集一组请求,对该批次执行一次解码步骤,并持续对同一组进行解码,直到该批次周期结束。换句话说,在处理批次时,活动请求集大体上保持固定。

这对LLM服务来说效果不佳,因为请求不会同时完成。一个短请求可能提前完成,但在长请求继续解码时,其位置可能闲置不用。

使用连续批处理,服务器可以立即填满这些空位。一旦空间可用,新请求就可以加入下一个解码步骤,而无需等待整个批次完成。

例如:

使用固定批处理时,B可能提前完成,但C可能仍需等待当前批次周期结束。使用连续批处理时,B释放一个位置,C可以加入下一个解码步骤。这使GPU保持更忙碌,并在负载下提高吞吐量。

前缀缓存

智能体经常重用相同的长系统提示、工具指令或工作流前缀。前缀缓存允许vLLM重用共享提示前缀的KV缓存,而不是每次重新计算。文档将其描述为自动前缀缓存。

一个简单的例子:

如果没有前缀缓存,这800个token的前缀将被处理50次。

800 × 50 = 40,000 prefix tokens processed

通过前缀缓存,共享前缀只需计算一次即可复用,从而大幅减少重复工作。

何时应使用 vLLM?

当您满足以下条件时,vLLM 是一个合适的选择:

对于流量较小的小型单用户原型,使用更简单的本地模型运行器可能就足够了。当推理吞吐量、并发性或 KV 缓存内存成为瓶颈时,vLLM 的价值会更加突出。

结论

在本教程中,我们探讨了 vLLM 如何改进 AI 应用背后的服务层。我们启动了一个本地 vLLM 服务器,并使用兼容 OpenAI 的 Python 客户端与其连接。

vLLM 旨在通过连续批处理、PagedAttention 和前缀缓存来提升并发推理性能。本地示例展示了集成过程,而要衡量特定机器上实际的吞吐量和延迟改进,则需要并发负载测试。

接下来,您可以尝试其他模型、添加负载测试,或将现有的 LangChain 或自定义代理连接到同一个 vLLM 端点。祝您玩得开心!

如果您喜欢本教程,可以在我的博客上找到更多文章(近期包括系统设计论文系列),在我的个人网站上了解我的工作,并在LinkedIn上获取更新。

——

🧑‍💻

zhirenhun

一个热爱技术的程序员,喜欢分享前沿AI知识和开发经验。

← 上一篇
如何管理代码库中的上下文文件,以从AI编码代理获得更好的输出
下一篇 →
连续批处理可改善P50,但可能毁掉P99:实测权衡

📌 相关推荐

GraphRAG 是推理问题,而非数据库问题
2026/8/30
构建市场时光机:使用 Python 和 WebSocket 重放交易会话
2026/8/30
如何自行基准测试LLM推理:值得信赖的数字设计标准
2026/8/30
← 返回文章列表