1. 端侧 Agent 为什么非要在本地跑 LLM
1.1 端侧 Agent 的工作链路
先把概念对齐一下。Agent 不是单个模型,而是一套“感知—决策—行动”的闭环系统。放在端侧(手机、机器人、车载盒子、边缘工控机上),这套链路通常长这样:摄像头/麦克风/传感器负责感知,把图像、音频、文本转成结构化输入;中间那颗 LLM 负责理解和决策,比如理解“帮我把客厅灯调暗”这句话并拆解成动作序列;最后再通过 API 或本地总线把指令发出去,控制设备执行。
所以 LLM 在这个链路里的角色,相当于人的“大脑皮层”——所有感知数据最终都要汇聚到这里做语义理解和规划。如果这颗大脑放在云端,那就意味着设备上每个交互动作都要把数据传到远程服务器,再等结果回来。延迟、隐私、断网、成本,四个问题会同时压过来。这也是为什么现在做端侧 Agent 的项目,几乎都绕不开一个核心课题:在算力有限的设备上,把 LLM 跑起来,而且要跑得能看。
1.2 三条硬理由:延迟、隐私、成本
先说延迟。人对话的自然节奏是几百毫秒以内有反馈,超过两秒人就会觉得“卡”。云端方案即使在 5G 网络下,一次完整请求也要经历“上行传输—排队推理—下行回传”三个环节,实测最理想也要 800ms 到 1.5s。而用本地模型,7B 参数量级在 Jetson Orin 或者带 NPU 的 RK3588 上,稳定做到 15~30 token/s,首 token 延迟可以压到 300ms 以内。这个差距在语音交互场景里体感是“能直接聊”和“像对讲机”的区别。
再说隐私。端侧 Agent 要处理的往往是设备本地的数据——通讯记录、相册、传感器数据、家中摄像头画面。这些数据一旦出了设备,哪怕走了加密通道,合规风险和用户心理门槛都是真实存在的。很多实际项目里,客户明确要求“数据不许出园区”,这时候本地部署不是可选项,是硬性条件。
第三是成本。云端 API 按 token 计费,看起来一次几厘钱不贵,但如果 Agent 是常驻的——比如车载助手每次启动都要做上下文摘要、智能终端每隔几分钟跑一次意图识别——累积账单会非常可观。自己部署本地模型,一次性购买硬件,之后边际成本几乎为零。尤其团队在做 PoC 阶段,调接口调试模型的时候,本地部署能省掉大量测试费用。
1.3 什么场景适合端侧,什么场景别硬上
端侧 LLM 不是万能的,得承认它的边界。我自己的判断标准很简单:任务中如果 80% 是“识别、提取、语义匹配”这类结构化操作,适合端侧;如果 80% 是“复杂推理、创意生成、长程规划”,老老实实接云端大模型。比如设备端的指令理解、槽位抽取、意图分类、敏感信息过滤,这些小模型完全能胜任;但你要 Agent 写一篇文章、做一个方案、推理一道复杂数学题,端侧 7B 模型确实吃力。另外,多模态场景要单独评估——视觉编码器会占掉不少内存带宽,如果任务既要有视觉又要长上下文,现阶段建议把基础识别放到专用小模型(比如 YOLOv8),LLM 专注做意图决策。
2. 硬件选型:先把显存和算力账算明白
2.1 部署前必做的内存预算
我在不少项目里看到过同一个问题:模型选好了,硬件买了,一跑就 OOM。根子上是没提前算内存账。这里分享一个经验公式,适配大多数 transformer 架构的 LLM:
模型权重显存 ≈ 参数量( B ) × 每参数字节数推理峰值内存 ≈ 权重显存 × 1.6~2.0(多出来的部分给 KV cache、激活值和临时张量)
以 7B 模型用 Q4_K_M 量化为例,每参数约 0.6 字节(4bit 权重加少量 overhead),权重约 4.2GB。但实际跑推理时,如果上下文窗口开到 4096,KV cache 还要额外占 1~2GB,加上推理中间变量,建议至少留出 8GB 可用内存。所以 Jetson Orin Nano 8GB 版本跑 7B 量化模型属于“卡着边用”,想舒服一点就上 16GB 版本或者 Orin NX 16GB。13B 模型 Q4 量化权重约 8GB,那就必须 16GB 起步,而且上下文窗口不能开太大。
2.2 主流端侧硬件横评
| 硬件平台 | 内存 | 算力 | 适配模型规模 | 优势 | 主要坑 |
|---|---|---|---|---|---|
| Jetson Orin Nano 8GB | 8GB LPDDR5 | 约 40 TOPS(INT8) | 1.5B~7B(Q4) | CUDA 生态成熟,兼容 llama.cpp 全套工具链 | 8GB 跑 7B 上下文窗口受限 |
| Jetson Orin NX 16GB | 16GB LPDDR5 | 约 100 TOPS | 7B~13B(Q4) | 性能强,能跑视觉模型 | 价格高,功耗约 15~25W |
| RK3588(16GB 版本) | 16GB LPDDR4X | NPU 6 TOPS + CPU 8核 | 1.5B~7B(需特定工具链) | 价格便宜,NPU 功耗低 | 需用 RKLLM 工具链,不支持常见 GGUF 直接复用 |
| 树莓派 5(8GB) | 8GB | 约 0.7 TOPS | 1.5B~3B(Q4) | 生态友好,适合原型验证 | 算力太弱,7B 跑起来只有 1~3 token/s |
选型这块我的核心建议是:不要只看 TOPS,要看“生态成熟度”和“社区踩坑数量”。Jetson 系列因为沿袭 CUDA,llama.cpp 和 Ollama 对它的支持最完善,很多问题搜得到答案。RK3588 的优势是便宜且能效比高,但 NPU 工具链相对封闭,YOLOv8 这类模型部署已经有成熟方案,LLM 部署则需要厂家专门适配的 RKLLM 工具包,GGUF 模型需要转换后才能在 NPU 上跑,工作量多出不少。
2.3 从业务场景反推硬件配置
具体选哪块板子,建议从业务场景反推:
- 如果做的是车载/机器人,需要连续行进中推理,建议 Jetson Orin NX 16GB,性能余量足,掉帧和卡顿容忍度高。
- 如果做的是智能家居中枢,设备固定、功耗敏感,RK3588 更合适,NPU 在跑视觉任务时功耗控制出色。
- 如果只是原型验证,先拿树莓派或者你的 Mac 笔记本跑通逻辑,再考虑换目标硬件。千万不要一上来就买高配板子——模型跑不通、Agent 链路没走顺之前,硬件就是吃灰。
另外提一个容易被忽略的点:电源和散热。Jetson Orin 满载时发热明显,如果装在密闭壳子里,拷机 20 分钟就会降频。我做过一个项目,因为散热没做好,推理速度从 25 token/s 掉到 8 token/s,排查了半天才发现是温度墙。所以选硬件时把散热方案的预算一并算进去,工业级场景直接买带主动散热的载板。
3. 模型选型与量化:别把 7B 模型看成“缩小版 GPT”
3.1 模型规模怎么定:7B、3B 还是 1.5B
部署之前先想清楚一个事情:端侧模型承担的任务边界在哪里。我的经验是——能用 3B 解决的,不要用 7B;能用 1.5B 解决的,不要用 3B。推理速度、内存占用、功耗、散热,全部跟模型规模正相关。1.5B~3B 模型适合做简单意图分类、指令槽位提取、闲聊回复;7B 模型适合做需要一定推理能力的任务编排、多步指令理解;超过 13B 的模型在端侧跑,现阶段基本属于自虐。
实测下来,中文场景里目前很值得试的模型包括:Qwen2.5 系列(有 0.5B/1.5B/3B/7B 多档,中文能力强)、DeepSeek 蒸馏版(7B 级别在推理任务上表现超出同尺寸平均水平)、Llama 3.2 3B(偏英文但通用能力强)。如果跑 DeepSeek 本地部署,7B 蒸馏版在 Jetson Orin NX 上 Q4 量化后大概 28 token/s,能用;3B 版能到 60 token/s,响应几乎无感。
3.2 量化方案:Q4_K_M 几乎是默认答案
量化这个东西,很多新手容易把它理解成“模型压缩后变差了”。实际上对于 7B 级别的模型,从 FP16 量化到 Q4 后,智力下降非常有限——实测在中文意图理解和指令跟随任务上,准确率差距在 2%~5% 以内,但内存占用和推理速度提升是肉眼可见的。我的经验:Q4_K_M 是性价比最高的量化档位。
| 量化档位 | 每参数字节 | 7B 权重占用 | 推理速度相对 FP16 | 适用场景 |
|---|---|---|---|---|
| Q8_0 | 1.06 | 约 7.4GB | 约 1.2 倍 | 内存充足,追求较高精度 |
| Q5_K_M | 0.72 | 约 5GB | 约 1.5 倍 | 精度与体积平衡 |
| Q4_K_M | 0.58 | 约 4.1GB | 约 1.8 倍 | 端侧首选,兼顾质量与体积 |
| Q3_K_M | 0.47 | 约 3.3GB | 约 2 倍 | 实在内存不够再考虑 |
另一个实操细节:量化不是模型精度的决定性因素,训练数据覆盖度才是。同一作者的同一个模型,4bit 量化版本可能比另一个团队训练不充分的 7B 高精度模型好用得多。所以选模型优先看社区评价和数据质量,量化位数排其次。
3.3 工具链横向对比:Ollama 最省事,llama.cpp 最可控
端侧部署 LLM 目前主流是三套工具链:
- Ollama:最省事,一条命令拉模型、启动服务、跑推理。官方仓库里的 GGUF 模型可以直接拉取,也支持自定义 Modelfile。如果你想快速把环境搭起来验证思路,选它没商量。
- llama.cpp:最灵活。支持细粒度参数控制、多线程调优、KV cache 量化,还能配合各种自写脚本做自定义推理逻辑。生产环境我做 Agent 服务时更倾向 llama.cpp,因为它暴露的控制参数全,方便做性能调优和异常排查。
- RKLLM:瑞芯微官方 NPU 方案。RK3588 上想让 LLM 在 NPU 上跑,只能用这个工具链。它支持把 Hugging Face 模型转成 RKLLM 格式,但转换过程有一些坑,后面专门说。
还有个补充方案:在 Jetson 平台上,你可以直接用CUDA 版 llama.cpp 或者带 CUDA 后端的 Ollama,这也是 Jetson 最香的一点——生态完全对齐桌面 Linux,几乎所有开源推理工具开箱即用。
4. 端侧部署实操:Jetson 和 RK3588 两条路都走一遍
4.1 第一步:把基础环境打磨干净
不管哪块板子,第一步都是把系统环境和依赖装干净。以 Jetson Orin 为例:
JetPack SDK 和 Docker 环境Jetson 建议直接用官方 JetPack 镜像,自带 CUDA、cuDNN、TensorRT。推荐用 Docker 方式部署,好处是环境隔离、方便回滚。这里有个坑:网上很多教程让你装 x86 架构的 CUDA 镜像,但 Jetson 是 ARM64 架构,镜像必须选带有l4t标签的 JetPack 专属版。Docker 安装用官方脚本一步到位,然后拉镜像这样操作:
# 在 Jetson 上安装 Docker(ARM64 版本) curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER # 拉取 JetPack 官方基础镜像(注意 l4t 标签) docker pull nvcr.io/nvidia/l4t-jetpack:r36.2.0 # 启动容器并挂载数据目录 docker run -it --rm \ --runtime nvidia \ --network host \ -v /home/user/models:/models \ nvcr.io/nvidia/l4t-jetpack:r36.2.0RK3588 的 NPU 环境RK3588 上,你首先需要在板子上安装 RKLLM 工具包。官方给的是一个完整的 runtime 库和 Python 推理脚本,安装流程不长,但要注意 RKLLM 目前只支持特定版本的 NPU 驱动,安装前务必备份原有系统,因为驱动升级可能导致其他功能异常。我建议在这类板子上先跑通官方自带的 demo(rkllm 测试模型),确认 NPU 可以正常推理,再替换成自己的模型。
4.2 用 Ollama 十分钟跑一个端侧模型
在 Jetson Orin 上,如果你选了 Ollama,部署流程非常快:
# 安装 Ollama(Jetson 上是 ARM64 版本) curl -fsSL https://ollama.com/install.sh | sh # 拉取 Qwen2.5 7B 量化版 ollama pull qwen2.5:7b-q4_K_M # 启动服务(默认端口 11434) ollama serve # 在另一个终端测试 curl http://localhost:11434/api/generate \ -d '{"model": "qwen2.5:7b-q4_K_M", "prompt": "用一句话解释什么是端侧Agent", "stream": false}'测试通了之后,我建议做一个 10 分钟连续推理压力测试,观察设备温度。Jetson 上 Ollama 默认配置没有针对 CUDA 做过特别调优,实测可能只有 15~20 token/s,但这只是开始,后面还有调优空间。
4.3 性能调优:线程数、KV cache 和批处理
这是最容易被忽略但其实影响最大的部分。以 llama.cpp 为例,我常用的调优思路:
- 线程数:CPU 推理时,线程数不是越高越好。实测 RK3588 的 A76 核心用 6 线程跑 3B 模型反而比 8 线程快 20%,因为线程切换开销超过了额外并行收益。Jetson 上如果走 CUDA,CPU 线程影响较小,但
--threads依然建议设为物理核心数而非逻辑核心数。 - KV cache 量化:llama.cpp 支持
--cache-type-k q8_0 --cache-type-v q8_0,把 KV cache 从 FP16 降到 8bit,内存能省 30% 左右,速度还有小幅提升。 - 上下文长度:端侧 Agent 很多场景用不到 32K 上下文。把
--ctx-size 4096甚至 2048,内存和首 token 延迟都会明显改善。我的经验是:上下文窗口越大,内存分配越多,KV cache 的预分配还会拖慢首 token。没有特殊需求,不要盲目开大窗口。
下面是一个我在 Jetson Orin NX 上实测过的一套 llama.cpp 启动命令:
./llama-server \ --model /models/qwen2.5-7b-instruct-q4_k_m.gguf \ --ctx-size 4096 \ --threads 6 \ --cache-type-k q8_0 \ --cache-type-v q8_0 \ --port 8080实测效果:7B Q4、上下文 4096,单请求稳定 22~28 token/s,首 token 延迟 420ms 左右。如果把模型换成 3B,速度直接翻倍到 55 token/s 以上——这说明 Agent 场景里,选小一号的模型往往体验反而更好。
4.4 RK3588 部署 YOLOv8 做视觉 Agent 的感知层
刚才提到端侧 Agent 是“感知—决策—行动”闭环,那感知层的视觉识别,在 RK3588 上最常见的方案就是部署 YOLOv8。这个过程也踩过不少坑,记录一下能绕开的弯路:
RKNN 工具链的核心步骤
RK3588 用的模型格式是 RKNN,不是 ONNX 直接能跑。官方推荐流程是:先训练/导出 ONNX,然后用rknn-toolkit2转成 .rknn 模型,最后在板子上用 RKNN Python API 或 C API 加载推理。我在实际项目中遇到的最大问题出在“归一化层”上——RKNN 转换器对模型输入尺寸和通道顺序很敏感,YOLOv8 默认的输出格式和官方工具要求不一致,容易直接报错。
实操中有个通用解法:先在模型导出环节固定输入尺寸(选 640×640 或者 320×320,优先选 32 的倍数),并在导出 ONNX 时把opset版本设置为 12 以下(RKNN 工具链对 opset 12 支持最好)。转换命令大概长这样:
# 在 x86 开发机上安装 rknn-toolkit2,执行转换 from rknn.api import RKNN rknn = RKNN(verbose=True) # 加载 ONNX 模型 rknn.config( target_platform='rk3588', mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]] ) rknn.load_onnx(model="./yolov8n.onnx", input_size_list=[[1, 3, 640, 640]]) # 构建并导出 RKNN 模型 rknn.build(do_quantization=True, dataset="./dataset.txt") rknn.export_rknn("./yolov8n.rknn")转换完之后,在板子上推理时,注意两点:一是输入图像要按 RGB 排列,别弄成 BGR;二是输出解析时 YOLOv8 的格式是“中心点+宽高+分类概率”,自行写后处理时别和 YOLOv5 的格式混淆。我一开始用 YOLOv5 的解析逻辑跑 YOLOv8,结果检测框偏了三天,最后查代码才发现是后处理公式差了一个维度。
如果你希望 YOLOv8 的检测结果和 LLM 的语义决策打通,可以把检测结果输出为 JSON 字符串,塞进 LLM 的 system prompt。比如“画面中有 2 个人、1 只猫,位置坐标分别是...”,让 Agent 模型基于这个结构化信息生成行动指令。这个思路在智能监控和巡检场景中很实用。
4.5 用 Docker 封装一套可复现的 Agent 服务
项目一旦跑通,下一步就是固化环境。我最推荐的做法是把 Agent 服务拆成两个容器:一个跑推理服务(Ollama 或 llama.cpp),一个跑 Agent 编排逻辑(负责调用模型、解析工具调用、控制设备)。这样定位问题时能快速判断是推理变慢还是编排逻辑卡住。docker-compose 大致长这样:
version: '3.8' services: llm: image: nvcr.io/nvidia/l4t-llama:latest runtime: nvidia command: ["./llama-server", "--model", "/models/qwen2.5-7b-q4_k_m.gguf", "--ctx-size", "4096"] ports: - "8080:8080" volumes: - /home/user/models:/models agent: build: ./agent-app depends_on: - llm environment: - LLM_ENDPOINT=http://llm:8080 ports: - "8000:8000"这里有个细节:如果你用的是 Ollama,容器编排时要避免把ollama serve和ollama run混在一起,前者是常驻服务,后者是交互式运行,在 Docker 里跑交互式会直接导致容器秒退。生产部署一律用ollama serve方式。
5. 常见问题与排查技巧实录
5.1 内存不足和设备重启
端侧设备内存是稀缺资源,OOM 是最高频问题。排查顺序:
- 看看是不是同时跑了视觉模型和语言模型。RK3588 上 YOLOv8 和 LLM 同时跑很容易内存爆掉,建议给视觉模型固定用 NPU、语言模型用 CPU,两者内存隔离。
- 检查上下文窗口。有个项目里我很奇怪为什么推理一长就重启,后来发现 Ollama 默认上下文 2048,但我的提示词加上历史记录涨到了 3000 多,内存迅速耗尽。
- 如果还不行,降低量化档位,从 Q5 换到 Q4,或者换更小的模型。
5.2 推理速度慢:排除散热墙
推理速度从 25 token/s 掉到 8 token/s,第一反应先查温度。
# Jetson 查看核心温度命令 sudo cat /sys/devices/virtual/thermal/thermal_zone0/temp # RK3588 同理如果温度超过 80°C,基本确定是撞上温度墙降频。解决办法:加强散热(加风扇/散热片)、降低满载时的模型并发、在系统层面做核心调度(把推理进程绑到高性能核心上,NPU 任务单独绑定)。
5.3 模型胡言乱语:先查采样参数
有时候模型推理结果完全离谱,第一反应别怀疑量化,先看 temperature 和 top_p。很多端侧场景直接用默认参数,但默认 temperature 0.7 在设备任务上偏“发散”。做 Agent 决策时,我建议调低到 0.2~0.3,让模型更确定性。另外注意 system prompt 的设计,比调参更重要——把设备能力列表、限制条件写清楚,比反复调采样参数管用得多。
5.4 并发请求卡死:Agent 扛并发要排队
端侧设备不是服务器,一次顶多跑一两个推理请求。如果你做的是多用户服务同时调用端侧 Agent,就会出现排队雪崩。解决办法有两个方向:一是应用层加请求队列(比如用 Redis 或异步任务队列),保证 LLM 一次只接一个请求;二是把 LLM 模型服务拆出多个实例(比如一个模型在 CPU、一个在同板 NPU 上),但这不是所有板子都支持。我的建议是:端侧 Agent 的设计目标应该是“个人专属助手”,而不是“多人共享服务”,架构上避免并发是高性价比方案。
5.5 常见问题速查表
| 现象 | 大概率原因 | 排查/解决 |
|---|---|---|
| 模型加载即崩 | 内存不足 | 减小模型、降低量化、关上下文动态分配 |
| 推理速度远低于预期 | 散热/降频、线程数不当 | 查温度,调整线程,尝试 CUDA 后端 |
| 输出语义混乱 | 温度过高、提示词不清 | temperature 调至 0.2,重构 system prompt |
| 首 token 延迟高 | KV cache 预分配过大 | 减小 ctx-size,启用 KV cache 量化 |
| 量化后效果崩塌 | 量化和任务场景不匹配 | 换 Q6/Q8 再试;数据分布窄的模型谨慎 Q4 |
| 摄像头推理丢帧 | 输入分辨率过高 | YOLOv8 输入降到 320×320 |
| Docker 容器秒退 | 服务与交互模式混用 | 一律使用常驻模式,如ollama serve |
最后分享一个我的体会
端侧 LLM 部署这件事,真正难的不是“跑起来”,而是“持续稳定地跑在真实业务里”。我见过太多项目,demo 阶段 30 token/s 引以为傲,一上产线就被温度、内存、并发这些“不太性感”的问题绊倒。以我的经验,做端侧 Agent 一定要先想清楚模型规模边界,再谈优化——很多时候换 3B 比纠结 7B 的调优更划算。把这个系列的思路拿去对照自己的项目,先跑通最小闭环,再慢慢加感知层和工具调用,这条路走起来才踏实。