端侧Agent工程化:从Demo到可交付的架构实践
发布时间:2026/10/3 15:55:23 锦皓数字建站

做了将近一整年的端侧 Agent 产品之后我越来越确信一件事跑通一个 Demo Agent 和交付一个能稳定运行的端侧 Agent中间隔着的不是算法能力而是工程化能力。这个系列前两篇聊了端侧 Agent 的基础概念和模型选型按理说接下来该进入实操搭建了。但我在实际项目里发现大部分人在“Agent 能回答问题了”这个阶段就卡住了——不是模型不行而是推理、内存、并发、工具调用这些工程问题全都堆在一起爆发。这篇我重点讲工程化的“上篇”从 demo 到可交付端侧 Agent 在架构层面必须跨过哪些坎。1. 跑通 Demo 之后端侧 Agent 工程化才真正开始我见过太多团队包括我自己早期死在同一个环节Agent 在开发机上语义理解、工具调用、多轮对话都表现完美一旦搬到端侧硬件上立刻原形毕露。这个问题很普遍想绕绕不过去。1.1 我踩过的第一个坑原型阶段的“假工程化”第一次做端侧 Agent 原型时我把模型装进手机写了个 Streamlit 界面用 LangChain 把几个函数包成 tools模型能回答问题、能查天气、能开关灯。我当时觉得这事儿快成了。但真正要把它变成一个后台常驻服务时第一轮压测就崩了。三个问题同时炸出来内存峰值到 1.6GB系统直接杀进程模型推理把 CPU 占满前后台切换卡成 PPT同时挂起两个任务第二个任务的上下文把前一个冲掉了。这三个问题没一个是模型能力导致的全都属于工程范畴。我把它们记下来才发现“端侧部署模型”和“端侧 Agent 产品化”压根是两码事——前者只要一个推理引擎后者需要一整套精心设计的调度系统、内存管理机制和上下文体系统。1.2 端侧 Agent 工程化 vs 云端 Agent 工程化的本质差异做云端 Agent 的同事常跟我说“并发、缓存、限流这套我们踩过坑了你照着抄就行。”但真端侧落地时发现完全抄不了。云端和端侧在“资源”这个词下的定义完全不同。维度云端 Agent端侧 Agent算力虚拟化后按需扩容固定算力不可升级内存动态分配、几十 GB 起步固定物理内存通常 4-8GB 共享功耗基本不考虑决定设备温度、续航、寿命网络高带宽低延迟弱网、离线是常态上下文上下文窗口几乎无上限必须精打细算这个差异直接决定了端侧 Agent 架构设计的第一原则一切都可以被牺牲唯独“可控”不能被牺牲。云端方案可以靠资源堆叠解决大部分问题端侧只能在资源边界内做文章。这个思维转变不完成后面的每一步都会走弯路。2. 端侧硬约束如何倒逼 Agent 架构决策端侧工程化绕不开四个硬约束内存、算力、功耗、网络。多数人把它们当作“性能优化”问题但实际上它们是架构层面的约束决定了 Agent 内部的每一条数据通路。2.1 内存预算模型权重、KV Cache 与上下文争抢同一块 RAM端侧 Agent 有个隐形内存杀手比模型本身更凶——KV Cache。很多人知道模型权重占内存但忽略了自回归生成时每生成一个 token都要存一份历史 token 的 key-value 结果。这个缓存随着上下文长度线性增长一旦 agent 工具调用链拉长几轮下来 KV Cache 可能吃掉几百 MB。我在项目里做过一次统计一个 7B 量化模型在端侧跑 2048 token 上下文权重占 3.5GBint8KV Cache 约 500MBAgent 自身状态和工具运行时占 300MB。设备总内存 8GB 的话系统只剩不到一半可用后台稍微开几个应用OOM 几乎是必然。所以现在我的做法是把内存当作独立预算来管理。给模型推理、Agent 状态、KV Cache、工具运行时分别划定上限超限的部分宁可降低上下文长度、主动触发缓存清理也不能让内存失控。这事在系统设计阶段就要定好否则后面调优时处处掣肘。2.2 算力与功耗NPU 调度、发热与推理时延的三角平衡端侧 Agent 的算力分配本质是“有限算力怎么切给多个消费者”的问题。模型推理要算力工具调用比如图上有图、服务里有 OCR也要算力NPU/DSP 核心只有一个谁来用是调度器说了算。最容易犯的一个错是把 Agent 推理和设备主线程绑在同一个线程。用户在主界面滑动时Agent 推理一来主线程直接被抢占画面掉帧用户感知到的就是“手机卡了”。我的处理方式是至少两个进程主进程管 UIAgent 进程管推理。两个进程之间用轻量级 IPC 通信这样即使 Agent 满载UI 也是流畅的。但这又会引出一个新问题Agent 进程持续推理时芯片温度会迅速爬升超过一定阈值触发降频。降频之后推理变慢、环路时间变长又进一步恶化体验。我解决方案是推理功率控制检测到温度快要过线时主动把推理 batch size 降下来或切换成低功耗推理模式宁可慢一点不要卡死或过热。2.3 网络与离线弱网环境下 Agent 的自主降级策略端侧 Agent 不能假设自己永远在线。我遇到过导航类 Agent 在车库没信号、翻译 Agent 在飞机上离线跑的场景。网络中断不应该是 Exception而应该是正常路径。我的降级策略分四档在线全量网络正常时可调用需要云服务的工具在线受限弱网时关闭大流量工具比如图像高清上传保留轻量工具本地兜底断网时切换到本地的知识库检索和离线模型推理任务挂起任务依赖外部服务时保存现场状态等到网络恢复再继续执行。这里最容易被忽略的点也是核心点工具的注册表里必须标注“网络依赖级别”这样调度器才能真正智能决策。我见过不少人直到断网才后知后觉地发现自己的 Agent 连切离线模式的接口都没预留。3. 工具注册表Agent 工程化中比模型更重要的基础层聊到工具调用必须先澄清一个概念混淆。我看到网上关于“harness 和 Agent 框架有什么区别”的问题特别多。这块不搞清楚选型时就容易选错。3.1 harness 与 Agent 框架两个常被混淆的概念框架层管的是“Agent 怎么组织”它怎么定义 system prompt、怎么管理 multi-turn 上下文、怎么调节 model 的参数这属于 Agent 自身的宏观结构。harness 层管的是“Agent 怎么与环境互动”它负责把 Agent 的内部状态连接到外部世界——怎么给 Agent 暴露函数、怎么解析返回结果、怎么处理调用异常。用个不恰当但形象的比喻框架是 Agent 的性格harness 是 Agent 的手脚。这个区别在工程化中有非常直接的影响。我在一个项目里发生过这样的事团队在框架层很认真地设计了多轮对话逻辑但给 Agent 暴露工具时用了一个没封装的 Python 函数参数校验、超时控制、结果规范化全都没有。结果 Agent 一旦工具调用频繁出错就会陷入“重新尝试-再出错-再尝试”的死循环整个 loop 转不出去。我后来强制团队形成一条铁律所有暴露给 Agent 的函数必须先经过 harness 层包装。包装干什么呢统一做输入校验、统一设置超时model 在等你回复可不能让它无限等、统一把结构化错误转成模型能读懂的文本描述。这层不做好工程能力再强也白搭。3.2 主流的端侧 Agent 框架选型对比现在社区里比较活跃的几个方案各有侧重点。我最近的实测感受是这样如果你只看 demo它们几乎没有差异一旦做端侧工程化差异立刻显现。框架优势端侧落地时的坑AutoGen微软多 Agent 会话调度能力强默认面向云端资源设计端侧需大量裁剪LangChain / LangGraph生态大、工具函数多依赖注入和回调链太重启动开销大LlamaIndex知识/RAG 管理成熟Agent 本身的 loop 控制偏弱自研迷你框架可控性强、内存可预测开发成本高需要极强的架构能力我个人对端侧项目的倾向是不迷信大框架优先考虑能否按需裁剪。自研一个只有模型推理 工具调度 上下文管理三层结构的迷你框架往往比硬扛一个通用框架更顺手。这不是技术洁癖而是端侧对资源敏感框架的每一个“便利功能”最终都要翻译成 ROM 和 RAM 的消耗。3.3 三大核心模块的拆分与分工根据我的项目实践端侧 Agent 内部的工程架构可以抽象成三个模块Reasoning core推理核心。负责模型加载、推理调度、流式输出。它只做一件事拿到 Request 返回 Output不掺和任何业务逻辑。这个模块的工程核心点是模型上下文窗口管理——多长上下文该进还是该出都由这层决定。Tool orchestration工具编排。负责把 Agent 的意图变成具体的工具调用。它需要管工具注册、参数提取、工具返回结果的结构化处理。这一层才是“智能”真正发挥作用的战场——给模型的工具描述写得清不清楚、返回结果表述得明不明确直接决定 Agent 的执行质量也就是智能程度的上限。Execution guard执行守护。负责预检和兜底。预检是调用前评估工具是否可执行避免 Agent 在无谓的方向上浪费时间兜底是调用失败后的降级策略——比如超时、重试、切换本地替代方案。这个模块混在 harness 里容易被忽略但它决定了 Agent 服务的稳定性上限。这三个模块的分工如果做清楚了复杂的 Agent 工程问题就能化简成纯模块问题推理慢了优化推理核心工具老出错看工具编排服务不稳定就查执行守护。问题定位速度会快一个量级。4. 端侧 Agent 的并发模型四核处理器上的多 Agent 调度“并发”这个词放到端侧 Agent 语境里很有意思。做后端的朋友理解的并发是“上百路请求进来机器要顶住”。但端侧设备上不会有上百路用户请求同时进来端侧真正的并发问题恰恰出在另一个地方。4.1 “扛并发”在端侧的真实含义端侧 Agent 的并发不是“同时在线上百用户”而是三种形态的混合多 Agent 实例并发日历提醒 Agent 和语音交互 Agent 可能同时活跃任务级并发一个 Agent 内部可能同时有多个任务在跑比如下载文件的同时在分析另一个数据源异步事件并发用户语音输入、定时触发器、外部通知这些事件随时可能打断当前任务。我做过一次压力测试在 8 核处理器上同时跑 3 个 Agent 实例每个约 20M 内存、每个 Agent 内部挂 2 个异步任务、同时处理 4 类外部事件。结果系统在 3 秒内整体延迟飙升了 4 倍。不是任何单个 Agent 的计算量爆炸而是线程切换、锁竞争、事件队列堆积全撞到一起了。4.2 单设备多 Agent 进程的内存隔离与通信多 Agent 不能挤在一个进程里共享全部内存。我的方案是进程级隔离每个 Agent 跑在独立进程持有自己的内存池和状态存储进程间通过一个轻量的消息总线通信。这个做法有两个好处一是防雪崩。某个 Agent 进程出问题会崩溃但不会把整个系统拖下水。隔离边界决定了故障半径进程崩溃就只伤及自己。二是防监听。额外的好处是某个进程的内存被意外撑爆时不会影响其他 Agent 的生存空间。每个进程都有独立的地址空间不能让 agent 自己从共享池里无限制地吃资源。这套模型实现起来也不复杂。消息总线我用的是 Socket借助操作系统的进程通信机制。每个 Agent 上报自己的状态busy/idle/error调度器维护一张状态表。通信协议用 JSON简单可靠不强上复杂的 IPC 方案端侧场景越简单越稳。4.3 任务循环的优先级注入与抢占策略现在我在端侧 Agent 里用的是“事件驱动 优先级抢占”这套组合。任务循环里有一个全局事件队列事件的类型决定优先级优先级0最高 用户显式打断 优先级1 定时任务、系统通知 优先级2 后台任务、数据处理 优先级3 模型推理非用户直接请求模型推理其实不应该永远排最优先。用户正在手动操作 UI 的时候后台推理应该主动降级。我采用的做法是推理调用前先检查 UI 活跃状态如果用户在拖动手势本轮推理自动延后或取消等用户停止交互后再恢复。这个策略虽小但对真实体验的提升巨大。“抢占”也要讲究策略。Agent 正在执行一个长任务时用户突然换了一个新指令旧任务不能立刻被杀死否则清理不彻底会留下脏状态。我选择了“协作式抢占”旧任务收到取消信号后在下一个检查点自己决定是否中止、保存现场、释放资源。这样避免了强杀导致的资源泄漏。5. 上下文与记忆工程化最容易翻车的隐藏深水区OpenAI 那个智能体协议的论文里特别提到Agent 的状态需要显式地管理包括当前任务目标、对话历史、工作记忆和外部记忆。端侧 Agent 工程化时这一块如果不做规划翻车概率极高。5.1 上下文窗口不是“可用内存”必须精确分配我发现很多端侧 Agent 的开发者在设计上下文时把模型的上下文窗口当成了一座无限制的仓库所有对话历史、工具响应、中间推理过程一股脑往里塞。结果上下文很快被塞满系统进入“挤出最旧内容”的被动模式——最关键的早期目标指令可能被踢出上下文Agent 就变成了没头苍蝇。现在我把上下文划分为三个独立区域每个区域使用互不抢占的独立预算区域预算占比存放内容目标区10%用户核心意图、当前 Task 目标对话区60%多轮对话历史、工具往返记录执行区30%中间推理过程、待处理的工具输出目标区的内容是最重要且最不能丢的。对话区可以剪裁压缩执行区可以随时清空重启但目标区一旦被挤出整个 Agent 就丧失了方向感。这个比例可以根据实际任务做调整但目标区永远必须是独立预算不能和其他区域混着用。5.2 Working Memory 与长期记忆的分离设计Working memory 和长期记忆是两套完全不同的存储系统技术上必须分开。Working memory 放的是当前任务的瞬时状态这个 Task 执行到哪一步了、已经调用过哪些工具、下一步计划是什么。它的特点是生命周期短、读写频繁、对时延要求高。我的实现是纯内存结构甚至用了一个简单的状态机来管理每次工具调用结束后更新一次状态。这样即使 Agent 中途休眠恢复后也能知道自己“进行到哪了”。长期记忆放的是用户偏好、历史知识、跨任务的沉淀内容。它需要持久化存储但读写频率低很多。端侧上用 SQLite 就足够了连向量数据库有时都未必需要。真正要处理的是“什么时候把工作记忆里的内容固化成长时记忆”——这个边界我还在摸索目前的做法是任务结束时做一次“内容归组整理”把重要片段写入长期记忆。5.3 端侧向量检索能用但不是银弹很多 Agent 想实现“懂你的偏好”就引入向量检索。在云端这是日常操作在端侧却要另说新建。本地跑 embedding 模型要占内存和算力建立索引也要时间和存储空间这些都要算进整个系统的资源预算。我的做法是“阈值触发”式检索平时不主动建索引只有当用户明确表达比如说了“记住我说的话”或者任务结束时才触发一次记忆写入和检索更新。这样既能在需要时提供个性化上下文又不会让 Agent 每时每刻都在跑 embedding 造成资源浪费。对于真的需要在端侧跑向量检索的场景我踩出来的一个经验是索引要按时间分片而不是一锅乱炖。同一个用户的记忆早期的应该被逐层压缩或丢弃近期的才保持高精度。否则索引膨胀后检索时延会明显上升最终拖垮整个 task 循环。5.4 端侧 Agent 的上下文压缩策略上下文压缩是我最后想讲的也可能是最有价值的。当对话轮次变多总能把部分对话历史揉成一段摘要把更早的原话挤出去。但这个摘要的质量直接决定 Agent 后续行为的准确度。我采用的压缩策略是分层压缩第一层把工具调用返回的大段结构化数据压缩成“关键值提取”只保留模型后续真正需要的数据第二层才轮到对话历史压缩时优先保留“用户的明确指令”和“对 Agent 行为的纠正”这两类信息丢一条都会带来体验的实质损伤。压缩时机也很讲究。我的触发条件是“当前上下文占用超过 70% 时预压缩”而不是等快满了再暴力清理。预压缩有一个额外的好处Agent 可以在低压力状态下将历史信息重组为稳定的摘要比紧急腾挪时硬处理质量高很多。这个技能skill系统、评测、安全话题都属于端侧 Agent 工程化的“下半场”我打算在系列下一篇继续展开。但就“上篇”而言上面这些系统底子没打牢后面的 skill 扩展和评测都无从谈起。我自己经历的一个朴素结论是端侧 Agent 工程化做得好不好先不看它多会回答问题先看它长时间连续运行、各种事件打断、资源极度紧张时还能不能有稳定可预期的输出。磨好底座应用才有说服力。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。