做了快两年移动端 GUI Agent,我最大的感受是:这个方向看着热闹,真正跑过一遍端到端的人才知道坑有多深。Fairy 这个内部项目就是我们团队从 0 到 1 的一次完整实践,核心是把 RGR、OCA、EMA 三个模块组合到一起,解决“手机屏幕上的智能体到底怎么可靠地看懂界面、规划操作、并持续变强”这个问题。如果你也在做移动端 AI 自动化、UI Agent 或类 RPA 的方向,这篇内容我把整个设计思路、落地细节和踩过的坑都梳理出来,希望能帮你少走几条弯路。
先说清楚 Fairy 能做什么。给它一个自然语言任务,比如“帮我把这个 App 的深色模式打开,再截个图发给我”,它能自己完成看屏幕、找按钮、点按、滑动、输入、校验结果这一整套流程。相比传统的基于坐标和控件的自动化脚本,Fairy 的优势在于不依赖 App 是否留了接口,也不要求每个页面都有可读的 View 层级,它更接近一个“长了眼睛和手的 AI 助理”,而不是写死的规则引擎。这套能力适合三类读者参考:正在做移动端自动化测试的工程师、研究 GUI Agent 方向的同学,以及想把大模型能力落到真实手机操作场景的产品团队。
1. 项目背景与整体设计:为什么移动 GUI Agent 这么难
1.1 移动端 GUI 自动化的痛点到底在哪
很多人一开始觉得“让 AI 操作手机”不就是把截图喂给大模型,再让它输出一个坐标点吗?真做起来完全不是这么回事。移动端 GUI 的环境复杂度远超 Web 页面,我大概归纳成四类核心痛点。
第一是界面信息获取不完整。原生 Android 页面还能通过uiautomator dump或者无障碍服务拿到控件树,但一旦碰到 Flutter、React Native、小程序、游戏引擎或者混合 WebView,控件树要么拿不到,要么拿到一堆没有语义的乱码节点。我见过最夸张的情况是一个 H5 页面 dump 出来的 XML 有四千多个节点,其中大量是无意义的 ViewGroup,真正能点的按钮在树里根本找不到对应关系。第二是视觉元素高度碎片化。同样是“确定”这个按钮,在不同 App、不同手机、不同深色模式下长得完全不一样,更别提那些只有图标没有文字的功能按钮,光靠 OCR 根本认不出来它是什么。第三是动态交互的干扰,弹窗、Toast、广告浮层、权限请求、开屏广告,随时可能插到当前页面上,把原本的界面状态完全打乱。第四是端侧资源约束,手机不是 GPU 服务器,图像推理、大模型推理都要在有限的算力和内存里完成,延迟稍微高一点,用户就会觉得这个 Agent 是个玩具。
1.2 Fairy 的整体架构与三个核心模块的关系
针对上面这些问题,Fairy 在架构上从一开始就没有选择“一个超大模型包打天下”的路线,而是拆成了三个模块各管一段,这也是标题里 RGR、OCA、EMA 三个词的由来。
RGR(Reliable GUI Recognition)负责“看”,也就是把屏幕截图转成结构化的界面状态描述,输出当前页面有哪些可交互元素、每个元素的类型、文本内容、位置和置信度。OCA(Operation Chain Agent)负责“想”,拿着 RGR 输出的界面状态,结合用户的任务目标,规划出一串原子操作并逐步执行。EMA(Exponential Moving Average)负责“学”,一个层面是模型训练时对权重的指数滑动平均更新,让策略参数更稳定;另一个层面是对历史经验样本做指数加权,让 Agent 在使用经验时更看重近期成功的轨迹、弱化过时旧经验。
三者之间的关系是一条非常清晰的流水线:屏幕截图先进 RGR,RGR 产出的结构化状态给 OCA 做规划,OCA 执行操作后产生新的截图和反馈,这些轨迹数据再经过 EMA 机制筛选和加权,用于迭代更新 RGR 的识别模型和 OCA 的策略模型。也就是说,RGR 和 OCA 是“前台干活”的,EMA 是“后台成长”的引擎。
1.3 为什么把识别、规划、更新拆成三个模块
这个决策当时在组里是有过争论的。有人主张直接端到端训练一个视觉-语言-动作模型,输入截图和任务输出动作,看着更简洁。但拆开做有三个非常实际的好处。
第一是故障定位清晰。线上任务失败时,我们能快速判断是识别错了、规划错了还是执行环境的问题。如果出问题的是 RGR,比如界面元素漏检,那我们只需要补识别数据;如果出问题的是 OCA,比如规划了非法操作,那要调整的则是规划策略。这种可分离性在实际维护里省了太多时间。第二是成本可独立优化。识别模型用轻量级模型跑在端侧,规划模块可以用中等规模的大模型跑在服务端,两者升级互不阻塞,不会因为某个模块变大而拖垮整个链路。第三是评估指标更细。拆开之后可以分别统计识别准确率、规划合理率、执行成功率,如果只用一个端到端成功率,根本说不清瓶颈到底在哪里。这一点在向老板汇报项目进展时尤其重要,拿着分模块的数据和拿着一个笼统的准确率,说服力完全不一样。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. RGR 可靠GUI识别:从层级解析到视觉理解的路线
2.1 识别层的核心挑战和设计目标
RGR 的目标不是“认出所有的 UI 元素”,而是在给定任务上下文的前提下,把当前屏幕中“跟任务相关的可交互元素”准确地找出来,并且给出可靠的空间坐标和文本信息。这跟传统 CV 里的通用目标检测有明显区别,UI 元素检测更强调语义完整性、文本准确性和可点击区域的定位精度。
设计目标我定了几条硬性指标,都是基于实际操作需求来的:
- 文本识别:对按钮、输入框、列表项内的文本要有较高的召回率,尤其要避免把“下一步”识别成“下—步”这类影响语义的错误。
- 元素定位:给出的点击坐标必须落在可交互元素的中心区域,坐标偏移超过元素尺寸 20% 就算不合格。
- 置信度分级:每个识别到的元素必须带一个置信度分数,低置信度元素不能参与规划,宁可不点也不能乱点。
- 延迟要求:在主流中端手机上,一次完整页面识别时间控制在 300ms 以内,否则用户体验会明显卡顿。
2.2 RGR 的落地方案:视觉为主、层级为辅、意图引导候选区
先说结论:RGR 最终采用的方案是“视觉为主、层级为辅、任务意图引导候选区”。
视觉为主的意思是,主要依赖对截图做目标检测和 OCR,而不是优先依赖控件树。原因前面提到过,H5、Flutter 等场景拿不到可用层级,而视觉方法是通用的。具体实现上,RGR 内部包含一个轻量级 UI 元素检测器、一个 OCR 引擎和一个图标语义分类器。
流程大致是这样的:手机屏幕截图先做一次全图预处理,包括分辨率归一化、亮度均衡和去噪;然后把处理后的图像交给 UI 元素检测器,检测模型输出一组候选框,每个框带有元素类型(按钮、输入框、滑动条、列表项、图标等)和置信度;接着对这些候选框区域并行做 OCR,提取文本内容;对没有文本的图标区域,再交给图标分类器判断语义,比如“齿轮”代表设置、“放大镜”代表搜索,“房子”代表主页。最后,把所有元素合并去重,加上任务文本嵌入,对元素做相关性排序,生成一个带优先级的 GUI 状态快照交给 OCA。
层级为辅体现在什么地方?当系统确实提供了可读的 View 层级信息时,RGR 会用层级数据来校验视觉检测结果。比如层级里标注了一个按钮,视觉模型也在附近检测到按钮,那这个元素的置信度会被提高;如果只有一边检测到,置信度就相应打折。这种“视觉 + 层级互为印证”的做法,比单独依赖任何一种方式都稳。
任务意图引导候选区是一个很有意思的优化。实际任务中,Agent 根本不需要识别屏幕上所有元素,很多任务只关心某几个区域。比如任务是“修改密码”,那重点区域就是“当前密码”“新密码”“确认密码”几个输入框和“保存”按钮,页面底部的 Tab 栏和广告位基本可以忽略。RGR 会把任务文本和历史操作中涉及的关键词作为提示,输入到一个轻量级区域定位网络,先生成可能存在相关元素的候选区域,再对这些区域做精细检测。这个设计直接减少了无效计算量,也让 OCR 的负担大幅降低。
2.3 识别质量的评测与提升手段
为了让 RGR 的迭代有据可依,我们建了一套离线评测集,覆盖 20 个主流 App 的 500 个任务场景,每张截图都有完整的人工标注:可交互元素框、元素类型、文本内容、任务相关性标记。
评估指标用了 mAP(元素检测)、文本编辑距离(OCR 质量)和“任务相关元素召回率”。后面这个指标其实是整套系统中最能预测线上成功率的,它统计的是“给定任务时,RGR 是否正确召回所有完成任务必需的元素”。很多时候 Agent 任务失败,不是因为规划模型笨,而是因为 RGR 漏掉了一个关键的“确认”按钮,导致规划模型根本不知道这个按钮存在。
提升识别质量的手段里,最有性价比的是数据增强。移动端截图有一种特有的噪声模式,比如 Toast 弹窗会短暂覆盖局部区域、深色模式会改变整体对比度、折叠屏的半屏显示会让元素被截断。我们针对这些情况专门制作了增强策略,在训练时随机叠加半透明遮罩层、模拟局部遮挡、调整色温,模型对这些干扰的鲁棒性提升非常明显。
注意:不要对整张截图做随机的几何变换增强,比如旋转和拉伸。UI 元素的位置和文本方向高度规则化,过度几何增强反而会让模型学会不真实的特征,在真实截图上性能下降。
3. OCA 操作链智能体:任务分解与执行闭环
3.1 从“一句话需求”到原子操作链
OCA 的输入是用户任务文本和 RGR 输出的当前界面状态;输出是一步步的原子操作。这里我们严格定义了 8 种原子操作,足够覆盖绝大多数移动端交互场景:tap、long_press、swipe、input_text、scroll、back、wait、open_app。所有更复杂的动作都由这 8 种组合完成。
OCA 内部先由大语言模型对任务做意图解析和步骤规划,生成一个“操作链草案”。这个草案类似伪代码,每一行包含操作类型、操作对象描述、输入参数和执行条件。例如“打开深色模式”这个任务,草案可能是这样的:
text复制1. open_app("设置")
2. wait(1.5s)
3. 查找元素 "显示与亮度"
4. tap("显示与亮度")
5. wait(1s)
6. 查找元素 "深色模式"
7. tap("深色模式")
8. wait(0.8s)
9. 校验截图,确认深色模式已开启
这里每一步都对应 RGR 输出的界面元素描述。注意第 9 步“校验”,这是操作链里非常重要的一个环节。没有校验的 Agent 就像闭着眼开车,很容易点到错误按钮后继续往下执行,最后任务失败了都不知道哪一步错的。OCA 要求每个任务至少有一个终止条件校验节点,要么是视觉校验(界面状态发生变化),要么是文本校验(出现了预期的提示信息),要么是截图对比校验。
3.2 操作链的复用与记忆机制
直接用大模型临时规划每一步操作,成本太高,而且每轮都要调用模型推理,延迟和费用都不可控。所以 OCA 实现了一个“操作链模板库”,把高频任务的操作序列沉淀成模板。
举个例子,“登录”这个任务在大部分 App 里的流程都是:打开 App → 点击“我的”→ 点击“登录”→ 输入账号 → 输入密码 → 点击“登录”。OCA 把这类通用流程做成模板,新任务进来时,先在模板库里做检索匹配,如果能匹配到模板,就只需要做参数填充和条件适配,不需要每次都完整地走大模型规划。
匹配不到模板的任务,才动用大模型做一次规划,并且规划成功后,这条操作链会经过一个“经验沉淀”流程存入模板库。经过一段时间,模板库会越来越大,任务的响应速度和稳定性都会明显上升。需要注意模板库不能无限膨胀,要有淘汰机制。我们每两周会做一次模板质量评估,把使用频次低或者失败率高的模板降权甚至清除。
3.3 执行回环与失败重试策略
OCA 执行操作不是一条路走到黑,每一步执行完都会拉取新的界面状态,跟执行前对比,确认操作是否生效。
比如规划师规划“tap 某个按钮”,手指点下去之后,截图里的界面没有发生任何可能的变化,那这个操作基本可以判定为失败。这时 OCA 会进入重试逻辑:第一次重试会稍微调整点击位置,排除点击偏移问题;第二次重试会重新调用 RGR 做一次完整识别,排除漏检和被遮挡的可能;如果还是失败,就标记该步失败,并且根据剩余步骤是否还依赖这个操作结果,决定是跳过、回退上一步还是终止整个任务。
重试参数是我们反复调出来的:
- 任务级最大重试次数:默认 3 次。
- 单步最大重试次数:默认 2 次。
- 每一次重试之间加一个 0.5 秒到 1 秒的随机间隔,避免在 App 响应慢时造成连点风险。
- 连续三次重试触发“人工接管”事件,系统会保存整个轨迹日志并通知维护人员。
这套策略下来,线上任务的失败率比最初的无重试版本降低了大约 40%,而且“瞎点乱撞”的情况少了很多,因为重试也不是无脑重来,每一步都有明确的校验依据。
4. EMA 指数滑动平均:权重更新与经验衰减的工程实现
4.1 权重 EMA:让策略更新更稳
EMA 这个名字容易让人想到股票软件里的指数移动平均线,虽然公式接近,但在 Fairy 里它干的是完全不同的活。我们用它做了两件事,第一件就是训练深度学习模型时的权重指数滑动平均。
模型训练时,每一轮 batch 的梯度更新都可能带来参数抖动,尤其在使用强化学习或者偏好优化时,一个 batch 的异常数据可能让策略参数瞬间跑偏。权重 EMA 的思路是维护一份“影子权重”,它不是直接等于最新权重,而是用指数滑动的方式缓慢跟随最新权重:
text复制shadow_weights = decay * shadow_weights + (1 - decay) * current_weights
推理时使用 shadow_weights 而不是 current_weights。这样即使当前权重在某个 batch 中被更新得比较激进,影子权重也会平稳很多。我见过有些项目拿到了一个很“跳”的当前权重跑线上评估,结果成功率忽高忽低,一直找不到原因,最后才发现是评估时没启用 EMA 权重。
decay 值的选择有一些讲究。我们的经验是:
- 偏保守的训练场景用 0.999,这个值会让影子权重非常平滑,适合大规模离线训练。
- 需要快速适应新环境时用 0.99,适合做在线增量微调。
- 不要小于 0.98,否则影子权重和当前权重几乎没区别,EMA 就失去意义了。
4.2 经验 EMA:让历史经验有“保鲜期”
EMA 在 Fairy 里的第二个作用,是给历史经验库里的每个样本做一个“新鲜度指数”。OCA 沉淀下来的操作链模板、RGR 积累的困难样本、线上任务保存的成功轨迹和失败轨迹,都会进入经验库。但经验库不是简单堆数据,每个样本都带有一个分数,这个分数用指数加权的方式随时间和使用情况衰减。
具体实现上,我们给经验样本定义一个 quality_score,初始值来自人工标注或者任务结果的强反馈。每过一段时间或者每被调用一次,分数按下面的规则更新:
text复制quality_score = alpha * reward + (1 - alpha) * quality_score
其中 alpha 是学习率,reward 来自本次调用的任务结果。如果这个样本被成功复用了,它的分往上抬一点;如果复用后任务失败了,它的分就会被往下打。
更关键的是时间衰减。我们经验库不会真的把旧样本直接删除,会让它的 score 随时间缓慢衰减。这就是 EMA 的“保鲜期”含义:过去成功的操作,对当前任务的参考价值会随着 App 更新、界面改版而降低。一个三个月前非常管用的按钮定位规则,今天很可能已经失效了,如果它还在高分区间占着位置,就会误导 OCA 的决策。
为了直观理解这个机制,可以把经验库想象成餐厅的推荐菜单。今天很多人点招牌菜,这个菜在菜单上的位置就会上升;连续一周没人点,它会慢慢沉到后面;如果某道菜因为食材变化导致大量差评,它的分数会快速跌到冰点。EMA 干的就是给菜单做动态排序这件事。
4.3 两个 EMA 的配合与调参经验
权重 EMA 和经验 EMA 虽然都叫 EMA,但使用场景完全不同。权重 EMA 是为了让模型参数更新平稳,防止训练震荡;经验 EMA 是为了让 Agent 的经验选择更偏向近期有效样本,防止策略僵化。两者配合时,有一条非常重要的经验:它们必须独立调参,不能共用一个 decay。
我们在一个版本里曾经为了省事,把两个 decay 都设成了 0.99,结果训练稳定了但经验更新明显滞后。模型权重已经适应了新任务,经验库还在推荐旧操作链,两个模块打架,任务成功率反而下降了。后来把权重衰减设成 0.999,经验衰减的 alpha 调成 0.2,才恢复正常。
经验 EMA 的 alpha 取值跟线上流量有直接关系。如果一天只有几百条任务日志,alpha 设太大就会让分数波动剧烈,一个偶然的失败就能把好经验彻底打死;如果一天有几万条日志,alpha 设太小就会出现经验更新跟不上 App 版本迭代的问题。通常我们建议在 0.1 到 0.3 的范围里根据流量调整,流量大就取大一点,流量小就取小一点。
提示:权重 EMA 的推理模型要定期做“解冻验证”。也就是每隔一段时间,用当前权重直接推理一批数据和用影子权重推理做对比,如果两者差异长期为零或者差异过大,都需要检查衰减率设置是否合理。
5. 综合落地实操:从环境准备到评测上线的完整流程
5.1 环境与工具选型
Fairy 在开发阶段的实验环境是 Android 模拟器和真机混合使用的。模拟器负责大规模的并行数据采集,真机负责验证模拟器上跑不出来的边缘场景,比如折叠屏、全面屏手势、不同厂商 ROM 的权限弹窗差异。
工具链上有几个非常值得推荐的基础设施:
- 设备控制层面:用 ADB 作为底层设备通信,配合 minicap 做高速截屏,minitouch 做多点触控注入。相比直接用 ADB shell input tap,minicap 和 minitouch 的延迟低很多,是 GUI Agent 操作手感的关键。
- 数据标注层面:用 Label Studio 自定义了一套 UI 元素标注模板,支持画框、填文本、选类型,标注效率比通用标注工具高出一截。
- 模型训练层面:检测模型用 PyTorch,OCR 用 PaddleOCR 的移动端模型做微调,图标分类用了一个轻量级 ResNet 变体。规划模型基于开源的 7B 级大模型做 LoRA 微调。
这套选型的原则是:能用现成工具解决的问题,坚决不自己重复造轮子。团队的精力应该放在 Agent 系统的逻辑和服务编排上,而不是从零训练一个 OCR 模型。
5.2 数据集构造与标注策略
数据是 GUI Agent 项目里最容易被低估的部分。Fairy 初期犯过一个错误:直接拿通用目标检测数据集的结构去标注 UI 元素,结果模型线上表现很一般。后来我们总结出一条核心经验:标注必须紧贴任务场景,脱离任务上下文标注出来的 UI 数据集价值会大打折扣。
最终的数据集构造分成三个部分。第一部分是静态页面数据集,覆盖 20 个 App 的核心页面和设置页,每屏标注所有可交互元素,这部分用来训练底层的 UI 元素检测能力;第二部分是任务导向数据集,一个任务对应一系列截屏序列,每张截屏只标注跟任务相关的元素和操作路径,这部分用来训练任务意图引导和操作链生成;第三部分是困难样本数据集,专门收集线上失败时的截图,包括各种弹窗、H5 混合页面、深色模式下的低对比度截图,这部分是提升模型鲁棒性的关键。
标注策略上,第一部分的静态页面可以走半自动化流程,用成熟的 OCR 先跑一遍文本标注,再人工校对;第二部分的任务导向数据需要标注人员真正理解任务目标,我们会给标注人员提供任务说明,让他们先在自己手机上操作一遍,再按照操作路径标注,这样标注出来的元素相关性和操作顺序才是准确可靠的。
5.3 训练、端侧部署与性能优化
模型训练过程分成两步。第一步用静态页面数据集训练 UI 元素检测器,这一步属于通用能力预训练,训练成本相对可控;第二步用任务导向数据集做端到端微调,让检测器学会“关注跟任务相关的元素”,这一步对最终效果的影响非常大。
部署上,RGR 的检测器需要直接跑在手机端,所以我们做了比较重的轻量化处理。图片输入分辨率从全屏压缩到最小边 640 像素,模型用 INT8 量化后体积从 40MB 降到 12MB 左右,单次推理在骁龙 8 系手机上大约需要 120ms,OCR 大约 80ms,图标分类 30ms,整体加起来在 300ms 以内的目标刚好达成。
OCA 的规划大模型没有放在手机端,而是通过内部服务对外提供接口。这样做的原因是,7B 级模型在手机上推理速度还不够理想,而规划模块对延迟的敏感度相对低一些,只要能在一秒内返回操作链,用户感知差异不大。识别放端侧、规划放服务端这种“混合部署”模式,是我们综合了体验、成本和隐私之后的折中方案,也是目前做移动端 Agent 相对主流的选择。
5.4 线上评测指标设计
上线前评测指标必须设计完整,不然根本不知道 Agent 是好是坏。Fairy 的评测体系分五个维度,每个维度都有独立的统计口径:
- 任务完成率:所有测试任务中成功完成的比例,这是最核心的北极星指标。
- 操作合规率:操作没有点击无关区域、没有误触危险控件的比例。合规率能反映 Agent 是否“懂事”,一个靠乱点碰运气完成任务的 Agent,完成任务率再高也不能放线上。
- 平均步骤数:完成任务所需的实际操作步骤数,对比人工操作的步骤数,可以判断规划是否高效。
- 平均耗时:从收到任务到完成校验的总时长,包括模型推理时间和设备执行时间。
- 稳定通过率:同一任务连续执行 5 次全都成功的比例。手机上随机因素很多,单次成功并不代表稳定,像“极速”“领取”这类会变化的控件和动画过渡,都会造成偶发失败。
同时我们要求记录每个任务执行过程的“轨迹回放”。每执行一步,系统都保存当时的截图、识别结果、规划决策、操作动作和模型输出的置信度分数。轨迹回放是排查线上问题最有力的工具,建议任何做 Agent 落地的人都把这件事作为硬性要求。
6. 踩坑实录与问题排查
6.1 高发故障归纳
把项目组遇到的线上问题汇总一下,高频的基本集中在几个类型。
点击位置偏差是出现次数最多的。明明识别到了按钮,但点击下去没有反应。排查后发现常见原因有三个:一是某些 App 的“安全区”和截图的坐标不一致,需要做坐标换算;二是按钮存在“防误触热区”,按钮视觉中心点不可点击,要取元素有效点击区域的中点;三是动画未结束,界面已经变了但截图还是旧帧,需要等待帧稳定后再做点击。
弹窗拦截是第二大类。隐私协议弹窗、更新弹窗、推送授权弹窗、青少年模式弹窗,每一种都能让 Agent 任务中断。我们花了很多精力构建了一套弹窗识别和应对策略,对常见弹窗做了预置处理流程,比如出现隐私协议弹窗就先点“同意”再继续原来的操作。但弹窗这个东西每天都在出新花样,目前还没有一劳永逸的解法,只能靠持续积累和维护。
文本识别错误也会造成连锁反应。OCR 在浅色背景白字、艺术字体、渐变背景上会出错,而一旦一个按钮的文字被识别错,OCA 就会找不到对应元素,任务直接卡住。针对这个问题,我们在 OCR 后面加了一道“文本纠错”层,结合按钮位置的语义上下文纠正明显错误。比如识别出“下—步”而按钮位置在页面底部居中,纠错层会根据上下文推断出应该是“下一步”。
6.2 排查方法与工具链
当一个任务失败时,我最常用的排查路径是:打开轨迹回放 → 找到失败步骤 → 对比该步骤截图和 RGR 识别结果 → 判断是识别问题还是规划问题。
如果识别结果里根本没有目标元素,那就是 RGR 的问题,需要查看该截图有没有进入数据采集,并把它加入困难样本集。如果识别结果有目标元素,但 OCA 没有规划到正确操作,那就是规划的问题,需要查看模型输出的原文和置信度,确认是不是输出格式错误或者理解偏差。
我们内部开发了一个 Web 端的轨迹回放工具,支持按时间轴逐帧回放每一步的截图和操作,还能同步显示 RGR 的识别框、置信度和 OCA 的决策文字。工具本身很简单,但在调试 Agent 时能节省大量时间,强烈建议做类似项目的团队尽早做出来。
6.3 我的调试心得与建议
我最大的心得是:做 GUI Agent 项目,先把“识别”做到足够可靠,再去折腾“规划”和“学习”。很多人一上来就想着用一个大模型把所有环节都包住,最后模型又大又慢,问题还多到无从查起。Fairy 能顺利落地,最关键的一点就是 RGR 的识别结果稳定可靠,OCA 拿到的是一个干净准确的界面状态,规划的成功率自然就高了。
另外,一定要在项目初期就把“人工接管”通道设计好。AI Agent 在真实环境里不可能永远不出错,遇到无法处理的场景,与其让模型强行操作造成误触,不如主动发起人工接管,由人来完成当前步骤。我看到过一些项目为了追求全自动效果而刻意去掉人工兜底,结果一个误触点到了支付相关界面,引发的事故让整个项目差点被叫停。Agent 的边界不是模型决定的,是安全的底线决定的。
还有一点是关于复现的。做 Agent 相关的实验,很多人只记录“成功了”这个结果,但忽略了记录当时的环境状态。手机型号、系统版本、App 版本、网络环境、屏幕分辨率,每一个变量都可能影响最终结果。我们后来要求所有实验都必须附带完整的环境指纹,这才让很多看似“偶发”的问题变得可复现、可排查。
最后再分享一个项目末尾才想明白的道理。Fairy 这个项目最累的部分不是写模型代码,而是维护一个不断变化的世界——App 在更新、手机系统在更新、用户的使用习惯也在变。Agent 系统必须构建自己的数据闭环,让线上每次失败都自动回流成为新的训练数据,让经验库中的每个样本都有清晰的生命周期。EMA 机制就是用来解决后半个问题的,前半个问题的答案,则是把数据回流做成一条贯穿整个系统的默认链路,而不是某一个模块的可选功能。
