
做端侧 Agent 也有几年了从最开始在手机上来回折腾 demo到现在系统化地把 Agent 工程化这层东西沉淀下来中间踩过的坑确实不少。“端侧 Agent”这个词这两年特别热但大部分人聊的还是模型怎么部署、推理速度多快真正把 Agent 当成一个系统来设计、来交付的团队其实不多。这篇是“深入理解端侧 Agent”系列的第三篇重点讲工程化也就是说怎么让 Agent 从“能跑”变成“能上线、能迭代、能扛住真实使用”。内容主要面向已经在端侧部署过模型、准备进一步搭建 Agent 能力的工程师或者正打算从云端 Agent 转向端侧方案的朋友。1. 端侧 Agent 为什么必须谈工程化1.1 从“能跑”到“能用”之间隔着一条很深的河很多团队第一次在端侧把 Agent 跑通的时候状态是相当兴奋的。模型加载了对话能通了工具也能调了看起来已经是一个完整的 Agent 了。但这种“能跑”的阶段本质上和做一个算法 demo 没太大区别你把 LLM 放在手机或者嵌入式设备上串了一个循环让它能处理几条指令demo 就算完成了。真正到了要交付给真实用户的时候问题就全冒出来了。设备内存本来就有限Agent 跑着跑着就 OOM用户的输入是千奇百怪的模型偶尔会生成一段结构错误的工具调用指令代码如果没有做容错处理整个 Agent 就直接崩溃了Agent 是需要长期记忆的但端侧没有云端的数据库和 Redis记忆要怎么持久化更别提版本更新之后老的 Agent 状态怎么迁移。这些问题没有一个是靠模型能力本身能解决的全部属于工程化的范畴。我个人的感受是端侧 Agent 工程化本质上是在一套极其有限的环境约束下把 Agent 的可靠性、性能、安全和可维护性做到及格线以上。云端 Agent 的很多做法可以借鉴但绝对不能照搬。云端你可以用 64G 内存的容器去跑 Agent端侧却要在一部几年前的中端手机、一个智能音箱、甚至一块开发板上完成同样的事情这就是工程化最核心的挑战。1.2 端侧 Agent 的工程优势和隐性成本为什么大家愿意折腾端侧 Agent很清楚低延迟、隐私保护、离线可用、带宽成本几乎为零。尤其在隐私敏感的场景下用户的声音、图片、聊天记录不出设备直接本地推理这在产品上就是巨大的卖点。延迟方面本地推理省掉了网络往返响应速度能明显快一截体感上的“智能感”会强很多。但这些优势都是有隐性成本的。端侧 Agent 的能力边界受限于设备硬件参数太大的模型跑不起来能力强的模型往往需要量化一量化多多少少会损失效果设备碎片化严重不同芯片平台的推理效率差异巨大你在骁龙上跑得很顺的 Agent换到一颗中低端芯片上可能就卡成幻灯片电池和散热的限制意味着你不能让 CPU/GPU 一直处于高负载状态否则用户手机发烫、掉电快产品口碑直接崩掉。工程化要做的就是在这些约束下找最优解。比如模型该用多大的量化等级推理引擎选哪个工具的粒度怎么拆分记忆库要不要做向量索引甚至 Agent 的推理循环里该设置什么样的超时和重试机制。每一个决策都牵扯着资源消耗、响应速度和稳定性之间的平衡这就是为什么端侧 Agent 不能只当算法项目来做它是个实打实的系统工程。2. 端侧 Agent 的系统架构与工程组件拆解2.1 搞清楚 harness 和 agent 本体的边界聊端侧 Agent 工程化必须先厘清一个概念harness 和 agent 本身的区别。这个词在热词里反复出现了好多次英文社区里也一直在吵。我的理解里Agent 本体是一个具备规划和决策能力的推理单元它的核心是模型加提示词能够根据用户目标决定下一步做什么。而 harness 是外围包围着 Agent 的工程框架负责模型加载、输入输出处理、工具注册与执行、记忆读写、循环控制、错误处理和观测日志。用一句话概括harness 提供了 Agent 生存和运转的“环境”Agent 在这个环境里做决策。工程化做得好不好绝大多数取决于 harness 写得好不好而不取决于模型本身。因为模型只能输出文本真正把它变成有实际功能的 Agent靠的是 harness 把模型输出解析成可执行的动作。比如模型输出一段 JSON 格式的工具调用harness 需要安全地解析它、校验参数、执行对应函数、拿回结果再喂给模型这一整套循环里任何一步做得不健壮Agent 就废了。我在实际项目里的习惯是先把 harness 和 agent 本体的边界画清楚写进设计文档。harness 里不允许出现任何业务逻辑它只做通用的事情运行循环、工具调度、状态管理agent 本体的业务理解、工具选择、规划能力则完全靠模型加提示词实现。这个边界一旦模糊了后面做多 Agent、做工具扩展的时候会非常痛苦。热词里还有一个说法叫“agent anywhere”其实就是想把 harness 做成一个可移植的运行时让 Agent 能够跑在任何设备上这个思路和边界划分是一脉相承的。2.2 推理引擎与模型服务层是架构的地基端侧 Agent 的架构里最底层的永远是推理引擎。常见的有 llama.cpp 系列、MLC-LLM、MNN、TFLite以及各家芯片厂商提供的专有推理框架比如高通的 SNPE、联发科的 NeuroPilot另外还有一些支持端侧 GPU/NPU 加速的方案。工程选型上有几个考量维度支持的模型格式、量化方案、算子覆盖度、CPU/GPU/NPU 的调度能力以及对多平台的兼容性。llama.cpp 是目前端侧使用最广的因为它是纯 C/C 实现内存占用相对可控对主流开源模型如 Llama 系、Qwen 系支持得比较快而且社区活跃新模型出来通常很快就有适配。MLC-LLM 的卖点是 TVM 编译栈带来的算子优化在一些 GPU 设备上有明显性能优势但工程复杂度也更高。选推理引擎的时候一定要用自己目标设备上的实际数据做 benchmark不要光看 GitHub 的 star 数。我见过不少项目在 PC 上跑得飞起上了 ARM 平台之后算子不支持或者性能暴跌最后只能临时换引擎返工成本极高。模型服务层往上是 Agent 运行时这一层在工程上主要管三件事。第一模型的加载生命周期管理包括预加载、按需加载、模型卸载端侧内存太宝贵不能让模型一直驻留第二请求队列和并发控制Agent 可能会连续多次调用模型推理如果推理引擎不支持并发就得自己做排队和合并第三采样参数、超时和错误码的统一封装让上层的 Agent 逻辑不用关心底层是 CPU 推理还是 NPU 推理。这三件事做扎实了上层的架构才有稳定的地基可以盖楼。2.3 记忆模块在端侧的特殊设计Agent 记忆是近期讨论很多的话题热词里“agent记忆”也刷了不少屏。记忆大致分为短期记忆、长期记忆和工作记忆。短期记忆对应的是上下文窗口内的对话历史长期记忆是跨会话保存的事实和偏好工作记忆则是当前任务执行过程中的临时状态比如已经完成了几步、当前在等哪个工具的结果。在云端 Agent 做法里长期记忆通常丢给向量数据库用 Embedding 做相似度检索。但端侧不太一样设备的存储和算力都有限跑一个本地 Embedding 模型本身就有开销。我自己的实践是做了分级记忆能结构化存的信息就用 SQLite比如用户偏好、任务完成记录这种结构化数据查询精准、代价低真正需要语义检索的内容才做向量索引而且会限制记忆库的规模定期做压缩和清理。为了控制工程复杂度初期先把短期记忆做好长期记忆用最简单的文件存储加关键词匹配跑通了之后再逐步引入向量检索。还有一个容易被忽略的问题端侧 Agent 的记忆文件是明文存在设备上的隐私安全是个大事。用户的对话记录、偏好数据如果不加密或者不做访问控制一旦被恶意 App 读取后果很严重。工程上至少要做轻量级的加密存储并且通过系统的安全模块比如 Android Keystore 或 iOS Keychain来管理密钥。这块内容在后面的安全设计部分还会详细展开。2.4 工具层设计比你想的更影响体验Agent 的能力边界在很大程度上取决于它能够使用哪些工具。工具层的设计在端侧工程化里面占据非常重要的位置。工具分两类一类是本地工具比如读写文件、访问系统剪贴板、操纵系统设置另一类是远程工具比如调用天气 API、搜索互联网、访问用户自己的服务端。本地工具要重点考虑权限隔离不能让 Agent 的模型输出直接驱动任意系统调用。我的习惯是做一层中间校验工具的执行参数必须经过白名单校验和类型检查比如模型说“删除文件”如果没在工具清单里注册过就直接拒绝同时还要记录日志。远程工具则要关注网络权限管理和 API Key 的安全存储端侧不像云端可以把密钥放在环境变量里端侧打包的时候如果硬编码了密钥反编译一下就全泄露了。工具层的工程化还涉及一个细节工具的描述信息。模型是靠工具描述来决定什么时候调用哪个工具的描述写得模糊模型就会频繁选错工具。所以工具描述要尽量具体包括参数含义、返回结果的结构、使用场景甚至可以给出一个 or 两个示例。这些描述词看似简单实际对 Agent 成功率的影响非常大工具从 5 个扩展到 20 个的时候如果描述质量跟不上工具选择准确率会明显下降。3. 端侧 Agent 框架选型与技术栈取舍3.1 主流框架横向对比LangChain、Dify、CrewAI 以及更多热词里都在问 LangChain、Dify、CrewAI 哪个好。我的观点是先把使用场景定义清楚再选型不要上来就争论框架优劣。LangChain 是目前生态最完整的 Agent 开发框架链路编排、工具集成、协议抽象这些能力都很全问题在于它过于重依赖多、抽象层级多在云端跑没问题搬到端侧往往要裁剪大量用不到的模块。Dify 更偏可视化和产品化适合快速搭出面向运营或内部使用的 Agent 应用但底层跑起来本质上还是云端那套架构端侧资源受限的设备基本跑不动。CrewAI 主打多 Agent 协作做任务拆解和角色扮演很方便但同样是为云端设计的如果你只是做一个单 Agent 的端侧助手它带来的复杂度远大于收益。端侧 Agent 目前更务实的方案是直接用轻量级的 harness 自研或者是在 LangChain 基础上做一次深度裁剪。我自己经历过一条路早期图省事直接引 LangChain后面发现光是为了让它跑在一台 4G 内存的开发板上就花了两周做依赖精简和运行时瘦身最后干脆自己写了一个两百行的循环控制反而更好维护。不是框架不行而是框架做的通用抽象和端侧追求的最小化资源配置本身就是矛盾的。3.2 自研 harness 的收益和边界自研 harness 不是意气用事是端侧 Agent 工程化的一个重要决策方向。harness 的核心职责前面已经讲了控制循环、工具调度、状态管理、错误处理、观测日志。这些职责其实并不复杂尤其对于单 Agent 场景核心循环就是一个 while 循环加工具分发。用几百行核心代码就能实现一个可控的 runtime带来的收益是极致的运行时效率和完全可控的依赖。但自研也有边界不能什么都自己造。如果涉及多 Agent 协作、复杂的状态机编排、可视化调试面板这些还是适合用成熟框架或者自建在 harness 之上。我目前的工程决策是核心 harness 自研保持精简上层编排逻辑和测试工具使用成熟方案不同的 Agent 业务通过配置化的方式挂载进来。这样既避免了大框架带来的臃肿又保证了业务开发的效率。热词里“agent框架与编排”是分开的这正好印证了我的思路——框架是底子编排是上层的戏两者不应该强绑定。3.3 为什么端侧 Agent 越来越多人用 Rust热词里“基于rust语言ai agent”刷出来了这确实是个真实趋势。Rust 这几年在端侧 Agent 工程里被讨论得越来越多最大的原因是它能提供与 C/C 接近的性能同时在内存安全上更有保障而这恰恰是端侧程序的两个核心诉求。Agent 是一个长期运行的进程内存管理稍微出点问题就会崩溃Rust 的所有权系统可以在编译阶段拦掉大量内存错误。另外Rust 的交叉编译体验比较好一套代码通过目标平台的 toolchain 就能编译出 Android、iOS、Linux 嵌入式等不同平台的产物这对端侧多平台适配来说是很大的红利。很多端侧推理引擎本身也是 C/C 写的Rust 通过 FFI 调用它们也很自然。如果你团队里有 Rust 能力用它来写 harness 是挺值得考虑的选择没有的话也不用勉强Go 或者 C 也能做好但至少要意识到这个方向的价值。3.4 一次端侧 Agent 项目的选型复盘我之前参与过一个端侧语音助手的项目目标设备是一台智能音箱硬件配置大概是 2G 内存、中端 ARM SoC。选型全过程的复盘大概是这样推理引擎直接选了 llama.cpp 的量化版本模型用的 Qwen 系小模型harness 用 Rust 自研核心代码不多但稳定性和性能都很满意记忆存储先用 SQLite 加简单的键值对没有上向量库工具层做了轻量化的注册表机制支持本地技能和远程 API 两种类型上层业务逻辑用 JSON 配置文件驱动方便产品和运营调整 Agent 的提示词和技能清单。这个选型组合不是最潮的但在当时的硬件约束下它是综合成本最低、风险最小的方案。回过头来看选型最重要的是别被流行框架绑架老老实实列自己的资源约束和功能需求再对照各家方案的优劣做取舍。热词里大家反复问“哪个好”其实没有标准答案只有“哪个在你的设备上、你的场景里更合适”。4. 工程化实操端侧 Agent 的关键环节4.1 上下文窗口与记忆管理的工程落地在大模型应用里上下文窗口是有限资源Agent 更是如此。Agent 要连续多轮交互最后上下文里可能塞满了历史对话、工具返回结果、中间推理过程很快就会触及窗口上限。如果只是粗暴截断最前面的系统指令可能被裁掉Agent 会突然“失忆”行为变得不可控。工程上我采用“滑动窗口 摘要压缩”的策略这套组合在端侧尤其有效。每次模型请求前先检查历史消息的总长度超过阈值就保留最近几轮完整消息作为短期记忆更早的内容用一段凝练的摘要替换附在最前面。摘要本身可以用大模型生成也可以用小模型完成其实用端侧已加载的模型做摘要就行省得额外加载一个模型。另外建议做记忆分区系统指令区、短期对话区、长期记忆区、工具结果区。每个区独立管理长度上限避免某一类内容异常膨胀挤掉其他部分。比如工具返回了一个特别长的 JSON就应该单独做截断或摘要不能让它占掉对话历史的配额。这个分区方案的实现不复杂但在实际运行里能显著提高 Agent 的稳定性和多轮任务完成率。4.2 “AI Agent 怎么扛并发”在端侧的另一种答案热词里有人问“AI agent 怎么扛并发”这个提问方式在云端语境下指的是应对大量用户同时请求。但在端侧单个设备上只有极少数用户在跑 Agent真正的挑战根本不是高并发流量而是进程内部的资源竞争和任务优先级调度。比如用户在语音助手执行任务的同时又发来一条文本指令怎么办后台在跑一个耗时的文件分析用户突然问天气怎么办这些都是端侧 Agent 每天会遇到的真实并发场景。我的做法是在 harness 内部做一个基于任务队列的调度器把 Agent 的请求切成三类高优先级交互类用户直接提问、中优先级后台类异步任务处理、低优先级预取类比如提前加载用户可能用到的信息。调度器按优先级和时间片去处理保证用户交互的响应速度最低。同时所有模型推理必须统一走请求队列超时和取消要处理干净尤其是用户已经取消了请求后推理如果还继续在跑浪费的是设备宝贵的算力和电量。另外还有一个容易被忽视的并发问题模型的加载和卸载。如果 Agent 在做完一个任务之后立刻卸载模型下一次任务就要重新加载等待时间可能高达数十秒。工程上是需要做模型常驻与空闲超时卸载的平衡的常驻时间设得太短用户会频繁等待设得太长内存和电耗又会上去。这个参数我一般会根据具体设备和用户行为数据去调优而不是拍脑袋定一个固定值。4.3 端侧 Agent 的安全边界设计Agent 安全在中文技术圈聊得不算多但它是工程师一定要尽早考虑的事情。端侧 Agent 的模型能够输出任意指令文本这些文本经过 harness 解析后成为工具调用的参数如果这中间没有安全校验那模型一旦被恶意诱导就可能让 Agent 执行危险操作比如删除用户文件、向任意网址发起请求、读取敏感数据。安全设计的核心原则是最小权限加层层校验。工具注册时要声明每个工具需要的最小权限比如读取文件列表的工具不需要写权限调用的参数必须做类型检查和范围校验。模型输出的 JSON 解析失败或者参数字段缺失harness 必须能优雅地返回错误而不是直接崩溃或者跳过校验。更稳妥的做法是把工具调用设计成“两次确认”模式对于高风险操作先返回一个待确认状态由用户在 UI 层决定是否继续执行。另外一个安全风险点是提示词注入。当 Agent 在处理外部输入比如网页内容或者一段文本时外部内容可能包含恶意指令试图覆盖系统设定。工程上要对这种输入来源做标记在喂给模型时用明确的指令边界包裹同时保留整个工具调用日志用于事后审查。用户隐私数据的处理要严格遵循最小收集原则能不碰就不碰必须碰的时候要有清晰的告知和授权流程。4.4 可观测性是端侧 Agent 迭代的底牌Agent 系统和传统软件最不一样的地方在于它的行为有一部分不可预测。同一个输入今天模型可能选择了路径 A明天就可能选择路径 B这给调试和迭代带来了巨大的挑战。没有可观测性你在端侧设备上看到一个 Agent 的异常行为基本无从下手。我建议在端侧 Agent 的工程体系里至少要埋以下日志完整的工具调用链包括输入参数、返回结果、耗时、模型推理的 token 数和耗时、上下文窗口的占用情况、每一次路由决策的原因、所有安全校验的拦截记录。这些日志输出体积不小平时可以只在内存里保留最近一段时间的环形缓冲区遇到问题时一键导出。云端可以再做汇总和分析但端侧至少要能自己兜底。通过分析这些日志你能快速定位大多数问题的源头。比如模型调用了错误的工具看路由日志就能知道是模型选了错工具还是工具描述写得不清楚响应变慢了看模型推理耗时和工具耗时就能定位是模型问题还是工具问题。热词里经常讨论 Agent 的调试困难其实只要把日志体系建立好大部分困难都会迎刃而解。5. 常见问题与排查技巧实录5.1 端侧 Agent 经典故障速查表做端侧 Agent 这么久绝大部分时间其实都花在了查问题上面。我把最常遇到的几类问题整理成了速查表团队里新同学排查时直接对照能省下不少弯路。问题现象可能原因排查思路Agent 运行中闪退内存不足触发 OOM查看崩溃日志中的内存峰值调整模型量化等级或关闭多余模块多轮对话后行为异常上下文窗口溢出导致系统指令被截断检查日志里的 token 占用启用摘要压缩策略工具调用失败但无报错模型输出的 JSON 参数格式不符在 harness 层打印原始模型输出检查解析器本身响应越来越慢长期记忆库膨胀导致检索变慢检查记忆库条目数和检索耗时定期做压缩清理工具选择错乱工具描述不清晰或语义重叠优化工具描述给每个工具加适用场景和示例设备发烫掉电快模型常驻推理负载过高调整空闲卸载时间确认是否有多余后台任务在跑模型输出结构频繁出错模型能力不足或提示词约束不够改用更大参数模型或在提示词中强化 JSON 格式约束这里面最迷惑人的其实是“工具调用失败但无报错”这一类。后来定位到是模型生成的 JSON 里混入了解释性的文字解析器直接取不到 key而 harness 层没有做严格校验导致静默失败。解决方案是在解析层里做容错清洗同时把解析错误明确返回给模型当作反馈让模型学到正确的输出格式。5.2 一次“上下文丢失”问题的定位过程再分享一个真实案例。之前同事负责的一个端侧 Agent在多轮对话之后突然变得答非所问像是完全忘记了之前聊过的内容。我们最初怀疑是记忆模块没写入把所有读写记忆的日志翻了一遍没有任何问题数据明明写进去了。后来检查模型的输入日志发现上下文管理器在压缩历史的时候把所有早期消息都替换成了摘要但是摘要生成异常只保留了“用户想查询天气”这样一句话把大量关键信息丢掉了。也就是说不是记忆模块的问题而是摘要压缩模块的问题。我们当时用的摘要模型是边角量化的小模型摘要效果本来就一般碰上复杂的多轮对话时质量就会严重下降。最后我们把摘要策略改成了混合方案能结构化的信息走数据库存储实在需要摘要的才走模型并且摘要模型换成了更大的量化档位。这次排查给我们的教训是端侧 Agent 的问题往往不是单一模块的问题模块之间的配合才会产生隐蔽故障。所以排查问题的时候不要只盯着一个模块看要从整体链路捋一遍日志记录得越完整定位就越快。这也是为什么我在 4.4 节强调可观测性的原因——没有日志这次定位至少要翻倍的时间。5.3 工程复杂度控不住时怎么办做久了你会慢慢发现端侧 Agent 工程化最大的坑不是技术搞不定而是复杂度失控。功能越加越多工具越来越多状态越来越多最后整个系统可能变成一座维护成本很高的迷宫。这时候很多团队会陷入一个误区以为问题出在代码结构上然后开始大规模重构。但实际上复杂度失控的根源往往是边界模糊和职责不清。我有几个控制复杂度的实操习惯。第一每一个新功能接入之前先回答三个问题它属于 harness 还是 agent 本体还是上层业务它改变的是状态还是行为它需要新增工具还是复用旧工具三个问题答不清楚就不允许动代码。第二工具数量超过二十个的时候主动做工具分类和命名规范比如用前缀区分工具的归属模块同时重新审视每个工具的存在价值合并或删除低频工具。第三对 Agent 的配置项做版本化不要允许无声无息地修改行为参数每个配置变更都要能追溯到原因。另外在代码层面我建议把 Agent 核心循环和外围逻辑严格分层。核心循环要短小精悍改动频率越低越好。哪怕是加一个新功能也优先通过新增工具、新增提示词模板、新增配置项来实现而不是去动核心循环的代码。核心循环一旦需要频繁改动说明你对 Agent 的抽象理解还不够清楚这时候应该停下来重新梳理架构而不是继续堆代码。5.4 关于稳定性的“软硬兼施”策略端侧设备的稳定性问题光靠工程手段硬扛是不够的还要在策略层面做一层“软”的方案。所谓硬方案就是 harness 层的崩溃恢复、看门狗机制、内存保护这些是底线必须有。所谓软方案是指 Agent 本身要学会处理“做不了”的情况。举个例子。素材库里的工具偶尔会超时硬方案的思路是设置超时时间超过就报错重试。但如果你调的工具是一个外部 API对方可能真的就是偶尔变慢这时候为了避免用户干等更好的做法是让 Agent 先给用户一个阶段性的反馈比如“信息还在获取中稍等一下我再回复你”然后继续在后台等待。这种体验上的细节往往决定了用户对 Agent 的耐心。再比如当设备内存非常紧张的时候Agent 可以选择一个更轻量但效果稍差的模式来继续服务而不是直接卡死。这种降级策略需要提前在工程上预留把不同模型的加载和切换做成配置化的能力而不是临时改代码。稳定性的本质不是不犯错而是犯错之后系统还能恢复、体验还能兜住、数据还能保住这才是工程化的心态。我个人在做端侧 Agent 这几年的最大体会是工程化不是在模型能力上用力而是在模型能力之外的所有地方用力。推理引擎选型、harness 架构设计、记忆管理、安全边界、可观测性这些不起眼的“脏活累活”才是决定端侧 Agent 能不能落地的关键。你模型再强没有一个稳定的 harness 包着它在真实设备上就是没法用反过来模型一般但工程做得扎实整体体验依然能非常稳定。这篇先聊到工程化的上半部分后面我打算把评测、迭代和上线后的运维经验再整理一篇出来到时候继续聊。