#028 Agent 的本地部署:Ollama、vLLM、llama.cpp 与边缘设备适配
昨晚凌晨两点我在一块Jetson Orin NX上跑一个多模态AgentOllama拉下来的Qwen2.5-VL-7B推理速度慢到令人发指——每秒0.3个tokenAgent调用一次工具函数要等两分钟。更离谱的是内存直接爆了系统OOM killer把SSH服务都给杀了。我盯着串口日志看到一行“Killed process 3276 (ollama_llama_server)”的时候突然意识到一个问题我们这些搞Agent的人是不是太依赖云端API了本地部署这件事远不是“pip install ollama”然后“ollama run”那么简单。今天这篇笔记我就把Ollama、vLLM、llama.cpp这三套方案在Agent场景下的真实表现、踩坑记录、以及边缘设备适配的硬核经验全部摊开来讲。不废话直接上干货。一、Ollama最易用但最容易被坑Ollama确实降低了本地跑模型的门槛但如果你把它当生产环境用迟早要出事。我见过太多人写Agent代码时直接调Ollama的HTTP API然后抱怨“为什么我的Agent响应这么慢”。Ollama的并发模型是个坑。默认情况下Ollama的API是串行处理的。你发一个请求它开始推理这时候第二个请求进来会直接排队。更坑的是Ollama的请求队列没有超时机制——如果你的Agent同时调用了多个工具每个工具都发一个推理请求后面的请求会一直等直到前面的跑完。我遇到过Agent卡死半小时的情况就是因为一个工具函数调用了三次LLM三次请求串行执行每次推理30秒。解决办法别用Ollama的默认配置。在启动Ollama服务时加上--num-parallel 4参数允许并行处理。但注意并行数不是越大越好取决于你的显存。我实测RTX 4090上跑Qwen2.5-7B--num-parallel 2是安全值超过4就会OOM。另一个坑是Ollama的模型加载策略。Ollama默认会把整个模型加载到显存然后一直占着。如果你的Agent需要动态切换模型比如先调一个7B的通用模型做规划再调一个1.5B的专用模型做分类Ollama会先卸载旧模型再加载新模型这个过程耗时5-10秒。我写过一个Agent每次工具调用都要切换模型结果90%的时间花在模型加载上。我的做法如果必须用Ollama就开两个Ollama实例一个跑大模型一个跑小模型分别监听不同端口。Agent代码里根据任务类型路由到不同实例。虽然浪费一点显存但避免了切换开销。二、vLLM吞吐量王者但Agent场景有隐藏问题vLLM的PagedAttention和continuous batching在纯文本生成场景下确实牛吞吐量能比Ollama高3-5倍。但用在Agent上有几个坑你必须知道。第一个坑vLLM的API设计偏向“一次性生成”。Agent场景下我们经常需要流式输出streaming让用户看到思考过程。vLLM支持streaming但它的streaming实现有个问题——如果你在生成过程中中断请求比如Agent发现不需要继续推理了直接关闭连接vLLM的worker进程不会立即释放资源。我遇到过vLLM的worker进程卡死导致后续所有请求都超时的情况。解决办法在Agent代码里不要直接关闭HTTP连接而是调用vLLM的/v1/chat/completions接口时设置stop参数或者发送一个特殊的abort信号。vLLM官方文档里其实有提到这个但很多人没注意。具体做法是在请求体里加一个stop_token_ids: [tokenizer.eos_token_id]这样Agent可以主动触发停止。第二个坑vLLM的显存管理在边缘设备上会翻车。我在Jetson Orin上试过vLLM它默认会预留大量显存给KV cache导致模型本身都放不下。vLLM的--max-model-len参数默认是4096但在边缘设备上你需要手动调小。比如在8GB显存的设备上跑Qwen2.5-7B--max-model-len设成2048才能跑起来。但注意调小这个参数意味着Agent的上下文窗口变小如果你的Agent需要处理长对话历史可能会截断。我的经验vLLM适合做Agent的推理后端但前提是你有足够的显存至少16GB以上。在边缘设备上vLLM的收益不如llama.cpp明显。三、llama.cpp边缘设备的救星但需要手搓llama.cpp是我在边缘设备上最常用的方案。它用C实现没有Python的GIL锁问题内存占用极低。我在树莓派5上跑过Qwen2.5-1.5B量化到Q4_K_M推理速度能达到每秒8个token虽然慢但至少能跑。但llama.cpp的坑在于它的API设计。它默认只提供一个简单的HTTP server功能非常简陋。没有vLLM那样的continuous batching没有Ollama那样的模型管理。你需要自己写代码来管理模型的生命周期。我踩过的一个大坑llama.cpp的server模式默认是单线程的。如果你用Python的requests库并发调用它会直接崩溃。我一开始没注意写了一个Agent同时调用了两次llama.cpp的API结果server直接segfault了。查了半天才发现llama.cpp的server需要加--threads 4参数才能处理并发请求而且这个参数不是越大越好取决于CPU核心数。另一个坑llama.cpp的量化模型在Agent场景下输出质量会下降。特别是当Agent需要做复杂的推理比如数学计算、代码生成时量化后的模型容易出错。我做过对比测试Qwen2.5-7B在Q4_K_M量化下Agent的tool calling准确率从95%降到了82%。如果你的Agent对准确性要求高建议至少用Q6_K或者Q8_0量化。我的做法在边缘设备上我用llama.cpp跑一个1.5B的小模型做Agent的“快速响应”模块处理简单的分类和格式化任务。复杂的推理任务通过MQTT转发到服务器上的大模型。这样既保证了响应速度又保证了准确性。四、边缘设备适配从Jetson到树莓派的实战经验边缘设备适配是Agent本地部署最难的部分。不同设备的架构、内存、算力差异巨大没有一个通用的方案。Jetson Orin系列这是目前最适合跑Agent的边缘设备。它有专用的DLA深度学习加速器和GPU但问题在于驱动和库的兼容性。我试过在Jetson Orin NX上装Ollama结果发现Ollama依赖的CUDA版本和Jetson的JetPack版本不匹配。折腾了两天最后用Docker解决了——NVIDIA官方提供了JetPack的Docker镜像里面预装了所有依赖。注意Jetson上的显存是共享的CPU和GPU共用所以不能直接用nvidia-smi看显存。要用tegrastats工具监控实际可用内存。我写了一个脚本在Agent启动前检查可用内存如果低于4GB就自动切换到更小的模型。树莓派5这玩意儿跑Agent纯属找虐。但如果你非要在上面跑记住几点第一用llama.cpp别用Ollama或vLLM它们依赖的库在ARM架构上编译会出问题。第二模型必须量化到Q4_K_M以下而且模型参数量不能超过3B。第三关闭所有不必要的服务包括桌面环境。我在树莓派5上跑Agent只保留了一个SSH服务和llama.cpp的server其他全关了。一个血泪教训树莓派的SD卡写入速度很慢而llama.cpp在加载模型时会频繁读写磁盘。如果你用SD卡模型加载时间会从几秒变成几分钟。解决办法用外接SSD或者把模型加载到内存盘tmpfs。我写了个脚本在Agent启动时把模型文件复制到/dev/shm共享内存加载速度提升了10倍。Intel NUC / 迷你主机这是最平衡的方案。有x86架构有Intel的OpenVINO加速。我试过在NUC上跑Ollama配合Intel的Arc显卡推理速度比纯CPU快3倍。但注意Intel的显卡驱动在Linux上还是有点坑需要手动安装intel-opencl-icd和level-zero库。五、Agent框架与本地推理引擎的集成不管你用哪个推理引擎最终都要和Agent框架比如LangChain、AutoGPT、或者你自己写的框架集成。这里有几个关键点1. 流式输出的处理。Agent框架通常需要流式输出来实现“打字机效果”或者实时更新状态。但不同的推理引擎流式输出的格式不一样。Ollama返回的是JSON LinesvLLM返回的是Server-Sent Eventsllama.cpp返回的是纯文本。我写了一个统一的适配层把三种格式都转成Python的异步生成器这样Agent框架就不用关心底层用的是哪个引擎。2. 工具调用的格式。现在主流模型都支持function calling但不同推理引擎对function calling的支持程度不同。Ollama原生支持vLLM需要加--enable-auto-tool-choice参数llama.cpp需要自己解析模型的输出。我建议在Agent框架里统一用OpenAI的API格式然后通过适配层转换成各个引擎的格式。这样换引擎时只需要改适配层不用改Agent逻辑。3. 错误处理和重试。本地推理引擎比云端API更容易出错。显存不足、模型加载失败、推理超时都是家常便饭。我写了一个重试机制如果推理失败先检查显存如果显存不足就释放一些缓存然后重试。如果重试三次还失败就切换到备用模型比如从7B降到1.5B。六、个人经验性建议写了这么多最后说点实在的。别迷信“一键部署”。任何号称“一行命令部署Agent”的方案在真实场景下都会出问题。本地部署的本质是资源管理你需要理解你的硬件、你的模型、你的Agent框架然后找到它们之间的平衡点。量化不是万能的。量化确实能降低内存占用但会牺牲模型质量。如果你的Agent需要做精确的数学计算或者代码生成建议用FP16或者BF16别用INT4。我见过一个Agent因为用了Q4_K_M量化在计算“11”时输出了“3”导致整个流程崩溃。边缘设备上别跑大模型。7B以上的模型在边缘设备上基本跑不动即使跑起来速度也慢到无法接受。我建议在边缘设备上只跑1.5B-3B的小模型处理简单的任务。复杂的任务通过边缘-云端协同来解决。最后做好日志和监控。本地部署的Agent比云端更容易出问题而且出了问题很难排查。我写了一个监控脚本每5秒检查一次推理引擎的状态包括显存占用、推理延迟、请求队列长度。如果发现异常自动重启引擎并发送告警。这个脚本救了我好几次。本地部署Agent这条路没有捷径。但当你真正把Agent跑在边缘设备上看到它离线也能正常工作的时候那种成就感是调用云端API永远体会不到的。