
1. 端侧 LLM 部署到底在解决什么问题端侧 Agent 这个话题最近一年被聊得很多但真正落到工程上第一道坎从来不是 Agent 的编排逻辑而是 LLM 本身怎么塞进设备里。云端 API 调用当然省事可一旦涉及隐私数据不出本地、网络不稳定、按 token 计费成本失控这几个场景端侧部署就从“可选”变成了“必须”。我做过几个把模型跑在 RK3588 和 Jetson Orin 上的项目踩过的坑比想象中多得多这篇就把端侧 LLM 部署这件事从头到尾拆一遍。先说清楚端侧 LLM 部署是什么。简单讲就是把原本跑在服务器 GPU 集群上的大语言模型经过量化、裁剪、格式转换之后部署到手机、开发板、边缘盒子这类算力和内存都受限的设备上让推理过程完全在本地完成。它能做的事情包括本地对话、意图识别、工具调用决策、文本摘要等适合那些对延迟敏感、对隐私要求高、或者根本连不上稳定网络的场景。如果你正在做端侧 Agent 项目或者想把 DeepSeek、Qwen 这类模型塞进一块 RK3588 开发板这篇内容基本能覆盖你 80% 的疑问。端侧部署和云端部署最大的区别在于资源约束。云端你可以随便堆 A100端侧你得在 8GB 甚至 4GB 内存里把模型、KV Cache、运行时全部塞下还要留出余量给系统和其他进程。这就决定了端侧 LLM 部署不是简单的“下载模型然后跑起来”而是一整套围绕内存、算力、功耗做取舍的工程决策。2. 端侧 LLM 部署的整体思路与方案选型2.1 先搞清楚你的硬件底线在哪里动手之前第一件事不是选模型而是把目标硬件的资源摸清楚。我见过太多人上来就问“RK3588 能跑 7B 吗”这个问题没有标准答案因为取决于量化精度、上下文长度、并发数三个变量。RK3588 典型配置是 8GB LPDDR4XNPU 算力 6 TOPSCPU 是 4 核 A76 加 4 核 A55。Jetson Orin Nano 则是 8GB 共享内存GPU 算力 40 TOPS。这两个平台的部署策略完全不同。判断能不能跑核心看三个数字模型权重占用、KV Cache 占用、运行时开销。模型权重占用约等于参数量乘以每参数字节数。FP16 是 2 字节每参数INT8 是 1 字节INT4 是 0.5 字节。一个 7B 模型 FP16 要 14GBINT4 只要 3.5GB。KV Cache 占用等于 2 乘以层数乘以隐藏维度乘以序列长度乘以精度字节数这个数字在长上下文时会爆炸。运行时开销包括推理框架本身、tokenizer、内存碎片一般预留 1 到 2GB。提示别只看模型文件大小。一个标称 4GB 的 INT4 模型加载后实际内存占用往往到 5GB 以上因为还有反量化缓冲区、中间激活值、框架开销。留 30% 余量是底线。2.2 量化方案怎么选才不踩坑量化是端侧部署的核心手段但量化不是越激进越好。我一般把量化分成三档来看。第一档是 FP16 或 BF16精度几乎无损但内存占用最大只适合 Jetson Orin 这种有 GPU 且内存相对宽裕的平台。第二档是 INT8精度损失通常在 1% 以内内存减半是大多数端侧场景的甜点区。第三档是 INT4内存再减半但精度损失开始明显尤其是数学推理和代码生成任务会出现答非所问的情况。量化方法上GPTQ 和 AWQ 是目前端侧最常用的两种。GPTQ 基于二阶信息做逐层量化压缩率高适合 GPU 推理。AWQ 是激活感知的权重量化对激活值大的通道做保护精度保持更好适合对质量要求高的场景。GGUF 格式则是 llama.cpp 生态的产物支持多种量化等级从 Q2_K 到 Q8_0灵活性最高CPU 推理首选。选量化方案时有个经验法则如果你的 Agent 主要做意图分类和工具调用决策INT4 完全够用因为这类任务对生成质量要求不高关键是分类准确。如果要做长文本摘要或复杂推理至少上 INT8否则输出会变得不可控。我在一个 RK3588 项目里用 Q4_K_M 跑 Qwen2.5-3B 做意图识别准确率比 INT8 只掉了 0.8 个百分点但内存省了 1.8GB这笔账很划算。2.3 推理框架的取舍逻辑端侧 LLM 推理框架现在主流的有 llama.cpp、MLC-LLM、TensorRT-LLM、ONNX Runtime 几个。llama.cpp 的优势是纯 C 实现依赖少CPU 推理性能强GGUF 生态成熟RK3588 上跑 CPU 推理基本是首选。MLC-LLM 基于 TVM支持 GPU 和移动端编译优化做得好但部署链路较长。TensorRT-LLM 是 NVIDIA 官方方案Jetson 上性能最强但只支持 NVIDIA 硬件且模型转换流程复杂。ONNX Runtime 通用性好但 LLM 推理优化不如前几个专门。我的选型逻辑是这样的如果是 RK3588 这类 ARM 开发板优先 llama.cpp因为 NPU 对 LLM 的支持还不成熟CPU 推理反而更稳。如果是 Jetson Orin优先 TensorRT-LLM能榨干 GPU 性能。如果要在手机端部署MLC-LLM 或 llama.cpp 的移动端版本都可以考虑。别迷信 NPU很多 NPU 对 Transformer 结构的支持有限算子不兼容时回退到 CPU 反而更慢。3. 核心细节解析与实操要点3.1 模型格式转换的完整链路从 HuggingFace 上的原始模型到端侧可运行的格式中间要经过几步转换。以 llama.cpp 为例流程是原始模型转 GGUF FP16再量化到目标等级。转换工具是 llama.cpp 自带的 convert_hf_to_gguf.py 和 llama-quantize。这里有个细节很多人忽略转换时的 tokenizer 配置必须和原始模型一致否则会出现 token 错位表现为模型输出乱码或重复。具体操作上先克隆 llama.cpp 仓库并编译然后执行转换脚本。转换命令大致是 python convert_hf_to_gguf.py 模型目录 --outfile 输出.gguf --outtype f16。这一步会生成 FP16 的 GGUF 文件体积约等于原始模型的两倍因为 GGUF 默认不做压缩。接着用量化工具做量化命令是 ./llama-quantize 输入.gguf 输出.gguf Q4_K_M。Q4_K_M 是混合量化对关键层用更高精度是质量和体积的平衡点。注意转换过程中如果报错说某个算子不支持通常是模型用了自定义算子或新架构。这时候要么换模型要么等框架更新。别硬改代码容易引入隐蔽 bug。3.2 内存布局与 KV Cache 管理端侧部署最容易翻车的地方就是内存。模型权重加载只是第一步KV Cache 才是运行时的大头。KV Cache 的大小和上下文长度成正比一个 7B 模型在 4096 上下文下INT8 KV Cache 大约占 1GBFP16 则要 2GB。如果 Agent 需要多轮对话上下文会持续增长内存压力越来越大。管理 KV Cache 有几个实用技巧。第一是限制最大上下文长度别默认用模型支持的最大值按实际需求设。第二是启用 KV Cache 量化llama.cpp 支持 --cache-type-k 和 --cache-type-v 参数设成 q8_0 能省一半内存。第三是及时清理历史Agent 场景下很多对话轮次不需要保留完整历史做滑动窗口或摘要压缩。我在一个项目里把上下文从 8192 降到 2048配合 KV 量化内存占用从 6.2GB 降到 3.8GB模型终于能稳定运行。3.3 线程数与批处理参数调优端侧 CPU 推理的性能对线程数非常敏感。RK3588 是 4 大核加 4 小核线程数设成 4 比设成 8 更快因为小核会拖后腿而且线程切换有开销。Jetson Orin 的 GPU 推理则要看 SM 数量和显存带宽。llama.cpp 的 -t 参数控制线程数-b 参数控制批处理大小-ngl 参数控制卸载到 GPU 的层数。调优时先用默认参数跑一遍记录 tokens per second。然后逐步调整线程数从 2 到 8 各跑一次找到峰值。批处理大小影响的是 prompt 处理速度对生成速度影响不大设成 512 通常够用。如果是 Jetson-ngl 设成 99 表示全部层卸载到 GPU但要注意显存是否够。我实测 RK3588 上 Qwen2.5-3B Q4_K_M4 线程能跑到 8 tokens per second8 线程反而降到 6.5。4. 实操过程与核心环节实现4.1 RK3588 上从零部署一个端侧 LLM以 RK3588 开发板部署 Qwen2.5-3B 为例完整流程如下。系统层面先确认内核版本和内存大小用 free -h 看可用内存用 nproc 看核心数。然后安装编译依赖包括 cmake、gcc、python3-dev。接着编译 llama.cpp开启 ARM NEON 优化编译命令是 cmake -B build -DLLAMA_NATIVEON然后 cmake --build build --config Release -j4。模型准备阶段从模型仓库下载 Qwen2.5-3B 的原始权重转成 GGUF FP16再量化到 Q4_K_M。这一步在 PC 上做别在开发板上做因为转换很吃内存。量化完成后把 GGUF 文件拷到开发板用 llama-server 启动服务。启动命令是 ./llama-server -m 模型.gguf -c 2048 -t 4 -b 512 --cache-type-k q8_0 --cache-type-v q8_0 --host 0.0.0.0 --port 8080。启动后先用 curl 测试接口发一个简单请求看响应。如果响应正常再接入 Agent 框架。这里有个关键点llama-server 默认是单并发如果 Agent 需要同时处理多个请求要加 --parallel 参数但并发数增加会成倍消耗内存RK3588 上建议不超过 2。4.2 Jetson Orin 上榨干 GPU 性能Jetson Orin 的部署路径不同优先用 TensorRT-LLM。流程是先把模型转成 TensorRT 引擎再用 Triton 或 TensorRT-LLM 的运行时加载。转换时需要指定精度、最大 batch size、最大序列长度。转换命令大致是 python convert_checkpoint.py --model_dir 模型目录 --output_dir 引擎目录 --dtype float16然后用 trtllm-build 构建引擎。构建引擎时有个参数很关键--max_batch_size 和 --max_input_len、--max_output_len。这三个参数决定了显存预分配量设大了浪费显存设小了请求会被拒绝。我的经验是按实际峰值需求的 1.5 倍设留出余量。Jetson Orin Nano 8GB 上跑 Qwen2.5-3B FP16max_batch_size 设 4max_input_len 设 1024max_output_len 设 512显存占用约 5.5GB能稳定运行。推理性能上Jetson Orin 的 GPU 比 RK3588 的 CPU 快一个数量级同样 3B 模型能跑到 40 tokens per second 以上。但代价是功耗高Orin Nano 满载约 15WRK3588 约 5W。如果是电池供电的场景这个差异很致命。4.3 端侧 Agent 的接口对接LLM 部署好之后下一步是接入 Agent 框架。端侧 Agent 和云端 Agent 的接口对接逻辑一样都是通过 HTTP 或本地 socket 调用 LLM 服务。区别在于端侧要考虑延迟和并发。llama-server 提供 OpenAI 兼容接口Agent 框架可以直接用 openai 的 SDK 指向本地地址。对接时要注意几个细节。第一是超时设置端侧推理慢超时要设长一点至少 30 秒。第二是重试策略端侧可能因为内存不足导致请求失败要有降级方案比如缩短上下文或切换到更小的模型。第三是流式输出端侧生成慢流式输出能显著改善用户体验llama-server 支持 stream 参数。我在一个端侧 Agent 项目里把 LLM 服务、工具调用、记忆管理都放在同一个进程里用共享内存传递数据避免了 HTTP 开销。这样做的代价是耦合度高但延迟从 200ms 降到了 50ms对于实时性要求高的场景很值得。5. 常见问题与排查技巧实录5.1 模型加载失败与内存不足最常见的报错是加载模型时提示内存分配失败。原因通常是模型文件太大或者系统预留内存不够。排查步骤是先看 free -h 确认可用内存再看模型文件大小和量化等级。如果内存确实不够降级量化等级比如从 Q5_K_M 降到 Q4_K_M或者换更小的模型。还有一个隐蔽问题是内存碎片。长时间运行后即使总内存够也可能因为碎片导致大块分配失败。解决办法是启动时预分配 KV Cachellama.cpp 的 --no-mmap 参数可以强制一次性加载避免运行时分配。另外定期重启服务也能缓解碎片问题。5.2 推理速度慢的排查思路速度慢的原因很多按优先级排查。先看线程数是否合理用 top 看 CPU 占用如果只有一两个核心跑满说明线程数设少了。再看是否用了 GPU 加速Jetson 上如果 -ngl 设成 0就是纯 CPU 推理慢是正常的。然后看上下文长度上下文越长注意力计算越慢尤其是超过 2048 之后。还有一个容易被忽略的点是散热。RK3588 和 Jetson 在高负载下都会降频如果散热不好跑几分钟后速度会明显下降。我遇到过一块 RK3588 板子刚开始 8 tokens per second十分钟后降到 4加了个散热片就稳定了。所以端侧部署一定要考虑散热设计。5.3 输出质量异常的定位方法输出乱码、重复、答非所问通常和量化、tokenizer、prompt 格式有关。先确认 tokenizer 是否匹配用相同的 prompt 在 PC 上跑原始模型对比。如果 PC 上正常端侧异常那就是量化问题换更高精度的量化等级试试。如果 PC 上也异常那是 prompt 格式问题检查是否用了模型要求的对话模板。重复输出是端侧小模型的常见问题尤其是 INT4 量化后。解决办法是调低 temperature 和 top_p或者加重复惩罚。llama.cpp 的 --repeat-penalty 参数设成 1.1 到 1.2 能有效缓解。另外prompt 里明确要求“不要重复”也有帮助虽然听起来很玄学但实测有效。5.4 常见问题速查表问题现象可能原因排查方法解决方案加载失败内存不足free -h 看可用内存降量化等级或换小模型推理慢线程数不合理top 看 CPU 占用调整 -t 参数输出乱码tokenizer 不匹配PC 上对比测试重新转换模型重复输出量化精度低换 INT8 测试调低 temperature运行一段时间变慢散热降频监控温度加散热片并发请求失败内存不够看并发数降低 --parallel提示端侧部署的问题排查核心思路是“先排除硬件再排除配置最后怀疑模型”。大部分问题都是配置和资源问题模型本身出问题的概率很低。6. 端侧 LLM 部署的经验心得与扩展方向6.1 几个反直觉的实操经验第一个反直觉的点是小模型不一定比大模型快。在端侧3B 模型和 7B 模型的推理速度差异往往没有内存占用差异那么大。因为推理速度主要受内存带宽限制而 3B 和 7B 在 INT4 下的内存占用差异是 2GB 左右如果设备内存够7B 的体验明显更好。我在 Jetson Orin 上对比过 Qwen2.5-3B 和 7B7B 只慢了 30%但回答质量高一个档次。第二个反直觉的点是NPU 不一定比 CPU 快。很多开发板宣传 NPU 算力多少 TOPS但 LLM 推理是内存带宽瓶颈不是算力瓶颈。NPU 的算力优势在卷积网络上明显在 Transformer 上因为算子支持和内存访问模式的问题实际加速有限。我实测 RK3588 的 NPU 跑 LLM因为算子不兼容频繁回退反而比纯 CPU 慢。第三个反直觉的点是量化到 INT4 后模型可能变得“固执”。表现为不管怎么调 prompt输出都差不多。这是因为量化损失了模型的表达能力让它倾向于输出高频 token。解决办法是换用混合量化对注意力层保持高精度或者干脆用 INT8。6.2 端侧 Agent 的并发问题端侧 Agent 扛并发是个难题。云端可以水平扩展端侧只有一块板子。llama-server 的 --parallel 参数能支持并发但每个并发请求都要独立的 KV Cache内存成倍增长。RK3588 8GB 内存跑 3B 模型 Q4单并发占 3.5GB双并发就 5GB 以上三并发基本就 OOM 了。实用的并发策略是请求队列加超时丢弃。Agent 框架层做队列管理超过队列长度的请求直接返回忙而不是排队等。另外可以把不同任务分配到不同模型轻量任务用小模型重量任务用大模型通过路由层分发。这样能在有限资源下支撑更多并发。6.3 后续可以扩展的方向端侧 LLM 部署做完之后有几个方向可以继续深入。一是模型蒸馏用大模型的能力蒸馏出更小的端侧专用模型在特定任务上达到接近大模型的效果。二是 speculative decoding用小模型做草稿大模型做验证能显著提升生成速度。三是端云协同简单任务端侧处理复杂任务上云兼顾隐私和效果。还有一个方向是端侧 RAG。把知识库向量化后存在本地Agent 检索时先查本地向量库再让 LLM 生成回答。这样既保证了知识更新又避免了把敏感数据传到云端。向量库可以用 sqlite-vec 或 faiss 的轻量版本在 RK3588 上跑几千条向量检索毫无压力。我个人在实际操作中的体会是端侧 LLM 部署没有银弹每个设备、每个模型、每个场景都要单独调优。但一旦跑通那种完全本地化、不依赖网络的 Agent 体验是云端方案给不了的。最后分享一个小技巧部署前先在 PC 上用相同量化等级跑一遍确认模型质量可接受再上端侧能省下大量调试时间。