1. 从一次请假失败说起这四件事为何必须一起设计我做过一个个人助理类实验项目核心功能很朴素让代理帮我在通讯软件里给同事发明天请假一天的消息顺便更新日程表。最初我把任务拆成了四步——读日历、打开通讯App、生成消息、发送。听起来很简单但真正跑起来之后问题一个接一个代理读了旧日历缓存把上周的会议安排当成了今天通讯App的自动化权限只授予了读取聊天列表真要发送时直接被系统拦截本地跑的大模型调度了云端服务结果隐私日志被同步了上去。这次失败让我意识到Personal AI Agent个人AI代理的架构难点不在单一模型多聪明也不在某一个功能多完善而在于四个模块的耦合设计Memory记忆、Permission权限、跨App集成、本地/云端部署。如果你分别做四个独立方案最后拼起来一定散架。这篇文章我就用自己实操过的架构思路把这四个模块怎么切、怎么接、怎么取舍讲清楚适合准备自己搭代理的开发者以及想深入理解代理产品底层逻辑的产品和技术人员。先说我的核心结论这四个模块不能按功能清单来设计而要按一次任务的生命周期来设计。一个任务从接收指令、调取记忆、申请权限、跨App执行到数据回流本地保存每一步都同时涉及四个模块。下面我按生命周期一层层拆。2. 记忆层设计工作记忆、语义索引与持久化一个都不能少2.1 为什么记得比聪明更影响体验很多人做AI代理时记忆层就简单当成聊天记录存下来下次拼进Prompt。但真实场景里记忆至少承担三种完全不同的职责工作记忆Working Memory当前任务进行中的临时上下文比如我在排请假流程时日历上读到的明天10点有评审会、通讯App里联系人的备注名。这部分要短小、快速、可覆盖。长期事实记忆Episodic/Factual用户偏好和过往事实比如我通常周五不安排会议请假需要同时通知主管和项目群。这部分要结构化存储能被主动召回。语义记忆Semantic Index把碎片内容向量化支持模糊想起比如用户没说过我讨厌早会但每次代理安排早会都被取消语义层能从行为里归纳出隐含偏好。分开设计是因为三者的存储介质和访问方式完全不同。工作记忆最好放在内存态用有限容量的滑动窗口长期事实记忆适合结构化查询JSON文档或SQLite都行语义记忆则必须走向量检索。如果混在一个K/V缓存里要么召回不精准要么上下文迅速膨胀。2.2 存储选型别一上来就上重型数据库我实验时第一版用了内存对象保存工作记忆长期记忆直接序列化成JSON文件。跑了两周后发现问题JSON文件一旦超过几百条全量加载再检索非常慢而且没法做某时间段内发生了什么的时间线查询。后来把长期记忆迁移到了SQLite加了简单的表结构CREATE TABLE memory_facts ( id INTEGER PRIMARY KEY, agent_task TEXT NOT NULL, entity_type TEXT NOT NULL, entity_value TEXT NOT NULL, relation TEXT, fact_text TEXT, confidence REAL DEFAULT 0.8, created_at TEXT, updated_at TEXT, source_app TEXT ); CREATE INDEX idx_memory_entity ON memory_facts(entity_type, entity_value); CREATE INDEX idx_memory_updated ON memory_facts(updated_at);这个表记录的是可被查询的事实比如 entity_typescheduleentity_value2025-03-14fact_text10:00-11:00 评审会。有了关系字段我可以让代理回答上周三下午我在干什么这类问题而不是只能模糊地拼Prompt。语义记忆我用了本地运行的小型向量模型做嵌入索引文件直接落盘查询时按cosine相似度取Top-K。这里有个关键取舍向量检索只负责候选召回最终要不要把某条记忆写进Prompt还要过一层规则过滤——比如敏感信息、过期时间超过一定阈值的记录不能盲目塞给模型。这层过滤就是记忆层和权限层的第一个接缝。2.3 召回策略不是所有记忆都要进上下文上下文窗口再大也有成本。我采用三级召回策略任务前置召回解析到用户指令后先把实体提取出来用实体名去SQLite拿强相关的结构化事实。比如请假触发组织架构、考勤规则类的记录。向量语义召回把用户指令做嵌入在语义索引里找相似历史用于发现隐含偏好。动态组装与压缩召回结果到了拼接阶段按相关性分 时间衰减 权限标记排序只保留头部若干条。时间衰减公式我用的是简单版本score relevance * pow(0.95, days_since_last_access)这样既能保证近期信息优先又不会让一条半年前的强相关内容彻底被淹没。这套策略里最容易翻车的是第一级——实体提取错误后面全歪。比如帮我问问张三明天有空吗实体是张三明天如果误提取成明天有空召回的就是别人的日程信任感瞬间崩掉。所以我在实体提取后面加了一小段校验逻辑提取出的实体必须能在联系人表或日程表里找到对应记录找不到就回退到向量召回而不是硬着头皮进任务队列。2.4 记忆的修正与遗忘个人AI代理最容易被用户吐槽的一点是记错了还嘴硬。我做了两个机制显式纠正用户直接说我之前说的是某某某时代理不仅要修改当前上下文还要回写记忆库把对应fact_text更新并且把confidence调低重新计算时间衰减的基线。隐式遗忘如果某条记忆超过30天未被召回且用户从未主动引用定期清理任务里把它降级为归档态不再参与默认召回。归档数据保留在磁盘用户可以随时通过代理你记得我曾经说过……来显示触发全量检索。这么做是为了对抗记忆污染——模型为了迎合用户很可能在几轮对话里把早期事实越改越偏离。定期归档其实是一种人工降权的保护机制。3. 权限体系给代理一把钥匙但别把整栋楼都给它3.1 最小权限与代理身份模型跨App操作里最危险的不是模型答错而是代理拿到了超过任务所需的权限一次误操作把通讯录删了。个人设备上的代理App通常申请的是系统级辅助功能权限或API授权一旦授权就是接管整个应用这非常可怕。我的方案是引入代理身份和操作意图双层校验代理身份注册在设备本地的服务账号与用户账号分离。代理的授权列表单独管理不继承用户的所有权限。操作意图每次具体操作执行前由本地的策略引擎判断这条操作是否在任务允许的动作集合内。比如代理在请假任务里需要发送一条消息它的身份被授予的动作集合是{ app_id: com.example.messenger, allowed_actions: [read_contact, read_chat_history, send_message], denied_actions: [delete_message, modify_contact, send_bulk_message], require_confirmation: [send_message] }策略引擎在本地跑规则文件也存本地。这个文件的更新只能由用户手动确认代理自身没有权限改自己的策略文件——不然等于形同虚设。3.2 分级授权自动执行、确认后执行、永远拒绝我把操作分成三级对应不同的用户体验级别示例执行策略L1 自动执行读取本地天气、读取日历标题直接执行事后写审计日志L2 确认后执行发送消息、创建日程、修改提醒弹出本地确认卡片用户点同意才执行L3 永远拒绝删除联系人、读取银行短信验证码策略引擎直接拦截不询问用户这里有一个细节确认卡片弹出时机必须在操作真正发生之前而且要把要操作的对象、内容、影响完整展示。很多代理只在弹窗里写是否允许发送消息用户根本不知道内容是什么等于盲批。我在L2的确认卡片上除了内容展示还加了一个本次批准和以后自动批准的切换方便高频场景逐渐降级为L1但降级动作必须用户手动选择。3.3 授权令牌与作用域如果跨App操作走的是平台API授权令牌Access Token也有讲究。常见的坑是代理App统一持有一个长期有效的refresh token一旦泄漏攻击者就拿到了所有App的访问能力。我的做法是每次任务生成一个短期scope token有效期与任务生命周期绑定比如10分钟。任务是读取日历发送消息scope token里就只写这两项的权限。任务结束立刻废弃token即使日志里被翻出来也是一个死凭证。本地App之间的自动化操作没有办法完全复用这种经典OAuth流程因为系统辅助功能授权是全局的。折中方案是权限策略引擎拦截之后再做一次上下文判断——如果当前前台App不是用户正在操作的App且代理的任务未处于用户确认过的活动状态直接拦截。3.4 审计日志本地存储、可回放跨App代理最大的信任危机来自它到底偷偷做了什么。我本地维护了一个独立的审计日志表每条记录包含触发任务ID、操作App、动作类型、目标对象摘要、执行结果、请求来源。日志只能追加不能修改。用户随时可以打开Dashboard看时间线回放。有次我排查一个日历莫名多了一条重复提醒的问题就是靠审计日志回溯发现某个旧任务在重试时把创建日程的请求发了两次而不是模型幻觉造成的。没有日志这种问题根本复现不了。4. 跨App通道官方API、辅助功能、深链路由三条路都有代价4.1 方案对比接口权限、能力边界、维护成本个人AI代理要操作其他App绕不开三种通道。我把它们放在一张表里对比通道能做什么优点致命缺点官方API/OAuth按平台开放能力走稳定、规范、有正式授权平台不开放的能力全做不了辅助功能/Accessibility模拟UI点击和读取界面几乎覆盖所有App脆弱、慢、容易被系统限制URL Scheme/深链打开指定页面或执行简单动作轻量、有标准能力有限很多App不支持复杂操作只看这张表很多人会选官方API为主。但现实里个人代理的场景非常长尾订酒店、查快递、编辑文档、批量改照片……每个App都接入官方API根本不现实。而辅助功能虽能通吃性能和稳定性又很差。我的取舍是分层高频核心场景日历、通讯、邮件、待办优先走官方API因为它们的数据价值最高容错率也最重要。次要场景但UI稳定的音乐播放、阅读类用辅助功能做只读或简单控制。一次性场景比如某个冷门App里查订单优先尝试深链能跳转到目标页面就跳跳不过去就明确告诉用户这个操作需要你手动完成。4.2 意图中枢把跨App动作抽象成统一协议跨App最麻烦的不是单点接入而是任务编排。比如请假任务涉及读取日历App A→ 检查日程App B→ 获取联系人App C→ 发送消息App D。如果每个App各写各的调用代码任务编排层就变成一团乱麻。我引入了一个轻量意图中枢Intent Hub概念。所有App接入时对外暴露的不是API函数而是统一的意图定义{ intent_name: schedule.read, required_params: [start_time, end_time], optional_params: [calendar_id], response_schema: list_of_events }任务编排层只跟意图打交道不关心具体App是谁、走什么通道。调度时意图中枢根据本地注册表选择当前可满足此意图的最优App通道。如果主通道失败比如官方API调不到可以自动降级到辅助功能通道重试。这套抽象让代理的可扩展性大幅提升。新增一个App只需要在接入层实现若干意图映射不需要改动任务编排逻辑。但代价也很明显意图定义阶段的枚举工作量很大尤其涉及语义相近的意图schedule.read 和 calendar.query时容易设计重复。我的建议是意图命名尽量从用户任务出发而不是从App功能出发。4.3 辅助功能方案的稳定性经验如果你决定在某个App上用辅助功能有几个实测经验可以省很多时间不要直接用坐标点击先解析UI层级里的content-desc或resource-id找不到再用坐标兜底。每次UI操作后加一个状态检查步骤验证页面是否真的跳转成功而不是盲目延时后执行下一步。App升级导致UI结构变化时代理会静默失败。我在任务回调里加了一个操作成功率指标某一个App连续几次失败就触发重接入提示而不是让用户感觉代理突然变笨。5. 本地与云端的切分冷热分离、离线降级与同步边界5.1 这个问题的本质是信任边界本地/云端不是单纯的技术部署问题它决定了一个基础事实代理的哪些状态可以被上传哪些必须留在设备上。我的切分原则很简单能本地算的绝不上云必须上云的先脱敏用户隐私数据只在本机构建索引。具体到架构上我把代理分成了三层设备本体的核心代理Local Core负责唤醒词、意图解析、策略引擎、记忆缓存、审计日志。这一层完全离线运行。本地模型管道On-device Model小参数模型处理分类、实体提取、敏感内容过滤复杂推理才走云端大模型。云端协同层Cloud Edge只在处理复杂生成任务时启用且只接收脱敏后的上下文片段不接收原始通讯录、完整日程等敏感数据。5.2 冷热分离部署有了这个边界部署策略就按数据冷热程度来热数据工作记忆、当前任务上下文、最近7天的操作日志 → 本机内存或SQLite。温数据结构化事实记忆、语义索引 → 本机磁盘加密存储可做定期备份。冷数据跨设备的同步状态、长期偏好画像 → 加密后上传云端但上传前做字段级脱敏。举个例子我的代理在日历里记住了每周一10点团队周会这条记录属于长期事实存在SQLite里。它被同步到云端时只上传一个随机ID加上weekly_meeting这样的分类标签具体时间、参与人列表都不传。云端能用来做跨设备一致性协调但拿不到原始语义。5.3 离线优先与降级策略个人代理跑在手机和电脑上网络不可能永远稳定。我采用的策略是离线优先所有任务先尝试在本地完成任务闭环云端只作为增强项。具体来说收到指令后本地意图解析器先判断类型。如果是读日历打开App这类纯本机任务直接执行根本不发起任何网络请求。如果是写邮件并发送这类需要语义生成的本地小模型先生成草稿把草稿存在任务队列。用户在线时代理用云端大模型润色一回再走权限确认流程发送。如果任务进行到一半断网状态机把当前进度存下来。恢复网络后不是重头执行而是从断点继续。这个断点保存逻辑非常关键否则用户会发现代理总是在重复已完成的步骤。5.4 同步协议的取舍CRDT还是操作日志多设备场景需要同步记忆和待办状态。我对比过两类方案纯操作日志同步简单、容易理解但冲突解决要靠最后写入者胜容易丢更新。CRDT同步能处理并发编辑但没有成熟的跨语言库调试困难。个人代理的同步场景以用户在一个设备上写另一个设备读为主并发写的情况很少。所以我选了操作日志方案只是在冲突发生时保留两个版本副本由用户在界面上合并而不是让代理自动丢弃一条。这样实现成本低出错面小。不过我提醒一句如果未来代理支持多设备同时语音交互CRDT基本是绕不开的选项。到时候同步层的复杂度会显著上升建议提前在数据结构里预留版本号字段。6. 从零搭建时最容易被低估的四个坑6.1 任务状态机比模型Prompt更容易出错很多代理框架用一个大循环让模型自己决定下一步省事但隐患很大模型一旦把等待用户确认与直接执行混淆任务就会在错误时机触发敏感操作。我在工程里维护了一个显式的任务状态机状态包括IDLE - PARSING - RETRIEVING - PERMISSION_PENDING - EXECUTING - VERIFYING - DONE模型只负责在某个状态节点给出建议动作真正的状态迁移由代码控制。比如在PERMISSION_PENDING状态无论模型输出什么指令策略引擎都会拦截直到用户确认卡被点击。6.2 权限冒泡提示的频控问题L2确认弹窗如果每个任务都弹三次以上用户会烦躁到直接禁用授权。我的经验是做一个信任累积机制同一个App下同一个L2动作连续成功三次且用户未撤销系统自动询问用户以后自动执行可以吗用户同意后才降级为L1。反过来如果某次操作结果异常比如发送失败、修改对象不符预期信任级别立刻回退到L2重新要求确认。这个机制让代理在省事与安全之间动态平衡而不是一刀切。6.3 记忆回写的时机记忆回写不能贪多。早期版本我在每次任务结束时都把全过程塞进长期记忆没过多久SQLite里就全是已读消息A已读消息B之类的噪音。后来改成只有任务涉及目标实体创建、修改、删除或用户显式表达偏好时才写长期记忆。比如请把下周的项目评审挪到周三一定要回写帮我查一下明天的天气就不需要。6.4 起步的最小闭环建议如果你准备自己做一个Personal AI Agent不要一上来就追求全平台跨App。我建议的最小闭环是本机日历读取 本地小模型解析意图。基于SQLite的事实记忆 简单向量召回。一个L2确认弹窗控制发送类操作。本地SQLite 加密备份解决云端同步。跑通这个闭环把记忆准确率、权限误拦率、任务完成率三个指标量化出来再决定要不要扩展到辅助功能通道和云端大模型。直接跳到复杂架构大概率会在某个晚上对着十几个日志文件崩溃。最后分享一个小的实操技巧给所有跨App操作的事务日志加上一个抽象trace_id从任务开始到结束贯穿所有模块。排查问题时用trace_id一键拉出完整链路比翻瞎猜高效太多。这个习惯我吃了很多次亏后才养成现在已经成为每次架构设计里第一个就要确定的东西。