1. 为什么我最后下定决心自己搭一个 Personal AI Agent
过去大半年,我一直在折腾各种云端 AI 助手,功能确实强,但越用越觉得不对劲。AI 不知道我上周跟同事聊了什么项目,每次都要重新交代一遍背景;我想让它帮我查个待办事项,它就得去翻我的邮箱和日历,可我又不敢把账号密码直接给它。说到底,那些通用助手的记忆是它自己的记忆,不是我的记忆;它的工作方式是在云端处理,可我大量的私人数据又没法真正交给它。直到我想明白一件事:我需要的是一个真正的 Personal AI Agent,是长在我自己的数据、自己的设备、自己的权限边界里的一套系统,而不是一个远程对话盒子。
于是我开始研究 Personal AI Agent 的架构,核心就是四个问题:Memory(记忆)怎么设计,权限边界怎么划,跨 App 操作怎么做,本地和云端怎么拆。这篇文章就是把我在设计这套架构时的完整思路、选型原因、踩过的坑和最终落地方案整理出来。适合正在自己动手搭个人助理的开发者,也适合想理解这类系统设计逻辑的产品人。即便你现在不写代码,看完至少能明白为什么市面上那些助手“记不住你”,以及一个真正贴近你生活的 AI 系统该长什么样。
2. Memory 分系统:让 AI 从“聊完就忘”到“真正记住你”
2.1 记忆的本质是分层:短期、情节、语义、程序
很多人一提到给 AI 加记忆,第一反应就是“把聊天记录存下来”。真做起来就会发现,一坨聊天记录塞给模型,效果极差。模型上下文窗口有限,塞太多不仅贵,而且真正关键的线索被淹没了。我最初也这么干过,结果 AI 经常把三个月前随口提的旧地址当最新地址回复我,尴尬得不行。
正确的做法是把记忆系统拆成几个层次,参考人类记忆的基础分类:
- 短期记忆:当前会话里临时需要的内容,比如“我正在写一篇关于记忆架构的文章”。会话结束,短期记忆的任务基本就完成了,只需要沉淀其中重要的信息到高层。
- 情景记忆:具体场景和时间点相关的经历,比如“上周三跟某位同事开过项目会,会议中确认了 Q3 要上线新模块”。这是时间线式的,AI 可以回忆“当时发生了什么”。
- 语义记忆:从各种经历里提炼出来的一般性事实,比如“这位同事负责后端接口,沟通风格偏直接,通常上午回复比较快”。它不再绑定某个时间点,而是通用的关于你、关于世界的认知。
- 程序记忆:关于“怎么做”的流程性知识,比如“我习惯每天下班前把今日进展同步给团队,格式分三段:完成、进行中、风险”。
分层的关键在于,不同层级有不同的写入时机、存储形式、召回方式和更新策略。我最终实现的代码结构大致是这样的:
class MemorySystem: def __init__(self): self.working = WorkingMemory() # 短期,进程内存,会话结束即回收 self.episodic = EpisodicMemory() # 情景,向量数据库,按时间+语义索引 self.semantic = SemanticMemory() # 语义,知识图谱+向量混合 self.procedural = ProceduralMemory() # 程序,规则脚本+参数化模板这里注意,情景和语义不是割裂的。情景记忆是“素材”,语义记忆是“结论”。我设计了一个异步管道:每次交互结束,先做情景记忆归档(谁、什么时间、做了什么事、结果如何),然后由后台任务跑一轮语义抽取,把经常出现的模式沉淀为语义记忆。比如 AI 连续七天注意到我都是晚上十点后开始写代码,它会自动形成一条语义记忆:“某位使用者的活跃编码时段集中在晚间”。
2.2 记忆的存取触发机制
记忆系统设计里面最容易做砸的,不是“存”,而是“什么时候存、什么时候取”。如果每次对话都把全部记忆塞进上下文,效果跟没记忆差不多。我的方案是给每条记忆打上三个维度:重要度、时效性、关联度。
- 重要度:0 到 1 的分数,由本地小模型对信息打分。判定为需要长期记住的事实(比如联系人关系、地址、偏好)会给 0.9 以上;今天吃了什么这种,三个月后基本不重要。
- 时效性:记忆并非永久有效。临时安排类的记忆 48 小时后降权,项目类记忆持续到项目结束,个人身份类信息长期有效。
- 关联度:召回的种子条件,比如当前对话涉及“旅行计划”,就只召回与旅行相关的记忆。
取用的逻辑是这样:用户说一句“我想去广州玩两天”,Agent 先把这句话解析出意图和实体,得到destination=广州、duration=2天,然后用这些作为查询条件,在语义记忆库里召回关联记忆。假设记忆库里有一条“你上次去广州住在某酒店,评价是位置方便但隔音一般”,这条就应该被召回。召回后还要做一步排序和截断,只保留最相关的几段,避免把无关记忆带进上下文。
我试过直接用向量相似度召回,发现一个问题:语义相似不等于场景相关。比如我聊“去广州”,向量库里关于“广州的同事”“广州的项目”“广州的旅行攻略”都可能是高相似度。所以最终我没有只靠向量,而是加了一层基于知识图谱的路径召回:从当前实体出发,沿着关系边找关联实体,再把这些实体的属性作为补充上下文。效果比纯向量召回扎实得多,尤其是处理人物关系、项目脉络这类多跳关联时。
2.3 记忆冲突与遗忘机制
记忆系统必然会遇到冲突:旧记忆说“该同事负责前端”,新记忆说“该同事转做后端了”。如果不处理,模型会自己纠结。我设计了版本化覆盖策略:每条语义记忆有created_at、updated_at、confidence字段。新记忆进来时先做冲突检测,如果新记忆置信度更高(比如来源是用户明确陈述,而不是模型猜测),就覆盖旧记忆并增加版本号。如果新旧记忆只是角度不同而非真正矛盾,就保留多条并存。
遗忘机制也特别重要。记忆不是越多越好,冗余记忆会让召回准确率下降。我的做法是定期跑一次压缩管道:把重复度高的记忆合并,把三周未触发且重要度低于阈值的记忆归档到冷存储。这样日常查询时热记忆库保持精简,同时不丢历史数据。
实操提醒:记忆系统的召回效果很难离线评估,我踩过最大的坑是“自说自话”——自己测试感觉很好,一换到真实对话,用户表达复杂多变,召回相关性马上下降。建议上线早期就设计人工反馈回路:每条召回结果允许用户点“不相关”,把这些负样本积累起来,定期分析为什么召回错了。
3. 权限设计:AI 能做什么,边界画在哪里
3.1 三层权限模型:工具、应用、数据字段
把 Agent 接到真实数据上,最大的顾虑其实就是权限。我见过很多初版设计只做了“允许/拒绝”的黑名单,这远远不够。合理的权限设计应该分成三层:
- 工具级权限:Agent 能不能调用某个能力,比如读取日历、发送邮件、执行终端命令。这一层是总闸。
- 应用级权限:在某个工具内部能访问哪些范围,比如能读工作日历但不能读个人日历,能读取邮件标题但不能发送。
- 数据字段级权限:更细粒度,比如读取邮件时,邮件正文里被标记为“含敏感信息”的字段,Agent 即使看到了也不能写入长期记忆。
用一个项目中的例子来说明:我需要 Agent 在开会前汇总项目进展。它需要读日历(拿到会议邀请)、读文档(拿到项目更新)、读邮件(拿到客户反馈),这三个都属于工具级。但我不希望它自动给客户发邮件,那我会在邮件工具上只授予读取权限,拒绝发送权限。然后我再想想,会议邀请里的人名要不要进入长期记忆?如果在数据字段级设置了“人员姓名属于受限字段”,那 Agent 可以把人名用在当前任务上下文里,但不能写进语义记忆库用于长期画像。
权限策略我建议用一个集中式策略文件管理,不要散落在代码里。大概长这样:
permissions: calendar: read: true write: false scope: work email: read: allowed_fields: [subject, body_preview] send: false files: read: glob: ["~/Documents/Projects/**", "!~/Documents/Proprietary/**"] write: false shell: read: false write: false memory: save_sensitive_fields: false3.2 动态授权与请求分析
静态权限能覆盖大部分场景,但真实使用中总有“意料之外”的操作。比如用户临时说“帮我查一下上次客户报的价格”,这句话涉及客户管理工具、报价文档两个敏感范围。事前没法把所有组合都配好,所以 Agent 需要一套动态授权机制。
我的方案是:Agent 在执行动作前,先经过一层请求分析器,换成大白话就是先自己审自己一遍。比如分析出“这个动作要读取客户表,且需要导出字段”,把它翻译成标准权限请求,然后交给本地授权服务判断。如果未配置明确策略,就走询问流程:弹窗提示用户,说明需要的权限范围、用途、预计影响面。用户批准后记录到本地策略库,下次同类请求自动放行。
这里有一个细节:拒绝比授权更值得记录。用户拒绝了一次操作,说明这个方向不合适,Agent 下次就不该再提类似的请求。我最初没做负向策略沉淀,结果同一个越权请求反复弹窗,用户直接被烦到卸载。后来加了“拒绝一次,下次同类行为默认降级”的规则,体验好很多。
3.3 敏感数据隔离与“不可见即不可存”
我自己的一个硬性规定是:没被授予权限的数据,不仅不能被读取,也不能因为任何中间推理环节被缓存或写入记忆。技术上就要保证记忆写入管道只在授权数据上运行。比如用户在邮件工具里只授权了标题字段,那 Agent 就不能把邮件正文摘要写入记忆库。这个要求在实际系统里很容易被突破,因为很多实现把消息先整个喂给模型,模型生成总结后存记忆,而这个“总结”究竟包含多少敏感信息,其实很难控制。凡是从未授权内容推导出来的信息,默认也当作受限内容处理。
注意:跨 App 操作时,权限模型要与每个 App 的接口能力对齐。比如某个 App 只支持文件级授权,不支持字段级授权,那就在接入层做过滤,宁可在接入层把字段截断,也不要让敏感字段流到模型侧。
关于安全红线,即使代码里的授权服务做得再完善,也要考虑一个基础事实:模型如果拿到了完整 prompt,可能自己“推断”出敏感信息。所以我在架构上用了另一个手段:把敏感字段替换成占位符再进入模型。比如短信验证码,Agent 知道“有一条验证码到了”,但模型看到的是[SENSITIVE_CODE],真正内容只用于特定动作。这个思路同样适用于身份证号、银行卡号这类数据。
4. 跨 App 操作:让 Agent 成为你的“万能遥控器”
4.1 三条跨 App 路径:接口、辅助功能、快捷键
要让 Agent 帮用户操作各种 App,绕不开技术路径选择。我自己把可行方案梳理成三条主流路径,其他都算变种。
第一条是原生或开放接口。如果 App 提供了自动化接口,这是最干净的方式。好处是稳定、隐私边界清楚、能精细控制权限。问题是现在绝大多数主流 App 不提供完整接口,很多甚至没有开放接口。
第二条是操作系统辅助功能。这是安卓、Windows 这类系统提供的无障碍能力,或者 macOS 的自动化控制能力。好处是能覆盖几乎所有 App,操作方式是模拟人的点击、滚动、输入。坏处是稳定性差,App 界面一变就可能失效,识别控件时的准确性也需要持续维护。
第三条是快捷键和剪贴板。很多 App 支持自定义快捷键、菜单栏命令、URL Scheme,这些接口很轻但功能受限。组合起来能做不少自动化,而且非常稳定。我把这三条路径按场景混着用:接口优先,辅助功能兜底,快捷键做补充。
在实现上我做了个统一抽象层,所有的跨 App 操作都走同一个接口,内部再做路由:
class AppActionRouter: def execute(self, action: AgentAction) -> ActionResult: if self.has_native_api(action.app): return self.call_native_api(action) elif self.has_a11y_path(action.app): return self.call_accessibility(action) elif self.has_shortcut(action.app): return self.trigger_shortcut(action) else: raise UnsupportedActionError(f"未找到可用的操作路径: {action}")4.2 智能路由与操作校验
设计跨 App 操作时,我意识到一个关键点:Agent 不只需要“执行”,更需要“判断”。比如用户说“把这张图片发给某位同事”,Agent 拿到指令后的完整思考链路是这样的:确定目标 App(通讯工具),确认识别出的同事(跟通讯录里的联系人做匹配),检查该联系人是否允许被搜索到,生成操作步骤,执行,再确认结果。每一步都可能出错,所以我在 Agent 执行前加了一个“操作预案校验”环节。
操作预案会生成一组预期结果,比如“发送成功后,该 App 会话列表里应该出现一条该联系人的新会话”。执行后,Agent 会做一次结果校验,拿实际结果和预期做比对。如果不一致,进入纠错流程:截图检查界面、查看错误提示、尝试替代方案。
这里需要特别留意一个问题:多步操作中的中间反馈必须显式呈现。AI 执行前你可以让它先确认第一步,但后面的步骤中如果出现意外,用户看不到就出错了。比如 Agent 自动打开了某个 App 的分享菜单,预期点击“发送”,结果弹窗布局变了,按钮偏移,点击落到了“取消”上。如果没有任何校验,这个操作就失败了用户还察觉不到。为此,我在关键步骤都加了“操作后校验点”,校验点可以是界面控件状态的改变、某个文件的新增、一条通知的出现。
4.3 上下文在 App 之间的传递
跨 App 操作的另一个难题是上下文传递。简单说,就是在一个 App 里拿到的信息,怎么高效地作为另一个 App 的操作依据。比如 Agent 从浏览器里复制了一段订单号,然后切到物流 App 查询进度。如果 App 都支持剪贴板,这挺好办;如果不支持,就需要 Agent 自己维护一个“当前工作上下文”对象,这个对象里面包含当前任务相关的实体、关键词、状态机。
我在设计时引入了一个“上下文暂存区”,这个暂存区存在于 Agent 本地,可以理解为一个临时便签。Agent 在任何一个 App 里获取到的关键信息,都会先写入上下文暂存区,并由解析器抽取实体和意图,再传递给下一步操作。这个设计很大程度上隔离了 App 之间的数据格式差异。比如浏览器里抓到的订单号格式跟物流 App 搜索框期望的格式不同,Agent 在暂存区里做标准化转换后再触发搜索。
不过我得泼一盆冷水:跨 App 自动化并没有想象中那么“智能”。最大的坑是状态不可预期。你永远不知道目标 App 当前处于什么界面、有没有弹窗、需不需要登录。所以对于高频核心场景,我的建议是不要做通用自动化,而是做场景化的固定流程模板。比如“查快递”就是一个固定流程:激活物流 App、定位搜索框、输入单号、截取结果。复杂的探索式操作留给 Agent 作为“尽力而为”的兜底方案,而不是主力方案。
5. 本地与云端的拆分:哪些放本地,哪些放云端
5.1 按隐私敏感度和计算需求分域
本地与云端的架构选择,本质上是在安全、成本、能力之间做平衡。我的原则很简单:隐私敏感的数据处理尽量本地,算力密集且不敏感的任务上云。这个原则落实到架构上就是分域部署:
- 本地域:记忆库全文索引、权限策略、敏感字段提取、短期会话处理。这些都涉及用户私密数据,放在本地。本地也能支撑小模型的推理。
- 云地域:大规模语言模型生成、复杂语义理解、跨语言翻译、高密度向量聚类这类需要大算力的任务,如果数据允许,就放到云端。
- 混合域:可以拆分成“本地提取敏感信息 → 脱敏后送云端 → 云端返回结果 → 本地结合敏感信息执行”,这个分法更适合隐私要求高的场景。
实际流量分配比例大概是:七成简单任务走本地小模型,比如意图识别、实体抽取、日程格式化,两成需要大模型能力的任务走云端,剩下一成复杂任务才做完整的多轮推理。这样既降低成本,也降低敏感数据的暴露面。
5.2 本地/云端的请求拆分逻辑
我给你演示一个混合处理的具体例子。用户说“帮我整理一下昨天跟某位设计师讨论的修改意见,发一份摘要到群里”。这个请求到了 Agent 后,处理流程是这样的:
第一步,本地意图分析器判定:这涉及“消息记录读取”“内容总结”“消息发送”三个子任务。第二步,本地过滤模块从聊天记录里提取原始内容,把涉及项目名的敏感片段做脱敏处理,脱敏后发送到云端总结接口。第三步,云端只看到脱敏后的文本,返回“修改意见摘要”。第四步,本地模块把脱敏片段还原,生成最终消息,再调用通讯工具的接口发送。整个过程中,敏感字段始终没有离开本地。
这里有个反向思考:如果任务需要用户私人知识作为背景,而这些知识在本地记忆库,云端又没有权限访问,怎么办?我的做法是把本地记忆库先做一层“可共享化”处理:并非所有记忆都能上云,只有经过隐私分级器判定为“低敏感”的记忆可以打包成上下文送出去。高敏感记忆(比如医疗记录、财务信息)只会保留在本地,模型如果需要这些信息,就用本地小模型先压缩成抽象描述,只把抽象描述传给云端。
5.3 断网降级与多端同步的考量
既然系统同时依赖本地和云端,就一定要考虑断网场景。我之前遇到过网络波动,云端接入超时,Agent 整体“卡死”。后来设计了降级策略:当云端不可用时,本地小模型负责完成简单任务,比如查日程、写会议记录模板、读取本地文件。大模型生成类任务暂时进入队列,网络恢复后重试。同时提供显式的“云端欠费/断网”提示,让用户知道目前 Agent 处于受限状态。
多端同步是另一个容易被低估的点。手机、电脑、平板如果各自一套记忆库,日子久了就分裂了。我的方案是以本地设备为主节点,记忆变更通过加密日志实时同步到其他设备,这个同步过程与其他设备上的本地小模型绑定,不支持云端中转。所以整套系统的同步只在自己控制的设备间进行。多设备之间权限策略也要保持基线一致,比如在电脑上禁止发送邮件的权限,在手机上同样禁止,不因为换了设备就能绕过限制。
设计经验:任何涉及个人数据的架构,安全性不能只靠“云端不存储”。真正的安全边界应该默认放本地方向。每个跨设备同步请求,都先想一遍:这条数据该不该离开当前设备;如果必须离开,是否做了加密;接收方有没有权限落地数据。把这些问题的答案写死成校验规则,而不是每次临时判断。
6. 日志与审计:AI Agent 做过的每件事都要留痕
6.1 完整审计日志的重要性
AI Agent 一旦能操作真实数据、发送消息、跨 App 行动,它的每个动作都可能有真实后果。所以“留痕”不是可选项,而是架构的必要组件。我做了一个本地审计日志模块,记录四个维度:谁发起、什么动作、读了什么数据、产生了什么影响。
有人会觉得这个设计多此一举,真正用起来才发现价值。有一次 Agent 在整理通讯录时误把一个联系人分组改掉了,如果没有审计日志,用户根本不知道发生了什么。另外,当记忆系统出现错误弹出一堆不相干的信息,审计日志能快速追溯是哪一条记忆在何时被写入、由哪次交互引起。所有动作要挂在时间线上,窜改不了。
6.2 交叉验证与异常检测
审计日志不只是给用户看的,我还做了一个交叉验证机制:定期拉取 Agent 实际执行的操作与权限策略授权记录做比对。一旦发现未授权动作,立刻给用户发送安全告警。这个机制能拦截住不少底层接口的异常调用,尤其是辅助功能路径触发界面操作时可能产生的误点击。
在实际运行中,我用日志数据发现过一个潜在问题:Agent 在访问某个文档工具的接口时,因为应用更新导致了新控件,操作序列被执行到了完全不同的功能区域。从日志里看,这次操作读取的数据范围远超预期。如果没有审计日志,这类越界行为很可能被当一次普通的异常就忽略了。
7. 常见问题与排查技巧实录
7.1 记忆召回不准
这是被问得最多的问题。症状是:用户问“我之前提到过那家店的推荐菜是什么”,Agent 半天答不上来,或者答了一条明显过时的记忆。排查方向:看召回时用的查询条件是否准确。很多时候不是记忆库没存,而是用户口语化的表达没有正确解析成实体。举例:“那家店”这个表达,需要先结合对话上下文锁定是“某家川菜馆”,再去召回,而不是拿“那家店”原样去做相似度检索。
另一个常见原因是多跳关系没做。如果记忆库只是简单的段落切块存向量,那“那家店”这类指代性表达根本没进入索引。我的经验是把实体抽取做扎实,给每个实体建立别名表,比如“那家店”“上次去的那个馆子”“那家川菜”都归一到同一个实体 ID,召回时用实体 ID 搜索。这样才能把记忆系统从“关键字匹配”真正升级成“关系检索”。
7.2 跨 App 操作不稳定
跨 App 操作的两大杀手:一是界面结构变化,二是不定时弹窗。界面变化基本无解,只能靠维护人员定期更新控件定位规则。弹窗问题我可以给一个偏方:在很多环境里,通过辅助功能或脚本接口检测“弹窗是否出现”,然后触发默认按钮,是可行的。我在设计里专门维护了一套“弹窗处理规则表”,每个 App 出现什么类型的弹窗,点击哪个按钮关闭,都提前配好。
更底层的问题还是路径选择。如果一个 App 既有原生接口又有辅助功能路径,我强烈建议优先走原生接口。辅助功能路径由于需要模拟真实操作,反映到行为上天然不够稳定,而且频繁调用可能会被系统或 App 判定为异常操作,触发安全限制。能用接口做的事情,就绝不用辅助功能硬做。
7.3 权限策略过严导致 Agent“变笨”
Agent 权限配置得太宽会危险,配置得太死则让 Agent 什么都干不了。我遇到过这个问题:为了让系统绝对安全,把几乎所有写操作都默认拒绝,结果 Agent 连“帮我改个日程时间”都做不到,用户逐渐失去使用兴趣。
建议的做法是:权限策略分层设计。核心高风险操作(发送消息、删除文件、执行命令)默认拒绝,只允许一次性授权;中风险操作(修改日程、整理笔记)默认允许但立即可撤销;低风险操作(读取标题、搜索文件)默认允许,不用打扰用户。这样既保住了安全底线,又不会让 Agent 显得“太蠢”。
8. 一点实际体会
做完这套架构之后,我最大的感触是:Personal AI Agent 的核心竞争力不在模型能力,而在系统设计。模型就像是发动机,但如果车上没有可靠的转向、刹车、仪表盘和路标,发动机再强也是一台危险机器。记忆分层、权限边界、跨 App 路由、本地云端拆分,这些看似繁琐的东西,恰恰是让 AI 从“玩具”变成“帮手”的关键。
如果你也准备自己搭一套,我的建议是从一个场景开始,比如“帮我在开会前自动整理昨天项目文档的变更”,先把这一个场景涉及的记忆、权限、跨 App 调用全部打通,再横向扩展。不要一开始就想做面面俱到的个人助理,那样大概率只会在记忆混乱和权限纠缠中失去耐心。系统这东西,修修补补永远比推倒重来划算,但前提是最初的架构骨架是健康的。希望这篇文章能帮你少踩几个坑。