尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

AI Infra实战:vLLM与SGLang选型、PyTorch环境调优及Agent技能封装指南

AI Infra实战:vLLM与SGLang选型、PyTorch环境调优及Agent技能封装指南 做 AI infra 和 agent 相关方向也有一阵子了平时打交道最多的就是 vLLM、SGLang、PyTorch 这一整条链路。经常有同事和朋友问“vLLM 和 SGLang 到底该选哪个”“为什么我 docker 启动 vLLM 总是 OOM”“torch 装上之后 CUDA 初始化又 warning 是怎么回事”每次都要从头解释一遍索性把自己这些年的经验整理成一份技能清单既能让自己查也能给刚转 infra 或者想自己部署大模型的同学当参考。这篇文章适合几类人看一是算法工程师模型训练完了想自己推到线上用二是后端工程师准备接入大模型推理服务或者做 agent 应用三是已经在跑 vLLM、SGLang 但经常被环境问题卡住的人。我会把选型逻辑、部署实操、环境配置、agent 技能封装这些事一次说清楚尽量少废话多给可以直接抄作业的命令和配置。1. AI infra 到底在搞什么一张全景图很多人觉得 AI infra 就是“装显卡驱动、调 CUDA、起服务”其实这只是冰山一角。真正做 infra 的人是在训练和推理之间搭一座桥让模型能稳定、高效、低成本地跑起来。1.1 从训练到推理落地infra 工程师的位置模型训练阶段有专门的分布式训练框架但训练完的模型要变成线上服务就需要推理引擎来接管。推理和训练不一样训练追求的是“跑完一轮尽量快”推理追求的是“在延迟可接受的前提下单位时间处理尽可能多的请求”。这种目标差异导致底层优化手段完全不同。vLLM 也好SGLang 也好本质上都是把模型权重加载到 GPU 上接受请求、执行前向计算、返回 token。但它们的调度策略、显存管理方式、对 prefix cache 的利用程度各不相同这直接决定了高并发场景下的吞吐和稳定性。作为 infra 工程师我日常要处理的事情包括GPU 资源分配、推理服务的高可用部署、性能压测、日志监控、故障恢复以及配合算法同学做模型精调和量化。这些事情听起来杂但核心只有一条保证模型服务“不挂 够快 尽量省钱”。1.2 为什么 agent 应用会把 infra 复杂度推高一个量级传统后端的调用模式是“一次请求一次响应”大模型推理最初也是这样。但 agent 应用不一样它会拆解任务、多次调用工具、反复推理可能在一次用户任务里产生几十次模型调用。每次调用之间还有依赖关系有些必须串行有些可以并行这对推理服务的调度能力提出了更高要求。举个实际例子一个 agent 要完成“查天气 - 根据天气生成穿衣建议 - 把建议翻译成英文”这个流程。如果每个步骤都调用一次大模型那就是至少三次推理而且中间还有工具返回结果需要拼进上下文。此时如果推理引擎只支持最简单的先来先服务前面的请求一旦卡住整个 agent 任务都会超时。所以我选推理引擎时很看重两点并发批处理能力和 prefix caching。这两点后面会具体展开。2. vLLM部署大模型绕不开的推理引擎vLLM 现在基本是开源社区部署大模型的事实标准OpenAI 兼容接口、Docker 镜像完善、社区活跃。我大部分线上服务都是用 vLLM 跑的下面从原理到实操讲清楚。2.1 vLLM 的核心机制PagedAttention 与 Continuous BatchingvLLM 之所以快第一功臣是 PagedAttention。以前跑大模型KV cache 是一整块连续显存预分配的即使空闲也不能给别的请求用。PagedAttention 把 KV cache 按页管理类似操作系统里的虚拟内存每个请求需要的缓存不一定要连续可以分散在显存的不同位置。这样显存利用率能提高不少同一个 GPU 上能同时跑的请求数量也变多了。第二功臣是 Continuous Batching。传统的 batching 是等一批请求凑满再一起推理先到的请求要等后到的很浪费。vLLM 采用的是迭代级调度每轮 forward 计算前动态决定哪些请求继续跑、哪些请求已完成移出去、新请求是否加进来。这样 GPU 几乎一直在算有效数据而不是空等。还有一个经常被忽略的点是 prefix caching。很多 agent 场景里多个请求共享系统提示词或长对话历史vLLM 会把已有的 KV cache 缓存下来新请求只要计算差异部分。实测在长上下文场景下能节省 40% 以上的计算量但对内存占用有一定要求部署时要注意留够显存。2.2 实操用 Docker 镜像部署 Qwen3-Embedding 和 DeepSeek我平时习惯直接用官方 Docker 镜像省去自己搭环境的麻烦。先拉镜像docker pull vllm/vllm-openai:v0.27.1这里有两个常见疑问镜像里带模型吗答案是不带。镜像只包含推理引擎和运行时依赖模型文件需要通过挂载目录或者让 vLLM 从模型仓库拉取。我自己一般会把模型放在宿主机的/models目录下然后挂载进容器。部署 Qwen3-Embedding 这种 embedding 模型时命令是这样的docker run --runtime nvidia --gpus all \ -v /models:/models \ -p 8000:8000 \ --ipchost \ vllm/vllm-openai:v0.27.1 \ --model /models/qwen3-embedding-0.6b \ --task embed \ --host 0.0.0.0 \ --port 8000注意--task embed这个参数vLLM 0.6 之后把 embedding 和生成任务分开指定了不写的话可能默认走生成逻辑导致接口行为不对。部署 DeepSeek 这类生成模型时我一般会加上显存利用率限制docker run --runtime nvidia --gpus all \ -v /models:/models \ -p 8000:8000 \ --ipchost \ vllm/vllm-openai:v0.27.1 \ --model /models/deepseek-llm-7b \ --max-model-len 8192 \ --gpu-memory-utilization 0.9 \ --enforce-eager--enforce-eager是为了避免 torch.compile 启动时的编译开销测试阶段很有用。--max-model-len 8192是把最大输入长度限制在 8K防止显存被超长请求耗尽。这两个参数是排查 OOM 时最先要检查的。另外--ipchost一定要加否则 PyTorch 的多进程数据加载会报 shared memory 不足。2.3 踩坑GLM 应该用哪个 vLLM 版本GLM 系列模型用户不少但经常有人问“怎么用 vLLM 跑 GLM 一堆报错”。GLM 的网络结构和 LLaMA 系列有差异vLLM 对它的支持是逐步完善的。旧版 vLLM比如 0.4.x跑 GLM 基本跑不通需要升级到 0.5.x 或更高版本。如果要跑 GLM-4.5、GLM-5 这类新模型最好直接用 0.6.6 以上的版本或者干脆用官方 release note 里指定测试过的版本。这里我的经验是部署前先查一下模型卡片的“部署说明”部分如果模型官方明确说了支持某个 vLLM 版本就别自作聪明用 latest。latest 虽然会包含最新优化但也是踩新 bug 的重灾区。2.4 调度逻辑浅析为什么看日志像在坐过山车vLLM 的日志里经常出现 running、waiting、swapped 这些状态初次看会一头雾水。其实这是调度器的内部状态running 是正在参与当前批次计算的请求waiting 是排队等待的请求swapped 是显存不够被暂时“赶到”CPU 上的请求。我遇到过最典型的情况是压测时并发一高日志里 swapped 数量飙升同时每请求延迟暴涨。这说明显存不够用了调度器在做 cache 换入换出非常伤性能。遇到这种问题优先调低--max-num-seqs限制并发序列数或者调低--gpu-memory-utilization留出一些余量再不行就加 GPU。3. SGLang另一种值得关注的高效推理方案SGLang 是另一个热门推理框架出自斯坦福和社区合作的项目。它的口号是“为复杂 LLM 程序而生的结构化生成语言”很多 agent 场景下表现很抢眼。3.1 SGLang 的设计亮点和与 vLLM 的差异SGLang 最大的特色是 RadixAttention它会自动把请求里的公共前缀缓存成树结构。对比 vLLM 的 prefix cachingSGLang 的缓存粒度更细允许在不同分支间复用更多前缀这对多轮对话和工具调用特别友好。另一个亮点是结构化输出能力。它可以基于 JSON Schema 约束生成的 token让模型输出直接符合业务要求的格式省去后处理解析的麻烦。这对 agent 工具调用至关重要因为工具参数必须是合法 JSON如果模型经常输出残缺 JSON整个 agent 流程都会崩。性能方面在长 prompt、高命中缓存的场景下SGLang 往往比 vLLM 更快但在简单短 prompt 场景下两者差距不大。所以我的选择标准是如果应用以 agent 为主、上下文模式化明显优先考虑 SGLang如果只是标准 OpenAI 接口服务vLLM 更省心。3.2 快速上手从源码安装到跑通一个 serverSGLang 的安装比 vLLM 稍微折腾一点建议直接用官方 Docker 镜像或者用 pip 安装预编译包pip install sglang[all]0.4.1 --find-links https://flashinfer.ai/whl/cu121/torch2.4/这里--find-links是为了装 flashinfer 的预编译包不指定的话容易现场编译慢到怀疑人生。启动服务python -m sglang.launch_server \ --model-path /models/deepseek-llm-7b \ --host 0.0.0.0 \ --port 8000 \ --mem-fraction-static 0.8SGLang 默认接口虽然不是完全 OpenAI 兼容但新版本也提供了--chat-template等参数用起来还算顺手。如果之前用的是 vLLM切换过来时可以通过sglang暴露的/v1/chat/completions接口无缝衔接。3.3 什么时候选 vLLM什么时候选 SGLang我给自己定的选型标准很简单列个表给参考场景推荐方案理由标准 OpenAI 兼容服务vLLM社区成熟、镜像完善、问题容易搜到长上下文多轮对话SGLangRadixAttention 对复用前缀更友好Agent 工具调用频繁SGLang结构化输出能力强JSON 可靠性高快速上线、团队运维经验一般vLLM文档多、参数直观、出问题好排查高并发短文本两者都行压测决定性能差异不大看资源情况需要注意的是这个表基于我自己的线上环境不同 GPU 型号、CUDA 版本、模型架构都会影响结论。最好的办法是同一个模型分别在两个框架上压测一轮用吞吐和延迟数据说话。4. PyTorch 环境安装与调优地基不能塌推理引擎跑得再花哨底层还是 PyTorch。vLLM、SGLang 都依赖 torch 做算子计算所以 torch 版本和 CUDA 的匹配就是地基。地基不稳上面全是空中楼阁。4.1 CUDA、torch、torchvision、torchaudio 的版本匹配很多人一上来就pip install torch装完才发现 torchvision 版本对不上或者 CUDA 版本不匹配。torch、torchvision、torchaudio 三个包必须配套不能随便混装。官方给出了版本映射表我常用几个组合列出来torchtorchvisiontorchaudio适用 CUDA2.4.10.19.12.4.1cu121 / cu1182.1.20.16.22.1.2cu118 / cu1211.13.10.14.10.13.1cu117 / cu1131.8.20.9.20.8.2cu111 / cu102这里要特别说明老项目里经常看到pip3 install torch1.8.2 torchvision0.9.2 torchaudio0.8.2 --extra-index-url https://download.pytorch.org/whl/cu111这种命令。--extra-index-url是告诉 pip 除了默认源还要去 PyTorch 官方源找带 CUDA 的包。如果直接pip install torch1.8.2很可能装到 CPU 版白高兴一场。4.2 常见安装命令和坑pip 索引、镜像源、Windows 路径还有一个很扎心的坑在 Windows 上写开发调试Python 环境可能不在系统默认目录里。比如我在某台 Windows 机器上跑 ComfyUI它的 Python 在d:\comfyui_image\python\下这时候直接打开 cmd 执行pip install往往会装到另一个 Python 里去。正确做法是切换到对应 Python 解释器目录再执行 pipd:\comfyui_image\python\python.exe -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121这里有个判断技巧执行where python看当前解释器位置再用python -c import torch; print(torch.__version__)验证。如果版本不对八成就是装到别的 Python 里了。国内用户装包慢是绕不开的问题但不要因此去碰一些奇奇怪怪的额外加速渠道用官方--index-url配合正常的 PyTorch 包源就够了。另外别设什么特殊的代理配置很多环境问题都是代理设置引起的。4.3 实战torch.cuda 初始化 warning 排查跑代码时经常看到类似d:\comfyui_image\python\lib\site-packages\torch\cuda\__init__.py:180: UserWarning: CUDA initialization: The NVIDIA driver on your system is too old的 warning。这个 warning 中文意思是“你的 NVIDIA 驱动版本太老当前 torch 需要的 CUDA 运行时不被支持”。排查步骤很简单先用nvidia-smi看驱动版本和驱动支持的 CUDA 版本再用python -c import torch; print(torch.cuda.is_available())看 torch 是否识别 GPU。如果驱动支持的最高 CUDA 版本比 torch 需要的版本低那就只能升级驱动或者降低 torch 版本。这里我更建议升级驱动因为低版本 torch 可能缺少后续 bug 修复。还有另一种常见 warningCUDA initialization: Found no NVIDIA driver on your system。这个大概率是 torch 装成 CPU 版本了检查一下包名是不是torch有没有带cu后缀。4.4 不务正业的场景torch 在机器人控制里的用法除了做语言模型torch 在机器人控制里也有一席之地。很多人用 MuJoCo 物理引擎配合 torch 训练机械狗比如几年前斯坦福那个“机械狗自学走路”的项目底层就是 MuJoCo torch 强化学习。这种场景下 torch 的作用是把状态关节角度、速度喂进神经网络输出关节力矩控制指令。训练时要自己写策略网络、价值网络和 PPO 循环对 torch 的张量操作和自动求导非常依赖。虽然跟 vLLM 推理是两个方向但都证明了一件事torch 是整个 AI 技术栈里最通用的底层框架值得把环境调好。5. Agent skills把模型能力包装成可用的“技能”前面说的都是底层推理现在聊聊 agent 这层。AI infra 搞到最后终究要支撑应用。Agent 的“技能”听起来玄乎拆开看就是一组可复用的工具调用流程。5.1 什么是 agent skills和 API 调用有什么不同简单说API 是一个函数你传参进去拿结果skill 是一个包含意图识别、参数解析、调用策略、异常处理在内的完整能力单元。比如“搜索天气”这个 skill不只是调一个天气 API还要包括判断用户问的是哪座城市、从对话里提取日期、决定是否需要追问、把 API 返回结果转成自然语言。这些逻辑写死在程序里就是硬编码写成一个 skill 后可以被多个 agent 在多个场景复用。从 infra 角度看每个 skill 背后可能挂着一个或多个模型调用。比如“根据天气生成穿衣建议”这个 skill需要调一个生成模型“把文本翻译成英文”也需要调模型。skill 越多模型服务承受的请求模式就越复杂infra 的调度能力就越重要。5.2 一个最小可用的 skill 封装流程我习惯用一个很朴素的方法封装输入输出定义 提示词 推理服务接口。先定义 skill 的输入输出结构然后是提示词模板最后调用 vLLM 或 SGLang 的接口完成推理。举例一个“信息提取” skillskill_schema { name: extract_entities, description: 从文本中提取人物、地点、时间实体, parameters: { text: {type: string, description: 待提取的文本} } }提示词模板给定一段文本提取其中的实体输出 JSON 格式key 为 人物、地点、时间。文本内容{text}调用推理服务curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: /models/deepseek-llm-7b, messages: [{role: user, content: 给定一段文本提取其中的实体输出 JSON 格式key 为人物、地点、时间。文本内容张三昨天去了北京。}], temperature: 0.1 }这里把 temperature 调低是为了让输出更稳定减少 JSON 格式出错的概率。如果用的是 SGLang还可以用 JSON Schema 约束输出进一步降低解析失败率。5.3 把 agent 和推理引擎连起来要注意的事第一个要注意的是上下文长度。Agent 会把多轮工具调用结果全部塞进 prompt很容易就超过模型的最大上下文。部署推理引擎时一定根据 agent 的真实使用习惯设置--max-model-len不要盲目调大否则显存不够用。第二个是并发超时。Agent 经常同时发起多个工具调用如果推理服务不支持高并发批处理每个请求都会排队最后整体超时。建议在 agent 侧设置超时重试同时给推理服务做容量评估用压测工具预先测出最大并发数。第三个是工具定义的格式。不同模型对工具调用的格式支持不一样有的要求tools参数有的只支持纯文本提示。我通常会在部署时先把工具定义写死成模型最容易理解的形式而不是让 agent 动态拼提示词省得模型“精神分裂”。6. 长期维护我总结的一些实操心得和检查清单部署一次很简单难的是长期稳定跑。分享一下我日常用的检查清单。6.1 日常巡检命令启动服务后先跑这几个命令确认状态nvidia-smi看 GPU 利用率、显存占用和温度。显存长期 95% 以上要警惕尽量留一点余量避免 OOM。curl http://localhost:8000/v1/models确认模型已经加载成功。如果 vLLM 模型路径配错这个接口会返回空列表。curl http://localhost:8000/metricsvLLM 暴露了 Prometheus 格式的指标重点看vllm:num_requests_running和vllm:num_requests_waiting。waiting 一直居高不下说明并发上限设置太保守swapped 频繁增长说明显存不够。6.2 版本锁定与复现我曾经在升级一个镜像后碰见推理结果全部变样折腾了一整天最后发现是依赖里某个算子库从 CPU 版换成了 GPU 版导致的。那之后我强制要求所有项目用 Dockerfile 固定基础镜像版本并且把关键依赖写死FROM vllm/vllm-openai:v0.27.1 RUN pip install torch2.4.1复现问题时先用docker images确认线上镜像标签再用docker exec -it 容器ID pip list核对依赖。别小看这一步能少踩好多坑。6.3 真实教训最后记录两个真实教训。第一个是有次部署模型时忘了给挂载目录加权限容器里一直报找不到模型文件但是宿主机上明明有。排查了好久才意识到是 docker 挂载目录的权限问题后来统一规定挂载目录权限必须755或更严格。第二个是端口冲突。测试环境有好几个服务都默认监听 8000 端口新服务一启动就报错。现在我的习惯是每个服务启动前先netstat -ano | findstr 8000确认端口空闲再启动同时给每个服务分配独立端口段。还有一点别把生产环境依赖的模型权重放在容器里。镜像只存推理引擎权重通过外部卷挂载这样模型升级时不用重新构建镜像只要换挂载目录就行。这也是我前面强调“vLLM Docker 镜像中不带模型”这一点的原因。做 AI infra 时间越长越觉得很多问题不是玄学而是版本、资源、调参这些细节堆出来的。如果你准备从零搭一套大模型推理服务我的建议是先确定用途再选框架最后动手部署。vLLM 和 SGLang 没有绝对的谁好谁坏只有适合不适合你的场景。PyTorch 环境问题多但无非是版本匹配和路径问题耐心一点都能查出来。Agent 的技能封装也没有多高深把每个小流程做扎实了整个系统自然稳定。希望这份整理能让你少走点弯路。
返回列表