1. 端侧 LLM 部署到底在解决什么问题
1.1 从云端 API 到端侧推理的动机转变
过去两年,大部分 Agent 项目都是把 LLM 放在云端,端上只负责采集输入、渲染输出。这个模式在 Demo 阶段非常舒服,但一旦进入真实产品,问题就集中爆发了。最直接的是延迟:一次请求要经过网络往返、排队、推理、回传,用户说一句话到看到响应,动辄两三秒起步。其次是成本,按 token 计费的账在用户量上来之后会变得非常难看,尤其是那些需要频繁调用工具、反复推理的 Agent 场景,token 消耗是普通对话的好几倍。再往下是隐私和可用性,用户的数据要离开设备,网络断了整个功能就废了。
端侧 LLM 部署要解决的就是这三件事:把推理搬到设备本地,让延迟降到几百毫秒级、让边际成本趋近于零、让数据不出设备。这不是一个"能不能做"的问题,而是一个"在什么约束下值得做"的问题。端侧设备的算力、内存、功耗都是硬约束,所以端侧部署的核心矛盾永远是:模型能力与设备资源之间的平衡。
我自己的判断是,端侧 Agent 并不是要把云端大模型完全替换掉,而是形成一个分层结构。高频、低复杂度、对延迟敏感的任务放在端侧,比如意图识别、槽位抽取、简单工具调用;低频、高复杂度、需要强推理的任务再交给云端。这种混合架构才是现阶段最务实的方案,而端侧 LLM 部署就是这套架构的地基。
1.2 端侧 Agent 对 LLM 的特殊要求
端侧 Agent 和普通的端侧对话应用不一样,它对 LLM 的要求更苛刻。普通对话只要生成流畅就行,Agent 还要稳定输出结构化内容,比如 JSON 格式的工具调用参数、固定的动作指令。这就要求模型不仅要小,还要"听话",指令遵循能力不能太差。
另一个关键点是首 token 延迟。Agent 的交互往往是多轮的,用户能明显感知到每一轮的响应速度。如果首 token 要等一秒以上,整个体验就崩了。所以端侧部署时,prefill 阶段的性能比 decode 阶段更值得关注,因为 Agent 的输入通常包含系统提示词、工具定义、历史上下文,prompt 长度不短。
还有一个容易被忽略的点是内存占用。端侧设备的内存是共享的,模型占多了,留给其他功能的空间就少了。一个 7B 模型用 FP16 存储要 14GB 左右,这在很多设备上直接不可行。所以量化几乎是端侧部署的必选项,而量化又会带来精度损失,这个权衡贯穿整个部署过程。
1.3 适合端侧部署的典型硬件盘点
目前能跑端侧 LLM 的硬件大致分几类。第一类是手机 SoC,比如高通的骁龙系列、联发科的天玑系列,它们的 NPU 算力在几十 TOPS 级别,内存通常 8GB 到 16GB。第二类是单板计算机和边缘计算盒子,比如 RK3588 这类芯片,NPU 算力 6 TOPS 左右,内存可以做到 16GB 甚至 32GB,性价比很高,适合做固定场景的边缘 Agent。第三类是带独立 GPU 的嵌入式设备,比如 Jetson Orin 系列,算力从几十到两百多 TOPS,能跑更大的模型,适合机器人、工业质检这类场景。
选硬件的逻辑很简单:先看你要跑多大的模型,再看你的延迟预算,最后看功耗和成本。如果只是跑 1B 到 3B 的模型做意图识别,RK3588 这种级别的板子就够了。如果要跑 7B 以上还要保证流畅,那就得上 Orin 或者带独显的设备。别一上来就追求最强硬件,端侧部署的性价比往往比绝对性能更重要。
2. 模型选型与量化:端侧部署的第一道坎
2.1 参数量、上下文长度与设备内存的三角关系
选模型的第一步是算内存账。一个模型在推理时占用的内存大致由三部分组成:权重、KV Cache、运行时开销。权重占用等于参数量乘以每个参数的字节数,FP16 是 2 字节,INT8 是 1 字节,INT4 是 0.5 字节。KV Cache 的占用和上下文长度、层数、隐藏维度、batch size 都相关,公式是 2 × 层数 × 上下文长度 × 隐藏维度 × batch size × 精度字节数。
举个例子,一个 7B 模型,32 层,隐藏维度 4096,上下文 4096,batch size 为 1,用 FP16 存 KV Cache,占用大约是 2 × 32 × 4096 × 4096 × 1 × 2 字节,算下来约 2GB。如果权重用 INT4 量化,大约 3.5GB,加上 KV Cache 和运行时开销,总共 6GB 左右。这意味着 8GB 内存的设备勉强能跑,但留给系统的余量很小。如果把上下文拉到 8192,KV Cache 直接翻倍到 4GB,就很可能 OOM 了。
所以端侧选模型,参数量和上下文长度要一起看。很多模型标称支持 32K 上下文,但端侧根本用不起,实际部署时往往要限制在 2K 到 4K。Agent 场景尤其要注意,因为工具定义和系统提示词会吃掉大量上下文预算。
2.2 量化方案怎么选:INT8、INT4 与混合精度
量化是端侧部署绕不开的环节。INT8 量化相对安全,精度损失通常在 1% 以内,内存直接减半,是很多场景的首选。INT4 量化能把内存再砍一半,但精度损失会明显一些,尤其是对指令遵循和结构化输出要求高的 Agent 任务,可能会出现格式错误、参数丢失的问题。
现在主流的做法是混合精度量化,也就是对敏感层用高精度,对不敏感的层用低精度。比如权重用 INT4,但 embedding 层和输出层保持 INT8 或 FP16,因为这两层对精度更敏感。还有一些方案会对 attention 的某些部分保留高精度。实际选型时,我建议先用 INT8 跑通,确认效果达标后再尝试 INT4,并且一定要用真实任务做评测,不能只看困惑度这种指标。
提示:量化后的模型一定要做端到端的功能测试,尤其是工具调用、JSON 输出这类结构化任务。困惑度下降一点点,可能对应的是工具调用成功率下降一大截。
2.3 主流端侧模型横向对比
目前端侧常用的模型大致有这么几类。Qwen 系列的小尺寸版本在中文场景表现不错,指令遵循能力较强,社区量化版本也丰富。Llama 系列的小模型生态最成熟,各种推理框架支持都很好。Phi 系列主打小体积强推理,在数学和逻辑任务上有优势。Gemma 系列在英文场景表现均衡。还有一些专门为端侧设计的模型,参数量在 0.5B 到 3B 之间,牺牲一部分通用能力换取极致的体积和速度。
选型时不要只看榜单分数,要看你的任务类型。如果是中文 Agent,优先考虑中文能力强的模型;如果是纯工具调用,重点看结构化输出稳定性;如果要做多模态,那可选范围会窄很多。我一般会准备两三个候选模型,用同一套真实任务跑一遍,看哪个在延迟和准确率上综合最优。
| 模型类型 | 参数量区间 | 典型内存占用(INT4) | 适合场景 |
|---|---|---|---|
| 超小模型 | 0.5B-1.5B | 0.5-1GB | 意图识别、分类、简单抽取 |
| 小模型 | 3B-4B | 2-3GB | 工具调用、短对话、轻量 Agent |
| 中等模型 | 7B-8B | 4-5GB | 复杂 Agent、多轮推理 |
| 大模型 | 13B+ | 8GB+ | 边缘服务器、高算力设备 |
3. 推理框架与运行时环境搭建
3.1 端侧推理框架选型逻辑
端侧推理框架的选择,核心看三件事:硬件后端支持、量化支持、以及和你的 Agent 框架的集成难度。常见的框架有 llama.cpp 系列、MNN、NCNN、ONNX Runtime、TensorRT 等。llama.cpp 的优势是 CPU 推理优化好、量化格式丰富、跨平台,在 ARM 设备上表现很稳。MNN 和 NCNN 是国内团队做的,对移动端和国产芯片支持好。TensorRT 在 NVIDIA 设备上性能最强,但绑定 CUDA 生态。ONNX Runtime 胜在通用,但端侧性能不一定是最优。
我的经验是,如果目标是快速验证,llama.cpp 是最省事的选择,几乎什么设备都能跑起来。如果要榨干 NPU 性能,那就得用芯片厂商提供的推理框架,比如 RK3588 上用 RKNN,Jetson 上用 TensorRT。这里有个坑:NPU 推理往往需要模型转换成特定格式,转换过程可能遇到算子不支持的问题,需要提前确认模型结构是否兼容。
3.2 从模型文件到可运行服务的完整流程
端侧部署的完整流程大致是:拿到原始模型权重,做量化或格式转换,用推理框架加载,封装成服务接口,再接入 Agent 逻辑。每一步都有细节。
以 llama.cpp 为例,第一步是把 HuggingFace 格式的模型转成 GGUF 格式,这一步会同时完成量化。转换命令大致是这样的:
python convert-hf-to-gguf.py ./model --outfile model-f16.gguf --outtype f16 ./quantize model-f16.gguf model-q4.gguf Q4_K_MQ4_K_M 是一种混合量化方案,对关键层保留较高精度,是端侧比较常用的档位。转换完成后,用 llama-server 启动一个本地 HTTP 服务:
./llama-server -m model-q4.gguf -c 4096 -t 4 --host 127.0.0.1 --port 8080这里的-c是上下文长度,-t是线程数。线程数不是越多越好,一般设成物理核心数,超线程反而可能因为调度开销导致性能下降。启动后就可以用标准的 OpenAI 兼容接口调用了,Agent 框架接入非常方便。
3.3 内存与线程参数的调优实操
参数调优是端侧部署里最需要经验的部分。上下文长度-c直接决定 KV Cache 大小,能小则小,够用就行。线程数-t要实测,不同设备差异很大。batch size 在端侧通常设为 1,因为 Agent 场景大多是单用户交互,增大 batch 只会增加内存占用。
还有一个关键参数是是否把模型全部加载到内存。有些框架支持 mmap,也就是内存映射,模型文件不全部读入内存,按需加载。这在内存紧张的设备上很有用,但会增加磁盘 IO,首次推理会慢一些。如果设备内存够,建议全部加载,延迟更稳定。
我实测下来,RK3588 上跑一个 3B 的 INT4 模型,4 线程,上下文 2048,首 token 延迟在 300 到 500 毫秒,decode 速度大概每秒 8 到 12 个 token。这个速度做 Agent 的工具调用是够用的,但做长文本生成就偏慢。所以端侧 Agent 的设计要尽量让模型少生成,多用结构化输出和工具调用。
4. 把 LLM 接进 Agent:端侧的特殊处理
4.1 提示词工程在端侧的简化策略
端侧模型能力有限,提示词不能像云端那样写得很长很复杂。系统提示词要极度精简,把最关键的规则放在最前面。工具定义也要压缩,只保留必要的参数说明,去掉冗余描述。我见过很多项目直接把云端的提示词搬到端侧,结果模型根本处理不了那么长的上下文,输出质量断崖式下跌。
一个实用的做法是把复杂任务拆解成多个简单步骤,每一步用短提示词单独调用模型。比如先做意图分类,再做参数抽取,最后做结果生成。虽然调用次数多了,但每次调用都简单,整体成功率反而更高。端侧模型的单次推理快,多调用几次的延迟代价可以接受。
4.2 工具调用与结构化输出的稳定性保障
端侧模型做工具调用,最大的问题是输出格式不稳定。有时候多输出一段解释,有时候 JSON 少个括号。解决办法有几个层次。第一层是在提示词里明确要求只输出 JSON,并给出格式示例。第二层是在推理时用 grammar 或 JSON schema 约束输出,很多推理框架支持这个功能,能从解码层面保证格式正确。第三层是在应用层做解析容错,解析失败时尝试修复或重试。
grammar 约束是最可靠的方案,但会稍微增加推理开销。如果框架支持,强烈建议开启。如果框架不支持,那就得在提示词和解析容错上多下功夫。我一般会写一个健壮的 JSON 解析器,能处理常见的格式问题,比如多余的前后缀、单引号、尾随逗号等。
4.3 多轮对话与上下文管理的端侧方案
端侧内存有限,上下文不能无限增长。多轮对话必须做上下文管理。最简单的策略是滑动窗口,只保留最近 N 轮。但 Agent 场景里,早期的工具调用结果可能对后续推理很重要,直接丢掉会出问题。
更好的做法是做摘要压缩,把历史对话用模型压缩成简短摘要,保留关键信息。但端侧模型做摘要本身也要消耗算力,所以要权衡。我的做法是分层管理:最近的几轮保留原文,更早的做摘要,再早的直接丢弃。同时把工具调用的结果结构化存储,需要时按需检索,而不是全部塞进上下文。
注意:端侧 Agent 的上下文预算要提前规划好,系统提示词、工具定义、历史对话、当前输入各占多少,心里要有数。否则很容易出现上下文溢出导致推理失败。
5. 性能优化与常见问题排查
5.1 首 token 延迟与吞吐的优化手段
首 token 延迟主要受 prefill 阶段影响,优化手段有几个。一是缩短 prompt,能精简就精简。二是用 KV Cache 复用,如果多轮对话的前缀相同,可以复用之前计算的 KV Cache,避免重复计算。很多推理框架支持 prompt cache,开启后多轮对话的首 token 延迟会明显下降。三是用更快的量化格式,比如 Q4 比 Q8 的 prefill 速度更快。
吞吐优化主要看 decode 阶段。端侧 decode 速度受内存带宽限制很大,因为每生成一个 token 都要读取全部权重。所以量化不仅省内存,还能提升 decode 速度。另外,减少生成 token 数也是提升整体吞吐的有效手段,让模型尽量输出短内容。
5.2 端侧部署典型故障速查表
| 故障现象 | 可能原因 | 排查方向 |
|---|---|---|
| 启动即 OOM | 模型太大或上下文过长 | 降低量化精度、减小上下文 |
| 首 token 极慢 | prompt 过长或未开 cache | 精简提示词、开启 prompt cache |
| 输出乱码 | 量化精度过低或格式不兼容 | 换量化档位、检查模型转换 |
| 工具调用失败 | 输出格式不稳定 | 开启 grammar 约束、加解析容错 |
| 推理速度波动大 | 线程数设置不当或热降频 | 调整线程数、检查散热 |
| NPU 不生效 | 模型未转换或算子不支持 | 确认模型格式、检查算子兼容性 |
5.3 我踩过的几个坑与应对经验
第一个坑是量化档位选太高。一开始为了省内存用了 Q3,结果模型输出质量惨不忍睹,工具调用基本全错。后来换成 Q4_K_M,质量明显回升,内存也没多多少。所以量化不要一味追求低比特,Q4 通常是端侧的甜点档位。
第二个坑是线程数设成 CPU 核心数。在 RK3588 上,8 核全开反而比 4 线程慢,因为大小核调度和内存带宽争抢。后来固定用 4 个大核,性能稳定多了。这个必须实测,没有通用答案。
第三个坑是忽略了散热。端侧设备体积小,长时间推理容易热降频,速度会掉一半。做压力测试时一定要跑够时间,观察速度是否稳定。如果降频严重,要么加散热,要么限制推理频率。
第四个坑是上下文长度设太大。为了支持长对话把上下文设成 8192,结果内存不够频繁 OOM。后来改成 2048 加滑动窗口,稳定多了。端侧部署要学会做减法,不是功能越多越好。
6. 端侧 Agent 的工程化落地建议
6.1 分层架构:端侧与云端的协同设计
纯端侧方案在很多场景下能力不够,纯云端方案又有延迟和成本问题。最务实的是分层架构。端侧负责高频、低延迟、隐私敏感的任务,云端负责低频、高复杂度、需要强推理的任务。端侧模型可以先做一轮判断,简单任务直接处理,复杂任务再转发云端。
这个架构的关键是路由逻辑。路由本身也要轻量,可以用端侧小模型做意图判断,或者用规则引擎。路由错了会导致体验下降,所以要有兜底机制,比如端侧处理置信度低时自动转云端。
6.2 模型更新与版本管理
端侧模型更新是个麻烦事。模型文件动辄几个 GB,不能像 App 那样频繁更新。我的做法是把模型和业务逻辑解耦,模型文件独立管理,支持增量更新和灰度发布。同时保留回滚能力,新模型出问题能快速切回旧版本。
版本管理还要考虑兼容性。不同版本的模型可能对提示词格式要求不同,业务代码要能适配多个模型版本。建议在模型文件里带上版本元信息,加载时校验,避免版本错配。
6.3 端侧 Agent 的评测与监控
端侧部署不能只看能不能跑起来,还要建立评测体系。评测要覆盖延迟、内存、准确率三个维度。延迟要分首 token 和 decode 分别测,内存要测峰值和稳态,准确率要用真实任务集测,不能只看通用榜单。
监控方面,端侧设备资源有限,不能做很重的埋点。我一般只采集关键指标:推理耗时、内存峰值、失败率、降频次数。这些数据定期上报,用于发现性能退化和定位问题。端侧 Agent 的稳定性比峰值性能更重要,因为用户对卡顿和崩溃的容忍度很低。
6.4 安全与隐私的端侧考量
端侧部署的一大卖点就是隐私,数据不出设备。但要注意,模型本身也可能泄露信息,比如通过提示词注入让模型输出系统提示词。端侧 Agent 要做好输入过滤和输出审查,尤其是涉及工具调用时,要防止恶意输入触发危险操作。
另外,端侧模型文件本身也要保护,防止被提取或篡改。虽然端侧模型的价值相对云端低,但如果模型是核心资产,还是要做一定的保护措施。权限控制也很重要,Agent 能调用的工具要严格限制,不能给它过大的权限。
我在实际项目里的体会是,端侧 LLM 部署最难的不是把模型跑起来,而是让它在资源约束下稳定地完成 Agent 任务。模型选型、量化、框架、提示词、上下文管理,每一环都会影响最终效果,而且这些环节是相互耦合的。调优的过程就是不断做权衡,没有一劳永逸的配置。建议从最小的可用方案开始,跑通端到端流程,再逐步优化每个环节,这样比一开始就追求完美配置要高效得多。