
1. 为什么我把这条技能线拆成infra底座和agent上层两个栈这几年做AI工程我最大的体会是模型能力已经不是唯一瓶颈真正决定项目落地质量的是底下那层AI infra以及在上面跑起来的agent系统。vLLM、SGLang、Torch这些词说起来每个都认识但把它们串成一条能实际工作的技能链需要反复踩坑才能做到。最近我把自己积累的vllm/sglang/torch技能和agent开发经验做了一次系统性整理这篇文章就是这次整理的详细记录适合两类人一是算法工程师想往工程化方向走二是agent应用开发者发现自己天天被推理引擎卡脖子。1.1 先分清要解决的问题层次很多人把AI infra和agent混在一起学结果两边都没学透。我的处理方式是先把问题分成两层infra层解决的是吞吐和延迟问题比如显存够不够、并发高不高、量化后精度还能不能接受agent层解决的是状态与决策问题比如多轮对话的上下文怎么管理、工具调用失败怎么恢复、长期记忆怎么存取。举个例子你就明白推理引擎像发电站agent像智能家居控制器。发电站输出的电压不稳定智能家居再聪明也是废铁反过来发电站稳定但控制器逻辑混乱家里一样会短路。所以我的技能整理从来都是两个栈并行不搞偏科。1.2 我实际维护的技能清单长什么样下面这张表是我在本地笔记里长期更新的总纲每次学完新东西都会回来改一遍技能域核心工具关键知识点最近踩过的坑推理服务vLLM / SGLang / TensorRT-LLMcontinuous batching、prefix cache、量化、分布式vllm新版本性能反而下降量化模型和镜像版本不兼容模型训练框架PyTorch / CUDA数据加载、设备适配、版本矩阵torch版本不匹配Jetson上直接pip install失败容器与部署Docker / K8s镜像选型、GPU驱动、动态挂载官方镜像tag和模型架构不匹配加载embedding模型报错Agent框架LangGraph / 自研循环状态图、工具调用、重试恢复agent execution terminated due to error定位半天Agent记忆与安全向量库 / AMemGuard 这类防御方案memory poisoning、prompt injection、权限边界记忆内容被污染后整个agent行为开始漂移这张表最大的价值不是列出工具而是每条都对应一个具体问题。比如看到vllm新版本性能下降我不会只看release note而是先翻自己的排错日志确认是attention backend切换还是torch版本变化引起的。这样学习才不是漫无目的的收集。1.3 这套体系适合谁如果你是刚从模型训练转向推理服务的算法工程师这套技能树可以帮你快速定位该补什么如果你是做agent应用开发的这套体系能让你明白智能体跑不稳有时候不是你的逻辑写得差而是vllm的调度参数没调对。接下来我把每条线都拆开讲里面包含具体命令、思考过程和真实踩坑记录不是泛泛而谈的科普。2. vLLM从能跑到敢上生产的部署与调优记录vLLM现在基本是大模型推理服务的事实标准。它到底解决了什么问题一句话传统推理框架在显存里存KV Cache时是连续分配的并发一高显存碎片多到离谱vLLM引入了PagedAttention把KV Cache切成固定大小的块有点像操作系统里的分页机制按需分配。配合continuous batching连续批处理一个请求生成完立即腾出位置给新请求不需要等整批结束。这两个机制叠加吞吐量能比naive推理高一个数量级。但能跑和敢上生产之间隔着一整套部署经验。2.1 部署层面最容易翻车的三个点第一是镜像版本。Docker Hub上的vllm/vllm-openai镜像每个tag对应不同release版本。我见过不少人拿一个很老的镜像去加载新出的embedding模型启动直接报错因为老版本根本没有--task embedding这个参数。如果你要部署的是qwen3-embedding这类模型务必选支持embedding task的新版镜像启动命令大致是这样docker run --gpus all --ipchost \ -v /data/models:/models \ -p 8000:8000 \ vllm/vllm-openai:latest \ --model /models/qwen3-embedding-0.6b \ --task embedding \ --max-model-len 8192注意--task embedding必须显式指定否则vLLM会把embedding模型当成生成模型来加载结果就是输出一堆莫名其妙的文本。第二是量化模型。很多人拿q8_0量化版的大模型往vLLM里塞比如qwen3.8-27b的q8_0版本如果镜像版本太老经常加载到一半崩掉。原因很简单GGUF/量化格式的加载后端是慢慢成熟的老镜像根本没有对应实现。我的建议是优先用官方release note里明确支持的模型格式镜像版本组合不要想当然。第三是部署DeepSeek这类带reasoning的模型。直接vllm serve虽然能跑但如果你不处理chat template输出的推理链路格式会很乱。我在部署DeepSeek-R1系列时会额外确认模板里的reasoning_content字段有没有被正确识别必要时写一个自定义template再挂进去。2.2 EngineCore与Scheduler、Executor的交互流程vLLM升级到0.6之后代码结构发生了一次大变化新引入的EngineCore概念让很多老玩家也犯迷糊。我理解的整个交互是这样的while engine.has_requests(): seq_groups scheduler.schedule() # 调度器决定本轮跑哪些请求 outputs executor.execute_model(seq_groups) # 执行器真正跑模型前向 scheduler.update(outputs) # 根据输出更新状态这里有几个角色要分清。Scheduler是交通警察它维护所有sequence的状态决定哪些请求继续跑、哪些先抢占、哪些已经结束。Executor是司机负责把Scheduler给出的sequence group真正送进GPU跑前向。EngineCore则是把引擎和HTTP Server解耦的产物让推理引擎可以被嵌入到更复杂的服务架构里而不是必须绑定OpenAI风格的API接口。我看源码的时候第一件事就是顺着LLMEngine的入口找到scheduler和executor模块而不是一上来读api_server。因为你如果先读HTTP层很容易被请求解析的细节带偏错过真正的核心逻辑。2.3 Scheduler的调度逻辑为什么并发上不去很多人在vLLM上调了半天并发发现吞吐就是上不去这时候需要回到Scheduler的逻辑上去想。vLLM的Scheduler每轮会做三个决策继续已有sequence、暂停某些sequence、为新请求分配资源。核心限制有两个max_num_seqs每轮最多处理的序列数和max_model_len单序列最大长度。你以为把max_num_seqs调大就能提升吞吐但显存是有限的KV Cache分配不够时Scheduler只能做preemption抢占。抢占有交换到CPU和重新计算两种策略。交换到CPU会引入显存-CPU拷贝开销重新计算则浪费算力。两种都不好所以更实际的做法是控制并发和显存利用率的平衡先跑vllm benchmark测出当前硬件的KV Cache上限再倒推合理的max_num_seqs和max-model-len而不是拍脑袋填参数。2.4 新版本性能下降是怎么排查的热度很高的一个问题就是vllm新版本性能下降。我真实遇到过从0.4升到0.6同样负载下tokens/s反而降了。第一反应不是骂开发组而是先做四件事查torch和CUDA版本是否和镜像内部构建一致用benchmark脚本对比新旧版本确认flash-attention backend是否真的开启看看新版本默认配置有没有变化比如prefix caching默认开启如果你的请求前缀复用率极低它反而会引入额外匹配开销。排查完之后我发现80%的情况不是vLLM本身变慢而是环境变了。比如升级后自动切换到了新的attention kernel你的GPU架构不在优化名单里性能自然回落。我的习惯是升级前先保存基线benchmark结果没有基线就谈不上排查。2.5 硬件适配从L20到MI50再到Windows生产环境里你没法挑显卡。我在L20上部署过minimax-h3印象最深的是L20的算力规格比较特殊需要CUDA 12.1以上的驱动环境否则vLLM起不来。L20显存够但计算卡偏推理优化部署时要适当降低--gpu-memory-utilization给未来动态batch留点缓冲。MI50这种AMD老卡也有不少人问。vLLM对ROCm平台的支持是存在的但你要接受一个现实ROCm版本的flash-attention等算子不一定齐全性能可能比同级别N卡打折扣。部署时注意设置HIP_VISIBLE_DEVICES而不是CUDA_VISIBLE_DEVICES这是新手最容易漏的。至于WindowsvLLM官方支持一直很弱你非得在Windows上跑我建议用WSL2 Docker而不是直接pip装否则编译过程会消耗掉你一整天。这套经验同样适用于给你的AI infra技能做环境隔离。3. sglang与vllm为什么我会同时保留两套引擎很多人问sglang和vllm到底怎么选我给出的答案可能有点反直觉我两套都在用而且不觉得重复。vLLM更像一个通用推理服务器稳定、生态大、接口OpenAI兼容适合做统一API入口SGLang则更偏编译器它把结构化生成、函数调用、多轮前缀复用直接设计进引擎里适合玩法比较重的agent场景。3.1 最核心的区别RadixAttentionSGLang最让我心动的是RadixAttention。简单说它把KV Cache做成了一棵树节点可以共享公共前缀。举个例子你的agent每次请求都带一个很长的system prompt如果100个并发请求里90个system prompt一样SGLang可以只存一份公共前缀后面的分支动态挂到树上。vLLM也有prefix cache但SGLang的树状复用更激进分叉之后如果某个分支后面又能对上还能继续共享。这意味着在超长system prompt 多工具定义 多轮对话这类场景里SGLang的显存效率和TTFT都能比vLLM明显好一截。3.2 从源码解析角度看SGLang该读哪三个模块我读SGLang源码的经验不建议从头到尾通读按下面顺序走效率最高parser/grammar模块SGLang的前端语言支持gen、select这类结构化指令看这个模块你能理解结构化生成是怎么解析成约束的。scheduler模块重点看Radix Cache的操作理解公共前缀的查找、命中、扩展是怎么实现的。router模块分布式部署时请求怎么路由到不同worker这里讲得很清楚。读scheduler时你会发现SGLang的调度和vLLM的调度思路完全不同。vLLM更关注通用批处理SGLang更关注如何把请求的结构化特性利用起来。理解了这点你就不会在两个框架之间反复横跳了。3.3 实际选型表我自己在项目里的选择大概是这样场景推荐引擎原因统一对外提供OpenAI风格APIvLLM生态成熟、兼容性好、问题排查资料最多agent高频调用、长system promptSGLangRadix前缀复用收益大需要严格结构化JSON输出SGLang结构化生成支持更自然大量流式对话、简单chatvLLM调度成熟稳定社区案例多实验新模型、快速验证两者都试用同一份benchmark脚本跑分再决定还有个经验sglang和vllm的benchmark结果千万不要只看官方数字因为负载特征差很多。我见过一个场景官方benchmark里SGLang遥遥领先但我们的业务请求全是短query结果vLLM反而更稳。选型这种事最终要落在自己的业务样本上。4. torch环境从pip install到Jetson交叉编译的整段记忆Torch看起来不算AI infra里的重型装备但它的安装问题能卡住一整条技术链。尤其是当你同时训模型、部署vLLM、跑机器人强化学习时torch的版本变成了一个全局约束牵一发动全身。4.1 安装torch时的版本陷阱先说说那个经典报错could not find a version that satisfies the requirement torch。我见过太多人栽在这上面包括我自己。这个问题的本质是pip在指定的源和Python版本范围内找不到满足条件的wheel。常见原因有四个Python版本太新比如3.12在pytorch早期版本根本没有对应的wheel指定的源不是官方源CUDA版本不在该torch版本的构建列表里拼写错误或者包名不对。如果你需要装老组合比如torch1.8.2配torchvision0.9.2就一定要用官方历史索引基本格式是pip3 install torch1.8.2 torchvision0.9.2 torchaudio0.8.2 \ --extra-index-url https://download.pytorch.org/whl/cu111注意torch 1.8.2那个时期只有cu101/cu111等版本没有cu118你硬指cu118就会报找不到版本。先确认你显卡驱动的CUDA版本再反查该torch版本支持哪些CUDA构建最后再装。另外我经常看到有人的代码长这样from datasets import dataset这是错的正确是from datasets import Dataset大写D。这种拼写错误会和torch安装问题混在一起让人误以为环境有问题其实只是Python代码缺个大小写。排查时先看报错发生在import阶段还是运行阶段能省很多时间。4.2 Jetson上配置torch不能当普通Linux处理如果你要在Jetson系列设备上跑模型直接在设备上执行常见的pip install命令大概率会失败或者慢到怀疑人生。因为Jetson是ARM架构很多torch wheel只有x86版本。正确姿势是先确认JetPack版本然后去NVIDIA官方论坛或预编译wheel仓库找对应版本的torch和torchvision。我踩过的坑是装完torch之后忘了检查它是否用了CUDA支持。Jetson上的torch有的编译成CPU-only跑模型跟蜗牛一样。验证方法很简单import torch print(torch.cuda.is_available())如果不是True换带CUDA的预编译包再装。这一步看起来不起眼但直接影响后面所有推理和训练任务。4.3 mujoco与机械狗torch只是链条里的一环有热搜词提到mujoco和torch机械狗恰好这块我也整理过。用PyTorch训练机械狗策略时torch只负责神经网络部分MuJoCo负责物理仿真二者通过gym接口衔接。真正坑人的不是torch而是MuJoCo库本身mujoco-py需要编译Cython扩展经常因为系统缺依赖失败MuJoCo新版的渲染依赖还要额外装egl或osmesa。训练循环里和torch相关的优化点倒是值得记一下pin_memoryTrue配合non_blockingTrue可以加速数据从CPU到GPU的搬运torch.set_num_threads(8)在仿真数据生成密集时控制CPU线程数避免和MuJoCo抢核心。这些细节能让机械狗训练数据管线稳定很多。4.4 环境隔离是我的底线我现在的原则是每个项目一个虚拟环境依赖全部用锁文件固定。torch这种包尤其敏感——你很难记住哪个项目用的是cu118、哪个是cu111、哪个是CPU版。我常用的是pip-tools写一个requirements.in然后编译出requirements.txt。lock出来的文件不仅能复现环境还能在换机器时快速恢复。很多人觉得麻烦但等你被新版本性能下降和torch版本不满足轮番折磨之后就会明白环境工程就是AI infra的一部分。5. Agent开发框架、编排与基础设施缺一不可说完了infra底座再讲上层agent。我的观点很直接agent开发入门其实门槛不高但真正的难点在于可靠性。而可靠性恰恰分布在框架、编排、记忆、安全四个方向。5.1 一条比较务实的agent开发学习路线最快捷的入门路径是先理解agent的本质。别一开始就上LangGraph、AutoGen这种重型框架先自己写一个最简循环messages [system_prompt] while not done: response llm.chat(messages, toolstools) if response.tool_calls: for call in response.tool_calls: result exec_tool(call) messages.append(tool_message(call.id, result)) else: done True final_answer response.content这个循环就是agent的全部骨架模型决定要不要调用工具、执行工具、把结果放回上下文、继续推理直到结束。吴恩达的agent教程很适合建立这个概念框架但如果你想真正掌握一定要自己手写一遍循环再去看框架怎么把它扩展成状态机。之后的学习路线我建议是先补function calling的细节再接触编排框架然后做记忆系统最后补安全与权限控制。不要反过来。很多人一上来学多智能体编排结果连单agent的工具调用失败都没处理好项目自然没法落地上线。5.2 skill、harness和agent到底有什么区别这个问题在agent社区问得特别多关键是没有统一术语。我的理解是分层看skill是原子能力。它可以是读取PDF、执行SQL、生成图表这类的可复用能力。甚至可以是一个小的子agent但对外只暴露清晰的输入输出。harness是承载agent循环的运行时。它负责调LLM、分发工具、处理异常、管理上下文窗口是操作系统那一层。agent是策略本身。它决定什么情况下调用哪个skill什么时候停止如何拆解复杂任务。用游戏类比harness是游戏引擎skill是技能按键agent是玩家策略。很多所谓的agent框架本质是提供harness 编排图而真正值钱的业务逻辑还是agent策略。5.3 agent记忆短期、长期和安全记忆是agent区别于普通对话系统的重要能力。短期记忆就是当前上下文里的消息序列简单直接长期记忆通常落在一个外部存储里比如向量库或数据库关键是把对话摘要、用户偏好、任务状态结构化地写进去。这里特别要提醒很多文章把vLLM里的KV Cache也叫记忆这是完全不同的概念。KV Cache是推理引擎的临时状态agent记忆是业务层面的持久化状态。你可以在vLLM的上下文中保存200k token但重启服务后什么都没了而长期记忆要的是重启后还在。记忆安全是我最近关注的重点。你让agent把对话摘要写进记忆库原始数据里可能混有恶意指令或不可信信息。我看到过一种叫AMemGuard的主动防御思路专门针对LLM-based agent的记忆投毒在记忆写入前做内容验证和权限检查避免攻击者污染记忆库后让agent在后续对话中持续产生危险行为。工程上我建议至少把外部工具返回的内容和系统指令分隔开不要无脑拼接进上下文。5.4 agent execution terminated due to error这类问题该怎么定位这个报错几乎每个agent开发者都遇到过。我第一次看到时以为是框架bug后来发现90%是下面三种情况报错类型真正原因解决方式模型返回了非法tool call输出不是合法JSON或函数名不符对模型输出做schema校验用pydantic或json schema强制工具执行时抛异常工具内部没有捕获业务错误在工具调用层统一捕获并回传给模型上下文溢出或关键字段丢失messages里丢掉了tool_call_id严格保持消息格式注意role和id配对一个非常实用的改善方式在工具调用循环里统一捕获异常把错误信息当成tool message回给模型。因为模型看到错误信息后往往能自我修正这会极大减少整个循环的崩溃率。代码结构类似for call in response.tool_calls: try: result tools[call.name](**call.arguments) except Exception as e: result {error: str(e), suggestion: 请尝试修正参数后重试} messages.append({role: tool, tool_call_id: call.id, content: json.dumps(result)})这段代码我从最早的agent项目用到现在是可靠性提升最关键的一招。宁可让模型多跑一步也不能让循环停在报错上。5.5 框架选型与工程化收尾框架方面我的建议是看场景。LangGraph适合可编排的状态图节点和边都比较明确多工具、多阶段任务很适合AutoGen偏向多agent对话协作CrewAI适合角色扮演式的agent团队。自研循环适合对可控性要求极高的场景。选型时先问自己我的agent状态流转是否复杂 不复杂就别上重框架。生产级agent最后一个建议是把agent后端包装成OpenAI兼容接口直接放在vLLM或SGLang服务后面。这样infra层和agent层就形成了清晰的上下层结构底层引擎负责高吞吐和稳定响应上层agent负责决策、记忆和工具调用。出问题时也能快速分层排查——是引擎慢了还是agent逻辑写错了。6. 我的日常整理方法这类skills清单怎么维护讲完具体技术线最后一章分享一下我整理技能的方法。因为AI infra和agent每天都在变整理不是一次性的而是要形成一个可迭代的习惯。6.1 每学一个技能都回答三个问题我在笔记里给每条知识都强制写上三个回答它解决什么问题为什么非它不可它跟同类方案之间怎么选边界在哪里哪些隐藏坑是文档里不会写的。以vllm scheduler逻辑为例我会写它解决的是动态batch和显存分配问题和TensorRT-LLM的调度器本质思路接近但vLLM对动态请求更友好坑是抢占策略和max_model_len强相关调参时必须一起看。这样归完类知识就不是零散笔记而是可以直接调用的决策依据。6.2 按操作手册原理笔记排错日志三类归档我所有的AI infra技能归档都分三类。操作手册只放命令、参数、步骤追求随手抄走就能用原理笔记放调度流程、源码逻辑、设计动机追求为什么是这样排错日志放错误信息、根因、解决过程追求下次遇到直接定位。这三类各有不同生命周期操作手册更新最频繁原理笔记最稳定排错日志最有个人价值。所以我写博客或者给人讲经验时几乎不用重新准备直接从排错日志里找一段典型case展开就行。这也是为什么我建议你也这么做——它不单是知识管理还是一种内容资产。6.3 一个可以直接抄的目录结构我的skills目录大概长这样skills/ ├── 00-infra/ │ ├── torch-install-notes.md │ ├── vllm-server-tuning.md │ ├── sglang-source-map.md │ ├── gpu-rocm-jetson.md │ └── bench-baselines/ ├── 01-agent/ │ ├── agent-loop-minimal.py │ ├── memory-design.md │ ├── skill-vs-harness.md │ ├── error-recovery.md │ └── safety-checklist.md └── 02-learn-log/ ├── 2025-xx-vllm-performance-drop.md └── 2025-xx-agent-memory-poisoning.md每次学完新东西先在learn-log里记一笔过两周如果发现它还能反复用再沉淀到infra或agent主文档里。这个冷静期机制帮我过滤掉大量一次性知识留下来的都是真正经得起项目检验的经验。最后说个小技巧维护这类清单别追求好看重点是记录你实际踩过的坑因为网上的教程和文档永远覆盖不了你那个具体环境的特殊之处。