端侧Agent工程化实战:从框架选型到记忆与工具链 很多人聊端侧 Agent聊的是模型、是推理框架、是“把 7B 模型塞进手机”。但真把 Agent 落地的团队都清楚模型只是第一公里后面还有一整套工程问题记忆放哪、上下文怎么省、工具怎么调度、死循环怎么兜底、多轮对话怎么不出错。我自己踩过不少坑也看过不少团队在“模型跑通了Agent 却跑不起来”的阶段卡住——问题基本都出在工程化而不是模型。这篇文章是“深入理解端侧 Agent”系列的第三篇聚焦 Agent 工程化的上半场。我会围绕框架选型、记忆设计、上下文预算、编排与工具链这几个方向展开再梳理几个我实测中遇到的高频问题。适合正在做端侧 Agent、或打算把 Agent 从 Demo 推向产品的工程同学参考。1. 端侧 Agent 工程化到底在工程化什么1.1 先给“端侧 Agent”画个边界先说清楚端侧 Agent 是什么。模型只是大脑Agent 是一个完整的系统感知输入、规划任务、调用工具、操作记忆、给出反馈然后根据反馈继续迭代。在端侧跑意味着这些全部要在用户的手机、PC、车载设备或智能硬件上完成至少大部分计算在本地完成。所以“端侧 Agent 工程化”不是“把模型文件压缩一下然后部署到手机上”——那是 MLOps / 端侧推理的范畴。工程化要解决的是Agent 作为一个常驻软件系统如何在有限算力、有限内存、不稳定的网络环境和碎片化设备上稳定、安全、可预期地完成多轮任务。我见过最好的 Agent 改造方向也见过最差的。最差的是把 Agent loop、工具调用、记忆全部揉在一个 Service 里所有状态都放内存最后用户多问几句就开始胡说八道。最好的则是把系统边界画得清清楚楚每一步都知道状态在哪、花了多少 token、下一次该做什么。1.2 端云差异带来的四大约束端侧 Agent 和云端 Agent 相比有几个工程上绕不开的差异直接决定了方案取舍。维度云端 Agent端侧 Agent上下文窗口通常可以给 8K、32K、128K4K~8K 是常态撑死 16K且 KV cache 占内存巨大算力GPU 集群可以暴力解码CPU/NPU/GPU 混合内存带宽是硬瓶颈网络常驻延迟低可能离线网络不可靠升级服务端热更新秒级生效要发版、要等用户同意、要处理旧版本残留隐私数据上云合规成本高本地数据但也要防止端侧能力滥用功耗与发热基本不考虑长时间推理会导致设备发热用户体验直接崩这六条里头最容易被低估的是上下文窗口和功耗。上下文窗口决定了你必须在端侧做“上下文压缩”而不是把整段历史丢给模型。功耗则决定了你不能让模型持续嗡嗡运行必须在“响应延迟”和“token 预算”之间做用户可感知的取舍。1.3 工程化的三个优先级先跑通、再稳定、再聪明很多团队一上来就上多 Agent、上 RAG、上复杂规划器结果 Demo 都跑不通。我的经验是端侧 Agent 工程化要严格按三个优先级推进先跑通最小闭环感知用户意图 - 调用少数工具 - 返回可验证的结果。哪怕只有两个工具、不做记忆压缩也要先把主链路打通。再稳定解决工具调用失败、token 超限、死循环、重复执行等稳定性问题。这个阶段的价值远超任何“聪明”特性。最后再变聪明加入记忆、检索、多 Agent 协作、主动学习等能力。为什么这个顺序重要因为端侧的调试成本远高于云端。云端出错你可以在服务器上打日志、看 trace几分钟定位端侧出错用户只会感受到“卡了一下”或“没反应”而你要从多个设备上报的日志和数据中还原现场成本高一个数量级。基础不稳定任何上层智能都是空中楼阁。2. 从 Demo 到产品Agent 运行时与框架选型2.1 Harness 到底是什么玩意“Harness”这个词最近特别火很多人第一次听是在研究 agent 评测或 agent 框架时。它和“框架”并不是一回事。框架是一个比较泛的概念指你做 Agent 时用的依赖库比如 LangGraph、Coze、Dify。而 Harness 在 Agent 语境下指的是围住模型推理循环的那一层“管线”输入怎么进、输出怎么解析、怎么调用工具、怎么处理错误、什么时候停止、如何记录 trace。你可以理解成模型是一个发动机Harness 是引擎盖下面把所有管路接好的那套系统。很多时候我们说的“Agent 框架”其实核心就是一套 Harness。把它单独拎出来讨论是因为你在端侧做 Agent不一定非要用完整的通用框架但无论如何都需要自己的 Harness。哪怕手写 500 行循环那也是 Harness。它决定了 Agent 的行为边界也决定了你能不能高效开发、测试、监控。2.2 自研、通用框架、端侧方案怎么选我总结了三条路线成熟度和匹配对象完全不同第一条直接用通用 Agent 框架LangGraph、Dify、Coze 等优点生态好、工具多、圈子大社区踩坑经验丰富。缺点绝大多数通用框架假设云端部署依赖 Python 运行时、大量依赖库、服务常驻内存直接搬到手机/PC 端会非常难受。强行套壳的后果是包体积暴涨、启动时间变长、权限模型和端侧不一致。适合服务端编排 端侧执行的混合形态或早期快速验证。第二条基于端侧推理框架自研轻量 Harnessllama.cpp、onnxruntime、MediaPipe LLM Inference API 等优点完全受控行为可预测资源占用极致可控适合嵌入式平台。缺点什么都要自己写包括解析、工具调用、记忆管理、并发保护初期成本高。适合已经有稳定端侧团队、Agent 场景明确、不想被框架绑定。第三条混合——云上编排 端侧推理/端侧工具执行优点兼顾灵活性、性能和生态。复杂规划放云端端侧负责低延迟响应和敏感操作。缺点架构复杂网络不可用时能力降级明显。适合产品形态本身就是“主端协同”或者希望快速迭代 Agent 逻辑但保留端侧体验优势的团队。我的建议是如果团队还处于第一个端侧 Agent 产品阶段不要急着选通用框架先用最简单的“循环 工具注册表 状态日志”手写一套 Harness跑通之后再看哪些环节需要引入更重的框架。通用框架解决的问题是“开发效率”但在端侧你首先面对的是“资源约束”框架的抽象层往往会掩盖关键问题。2.3 一个能上线的极简分层无论选什么框架最终系统一定长这样从下往上执行层负责真正跑模型推理对流式输出、上下文缓存、停止条件负责。端侧这层要接推理引擎比如 llama.cpp 或 MediaPipe。工具层所有可被 Agent 调用的能力统一定义成 JSON Schema 描述的函数或本地接口比如读相册、发通知、访问日历、执行快捷指令。记忆层保存工作记忆和长期记忆提供读写和检索能力。端侧通常用 SQLite 或本地 KV 存储。规划层根据用户请求和工具描述生成执行计划。可以是 ReAct 式的逐步推理也可以是 Plan-Execute 式的先规划再执行。校验层最后对 Agent 产生的结果做校验包括权限校验、输出格式校验、安全性检查避免模型幻觉直接落到用户界面。每层之间只通过定义好的接口通信状态尽量不放在全局。这样排查问题的时候你能一眼看出是模型输出问题、工具返回问题还是记忆读取问题。这个极简分层我建议所有团队都照着搭一遍哪怕最后你用了某个现成框架。3. 端侧 Agent 的记忆与上下文工程3.1 别把记忆只当成“聊天记录”“记忆”是 Agent 工程化里最容易被搞错的概念。很多人以为记忆就是保存用户说了什么然后塞回 prompt。实际上记忆是一个立体结构至少要分三层工作记忆Working Memory当前任务进行中产生的临时信息比如用户本次提出的需求、正在执行的工具输出、当前规划步骤。它存在于单次会话甚至单次任务的上下文里任务结束就可以丢弃。热词“agent 存储 working memory”指的就是给这层做持久化目的是支持任务中断恢复。长期记忆Long-term Memory用户长期偏好、历史诉求、重要事实比如“用户习惯把日程安排在上午”“用户上次让我处理过这类文件”。这部分适合存向量 结构化摘要端侧存放不上云。程序性记忆Procedural MemoryAgent 会用的技能和工具比如“如何格式化文档”“如何调用系统通知”。在端侧 Agent 里这层通常表现为 Skill 注册表和工具描述库是 Agent 后天的“肌肉记忆”。在端侧这三层都受存储容量和查询耗时的限制所以记忆工程的核心其实是三个字丢得掉。很多人做了记忆之后反而把 Agent 变笨了因为检索出来的旧记忆干扰了当前判断。所以端侧记忆体系的第一原则是默认不加载需要时再查。3.2 上下文预算怎么算端侧 Agent 的上下文窗口是单选题不是主观题。以 4K 上下文为例你需要把预算分给用途预算说明系统指令300~500 token角色定义 行为规则尽量精炼工具描述600~1200 token每个工具的 name 和 JSON Schema 都会占 token历史摘要500~1000 token压缩后的对话摘要而不是原文当前请求300~800 token用户当前输入模型输出剩余必须给生成留出余量否则容易中途截断一个 4K 的上下文真正能拿来存“任务过程”的其实只有很小的比例。所以工程上的关键动作是把工具描述做成精简版系统指令做成模板历史摘要动态压缩。我实测下来端侧 Agent 的上下文分配公式大致是可用上下文 窗口大小 - 系统指令 - 工具描述 - 保留输出余量比如窗口 4096系统指令 400工具描述 800保留输出 800那给历史和用户输入的预算只有 2096 token。再扣掉用户正在输入的内容历史摘要实际上最多分到 1300 左右。如果你的 Agent 有五个工具、每个 Schema 两百 token那工具描述就吃掉 1000历史预算会进一步萎缩。所以控制工具数量、控制 Schema 篇幅是端侧上下文优化的第一课。3.3 摘要压缩与检索的实操做法在端侧做记忆我推荐一套组合拳短期对话用滑动窗口 摘要每轮对话结束后把旧的几条消息交给模型生成一段摘要替换原文。摘要消息本身带着一个时间戳下次请求时优先参考最近摘要。注意摘要输入本身也占 token所以摘要也要每 N 轮做一次二级压缩避免摘要积累成新的“长尾巴”。长期记忆用向量检索 结构化 KV端侧向量库首选 SQLite 简单的 embedding 表不一定要上独立的向量数据库。索引维度不要太高128~384 维足够。Chunk 大小建议 128~256 tokenOverlap 建议 16~32 token。检索 topK 在端侧通常取 3~5 个多了既耗时又容易引入噪声。程序性记忆用 Skill 清单 按需加载不要把 20 个 Skill 的描述全部塞进上下文。端侧做法是在系统指令里只写一个“你有以下能力”的目录行具体描述按需查询模型先输出“需要读相册能力”Harness 再查出该能力完整描述注入下一轮。这个机制能省一大截 token。说到检索我特别想提醒端侧 embedding 模型质量有限检索到的结果不一定相关。所以任何检索注入内容的开头都要带上显式标记比如“【记忆检索】以下是用户历史偏好可能与当前任务相关但请以用户最新指示为准”。这个分隔手段能显著降低端侧模型把旧记忆当成事实的概率。3.4 我的记忆工程踩坑记录第一坑记忆污染。我早期把用户历史摘要直接拼接进系统指令结果用户在某次会话里说“我不喜欢某 App”之后好几轮 Agent 都反复提这个偏好因为它在摘要里被留了下来。后来我规定摘要只记录用户明确表达的持久偏好而且每次引用偏好时必须带上“用户曾说过”的引述语气而不是作为事实陈述。第二坑上下文超限不报错。端侧推理引擎遇到超过窗口的内容通常不是报错而是悄悄截断。结果就是 Agent 莫名失忆用户以为你傻了。对策很直接每次组装 prompt 前做 token 计数超过预算就强制做摘要压缩宁可丢细节也不要让上下文被截断。第三坑检索召回了完全无关的内容。有一次用户问“帮我设置闹钟”检索层把长期记忆里的“用户喜欢在睡前听播客”给拉了出来Agent 就开始聊播客。后来加了打分阈值低于阈值直接不注入模型没有历史可用时反而表现更稳定。4. Agent 编排与工具调用的工程细节4.1 编排方式ReAct、Plan-Execute 与状态机Agent 的编排方式决定了它处理复杂任务的上限。端侧最主流的两种ReAct边想边做每一步都基于模型输出决定下一步。优点是很灵活缺点是每一步都要推理token 开销大而且模型在长任务里容易迷失方向。适合单一、直接的任务。Plan-Execute先让模型制定一个计划按步骤执行每执行完一步再确认下一步。优点是省 token、行为更可控缺点是计划可能不合理而且中途出现意外时不够灵活。适合操作链较长但步骤明确的场景。端侧 Agent 我强烈建议默认 Plan-Execute 变体先让模型输出任务列表然后 Harness 按顺序执行每步结束后把结果追加到上下文再让模型判断是继续、修复还是终止。这本质上就是一个轻量状态机。你可以把每一步的状态定义成pending / running / success / failed / retry用状态机约束模型的自由度。为什么端侧不适合全自由 ReAct因为 token 预算有限。ReAct 的“思考”会消耗大量上下文在端侧尤其致命。Plan-Execute 把思考集中在开头执行阶段只做“确认”能省下超过一半的 token。4.2 Function Calling 在端侧的坑端侧模型的 Function Calling 能力远不如云端大模型这是所有做端侧 Agent 的人迟早要面对的现实。具体表现经常不按 Schema 输出或者输出残缺 JSON同一个工具描述云端模型准确率高端侧模型会漏参数工具返回错误后模型会重复调用同一个工具十几次形成死循环。我在项目里踩得最深的一个坑就是工具返回“未找到文件”模型偏不信反复用同一参数重试同一工具直接烧掉了整个上下文。之后我给 Harness 加了三道保险解析容错模型输出不完整 JSON 时不直接报错而是做一次修复尝试。比如补全引号、合并残缺字段、提取嵌套 JSON实在解析不了再返回“格式错误”。最大重试次数同一个工具调用失败后重试次数默认控制在 2 次以内第二次失败直接返回给用户“这个操作暂时没完成”而不是继续循环。工具输出截断与摘要文件内容太长时不给模型全文只给前 200 字符 “内容已被截断如需更多请调用查看指定段落工具”。这样既保上下文又避免模型读一半就开始编。还有一点很实用工具描述里用示例。端侧模型对“参数格式”的理解高度依赖示例与其写严谨的 JSON Schema不如在 description 里写“例如查询用户名为 admin 的文件时传入 {owner: admin}”。实测下来示例比纯 Schema 对端侧模型友好得多。4.3 Skill 是工具调用的标准化进阶最近 Skill 这个词热度很高其实它在端侧工程里的价值比在云端更明显因为 Skill 是“工具调用 多步流程 局部记忆”的打包单元。一个端侧 Skill 的标准形态应该是一个 manifest 文件描述触发条件、输入输出参数、权限使用范围和执行步骤一段可执行的本地代码或脚本以及一段模型可见的“何时使用我”的描述。下面这个 manifest 是我在项目里常用的简化模板{ name: set_reminder, description: 在本地日历中创建提醒事项。当用户要求设置提醒/闹钟时使用。, trigger: { keywords: [提醒, 约会, 待办, remind] }, inputs: { title: {type: string, required: true, example: 下午三点开会}, time: {type: string, required: true, example: 15:00}, repeat: {type: string, required: false, enum: [none, daily, weekly]} }, permissions: [calendar:write], steps: [parse_datetime, create_event, notify_user] }把工具升级成 Skill核心收益是幂等性。比如“设置提醒”这个 Skill如果模型因为第一遍执行没返回成功信息又调用了一次你不能再创建一个重复提醒。所以每个端侧 Skill 都应该自带去重逻辑要么在执行前检查时间标题是否已存在要么执行后返回一个 operation_id重复调用时直接返回上次结果。另外Skill 也要有版本。端侧升级麻烦Skill 一旦被多个流程引用改参数格式会牵连很多旧上下文。我的习惯是 manifest 里加 version 字段模型调用时如果发现版本不匹配Harness 直接拒绝并返回“该技能已升级请重新描述需求”。4.4 多 Agent 协同别急着上“多 Agent”这两年特别火但在我看来端侧场景 90% 的情况不该上多 Agent。原因很简单端侧每多一个 Agent 循环就多一份上下文开销、多一份状态管理复杂度、多一条出错的链路。端侧的多 Agent应该专注做角色切分而不是做“群聊式协作”。我推荐一种保守但实用的模式一个主控 Agent 若干个工具型 Agent。主控 Agent 负责任务拆解工具型 Agent 只专注执行自己的特定领域比如信息检索、系统控制把结果传回主控。它们之间不自由对话只有主控发指令、子 Agent 返回结果。这样可以避免多 Agent 之间的“幻觉传播”和 token 爆炸。有一点需要提醒子 Agent 的返回结果也必须经过校验层不能默认“子 Agent 比我聪明所以结果绝对可靠”。在我测试中端侧子 Agent 在领域内确实表现更专注但一旦超出它的领域它也会一本正经地编造结果其幻觉程度和主控 Agent 半斤八两。5. 常见问题与排查技巧实录5.1 一次真实死循环 Debug 过程说一次我最典型的排查经历。用户要求“帮我整理桌面上的文件并归类”Agent 的规划是先列出桌面文件再逐个移动。但半天没有任何输出。查 trace 之后发现Agent 第一轮输出了一个不完整的文件列表Harness 解析失败于是模型又重试了一遍 list_files 工具。list_files 返回了正常结果但模型没有按计划继续而是再次调用了 list_files因为它的上下文里堆满了历史和失败信息。就这样连续三次 list_files第四次才勉强开始移动文件但此时上下文已经快被塞满移动文件的调用参数又被截断了。排查思路很简单分阶段看 trace。第一步看模型输出是否合理第二步看工具返回是否可解析第三步看控制流是否进入死循环。结果发现三个环节都有问题模型不该重试那么多次、Harness 不该允许同样参数重复调用、上下文管理本该在第一次失败时就做一次历史摘要压缩。修复方案也对应三条引入“相同工具相同参数不允许连续调用超过一次”的规则解析失败时先做摘要 压缩再重试每次工具调用完成强制检查剩余上下文预算低于阈值时立即触发压缩。5.2 高频问题速查表问题根因解法建议Agent 反复调用同一工具模型卡在重试循环缺终止条件同一工具同参数连续调用不超过 2 次加超时和循环保护工具返回未格式化内容端侧模型 JSON 输出不稳定引入解析修复层结果先截断再注入上下文对话久了 Agent 变笨上下文被历史撑爆关键信息被截断做滑动窗口 摘要压缩实时监控 token 预算记忆检索乱入无关内容阈值过低或 chunk 切分不合理加阈值缩小 chunk在注入内容上加“仅供参考”标记任务执行一半闪断设备资源不足或 App 被系统回收工作记忆持久化到 SQLite启动时恢复未完成任务新 Skill 上线后旧流程异常版本不匹配模型还在按旧参数调用Skill 带版本号Harness 拒绝旧版本调用并提示用户重新描述模型输出安全内容被用户投诉端侧模型安全对齐弱在系统指令中强调边界校验层加入输出过滤敏感操作二次确认设备发热严重连续推理时间过长分段推理显示层提前输出“正在处理”限制单次任务推理轮数5.3 给新手几个避坑建议围绕端侧 Agent 工程化我最后给几条具体的建议都是踩过坑之后沉淀下来的一工具数量宁少勿多。端侧每多一个工具模型的选择焦虑就高一分token 占用也高一截。先保留最核心的 5~8 个工具验证链路稳定后再逐步增加。二所有工具都要幂等。被重复调用是常态不是意外。像“发送消息”“创建文件”“下单”这类有副作用的操作必须设计 operation_id 去重机制。三日志里必须有 request_id。端侧排查问题时没有 request_id 几乎等于没有现场。从入口到工具调用再到模型生成全程贯穿同一个 ID才能把分散的日志串成一条完整 trace。四模型版本冻结要谨慎。端侧模型升级不像云端改个参数那么轻量一旦新版模型在真实场景表现不如旧版回滚成本极高。上线前必须准备一个包含 30~50 个真实用户任务的回归集自动跑一遍再决定。五状态保存比模型推理更重要。Agent 进程可能随时被杀但只要状态在用户就不会觉得“它失忆了”。尽量把每一步的状态变化都落到 SQLite 事务里而不是只存在内存里。六不要迷信“全端侧”。很多操作如复杂知识检索、高难度写作端侧模型的能力确实有限。务实的做法是设计好降级链路能本地完成就本地本地判断不了再走云端辅助并明确告知用户当前处理方式。做端侧 Agent 工程化这段时间我最大的体感是模型能力决定天花板工程能力决定你能不能摸到那层天花板。很多团队抱怨端侧模型不够聪明但实际排查下来更多问题出在工具调用失控、记忆管理混乱、上下文被截断这些工程环节上。先把 Harness 做好把状态管清楚把每个失败路径都想明白再回头看模型能力你会发现事情比想象中简单。下一篇我会接着聊端侧 Agent 的并发处理、安全沙箱设计和端云协同的降级策略这些都是“工程化”下半场的主战场。