深入理解端侧Agent二端侧LLM部署上一篇文章聊了端侧Agent的整体架构有朋友私信问我为什么端侧Agent的落地难点是LLM部署云上部署一个7B模型不是挺简单吗其实问题恰恰在这里——云上是真简单端侧是真麻烦。你在服务器上随便拉个vLLM就能服务几百个请求但在一个内存只有8GB的盒子上7B模型光权重就占了4到5个GB还要跑系统、跑Agent逻辑、跑工具调用每一步都是预算上的挤牙膏。这篇我就把端侧LLM部署这条路完整走一遍从选型到实操到踩坑把我实测过的方案和数字都摆出来给正在做端侧Agent的朋友做个参考。先说本文覆盖的范围端侧Agent为什么必须自己部署LLM、三条主流部署路线llama.cpp、Ollama、MLC-LLM怎么选、模型量化和选型到底该怎么算账、手把手部署一个Qwen2.5 7B或者DeepSeek-R1蒸馏版到本地盒子、性能优化三板斧、以及Agent场景里最容易被忽略的Function Calling和记忆管理。这篇不是泛泛的概念文每一步我都会给出具体的配置和可验证的结果。1. 为什么端侧Agent必须自己部署LLM从Token三问说起1.1 云端API解决的只是“有”的问题不是“好用”的问题很多端侧Agent在原型阶段会直接把LLM托管在云端OpenAI、国内的几家大模型API注册个key就能调。响应质量确实高代码能力也强但做产品的人很快会发现四个绕不开的问题。第一是延迟。Agent的每一步思考都要调用一次LLM一次聊天可能要轮询三到五次。云端大模型首token延迟通常在1到3秒一轮对话下来十几秒是常态。做在手机或者机器人上这个延迟能直接把用户体验拖垮。第二是隐私。摄像头画面、录音、传感器数据这些端侧Agent天然会接触如果每次都要传云端很多行业场景根本不让你过这一关。第三是成本。按Token计费的模型一个高频Agent每天可能要消耗几块钱甚至几十块钱的API费用一年下来比硬件还贵。第四是断网端侧设备经常出现在弱网和离线环境这是Agent无法回避的物理边界。所以我的判断很简单端侧Agent的绝大多数场景最终都要走到本地部署LLM这一步。云端LLM可以作为高难度任务的兜底但主力模型必须是本地的。1.2 用“Token三问”理解LLM在Agent里的角色前阵子刷到一句话总结得很到位大模型的Token有“三问”——Key我是谁、Query我在找什么、Value我能提供什么。这个框架对理解LLM在端侧Agent中的职责非常有用值得掰开聊聊。Key我是谁对应的是Agent的身份与系统提示词。端侧Agent其实是一个带有感知识别能力和工具执行能力的LLM外壳系统提示词定义了它是家庭助手、工单助理还是设备巡检员。Query我在找什么对应的是Agent接收到的当前输入与任务目标也就是每一轮对话里用户的指令。Value我能提供什么则有两层——一层是模型本身的参数与知识储备另一层是模型能调用的工具能力。端侧LLM部署的本质就是把“Value”这层尽可能做得大既让模型参数在设备上跑得动、跑得快又让工具接口被模型正确发现和调用。这两部分缺一不可。我自己踩过的坑是最开始只看了模型参数跑没跑起来完全没管工具调用结果Agent就只能聊天不能干活。后来补上了Function Calling的格式支持才真正把模型从“聊天机器人”变成“端侧Agent”。所以本篇虽然是讲LLM部署但部署的目的非常明确让模型在端侧具备Agent能力而不是单纯把LLM跑起来。1.3 部署的最终评估指标延迟、显存/内存占用、上下文可控性端侧LLM部署和云端容器部署完全不是一回事评估指标也不同。云端你可以看吞吐、看并发、看利用率端侧你要盯的是三个维度。延迟从拿到输入到流式输出首Token的时间以及每秒钟能生成多少个Token。端侧Agent的要求通常是首Token少于800ms生成速度不低于10 Token/s再低就会让人明显感觉到“卡顿”。内存占用模型权重量化后的大小加激活值、KV Cache和Agent运行环境的开销必须低于设备总内存的60%到70%否则系统会被频繁清理后台进程。上下文可控性Agent要自己审批上下文窗口决定哪些进长时记忆、哪些丢进向量库端侧模型窗口本来就不大这个能力反而要比云端更有优先级。我通常用一个非常简单的公式来预判一个设备能不能跑某个模型设备内存带宽÷模型大小乘以0.5到0.7的经验系数大致就是这台设备推理的Token/s天花板。举个例子一台Jetson Orin NX 16GB内存带宽约102GB/s跑一个7B Q4量化模型约4.5GB理论速度约23Token/s实测会在15到18Token/s这个档次就非常适合端侧Agent。而RK3588的带宽只有约68GB/s跑同样的模型只能到9到12Token/s这就需要用更激进的量化或者更小的模型。先把这笔账算清楚后面选硬件和模型心里就有底了。2. 部署路线选型llama.cpp、Ollama、MLC-LLM到底怎么选2.1 三条路线的底层逻辑差异端侧部署LLM绕不开三个主力方案llama.cpp、Ollama和MLC-LLM。它们把同一件事用了三条完全不同的技术路线来实现选型之前先搞清楚它们的“性格”。llama.cpp是底层最扎实的纯C/C推理引擎支持的平台非常广从x86到ARM再到Metal、CUDA都有所以很多端侧落地项目在它上面跑起来最稳。它的GGUF格式已经成了模型量化的通用标准跟Agent对接也比较灵活既有自带server模式可以拉起OpenAI兼容的HTTP接口也可以作为C库被嵌入到自己的程序里跑进程内推理。Ollama是站在llama.cpp肩膀上做了一层很好的产品化封装安装即用一条命令拉取模型、一条命令启动服务默认就是OpenAI兼容的/v1/chat/completions接口。代价是灵活性变差很多东西要跟着Ollama的规则来底层的采样参数、KV Cache管理、多模型调度这些虽然也开放但总觉得隔了一层。MLC-LLM走的是TVM路线先把模型编译成设备上的原生代码利用TVM的算子优化和代码生成能力在异构硬件上往往能压出比llama.cpp更高的性能。代价是编译链路过重跨平台调试成本高入门门槛直接劝退新手。它比较适合已有量产规划的团队用于针对固定SoC做极限性能优化。2.2 选型决策表什么场景选什么方案拿我自己的使用经验可以比较粗暴地按下面这个表来选场景推荐方案理由快速验证模型效果Ollama安装零门槛模型仓库齐全接口标准当天跑通产品原型转量产llama.cpp稳定可控能进程内集成能细调缓存和采样参数特定SoC极限性能优化MLC-LLMTVM编译能压榨算子潜力适合定制化部署需要在Qt/Go/Rust里集成llama.cppC库可以直接链接没有语言兼容问题只需要跑一个模型、不想折腾Ollama系统占用小模型管理方便更新简单要注意的是Ollama虽然底层引用了llama.cpp但调度和显存管理逻辑完全不同。比如Ollama默认会常驻模型在内存里当你同时跑两个模型的时候它会频繁卸载加载这个切换延迟在Agent场景里非常致命。如果你要跑多模型一个对话模型加一个Embedding模型我建议要么把Embedding模型跑在独立进程里要么干脆用llama.cpp的server分别拉起。2.3 为什么我更倾向llama.cpp做Agent底座我的Agent项目目前跑在llama.cpp上理由有三点都是Agent场景特有的。第一是进程内推理与工具执行的紧耦合。Agent的每次推理结果往往要触发函数调用、操作外设、更新状态这些动作跟推理逻辑是紧密交织的。Ollama只能通过HTTP通信一旦Agent处于低功耗状态或者后台被系统挂起HTTP链路容易断。llama.cpp可以做成同一个进程内的库调用状态全在内存里稳得多。第二是KV Cache的可控性。Agent经常需要在多轮对话里保留关键信息llama.cpp允许你通过/v1/chat/completions的cache_prompt参数控制是否重用缓存或者通过底层接口精确清除某一段历史。Ollama的缓存策略更偏向对话轮次的自然延续对Agent这种人机交替、有时要主动遗忘的场景容易帮倒忙。第三是启动速度。Ollama的冷启动要拉起独立的后台进程加载模型权重这个过程在低端设备上可能要花10秒以上。llama.cpp的server模式或者进程内模式可以实现模型常驻首请求几乎零额外延迟。端侧Agent的每一次唤醒都要快速响应启动速度直接决定了体验上限。3. 模型选型逻辑先算量化账再谈模型能力3.1 量化到底在做什么用精度换速度的数学账端侧部署离不开量化。简单说量化就是把模型的FP16权重压缩到更低位宽的表示比如4bit、5bit、6bit、8bit。这背后的逻辑并不神秘模型训练好的权重是高精度浮点数但绝大多数浮点数的小数部分对推理结果的贡献并没有想象中那么大用更少的bit去近似它效果会打折扣但换来的是内存占用减半到大半、推理速度提升、功耗降低。以7B模型为例FP16权重约14GBQ8量化约7GBQ4_K_M量化约4.4GBQ3量化约3.5GB。在内存只有8GB的端侧设备上FP16根本跑不了Q8勉强Q4才是常态。不同量化格式的差别在于分块策略Q4_K_M这种混合精度方案会把权重矩阵按block分块核心奇异值保留更多bit非核心部分用更少bit效果要比单纯的Q4_0好20%以上所以现在业界基本都以Q4_K_M作为端侧部署的标配。llama.cpp的GGUF文件名里的这些量化后缀不是随便取的它们对应着一套精心调过的格式策略。我个人的经验法则是能上Q6/Q8就别用Q4但一旦内存预算卡住Q4_K_M是质量与体积比最佳的选择。Q3以上的损失肉眼可见只适合那些本身精度要求不高的对话场景。另外KV Cache也要量化llama.cpp里cache_type_kq8_0、cache_type_vq8_0这两个参数可以把缓存占用砍掉一半以上代价是轻微的精度损失。对于Agent场景KV Cache通常比权重更吃内存尤其是你打算把上下文窗口拉到8K以上时。3.2 参数量、内存带宽和上下文窗口的三角关系选模型不能只看参数名气要算三角账参数量决定模型大小内存带宽决定推理速度上下文窗口决定KV Cache消耗。三者互相制约。先看一个典型配置Jetson Orin NX 16GB跑Qwen2.5-7B-Instruct的Q4_K_M量化模型约4.4GB如果设置上下文4096KV Cache大约再吃1到2GB加上系统占用和Agent运行时总内存约8到9GB。这个配置在16GB设备上是安全的。同样的模型放在RK3588的8GB内存上KVCache要压缩到2048系统还得做内存切割跑起来就要掂量掂量了。再看速度的计算。RK3588的lPDDR4X带宽约68GB/s跑Q4_K_M的7B模型每生成一个Token需要读取约4.4GB的权重理论极限约15Token/s但CPU推理中权重读取只占一部分算子调度、内存碎片都会拖后腿实测一般落在8到12Token/s之间。如果你选了3B的Qwen模型只有2GB理论速度就能干到约30Token/s实测20Token/s以上体验截然不同。所以当你觉得模型卡第一步不是优化代码而是检查是不是模型体积和设备带宽不匹配。用Open LLM Leaderboard这类榜单来选模型在端侧场景其实是“看着热闹买了就后悔”。榜单分数是在A100/H100这类数据中心硬件上加长推理时间测出来的跟4bit量化后在ARM CPU上的真实表现完全是两码事。好的实践是先把榜单当作初筛锁定2到3个候选模型再在目标设备上实测速度与效果而不是只看分数排名。3.3 端侧Agent的最佳模型段位与实测选型表我测过不少组合下面这张表是当前比较能打的端侧Agent模型段位覆盖从入门到进阶模型量化显存/内存占用实测速度(Jetson Orin NX)适用场景Qwen2.5-0.5B-InstructQ80.6GB45 Token/s极低功耗设备、简单意图识别Qwen2.5-3B-InstructQ4_K_M2GB25 Token/s轻量Agent、日常对话、工具调用入门Qwen2.5-7B-InstructQ4_K_M4.5GB15 Token/s主力端侧Agent、复杂任务规划Llama-3.2-3B-InstructQ6_K2.5GB22 Token/s英文场景为主、需要更好指令遵循Phi-4-14BQ4_K_M8.5GB8 Token/s高内存设备、数学与逻辑推理DeepSeek-R1-Distill-Qwen-7BQ4_K_M4.6GB13 Token/s需要深度推理的Agent任务这里特别说下DeepSeek-R1-Distill系列。最近它热度很高因为它把推理模型的思维链能力蒸馏到了小模型上在端侧跑Agent确实有用。但我实测的体验是它的思维链会明显延长Token消耗同样一个工具调用指令普通模型可能30个Token就输出了R1-Distill可能要先输出200个Token的“思考过程”速度直接掉一半。所以如果你的Agent更多是高频执行型任务用普通指令模型如果是低频高难度任务比如设备故障诊断再上R1-Distill让它慢慢想。4. 端侧部署实操以Ollama和llama.cpp为核心的手把手指南4.1 用Ollama快速启动一个端侧LLM服务先讲最快能跑起来的路径适合验证模型效果和搭建Agent原型。以Ubuntu 22.04的ARM设备比如RK3588或者Jetson为例Ollama安装命令如下curl -fsSL https://ollama.com/install.sh | sh装完之后拉模型ollama pull qwen2.5:7b-instruct-q4_K_M ollama pull qwen2.5:3b-instruct-q4_K_M启动服务默认监听11434端口直接调用curl http://localhost:11434/v1/chat/completions \ -H Content-Type: application/json \ -d { model: qwen2.5:7b-instruct-q4_K_M, messages: [{role: user, content: 你好介绍一下你自己}], stream: false }注意Ollama的OpenAI兼容端点只实现了部分参数比如max_tokens、temperature、tools更细的采样参数要放到options字段里。它默认会做上下文拼接多轮对话会把历史所有消息都送进去这是Agent场景容易爆上下文的隐患之一——后面我会详解怎么处理。4.2 在llama.cpp里跑起生产级Agent后端上文提到Agent的最终底座我推荐llama.cpp这里给出一套我实际在用的部署流程。首先编译llama.cppARM机器上注意启用特定指令集选项git clone https://github.com/ggerganov/llama.cpp cd llama.cpp cmake -B build -DGGML_NATIVEOFF -DGGML_OPENMPON cmake --build build --config Release -j$(nproc)编译完成后把GGUF模型放进models目录用server模式启动./build/bin/llama-server \ -m models/qwen2.5-7b-instruct-q4_K_M.gguf \ --host 127.0.0.1 --port 8080 \ -c 4096 \ --cache-type-k q8_0 --cache-type-v q8_0 \ -ngl 0解释几个关键参数-c 4096把上下文窗口设置为4096--cache-type-k/v把KV Cache量化到8bit这一条直接决定你能不能在低内存设备上稳定跑多轮Agent。-ngl 0表示不加载GPU层如果你的设备有GPU/NPU且llama.cpp支持可以改成更大的数字。然后就可以用标准的/v1/chat/completions接口联调Agent了llama.cpp的这个接口对tools参数支持得比Ollama更完整。这里有一个实战细节llama.cpp默认的系统提示词格式跟模型本身的对话模板不总是完全匹配比如Qwen系列有自己的chat template启动时最好显式指定--chat-template qwen2.5否则会出现模型答非所问、前缀缺失这类诡异的格式问题。4.3 把嵌入模型和重排模型也搬到本地Agent的RAG记忆底座前一篇讲过Agent除了对话模型还需要一个Embedding模型做记忆检索。这里也一并部署我优先用BGE系列中文效果好支持的上下文长度也够用。在Ollama里拉取很简单ollama pull bge-m3在llama.cpp里则用多模型端口方案比如再起一个llama-server模型换成bge-m3.gguf端口8081。注意两个server的模型不要混用Embedding模型的启动参数里推荐把-c设到2048因为bge-m3原生支持到8192但你给太多上下文会让向量化任务变慢对Agent来说2048通常够用。到这里你就有了一个“对话模型嵌入模型”的双服务架构。这样做的好处是检索时不会占用对话模型的KV Cache两个模型各司其职。在Agent的RAG流程里嵌入模型负责把用户问题和知识库文本向量化重排模型如果有再把检索结果按相关性拉一次排序端侧完整链路才算打通。5. 让端侧LLM跑得更快性能优化三板斧5.1 直接看结论同样模型在不同设备上的性能对照先给大家看一组我在真实设备上跑Qwen2.5-7B-Instruct Q4_K_M的实测数字方便大家对端侧性能有个直观概念设备内存权重加载耗时首Token延迟生成速度8K上下文峰值内存PCi5-12400DDR432GB6s0.8s18 Token/s8.1GBJetson Orin NX 16GB16GB4s0.9s15 Token/s7.8GBRK35888GB版8GB5s2.1s9 Token/s7.9GBOOM边缘树莓派5 8GB8GB18s5s以上4 Token/s接近OOM数据说明两件事第一设备的内存带宽基本决定了上限代码怎么调都突破不了物理极限第二8G内存设备跑7B模型已经非常吃紧一旦Agent在自己的运行环境里再多开两个软件就会触发OOM。所以我能给出的最直接建议是要做认真的端侧Agent内存低于16GB的设备优先选3B模型7B模型留给16GB及以上的设备。5.2 三板斧之一KV Cache量化与上下文压缩性能优化里最立竿见影的是KV Cache。它是在推理过程中缓存历史Token计算结果的组件Agent场景下历史越长缓存占用越大。很多人的设备不是跑不动模型而是被KV Cache挤爆了。llama.cpp里把--cache-type-k q8_0 --cache-type-v q8_0打开后内存占用能降20%到35%速度还有轻微提升因为内存带宽被释放了。另一个常用技巧是上下文压缩。Agent每轮对话完后可以把关键的历史信息提取成摘要替换掉原始对话保证KV Cache只保留最近几轮原文。你可以用一个小模型做摘要也可以用LLM本身执行。这比无脑把整段历史塞进去能省出大量内存。5.3 三板斧之二并发时的批处理与模型常驻另一个容易被忽略的点是并发。Agent场景里经常有多个异步任务同时触发推理每来一个请求就去加载一次模型或者并发度高时由于没有批处理导致吞吐崩塌。Ollama和llama.cpp都有各自的并发处理逻辑但对端侧Agent而言最稳的配置是保持一个推理进程常驻模型权重一直占着内存然后通过进程内的异步队列来处理请求。llama.cpp的server本身就支持并发批处理多个请求会排队并按batch维度推理GPU/NPU有专门算子时能大幅提升吞吐。Ollama则要小心它的“多模型串行”策略同时拉起的第二个模型会先卸载第一个模型导致巨大的切换开销。实测下来同时用Ollama跑对话模型加Embedding模型时一次切换就要多花3到5秒这在Agent并发场景里是灾难性的。5.4 三板斧之三模型结构优化与低功耗策略如果标准优化已经做完速度还是不够就需要往模型结构上动刀。常用的招数做深度剪枝或稀疏化把不重要的层去掉用LoRA蒸馏把大模型能力迁移到更小的基座或者干脆换专用的端侧小模型。低功耗策略同样重要。在电池供电的端侧设备上推理时的功耗曲线往往是锯齿状模型加载瞬间CPU/GPU全速运转功耗飙高后续生成时又回落。如果设备支持DVFS可以尝试把推理进程绑定到大核把其他进程隔离到小核这样可以降低整体功耗并减少推理抖动。我在Jetson上用jetson_clocks固定到最高频率跑推理生成速度确实稳定不少但功耗也上去了需要结合散热方案一起考虑。6. Agent侧的LLM工程Function Calling与多级记忆的实际配置6.1 端侧LLM最容易被忽略的杀手级能力Function Calling光会对话的LLM不是AgentAgent的灵魂在于让模型学会“调用工具”。但端侧模型参数量小指令遵循能力比大模型弱Function Calling反而成了端侧Agent成败的分水岭。Function Calling的本质是让模型在回答中加入结构化的函数调用意图。比如用户说“把客厅温度调到26度”模型不直接回答“好的”而是输出一个JSON{ name: set_temperature, arguments: { room: livingroom, temperature: 26 } }Agent收到这个JSON后执行设备控制API再把结果反馈给模型。这个机制对端侧LLM的部署格式有直接影响必须在部署时就把tools参数完整传入模型才会按约定输出可解析的JSON。我用Qwen2.5-3B和7B都试过Function Calling7B的稳定度明显高于3B。3B在工具选择少的时候还行一旦超过5个工具就经常出现幻觉文本夹杂JSON的情况Agent框架一解析就崩。如果你必须用3B模型我的建议是要么减少工具数量要么在后端做一个基于规则的JSON修复层把模型输出的杂音清理掉再喂回给解析器。6.2 用LLM的“金丝雀层”保证输出可解析这里分享一个我一直在用且效果很好的小技巧让模型先输出“思考摘要”再进行工具调用。也就是设置一套输出格式规范要求模型严格按两步走第一段是Thought:用自然语言说它打算怎么做第二段是Action:输出一个JSON对象。这最初是ReAct思路的工程化落地在端侧小模型上效果尤其明显。实际操作时我会在系统提示词的末尾强行加上这段话你是一个智能Agent。你的每次行动必须按以下格式输出 Thought: 简要说明你为什么这样做。 Action: 一个JSON对象其中name为工具名arguments为工具参数字典。 如果认为不需要调用工具Action里name置为空字符串, arguments为空对象{}。然后我在部署层做一个解析器先用正则把Action:后面的JSON片段截出来再用json.loads校验失败时自动重试一次并把错误信息回传给模型。这个“金丝雀层”让模型输出被双保险夹住既有格式约束又有解析兜底。实测上线后Agent的工具调用成功率从68%提升到了93%效果非常可观。6.3 RAG、GraphRAG与LLM Wiki式记忆在端侧Agent的落法端侧Agent的记忆体系一般分三层第一层是对话上下文窗口里的短期记忆第二层是长期向量库第三层是外部知识库。c内部持久化。向量库我用纯SQLite加sqlite-vec扩展就够了几百MB的库做相似度检索毫秒级返回完全不用上独立的向量数据库。这个重量级的差别是端侧和云端的显著分歧——云端搜几十亿条向量端侧只要能搜几十万条就够用。说到知识库组织最近常被提到的“LLM Wiki”思路其实非常好用不是把所有知识都灌给模型而是以一个知识图谱的结构组织实体与关系用本体Ontology做约束再在检索时用GraphRAG做多跳查询。端侧跑本体约束本来就轻量因为它主要影响的是嵌入和检索的索引结构不需要模型做重推理。我把这个思路用来管理设备API文档和故障排查知识库检索准确率比纯向量相似度高了约18%因为GraphRAG能命中“排气温度高→可能联动冷却液不足”这类跨实体关系单靠向量相似度很难挖出来。6.4 上下文窗口管理Agent的“内存卫生”最后必须提上下文管理。端侧LLM的窗口一般只有4096或8192而Agent的一轮完整任务往往需要多次工具调用与结果反馈极易把窗口塞满。塞满后的后果是灾难性的要么最重要的信息被挤掉要么推理速度大幅下降甚至是产生“假性遗忘”——模型明明记得这件事却因为窗口后半段被塞满而表现成不知道。我总结了一套端侧Agent的上下文卫生规范建议直接照做先按预算划分给注入的系统提示词预留15%到20%给最近的两轮原始对话预留40%左右给长期记忆的检索结果预留20%剩下的给工具调用过程和中间结果。当总长度超过阈值时优先把历史对话压缩成摘要而不是直接丢弃。每当用户说出新的任务目标时主动从长期向量库检索相关记忆注入上下文而不是被动等待模型去“想起”。这套规范听上去很工程化但在端侧Agent里它不是锦上添花而是必要的基础设施。我见过很多项目明明模型部署没问题最后全部砸在“上下文无脑膨胀”这个坑里。7. 踩坑实录端侧LLM部署的几个高压线7.1 上下文窗口被系统提示词“偷走”很多人在配置-c 4096后以为真的就有4096个Token可用。实际上系统提示词、工具定义、Few-shot示例都会占掉窗口。如果你把Agent的完整提示词排出3000个Token那么真正能用的上下文就剩不到1000个稍微多几轮对话就爆。这个坑我踩了不止一次解决方案是每次启动时先打印一次llama-server的已使用Token数算清楚可用余量再设计提示词长度。工具定义尽量压缩能用一句话描述的别写一段话能用参数的别用描述性文字。7.2 量化带来的幻觉与稳定性问题Q4量化在小模型上会放大幻觉概率尤其是数字、日期、人名这类高敏感信息。我的实测经验是Q4_K_M在对话类任务上的幻觉率比Q8高约10%到15%。如果Agent涉及身份判断、危险预警这类需要高可靠性的任务请不要吝啬内存直接上Q8或者Q6哪怕因此牺牲一点生成速度。在Agent的决策链里一次错的工具调用比十次慢的正确调用更致命。7.3 多轮对话后生成速度突然跳水这是最隐蔽也最常见的问题第一轮对话飞快第五轮突然慢成乌龟CPU占用还不高。原因几乎总是KV Cache占满后被强制重算同时上下文窗口里的重复Token导致内存访问模式恶化。我在项目中加入了一个监控脚本每轮对话后检查生成速度当速度掉到初始值的50%以下时触发上下文压缩。压缩完成后速度回升但压出来的摘要会丢细节所以我只把摘要写进长期向量库原文在短期窗口里还能保留几轮形成双保险。7.4 设备散热导致推理性能断崖端侧设备跑LLM是持续高负载很多盒子的被动散热完全压不住。我有一个实测案例RK3588裸板在室温25度下跑7B模型前两分钟有10Token/s五分钟后就掉到5Token/s因为CPU已经撞到温度墙开始降频。你以为配置没变其实性能已经被散热偷走了。解决方法是加主动散热风扇、固定较高频率但限制负载时长、或者在Agent任务间插入功率控制的休眠期。这些藏在部署清单之外的物理工程往往是项目成败的关键。7.5 多进程抢占推理资源端侧Agent通常不只跑LLM还有其他资源密集型任务如物体识别模型、音频采集等。我遇到过一次很典型的案例Agent正在跑推理时摄像头的YOLOv8推理进程突然启动8GB内存的盒子一下OOM整个Agent重启。从那以后我在设计上强制推行资源隔离重任务要么串行要么把LLM推理进程的优先级提到最高并给摄像头任务单独划内存。后端服务重启容易端侧Agent的状态恢复成本非常高一定要防患于未然。7.6 排查链路示例一个“偶发超时”的完整排查过程最后举一个真实的排查链路当作方法论总结。现象是Agent在使用Llama-3.2-3B模型时每十次任务有两次会偶发超时没有任何报错日志。我先检查了推理日志发现超时发生时首Token延迟从800ms涨到5s以上接着用top审视内存发现此时根本没有内存压力但CPU使用率却出现一个诡异的尖峰进一步排查原来是向量检索和推理进程用同一个线程池检索大量文本时锁竞争严重把推理线程卡住了。最后我把推理和检索拆到两个独立进程用共享内存做结果回传问题彻底消失。这个案例提醒我端侧Agent的性能问题往往不在推理本身而在周边系统的协作方式上排查时必须跳出“只看LLM”的思维。写在最后的几句实在话做端侧Agent部署我最大的体会是它不像云端部署那样“模型一拉、容器一跑”就完事而是一个持续做减法与平衡的过程。你选7B模型就要接受3B的速度和内存你选Q4量化就要接受略高的幻觉率你选长上下文就要接受KV Cache的压力。没有全局最优解只有针对你的设备、任务和场景的局部最优解。如果你现在正卡在“模型跑通了但Agent不干活”这个阶段我强烈建议先检查三件事一是你的Function Calling协议被模型理解了吗二是你的上下文窗口够不够一轮完整任务跑下来三是你的检索和推理是否在抢资源这三关过了端侧LLM这一层才算真正打通。下一篇文章我打算聊聊端侧Agent的工具编排与状态管理我们到时候见。
阅读完成 · 觉得有帮助?