如何利用vLLM来扩展大语言模型在AI智能体中的应用范围
在本教程中,我将向您展示如何使用vLLM来优化大型语言模型的推理性能,从而提升AI代理的工作效率。我会帮助您理解大型语言模型推理的原理,分析为什么AI代理的任务会引发GPU调度和内存压力问题,并探讨vLLM是如何被设计出来以提高处理能力的。 接下来,我们将运行一台本地的vLLM服务器,并通过其兼容OpenAI的API,使用AI代理与它进行连接。 目录 背景知识 先决条件 什么是大型语言模型推理? 大型语言模型推理如何利用CPU和GPU 为什么AI代理的任务难以处理 vLLM是如何为AI代理任务提供支持的 设计动机与架构 步骤1:安装vLLM 步骤2:启动vLLM服务器 步骤3:将AI代理连接到
在本教程中,我将向您展示如何使用vLLM来优化大型语言模型的推理性能,从而提升AI代理的工作效率。我会帮助您理解大型语言模型推理的原理,分析为什么AI代理的任务会引发GPU调度和内存压力问题,并探讨vLLM是如何被设计出来以提高处理能力的。
接下来,我们将运行一台本地的vLLM服务器,并通过其兼容OpenAI的API,使用AI代理与它进行连接。
目录
背景知识
一个简单的AI代理通常可以很好地处理单个用户、单个请求以及模型返回的一个结果。但实际的生产环境却截然不同。
想象一下,如果有数百个用户同时发送请求,这些请求很可能会转化为10到30次针对不同功能的大型语言模型调用——比如规划、工具选择、内容总结、重试操作,最终生成响应结果。当这种场景发生在几十甚至上百个用户身上时,推理环节很快就会成为整个系统的瓶颈。
先决条件
要学习本教程,您需要熟悉Python基础知识以及终端命令操作。同时,您还需要安装Python编程语言、诸如pip或uv这样的包管理工具,以及一款代码编辑器。
对大型语言模型相关的请求格式和API客户端有所了解会更有帮助,但并不要求您之前有使用AI代理、vLLM或进行推理优化的相关经验。如果您想了解更多关于AI代理的信息,可以阅读这篇文章。
本教程使用了vLLM-Metal,因此这些示例可以在苹果硅架构的设备上本地运行。本教程适用于macOS、Windows和Linux系统。我使用的是一台配备32GB内存的MacBook Pro,没有外接GPU,但通过使用规模较小的预训练模型,即使在配置较为有限的硬件环境下,这些流程也同样可以正常运行。
什么是LLM推理?
推理是指利用已训练好的模型根据输入生成输出的过程。对于大型语言模型而言,这意味着需要逐个处理输入内容并预测相应的输出结果。
推理与训练过程是不同的。在训练阶段,模型会通过调整自身的权重来学习知识;而在推理阶段,这些权重保持不变,模型则利用之前学到的知识来生成输出结果。
尽管模型在此阶段不再进行学习,但推理过程仍然可能非常耗费资源。大型模型需要更多的内存和计算能力,较长的输入内容会增加处理难度,而较长的输出结果则需要经过更多的计算步骤才能生成。当多个用户同时提交请求时,推理环节很快就会成为性能瓶颈。
LLM推理是如何利用CPU和GPU的?
模型服务系统主要有两项职责:协调各类请求并执行模型计算。
在主机端,服务系统负责接收请求、对输入内容进行分词处理、跟踪请求状态,并决定哪些请求应该被纳入每次模型执行流程中;而在加速器端(通常是GPU),模型会执行处理输入内容及生成输出结果所需的数学运算。
LLM推理主要包括两个阶段:预填充和解码。
在预填充阶段,模型会处理输入内容中的所有字符。由于许多字符可以同时被处理,因此这一阶段通常会消耗大量的计算资源。如果输入内容较长,包含了对话历史、检索到的文档或工具使用说明等信息,那么生成第一个输出结果所需的时间就会相应增加。
在解码阶段,模型会逐个生成输出结果。每个新的输出字符都依赖于之前的字符,因此整个生成过程是顺序进行的。因此,较长的输出结果需要经过较多的计算步骤才能完成。
简单来说:
较长的输入内容会导致预填充阶段耗费更多资源。
较长的输出结果会导致解码阶段耗费更多资源。
同时处理的请求越多,系统在调度和内存管理方面的压力就会越大。
GPU在计算能力和内存容量方面都存在局限性。它必须存储模型的权重、临时计算数据以及与当前正在处理的请求相关的状态信息。
其中,KV缓存是请求状态中非常重要的组成部分。在模型进行注意力计算时,它会为之前处理过的字符生成键值对形式的存储结构。通过使用这些缓存数据,模型可以在后续的计算过程中直接重用这些结果,而无需在每次解码时都重新进行计算。
KV缓存使得自回归生成成为可能,但同时也会消耗大量内存。随着提示语和生成响应内容的增加,每个活跃请求所需的KV缓存空间也会随之增大。这意味着可用的KV缓存内存数量会直接影响服务器能够同时处理多少个请求。
为什么AI智能体的工作负载难以处理
AI智能体会加剧这些推理挑战,因为一个用户请求可能会触发多次模型调用。
例如,一个智能体可能需要调用模型来规划下一步行动、选择工具、解读工具结果、总结获取的信息、从错误中恢复,或者判断是否需要进一步处理才能生成最终响应。
一次用户交互可能会产生10次、20次甚至更多的模型调用。当有数十或数百个用户同时在线时,模型调用的次数会迅速增加。
此外,这些请求的复杂程度也不尽相同:有的请求可能只包含一个简短的问题,而另一些请求则可能包含长长的系统提示语、对话历史记录、检索到的文档以及多个工具的输出结果。因此,它们生成的响应在长度上也会有很大差异。
这种动态的工作负载特性意味着请求会在不同时间到达,消耗不同数量的内存,并以不同的速度完成处理。要高效地处理这些请求,仅仅将模型加载到GPU上是不够的;服务层还需要不断安排任务顺序、管理内存资源,并防止较短的请求被较长的请求不必要的延迟。
vLLM是如何处理智能体工作负载的
vLLM是一款专为大型语言模型设计的开源推理运行时和服务器引擎。它提供了与OpenAI兼容的API,同时负责管理模型执行、请求调度、批量处理以及KV缓存内存的分配。
应用程序并不是直接在内部加载模型然后调用像model.generate()这样的方法,而是向vLLM服务器发送HTTP请求。这种方式将应用程序或智能体的逻辑与底层的推理基础设施分离开来。
当有多个请求同时存在时,vLLM会将它们一起进行调度,而不是通过单独的模型循环来处理每个请求。这样,服务层就能更高效地利用可用的计算资源。
以下几项vLLM的功能对于处理智能体工作负载尤为重要:
连续批量处理:随着请求的到来和完成,系统会不断更新当前的批量处理列表。当一个请求完成后,另一个请求可以立即进入后续的处理流程,而无需等待原来批次中的所有请求都结束。
分页注意力机制:vLLM以固定大小的块来管理KV缓存内存,而不是要求每个请求占用连续的一大块内存空间。这种设计可以有效减少内存碎片,使被释放的缓存块更容易被重新利用。
自动前缀缓存:对于那些提示语前缀相同的请求,vLLM会允许它们重用现有的KV缓存块。当多个智能体请求使用相同的系统提示语、工具定义或检索结果时,这一功能尤为有用。
与OpenAI兼容的API:这使得现有的应用程序和智能体框架只需进行少量的配置调整,就能轻松连接到vLLM。
普通的KV缓存是现代自回归推理中的标准组成部分。vLLM的优势在于它能够如何安排请求,以及如何在并发工作负载中管理、分配和重用KV缓存内存。
前缀缓存尤其能够在预填充阶段减少重复性操作。虽然它并不能加快新输出令牌的生成速度,但当请求包含较长的前缀时,其效果最为显著。
这些优化措施共同使得vLLM在代理应用程序从单用户原型发展为能够处理并发、分布不均且耗内存较大的推理任务时变得非常有用。
动机与架构
一旦人工智能代理开始处理并发请求,模型推理就会成为其性能瓶颈之一。代理程序可能会花费大量时间等待模型处理输入并生成输出结果。
与其重新编写代理逻辑,不如改进其底层的模型服务层。这时vLLM就派上了用场:它提供了一个与OpenAI兼容的推理服务器,通过连续批量处理和KV缓存管理等机制,能够高效地处理并发请求。
用户发送输入指令
↓
代理程序发送与OpenAI兼容的请求
↓
vLLM接收请求并安排处理顺序
↓
输入指令被纳入连续批量处理流程
↓>预填充模块处理输入指令并填充KV缓存
↓>解码模块在重用KV缓存的同时生成输出令牌
↓>
vLLM返回处理结果
↓>
代理程序接收最终文本结果
当多个请求同时到达时,vLLM可以将这些兼容的请求合并成连续处理的批次。新的请求可以在之前的请求完成之后立即被加入处理流程,从而提高硬件利用率和整体处理效率。
步骤1:安装vLLM
标准的vLLM安装版本主要是为配备NVIDIA GPU等加速器的Linux系统设计的。在Apple Silicon Mac上,你可以使用由社区维护的vLLM-Metal硬件插件,该插件利用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服务器:正在等待HTTP服务器启动
(APIServer pid=35422) INFO: 已启动服务器进程[35422]
(APIServer pid=35422) INFO: 正在等待应用程序启动。
(APIServer pid=35422) INFO: 应用程序启动完成。
(APIServer pid=35422) INFO 08-13 22:17:01 [launcher.py:105] API服务器:HTTP服务器已启动
一旦服务器启动完毕,它通常会监听一个本地端点,例如:
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,"allowsampling":true,"allow_logprobs":true,"allow_search_indices":false,"allow_view":true,"allow_fine_tuning":false,"organization":"*","group":null,"isblocking":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": "你是一个乐于助人的助手。"},
{"role": "user", "content": user_input},
],
temperature=0,
)
return response.choices[0].message.content
print(ask_model("自动化测试有什么作用?"))
在这里,你不需要使用真实的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:平均提示处理速度为2.5个标记/秒,平均生成响应速度为20.4个标记/秒;当前正在处理0个请求,另有0个请求处于等待状态;GPU键值缓存使用率为0.0%,前缀缓存命中率为33.7%
前缀缓存命中率为33.7%,这意味着在vLLM的缓存中找到了33.7%符合条件的提示前缀标记,这些标记被直接重用而无需重新计算。这种方式减少了冗余的计算量,节省了处理时间,从而体现了vLLM的一项关键性能优势。
代理程序的输出结果如下:
自动化测试具有多种好处:
1. 效率:自动化测试可以快速高效地运行,使开发人员能够将精力集中在代码库的其他方面。
……
总体而言,自动化测试是确保代码质量良好且经过全面测试的宝贵工具。它们能够帮助保证代码的质量和测试的完整性。
真正的好处并不在于响应是否正常工作,而在于现在同一个代理程序可以运行在那些为支持更高并发性和更好的GPU利用率而构建的服务层之上。
为什么KV缓存、分页注意力机制、连续批处理以及前缀缓存如此重要
通过一些简单的计算,这些概念会更容易被理解。
KV缓存
在Transformer模型中,注意力机制会生成一些被称为“查询项”、“键”和“值”的内部表示数据。
在生成新内容的过程中,模型需要利用之前生成的那些“键”和“值”信息,以便能够参考之前的内容。为了避免每次都需要从头开始重新计算这些信息,模型会将它们存储在内存中。这种存储机制就是KV缓存。
KV缓存使得内容的生成速度大大提高,但同时也会占用GPU的内存。一个请求所涉及的信息量越大,所需的KV缓存内存也就越多。因此,较长的提示语、较长时间的对话以及需要检索的大量上下文信息,都会导致推理过程的计算成本显著增加。
对于每个标记而言,KV缓存所需内存的大致估算公式如下:
2 × 层数 × KV头部数量 × 每个头部的维度 × 每个“值”所占用的字节数
以一个拥有32层结构、8个KV头部、每个头部维度为128、且采用FP16精度格式的模型为例,每个标记所需的KV缓存内存大约为128KB。不同模型的KV缓存大小可能会有所差异,但总体而言,上下文信息越长,消耗的GPU内存就越多。
分页注意力机制
分页注意力机制是vLLM用于管理KV缓存的一种方法。与传统方式不同,分页注意力机制不会要求每个序列的KV缓存数据占用连续的一段GPU内存空间,而是将这些数据存储在多个固定大小的块中,这些块可以独立分配和重复使用。
这样设计有什么好处呢?在传统的系统中,为长度不可预测的序列预留连续的大块内存空间,很容易导致内存资源被浪费。而分页注意力机制将KV缓存数据分割成固定大小的块,这些块可以根据需求动态分配,而且不需要保证它们在物理上必须是连续的。当请求完成后,这些块可以被释放回内存池中,供其他请求再次使用。这种方式大大提高了内存利用率,使服务器能够同时处理更多活跃的序列请求。
连续批处理
传统的批处理方式通常是以固定的轮次来进行的。服务器会收集一组请求,对这一组请求进行解码操作,然后持续为同一组请求执行解码步骤,直到整个批处理周期结束。换句话说,在批处理过程中,处于活跃状态的请求集基本上是不变的。
但对于大型语言模型来说,这种处理方式效果不佳,因为各种请求的完成时间并不一致。一些耗时较短的请求可能会很快完成,但它们的处理资源可能会被闲置,而那些耗时较长的请求则会继续进行解码操作。
采用连续批处理的方式后,服务器可以立即重新利用这些空闲的资源。新的请求一旦有足够的处理空间,就可以立即加入下一个解码步骤中,而不需要等待整个批次的所有请求都完成才能开始处理。
举个例子:
请求A需要100个输出结果
请求B需要20个输出结果
当请求A还在处理中时,请求C到达了
如果采用传统的批处理方式,请求B可能会很快完成处理,但请求C仍然需要等待当前批次的所有操作结束。而采用连续批处理的方式后,请求B完成后会立即释放资源,请求C就可以立刻加入下一个解码步骤中。这种方式能够让GPU更充分地发挥作用,从而在负载较大的情况下提高处理效率。
前缀缓存
智能体通常会重复使用相同的长系统提示语、工具指令或工作流程前缀。前缀缓存技术使得大型语言模型能够复用KV缓存中的共享前缀信息,而无需每次都重新计算这些前缀。相关文档将这种机制称为“自动前缀缓存”。
举个简单的例子:
共享的系统提示语由800个字符组成
有50个请求都是以这个前缀开始的
如果不使用前缀缓存,那么这800个字符的前缀就需要被处理50次:
800 × 50 = 40,000个字符被重复处理
而如果使用了前缀缓存,这个共享前缀只需被计算一次,之后就可以被多次复用,从而大大减少不必要的重复计算工作。
何时应该使用大型语言模型?
在以下情况下,使用大型语言模型会非常合适:
当你需要自己托管开放权重的语言模型时
当你需要为多个用户同时提供服务时
当你需要更高的推理处理效率时
当你需要运行那些会频繁调用模型的智能体、聊天机器人或问答系统时
当你希望在自己的推理基础设施上使用与OpenAI兼容的API时
对于那些规模较小、仅用于单用户测试且流量不大的场景来说,使用较为简单的本地模型运行工具可能就已经足够了。只有当推理处理效率、并发处理能力或KV缓存容量成为瓶颈时,大型语言模型才会显示出其真正的价值。
结论
在这篇教程中,我们了解了大型语言模型是如何提升AI应用后端服务能力的。我们首先搭建了一个本地的大型语言模型服务器,并使用与OpenAI兼容的Python客户端与之进行了连接。
大型语言模型的设计目的在于通过连续批处理、分页注意力机制以及前缀缓存技术来提高并发推理效率。通过本地测试示例,我们可以看到这些技术的实际应用效果;而要全面了解它们在特定硬件环境下的性能提升情况,还需要进行并发负载测试。
从这里开始,你可以尝试使用其他模型,进行负载测试,或者将现有的 LangChain 或自定义代理连接到同一个 vLLM 端点上。祝你在探索过程中取得成功!
如果你喜欢这个教程,可以在我的 博客 中找到我更多的文章(最近的文章包括一系列关于系统设计的论文);也可以在我的个人 网站 上查看我的工作成果;同时,你还可以在 LinkedIn 上了解我的最新动态。
相关文章
OpenTelemetry的工作原理:一份全面的指南
如果你是一名软件开发人员或DevOps工程师,那么你很可能已经听说过OpenTelemetry。在讨论可观测性、监控或分布式系统的调试时,这个术语经常会被提及。 你可能也知道它的基本定义,但了解OpenTelemetry是什么与真正理解它的运作原理其实是两回事。 读完本指南后,你将能够明白OpenTelemetry是如何从端到端工作的——从请求进入你的应用程序的那一刻起,直到这些数据被显示在可观测性后端系统中。你还会了解到追踪信息、时间跨度、上下文传播机制以及数据导出工具是如何共同构成一个完整的处理流程的。 如果你完全不了解OpenTelemetry,也别担心:接下来的部分会帮助你快速掌握相关
阅读全文
为什么你绝不应该在API请求中包含电子邮件地址
在开发环境中,你的注册接口看起来没有任何问题。用户完成注册后,你会将相关数据保存到数据库中,然后调用邮件服务提供商,并返回状态码 201 Created ,这样用户就会收到欢迎邮件。一切似乎都很顺利。 然而,当生产环境中的请求开始涌入时,问题出现了: 邮件发送接口现在需要2秒钟才能完成响应,而不是原本的200毫秒。因此,有些请求会超时失败。而在邮件服务提供商出现故障的情况下,所有注册请求都会返回状态码 500 。技术支持人员很困惑:为什么用户能够创建账户,但却始终收不到确认邮件链接? 其实问题并不出在你的邮件模板上,而在于你选择将邮件发送处理逻辑放在HTTP请求路径中这一决策。 在这篇文章中,
阅读全文
创造了现代人工智能的那篇论文:Transformer背后的故事
每次你与现代人工智能模型进行交互时,其实都在使用一种源于2017年那篇名为《注意力才是关键》的研究论文所提出的架构。 我们在freeCodeCamp.org的YouTube频道上发布的最新视频,详细讲述了八位谷歌研究人员是如何着手改进Google Translate的,最终又如何彻底改变了整个科技领域的格局的。 在这一突破性成果出现之前,人工智能领域主要依赖循环神经网络。这类模型会按顺序逐词处理文本,但这带来了两个巨大的障碍: “上下文丢失”:当处理长句或长段落时,模型会忘记开头那些重要的信息。 “训练速度缓慢”:由于需要顺序处理数据,各个步骤无法并行执行,因此即使使用庞大的GPU集群,模型的
阅读全文
从Mixtral到Kimi K3:专家混合模型是如何发展的
在本文中,我们将探讨混合专家模型是如何从最初仅有少数几个“专家”组件,发展到每层包含近900个“专家”组件的,以及那些使得这种稀疏架构仍能保持可训练性且成本可控的压缩机制与稳定性机制。 开放权重的混合专家模型发展速度惊人:Mixtral的总参数数量约为470亿,DeepSeek-V3达到了6710亿,而Kimi K3的参数数量更是突破了万亿大关。令人惊讶的是,并非这些模型的规模本身,而是每个模型实际上只使用极少量参数来处理每一个输入数据。 Kimi K3拥有2.8万亿个参数,但其中仅有约1040亿个参数被用于处理任何一个输入数据。在几乎每一层中,都有一个小型“路由器”从896个专门的前馈网络中
阅读全文