
端侧 Agent 工程化这事我踩了整整两个月的坑才稍微摸出门道。大家聊 Agent 时默认的往往都是云端方案——LangChain、Dify、CrewAI 这些框架跑在 GPU 服务器上模型动辄几十上百 B但真正要做到端侧也就是把 Agent 跑在手机、平板、车载盒子、边缘网关这些设备上工程化的问题是另外一套逻辑。这篇文章是我的“深入理解端侧 Agent”系列的第三篇先讲工程化的上篇从架构设计到模型部署再到工具调用、记忆管理和稳定性排查把从 demo 变成可交付系统的关键路径捋一遍。很多朋友问我端侧 Agent 是不是就是把一个开源模型用量化工具压一压塞进 App 里然后调用一个 OpenAI 兼容接口就行真不是。模型能跑起来只算第一步工程化要解决的是另外一堆问题设备内存只有 8GB你留给模型多少Agent 一次任务可能要在工具和模型之间来回五六轮每轮几百毫秒用户受得了吗没有网络的时候工具调用的兜底方案是什么这些问题不搞清楚Demo 永远只能停留在展示阶段。1. 先搞清楚端侧 Agent 和云端 Agent 的本质差异1.1 端侧 Agent 到底是什么为什么要做工程化端侧 Agent在我的理解里不是“在手机上部署一个小模型就算 Agent”而是指一个完整的智能体系统运行在用户设备本地模型推理在本地执行、工具调用以本地能力为主、用户数据不出设备。它追求的东西很直接隐私不裸奔、交互零延迟、离线能兜底、省流量。但“能跑”和“工程化”是两回事。工程化意味着可交付、可维护、可监控、可回滚。举个例子模型文件放在哪是打进安装包里还是首次启动下载如果打进安装包包体积大了好几个 GB很多用户根本不会安装如果首次下载用户等待期间你给不给进度条弱网环境下载失败如何重试断点续传做不做这些问题不在模型 accuracy 里却是真正决定端侧 Agent 能不能上线的关键。1.2 端侧环境的核心约束端侧和云端的差距看这张表最直接约束项云端服务器端侧设备算力数千 TOPS多卡并行几 TOPS 到几十 TOPS 的 NPU/GPU内存几十 GB 到几百 GB 独立显存6GB 到 16GB 共享内存能耗数百瓦不敏感毫瓦到几瓦直接决定发热与续航网络稳定高速带宽波动、弱网甚至离线框架依赖Python 生态随便装动态库、打包体积、系统兼容性处处受限这张表是端侧工程化的出发点。算力约束决定你只能用几 B 到十几 B 的模型内存约束决定你必须量化、必须限制上下文长度能耗约束决定你不能让 NPU 或者 GPU 长时间满载跑网络约束决定你的工具调用不能都走云端 API本地能力要占大头。任何架构决策回到这张表上都能找到理由。1.3 云端那套 Agent 架构搬到端侧为什么跑不动在云端写 Agent天然容易想到 LangChain、Dify 这类框架定义 Prompt、接一堆工具、再加一层 memoryAgentExecutor 就开始循环了。这套东西搬到端侧通常会遇到三个硬伤。第一个硬伤是运行时太重。LangChain 这类 Python 框架动辄几十上百个依赖包装到轻量级宿主机上体积爆炸更别说很多端侧主语言是 C、Rust、Kotlin 或者 Swift。第二个硬伤是循环开销太高。云端 Agent 内部每轮 tool call 都要调一次大模型GPU 上几百毫秒无所谓端侧一次推理就要一到三秒如果一个任务要来回五六轮用户早就关掉页面了。第三个硬伤是记忆和工具几乎都挂在云端。向量库在服务器上工具是云函数、HTTP API一旦断网就全线瘫痪。所以端侧 Agent 工程化的第一步不是选框架而是做减法和重构把能放本地的全放本地把循环次数降到最低把依赖体积压到最小。这个思路贯穿整篇文章。2. 端侧 Agent 工程化的整体设计思路2.1 从 demo 到可交付缺的不只是模型很多人觉得只要模型能输出 JSONAgent 就成型了。实践下来完全不是。你需要提前设计几件事模型文件的分发与版本管理、推理服务的生命周期、异常降级策略、日志与监控埋点、权限与隐私边界、以及一套能在真机上跑的回归评测。这些都是“工程化”里“工程”两个字的分量。我可以给一个很具体的例子。之前我做一版端侧助理模型是 3B 量化版本地效果不错。打包之后才发现模型文件占 2GB安装包直接超线于是改成启动后二次下载。但下载完发现部分老机型解压到私有目录时 IO 很慢导致启动时用户点击按钮没有反应。最后不得不在安装包里放一个最小的意图识别模型几十 MB做兜底完整模型在后台偷偷下载。这个“主模型 兜底模型”双轨设计就是工程化问题逼出来的。2.2 分层架构模型层、推理层、工具层、编排层我建议把端侧 Agent 拆成四个层级各层只解决自己的问题层级主要职责关键决策点模型层模型文件、量化版本、更新机制选几 B、量化精度、是否支持 function calling推理层加载模型、推理调度、流式输出推理引擎、内存复用、线程优先级、批处理策略工具层本地能力封装、工具 schema、权限控制工具白名单、参数校验、外部 API 桥接编排层上下文管理、循环控制、Agent 状态机最大轮数、摘要策略、失败重试、用户确认分层的好处是每个问题都有明确的修改边界。模型效果不好你换模型层不用动工具层推理慢调推理层工具的返回格式变了只改工具层。之前见过不少端侧 Agent 项目所有逻辑写在两三个文件里短期跑得欢等要加权限控制、做多轮记忆时完全没法下手。我自己的习惯是每层保留一份设计文档和一个压测脚本模型层压精度、推理层压延迟、工具层压成功率、编排层压轮数收敛各自有各自的门禁。2.3 一个可落地的端侧 Agent 骨架下面这个伪代码描述的是骨架逻辑不依赖具体语言。无论你主语言是 Kotlin 还是 C 还是 Rust核心循环大致相同async def agent_loop(user_input, session): # 1. 加载/复用推理引擎保持模型常驻内存 engine get_shared_engine() # 2. 经过规则层过滤判断是普通对话还是需要调用工具 plan session.planner(user_input) # 3. 循环上限防止 Agent 无限套娃 for step in range(MAX_STEPS): messages build_messages(session, plan, step) llm_response engine.generate(messages, toolsschema_list) if llm_response.is_tool_call: result execute_local_tool(llm_response.tool_call) if result.needs_user_confirm: return await request_confirmation(result) session.append_tool_result(result) continue # 把工具结果交回给模型继续推理 else: return llm_response.final_answer return fallback_response(任务太复杂已超最大步骤)骨架里最容易被忽略的是第 3 步的 MAX_STEPS。云端 Agent 套娃个二三十轮可能还行端侧每轮动辄一秒起步轮数上限必须根据实测延迟和业务容忍度来定。我一般默认给 4 到 6 轮超过就转人工兜底绝不硬跑。这个设计不是为了限制大模型能力而是倒逼上游的意图拆解和工具调用做扎实——如果模型在五六轮内还收敛不到最终答案多数情况下不是它笨而是你的工具 schema 太乱、Prompt 边界不清或者记忆被无关内容带偏。3. 核心环节实操模型选型、推理引擎与资源预算3.1 模型选型任务复杂度与参数量的平衡端侧模型选型上不要迷信“越大越好”。我的经验是先把任务拆成两档第一档是意图识别、简单问答、信息抽取3B 以下的小模型完全够用第二档是复杂多轮推理、工具编排这类任务至少要 7B 起步实话说 3B 的 tool calling 能力普遍不太稳。还有一些容易被忽略的细节模型是不是原生支持 function calling词表大小和 tokenizer 的实现有没有测试过中文场景下的 token 切分效率。同样是 7B 模型有的在中文场景下因为 BPE 合并效率低输出同一段内容要多烧百分之二三十的 token延迟和耗电都会显著放大。内存预算是手机场景的最高优先级。模型权重占用的内存按这个近似公式来估模型内存 ≈ 参数量 × 每权重字节数例如 7B 模型用 Q4_K_M 量化每个权重约 0.5 字节权重部分约 3.5GB如果 FP16就是 14GB绝大多数手机直接出局。再算上 KV cache按 4bit 缓存、上下文 2048、批处理 1 来算一般再吃 0.3 到 0.6GB最终常驻占用大概 4GB 上下。这时候留给 App 其他业务的内存就不多了所以上下文长度、缓存精度、模型版本都要打包起来做预算而不是只盯着模型文件大小。3.2 推理引擎与硬件适配量化、算子、NPU/GPU推理引擎的选择直接决定你能榨出多少硬件性能。常见的几条路线llama.cpp 类的 GGUF 方案成熟稳定社区模型多适合快速验证MNN、NCNN 这类移动端推理框架对安卓/iOS 系统兼容性做得更细ONNX Runtime 适合希望跨平台统一模型格式的团队。没有绝对谁最好只有适不适合你的设备矩阵。真正需要花时间的是算子级适配。端侧 NPU 支持的算子集合往往比 GPU/CPU 小很多RoPE、GQA、RMSNorm 这些模块在 NPU 上跑不动就得切回 CPU 分支而 CPU 和 NPU 之间的数据搬运又很贵。我的建议是不要一开始就追求全设备全算子 NPU 化先在主力机型上打通整条链路再把算子逐渐下沉到 NPU。这样做工程风险最可控调优也有明确的对照基线。另外工具调用场景下模型必须稳定输出合法的结构化内容。只靠 Prompt 让模型输出 JSON在端侧模型上很容易翻车漏一个引号整套逻辑就断了。所以推理层要支持约束解码也就是在采样阶段用 grammar 或正则把输出空间限制成合法的 JSON 结构。这个能力很多端侧推理引擎已经内置务必在选型时把它列为必选项别等项目中期再补。3.3 内存与耗电预算怎么算、怎么压端侧的工程化内存预算不是上线前看一眼就行而是架构层面反复权衡的过程。一个推荐的启动流程是App 冷启动时不预加载大模型等用户第一次真正触发 Agent 功能时再初始化推理引擎引擎初始化完成后常驻但设置空闲回收策略比如 App 退到后台超过 5 分钟就卸载模型回到前台再快速重新加载。这样既保证会话连续性又不会让后台进程长期霸占宝贵内存。耗电这块最容易踩的坑是让推理任务长期占据高频率核心。推理引擎跑在后台CPU/GPU 频率会一直维持在高位用户会明显感觉到手机发热。我建议做频率和时长的双重控制单次推理超过一定时长主动降频连续推理任务之间插入冷却窗口能走 NPU 的算子尽量走 NPU因为 NPU 处理推理的每瓦效能通常比 CPU 高很多。上线前用电源监测工具统计一次完整任务的耗电增量这个数字应该作为和延迟、内存同等级的发布门槛指标。只有把内存和耗电都放进 KPI团队才会认真对待资源问题。4. Agent 工具调用与编排的端侧化改造4.1 Tool 调用不是简单发个 JSON云端 Agent 的工具调用链路通常是这样模型输出 JSON → 代码解析 → HTTP 调用远端服务。到端侧这个链路要改出三层东西。第一层是工具 schema 的瘦身。端侧模型能力有限你塞二三十个工具的 schema 给它它很容易选错或漏参数。我建议一次请求最多挂 6 到 8 个和当前意图相关的工具其他工具通过意图分类路由到不同分支这比一股脑全都塞进去稳定得多。第二层是结构化输出约束。前面提到用 grammar 约束模型输出具体实现上就是把工具定义变成一套严格的 schema让推理引擎只能在 schema 允许的 token 序列里采样。这么做之后工具调用的解析失败率能从百分之十几压到千分之几。第三层是 harness 和 Agent 的职责边界。很多人会把 harness 和 Agent 当成一回事其实 harness 是承载 Agent 的运行环境它负责模型加载、工具分发、状态保存、超时控制Agent 是那个产生决策的智能体。工程化的核心稳定器在 harness而不在 Agent 本身。harness 设计得好Agent 模型换掉、工具增删都不需要大改。4.2 记忆与上下文的本地化存储端侧 Agent 的上下文管理比云端更烧脑筋因为每多塞一个 token推理延迟和内存都要增长。云端可以把所有历史消息摞在一起端侧不能这么浪费。推荐的做法是三段式记忆当前会话的最近几轮完整保留作为精细上下文更早的内容统一压缩成结构化摘要交给模型作为 background summary涉及用户日程、位置、偏好等持久信息写入本地 SQLite 或轻量键值库只在必要时以摘录形式注入 Prompt。工具返回的结果尤其要控制体积。比如一个搜索工具返回了一篇长文你不能把全文塞进上下文而要先把内容做摘要只把关键实体、时间、结论存入记忆。这既省 token也避免无关细节把模型带偏。数据存储在本地还要做基本的访问控制和加密至少不能明文躺在数据库里否则用户隐私保护就是一句空话。4.3 单 Agent 还是多 Agent端侧怎么选端侧对多 Agent 的态度我的结论很直接尽量别做真正的多 Agent。为什么因为多 Agent 的本质是用多轮完整推理交换任务拆解能力每一个 Agent 都是一次完整的大模型调用端侧的时延和能耗扛不住。但这不代表你只能有一个笨重的单体 Prompt。可以做得更轻巧一个主 Agent 负责理解用户意图和生成最终答案背后挂的是一堆“轻量分类器 专用流程”。比如检测到用户想订外卖不要启动一个完整的外卖 Agent而是先用一个小模型判断意图然后走规则化流程去调本地或云端的外卖工具。这种“重决策、轻执行”的结构比多个 LLM Agent 互相聊天要靠谱得多。如果业务上确实需要多角色协作也要控制在同一个进程内共享同一个模型实例靠 Prompt 切换角色和状态机控制流程而不是同时跑多个模型副本。否则不仅内存翻倍还会出现两个 Agent 各说各话、谁也无法收敛的局面。5. 工程化落地中的常见问题与排查思路5.1 首包延迟和线程调度问题端侧 Agent 最常见的用户体验问题是用户说完一句话之后迟迟等不到第一个字。这里的关键指标是 TTFTtime to first token它不是模型算得慢而是启动链路太厚。我排查过几个真实案例最后发现瓶颈往往不在推理而在这些地方模型文件首次加载没有做 mmap文件 IO 占了几百毫秒推理前有大量同步的 tokenizer 初始化卡在 UI 线程消息构建时把整份历史来回拷贝GC 被频繁触发。优化思路三步走第一模型加载和引擎初始化放到后台线程并在 App 启动时做预热第二用 mmap 方式加载模型权重避免整文件读入内存第三把 tokenizer 实例设成全局单例并发请求共享。做完这三步很多设备上的 TTFT 会从三秒以上降到一秒以内。排查这类问题一定要先在真机上埋点把耗时拆到模型加载、tokenizer、prefill、decode 每一段否则很容易在错误的地方做优化。5.2 模型注入与安全边界端侧 Agent 的工具非常多涉及本地敏感能力发短信、读通讯录、操作系统设置。这类能力不能由模型自由执行必须在 harness 层加一道白名单和权限闸门。我的原则是模型有建议权用户有决定权。任何写操作、跨应用操作、涉及个人隐私的操作都必须弹确认框只读类操作也要在日志里留痕。另外要注意提示注入。端侧 Agent 经常会把外部获取到的内容网页、邮件、聊天消息放进上下文模型看到这些内容后可能被诱导去执行预设之外的工具调用。处理方式很简单但很关键在 Prompt 里清楚地划分边界用不可信内容标记包裹外部数据并在工具层单独校验参数绝不让模型输入直接拼进 shell 命令或 SQL。这个基础安全设计必须在第一批代码里做好后面补特别被动。5.3 端侧 Agent 的评测回归怎么做云端 Agent 评测可以跑几百上千个用例端侧只能挑最核心的用例而且要真机跑。我建议维护一套“最小回归集”包含四类意图识别准确率、工具调用成功率、任务完成率、语义一致性回答是否偏离用户意图。每类十来个典型用例就够了关键是每个用例要有明确的通过标准。指标不只是模型指标还要记录 TTFT、tokens/s、峰值内存、耗电增量、崩溃率这些工程指标和模型指标一起进发布门槛。每次改模型版本、改量化参数、改 harness 逻辑都跑一遍全量回归。我见过一次事故团队只改了模型量化精度离线单测全过上了真机之后工具调用连续崩因为量化后的模型在特定设备上产生了 NaN 输出没人真机跑回归直接投放线上第二天用户投诉就上来了。所以真机回归不是可选项是发布流程的硬卡点。6. 给正在做端侧 Agent 工程化的你三条实战体会6.1 先定资源红线再选模型和方案我在实际项目里学到的第一课是一开始就量化预算。先定好内存上限、TTFT 上限、单任务轮数上限再回头选模型和推理方案。没有红线约束你很容易被“换个更大模型效果更好”诱惑最后上线直接被系统杀死。比如我这里定的红线就是常驻内存不超过 4GBTTFT 不超过 1.5 秒工具循环不超过 4 轮。所有模型版本、量化参数、上下文字数的调整都围绕这三条做取舍冲突时宁可牺牲一点效果。6.2 一定要留降级通道端侧环境千奇百怪总有你测不到的机型。所以每个关键链路都要设计降级大模型加载失败降级到规则引擎模型输出非法 JSON降级到重试或简短兜底回答弱网时云端增强能力不可用就先保证本地能力能跑。例如某个机型 NPU 驱动有 bug模型加载后推理结果全错我们要能自动回退到 CPU 版本并告知用户当前是降级模式。降级开关建议支持远程配置下发紧急情况下可以迅速关闭某类高风险工具。6.3 框架是工具不是答案LangChain、Dify、CrewAI 这些框架本身不差但它们是云端优先的一等公民。端侧 Agent 工程化我更建议从 harness 和分层架构入手哪怕早期只用轻量代码实现也要让各层边界清楚。比如 LangChain 的 ConversationBufferMemory 默认假设内存不是问题Dify 的可视化编排更多面向服务端部署CrewAI 的多 Agent 原语在端侧轮数限制下非常尴尬。不是说这些框架一无是处而是它们默认的边界条件和你遇到的完全不一样。你需要的是一套适合自己的轻量 harness把模型实例、工具权限、轮数控制、状态持久化这些端侧命脉捏在自己手里。等团队对端侧约束真正有手感之后再去评估要不要引入成熟框架的裁剪版本这样才不会被框架的隐式约定拖住。