ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

手机GUI Agent记忆层设计:事实常驻与技能按需的工程实践

手机GUI Agent记忆层设计:事实常驻与技能按需的工程实践 1. 为什么手机 GUI Agent 需要一个“记忆层”做过手机端 GUI Agent 的人大概都有过这种体验模型第一次帮你点开某个 App 完成一个任务第二次再让它做同样的事它还是像失忆一样从头摸索。每一步都要重新截图、重新理解界面、重新推理下一步点哪里。单次任务跑下来token 消耗惊人延迟也高得让人抓狂。这个问题的本质是 Agent 没有记忆。它每次都在“从零开始理解这个世界”。我在给自己的 Android GUI Agent 加记忆层之前专门统计过一组数据一个典型的“打开外卖 App 搜索某家店并下单”的任务平均需要 18 到 25 步操作每步都要把当前屏幕截图喂给视觉模型做理解单次任务消耗的 token 在 15000 以上。而如果 Agent 能记住“这个 App 的搜索框在顶部”“下单按钮在右下角”这类信息至少能省掉三分之一的探索步骤。但记忆层不是简单地把所有历史操作都塞进上下文就完事了。我踩过的最大坑就是一开始把所有历史记录无脑拼接进 prompt结果上下文迅速膨胀到几万 token模型反而因为信息过载变得迟钝该记住的没记住不该记的全塞进去了。所以后来我重新设计了整个记忆架构核心思路就一句话事实常驻、技能按需用一条统一的写入链把两者串起来。事实类信息比如某个 App 的界面布局特征常驻在系统提示词里技能类信息比如“如何完成一次搜索操作”按需检索注入。两者通过同一条写入链从 Agent 的执行轨迹中自动提取和沉淀。这套方案跑下来同样的下单任务token 消耗降到了 6000 左右步骤数从平均 21 步降到了 13 步而且随着使用次数增加Agent 对常用 App 的操作越来越熟练。下面我把整个设计和实现过程拆开讲。2. 记忆层的整体架构设计2.1 两类记忆的划分逻辑在动手写代码之前我先想清楚了一个问题Agent 到底需要记住什么从实际执行轨迹来看信息可以分成两大类。第一类是事实也就是那些相对稳定、不随任务变化的描述性知识。比如“微信的搜索入口在首页顶部的放大镜图标”“设置 App 的返回按钮在左上角”“这个 App 启动后有 3 秒开屏广告”。这类信息的特点是跨任务复用率高变化频率低适合常驻在上下文里。第二类是技能也就是完成某类任务的操作流程。比如“如何在购物 App 里完成一次搜索”“如何关闭一个弹窗广告”“如何从列表中找到目标项并点击”。这类信息的特点是数量多、按场景触发、不需要一直占着上下文。这个划分直接决定了记忆的存储方式和注入策略。事实常驻意味着它要放在 system prompt 里每次请求都带着技能按需意味着它要存在外部存储里通过检索机制在需要时才注入到当前轮次的上下文中。这里有个容易混淆的点有些信息既像事实又像技能。比如“这个 App 的搜索框在顶部”是事实“点击搜索框然后输入关键词”是技能。我的处理原则是静态描述归事实动态操作归技能。如果一条信息里既有静态描述又有操作步骤就拆成两条分别存储。2.2 为什么不用向量数据库做全量检索很多人一提到记忆层第一反应就是上向量数据库把所有记忆做 embedding 然后语义检索。我一开始也是这么想的但实际跑下来发现两个问题。第一个问题是延迟。手机端 Agent 的每一步操作都需要快速响应如果每次注入记忆都要做一次向量检索哪怕只增加 200 毫秒累积到 20 步的操作里就是 4 秒的额外延迟。对于交互式任务来说这个延迟是能感知到的。第二个问题是精度。向量检索擅长语义相似但 GUI 操作场景下很多记忆的触发条件是高度结构化的。比如“当前前台 App 是某购物 App 且当前页面包含搜索框”这种条件用向量检索反而容易召回不相关的记忆。所以我最终采用的方案是事实直接常驻技能用结构化索引加关键词匹配做检索。技能库用 JSON 存储每条技能带有明确的触发条件App 包名、页面特征、任务类型检索时先按结构化条件过滤再用关键词做二次排序。这样既保证了速度又保证了精度。2.3 一条写入链的设计记忆层的难点不在于读而在于写。怎么从 Agent 的执行轨迹中自动提取出值得记住的事实和技能这是整个系统能不能持续进化的关键。我的写入链设计是这样的每次任务执行完成后把完整的操作轨迹包括每步的截图描述、操作类型、操作目标、执行结果送入一个蒸馏模块。这个蒸馏模块本身也是用大模型做的通过精心设计的提示词让它从轨迹中提取出两类输出新发现的事实和可复用的技能。这里的关键是提示词的设计。我试过很多版本最后稳定下来的提示词核心逻辑是让模型先判断这次任务是否成功成功的任务才值得提取技能然后让模型区分哪些信息是跨任务稳定的事实哪些是本次任务特有的操作序列。提取出来的事实和技能分别写入对应的存储事实更新到常驻区技能追加到技能库。3. 事实常驻层的实现细节3.1 事实的存储结构事实层我用的是一棵简单的树形结构按 App 包名做一级索引下面挂这个 App 的各种事实条目。每条事实包含几个字段事实内容、置信度、最后验证时间、来源任务 ID。{ com.tencent.mm: { facts: [ { id: fact_001, content: 搜索入口位于首页顶部右侧的放大镜图标, confidence: 0.95, last_verified: 2025-01-15T10:30:00, source_task: task_20250115_001 }, { id: fact_002, content: 启动后有约2秒的开屏广告需等待或点击跳过, confidence: 0.88, last_verified: 2025-01-14T20:15:00, source_task: task_20250114_003 } ] } }置信度这个字段很重要。不是所有提取出来的事实都靠谱有些可能是模型误判有些可能因为 App 版本更新而过时。我给每条事实设了初始置信度 0.7每次被成功验证即 Agent 依据这条事实操作成功就加 0.05上限 0.99如果依据这条事实操作失败就减 0.15。置信度低于 0.5 的事实会被标记为待验证低于 0.3 的直接淘汰。3.2 事实如何注入 system prompt事实常驻不等于把所有事实都塞进去。我的做法是按当前任务涉及的 App 做过滤只注入相关 App 的事实。具体来说Agent 在接到任务后先做一次任务解析识别出可能涉及的 App 列表然后从事实库里捞出这些 App 的事实按置信度排序取前 N 条N 根据 token 预算动态调整一般不超过 15 条注入到 system prompt 的一个专门区域。注入格式我试过几种最后发现用简洁的自然语言描述效果最好不要用 JSON 或表格模型对自然语言的理解更准确。比如你已知的关于当前App的信息 - 搜索入口位于首页顶部右侧的放大镜图标 - 启动后有约2秒开屏广告 - 底部导航栏有首页、发现、通讯录、我四个标签实操心得事实描述要尽量具体避免模糊表述。“搜索框在顶部”不如“搜索框在首页顶部右侧是一个放大镜图标”来得有用。前者模型还要猜后者直接就能定位。3.3 事实的更新与淘汰机制事实不是写完就不管了。App 会更新界面会变化旧的事实会失效。我的更新机制分两种主动更新和被动更新。主动更新是在每次任务执行前如果任务涉及的 App 有超过 7 天未验证的事实就先跑一个轻量的验证流程——让 Agent 打开这个 App截图然后让模型判断已有事实是否仍然成立。这个流程消耗不大但能有效防止用过时事实指导操作。被动更新就是前面说的置信度机制。每次操作成功或失败都会反馈到相关事实的置信度上形成自动的优胜劣汰。4. 技能按需层的实现细节4.1 技能的粒度控制技能粒度是设计中最难拿捏的部分。粒度太粗比如“完成一次购物”作为一个技能那它跟直接执行任务没区别没有复用价值粒度太细比如“点击坐标 (100, 200)”作为一个技能那它跟具体屏幕分辨率绑定换个设备就废了。我最后确定的粒度是原子操作序列一个技能对应一个语义完整的操作单元通常包含 2 到 5 步操作。比如“在搜索框中输入关键词并提交”是一个技能“从弹窗中关闭广告”是一个技能“在列表中滑动找到目标项并点击”是一个技能。这个粒度下技能既有复用价值又不会跟具体任务绑定太死。一个复杂任务可以被拆解成多个技能的串联Agent 在执行时按需调用。4.2 技能的触发条件设计每条技能都带有明确的触发条件这是实现精准检索的关键。触发条件包含三个维度App 维度这条技能适用于哪个或哪些 App。有些技能是通用的比如“关闭弹窗”有些是 App 特有的。页面维度这条技能适用的页面特征。比如“页面包含搜索框元素”“页面标题为‘订单确认’”。任务维度这条技能适用的任务类型。比如“搜索类任务”“支付类任务”。检索时Agent 根据当前的前台 App、当前页面截图描述、当前任务类型三个维度同时匹配召回符合条件的技能。匹配度用加权分数计算App 维度权重最高页面维度次之任务维度最低。4.3 技能库的存储与检索技能库我用的是 JSON 文件加内存索引的方式。JSON 文件持久化存储Agent 启动时加载到内存构建倒排索引加速检索。{ skill_id: skill_search_001, name: 在搜索框中输入并提交, trigger: { apps: [com.tencent.mm, com.taobao.taobao], page_features: [包含搜索框, 包含输入框], task_types: [search, query] }, steps: [ 定位页面中的搜索框元素, 点击搜索框使其获得焦点, 输入目标关键词, 点击提交按钮或按回车键 ], success_count: 23, fail_count: 2, avg_steps: 4 }检索时先按 App 维度过滤再按页面特征做关键词匹配最后按任务类型排序。召回 top 3 的技能注入到当前轮次的上下文中。注入时只注入技能名称和步骤概要不注入完整细节避免占用过多 token。5. 写入链的完整实现5.1 轨迹采集与预处理写入链的输入是 Agent 执行任务的完整轨迹。轨迹采集从 Agent 启动任务时开始每一步操作都记录以下信息时间戳、操作类型点击/输入/滑动/返回等、操作目标元素描述或坐标、操作前的页面截图描述、操作结果成功/失败/未知。轨迹采集的关键是页面截图描述的质量。我用的是一个视觉模型对截图做结构化描述输出包括当前 App 包名、页面标题、可见的交互元素列表、页面布局特征。这个描述质量直接决定了后续蒸馏的效果。预处理阶段会把原始轨迹整理成结构化的 JSON去掉冗余信息比如连续多次滑动只保留一次标注出关键节点任务开始、任务成功、任务失败、遇到异常。5.2 蒸馏提示词的设计蒸馏模块是整个写入链的核心它决定了什么信息会被记住。我用的蒸馏提示词经过多次迭代核心结构如下你是一个GUI操作轨迹分析专家。请分析以下Agent执行轨迹提取出两类信息 1. 事实跨任务稳定的描述性知识。判断标准这条信息在下次执行类似任务时仍然成立且不依赖于具体任务目标。 2. 技能可复用的操作序列。判断标准这个操作序列在类似场景下可以被重复使用且不绑定具体任务参数。 输出格式要求 - 事实用一句话描述具体到可操作的程度 - 技能包含名称、触发条件、步骤列表 - 只提取本次任务中成功验证过的信息 - 如果任务失败只提取失败原因相关的教训不提取技能 轨迹数据 {trajectory_json}这个提示词的关键在于“判断标准”部分。我试过不给判断标准让模型自由发挥结果提取出来的东西要么太泛“这个 App 可以用”要么太具体“点击了坐标 350, 800”。给了明确的判断标准后提取质量明显提升。5.3 写入冲突的处理写入链会遇到冲突新提取的事实和已有事实矛盾或者新技能和已有技能重复。我的处理策略是事实冲突时比较置信度和时间。新事实置信度更高且时间更新就替换旧事实否则保留旧事实把新事实标记为待验证。如果两条事实置信度接近但内容矛盾就都保留在注入时都带上让 Agent 在实际操作中验证哪条正确。技能重复时比较步骤相似度。如果新技能和已有技能的步骤相似度超过 80%就合并——更新已有技能的触发条件取并集增加成功计数。如果相似度在 50% 到 80% 之间就作为独立技能保留但在检索时做去重。6. 常见问题与排查技巧6.1 记忆注入导致上下文超限这是最常见的问题。事实和技能都注入后system prompt 可能膨胀到几千 token加上对话历史和截图描述很容易超过模型的上下文窗口。我的解决方案是动态预算分配。给记忆注入设一个 token 上限比如 1500 token事实和技能各分一半。如果事实超过预算按置信度排序截断如果技能超过预算按匹配分数排序截断。同时监控实际使用量如果连续多次任务都触发了截断就说明记忆库需要做一次清理和压缩。6.2 技能检索召回不相关有时候 Agent 会召回一些看起来相关但实际上用不上的技能浪费上下文空间。排查这类问题我一般按以下顺序检查排查项检查方法常见原因触发条件是否过宽查看召回技能的 trigger 字段apps 列表包含太多 App页面特征是否模糊对比当前页面描述和技能页面特征特征词太泛如“包含按钮”匹配权重是否合理检查 App/页面/任务三维度权重任务维度权重过高导致误召回技能本身是否过时查看技能的 fail_countApp 更新导致技能失效6.3 蒸馏提取质量不稳定蒸馏模块的输出质量直接决定记忆库的质量。我遇到过的典型问题包括提取的事实太泛没有操作价值、提取的技能步骤不完整、把失败的操作也提取成技能。解决这些问题的核心是提示词的约束要足够强。我后来在提示词里加了几个硬性约束事实必须包含具体的位置或特征描述技能必须包含完整的起止步骤只提取成功轨迹中的技能。另外我加了一个后处理校验步骤用规则过滤掉明显不合格的输出比如步骤数少于 2 的技能、不包含任何位置信息的事实。避坑技巧蒸馏模块本身也会消耗 token如果每个任务都跑一次全量蒸馏成本不低。我的做法是只对成功任务做蒸馏失败任务只提取教训类事实。另外对于高度重复的任务类型可以设置蒸馏频率限制比如同一 App 同一任务类型每天最多蒸馏 3 次。6.4 记忆库膨胀导致检索变慢随着使用时间增长记忆库会越来越大检索速度会下降。我的经验是当技能库超过 500 条时就需要做一次压缩。压缩策略是合并相似技能、淘汰低成功率技能、归档长期未使用的技能。具体操作上我写了一个定时任务每周跑一次记忆库维护。维护内容包括重新计算所有技能的相似度矩阵合并相似度超过阈值的技能淘汰 fail_count 超过 success_count 且最近 30 天未使用的技能把 90 天未使用的事实标记为归档不再注入但保留在存储中。7. 实际效果与后续优化方向这套记忆层跑在我的 Android GUI Agent 上已经有一段时间了。最直观的变化是常用 App 的任务执行步骤数平均下降了 35% 到 40%token 消耗下降了约 55%。更重要的是Agent 的行为开始有了“熟练感”——第一次做某个任务可能磕磕绊绊第三次第四次就明显流畅了。不过也有做得不够好的地方。目前事实和技能的提取还是依赖大模型蒸馏偶尔会有误提取。我下一步想尝试的是引入一个轻量的验证机制让 Agent 在执行任务时顺便验证一两条已有记忆形成一个持续的自我校验循环。另外跨 App 的通用技能提取目前效果一般很多技能还是绑定在具体 App 上这块的泛化能力还有提升空间。如果你也在做类似的手机端 Agent我的建议是先把写入链跑通哪怕提取质量一开始不高也没关系重要的是让记忆流动起来。有了流动才有优化的基础。事实常驻和技能按需这个框架本身不复杂难的是让它在真实使用中持续沉淀出有价值的记忆。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表