如何使用真实环境、轨迹级验证校准奖励、思维链合成等,迭代训练出能在手机上稳定完成任务的GUI-Agent。下面用一个简单例子说明:打开外卖App搜索奶茶。
1 分布与奖励难题
1.1 标注数据的陷阱
✅分布不一致(核心矛盾)
人工标注往往发生在页面稳定、无弹窗、网络正常时才下一个 action,导致训练得到的模型等于活在理想世界。
以搜奶茶为例:标注员在干净环境里录到的是:打开App后首页就有搜索框、点一下就能输入、结果列表立刻出来。但线上真实分布经常是:加载中、骨架屏、弹窗遮挡、权限框、异步刷新:模型就会蒙圈:
- 要么背答案(按训练记忆去点搜索框坐标,但此时被权限弹窗遮住,点到弹窗背后空白);
- 要么乱点(加载中狂点/乱滑),导致状态漂移更严重,越救越偏。
✅屏幕截图只是部分可观测
登录态、页面层级、网络请求进度是隐变量。
还是搜奶茶这个例子:同样是首页截图,已登录可能直接显示搜索框;未登录可能先弹登录/隐私同意;网络慢时搜索后先出现加载中。这些隐变量决定了下一步动作。
因此只靠静态标注复现轨迹很难覆盖真实分布:模型学到的是看到像首页就点某处,而不是先判断是否被弹窗/加载状态影响。
1.2 两个抓手:高置信奖励+思维链
✅Advantage A:高置信奖励
先做轨迹级成功/失败验证,拿到可靠的终局信号,减少噪声。
例:搜奶茶任务的成功不是点对了某一步,而是最后确实进入店铺页并看到商品/价格列表;失败可能是卡在权限弹窗、停在搜索页没出结果、或误入广告页。先把整条轨迹判对/判错,比每一步主观打分更可靠。
✅Advantage B:高质量思维链(CoT)
用更强的Thinking Model把每一步该怎么想(progress/state/effect/self-reflect/verify)合成出来,作为训练数据的一部分,提升泛化和稳定性。
例:模型不只学点搜索框,还学如果出现权限弹窗,应该先处理弹窗;点完要验证键盘/输入框是否出现;若无变化要换策略(等待/返回/关弹窗)。
2 冷启动
2.1 冷启动数据怎么准备
✅少量人工演示轨迹
每条任务 1~3 条即可,覆盖基本动作:打开 app、搜索、滑动、点击列表、返回、输入文字、处理常见弹窗。例:搜奶茶可录 1~3 条:
- 一条无弹窗、网络正常的理想轨迹;
- 一条弹权限框/隐私同意的轨迹;
- 一条网络慢、需要等待加载的轨迹。
✅强烈建议同时记录的字段
- s_t: 截图(必要)
- u_t: UI tree/可访问性节点(可选但很加分)
- a_t: 原子动作(tap/swipe/type/back/launch)
- meta: 时间戳、网络状态、设备分辨率、旋转、包名、Activity、是否在 loading,例:在搜奶茶里,记录是否在 loading能解释为什么点了没反应;记录 Activity 能区分是首页/搜索页/店铺页)
2.2 冷启动训练目标
✅先别上来就RL:先SFT把手眼协调练出来
SFT(监督微调):学最基本的 action grounding(看图点哪、该滑还是该点)。例:看到首页搜索框→点;看到输入法→输入奶茶;看到结果列表→点第一条。
输出尽量结构化(后面 CSRS 和工具执行都吃这个):
{ "state_summary": "...", "action": "TAP|SWIPE|TYPE|BACK|LAUNCH", "args": {...}, "expect": "...(执行后应该看到什么)" }✅冷启动的唯一目标
能在真实手机上连续跑几步不崩,让 rollout 开始转起来。
例:先保证模型能稳定完成打开App→点搜索框→输入→看到结果这几步,哪怕还不够聪明)
3 真实环境Rollout
3.1 把模型丢进真实环境跑
✅在线 Rollout 轨迹采集
让模型在真实手机上执行任务,得到大量轨迹(成功/失败都要)。每条轨迹记录:截图/(可选UI树)/动作/时间/结果页面特征/错误信号。
例:搜奶茶在真实环境中会自然遇到:权限弹窗、推荐流插入、搜索结果延迟、偶发无网:这些都是你最想让模型学会处理的真问题。
✅分拣系统
对轨迹做自动+人工的分拣:成功的可作为正样本(可复现路径、可学习策略);失败的保留为负样本(告诉模型哪些动作会导致哪些后果)。
例:失败轨迹里,弹窗出现后仍点背后区域导致无变化就是高价值负样本;比随便乱点直到超时更有教学意义。
3.2 轨迹建议记录什么
- o_t:截图 +(可选)UI tree
- prompt_t:任务指令 + 历史摘要(避免上下文爆炸)
- cot_t:模型当步的思考(可选:线上不输出,训练时用 thinking model 补)
- a_t:动作类型与参数(原子动作)
- o_{t+1}:执行后截图
- sys:执行器返回码、是否点击命中、是否超时、是否检测到弹窗。例:点击搜索框后 sys 里可记录click_hit=false / keyboard_not_shown,用于后续判定这一步无效)
- terminal:是否结束/失败原因标签(先空着,等验证)
3.3 rollout 的工程要点
✅同步等待
动作后要等页面稳定再截图(否则观测是半帧/过渡态)。
例:点搜索框后应等待键盘出现/输入框聚焦再截屏;点搜索结果第一条后等待店铺页关键控件出现/加载消失。
✅循环检测
相同界面 hash 连续出现 N 次,判定卡死,提前终止并记录失败类型。
例:权限弹窗一直在、模型反复点背后空白,界面 hash 不变:应尽快停下并标记弹窗未处理/无效点击循环。
✅安全护栏
支付/下单/发送消息等高风险动作要拦截或二次确认,否则会采到危险成功。
例:本例只到看到价格列表就结束,避免把去结算/提交订单当作可自动化目标
4 轨迹级验证
4.1 轨迹级验证:高置信奖励来源
✅为什么先判整条 success/failure
GUI任务奖励稀疏:做对几十步才算成功;逐步打分噪声大(弹窗绕路、加载延迟都会污染这一步对不对)。所以先在轨迹层面验证 success/failure,这一步最可靠、噪声最小(RLVR 思路)。
例:搜奶茶的判题点很清晰:末帧是否是店铺页并出现商品/价格相关元素,比去争论第7步滑了一下算不算好更靠谱。
✅验证三段式(从便宜到贵)
规则验证(便宜高精度):是否到达目标页面(关键字/控件出现);是否得到目标产物(价格数值/车票信息/订单页字段)
例:末帧出现商品/加入购物车/¥或某些店铺页特征控件,即判成功。
轻量模型验证(中等成本):输入末帧截图 + 任务目标 + 关键中间产物,输出成功概率 + 失败原因分类
例:判失败类型是卡在权限弹窗搜索无结果误入广告页。
人工复核(贵但兜底):只抽检高价值/高不确定轨迹
例:规则和模型都不确定时才让人看,避免人工逐步打分的高成本。
✅最终标签
Y ∈ {success, failure},可选:c ∈ [0,1] 置信度、fail_type 失败类型
4.2 把终局信号校准到步骤
✅步骤奖励校准
先用 verifier 把整条轨迹判成成功/失败(高置信),再把这份终局结果按每一步的贡献分摊,得到每一步权重 w_t(或逐步奖励 r_t),让模型只强化真正有用的步骤,而不是把绕路/误触也学进去。
✅为什么不能直接整条 reward=1
GUI 轨迹里有大量无关步/绕路步/重复步。不校准会学到没用甚至有害模式(比如无脑刷新、乱滑)。
例:搜奶茶进第一家店铺。一条成功轨迹可能中间误触了一下空白区域,但最后还是成功。如果整条都 reward=1,模型会把误触空白也当成成功经验的一部分去模仿。
4.3 一种可落地的做法
对每条轨迹先做轨迹级验证得到 (Y, c),再对每一步算权重 w_t,最后生成 step reward r_t:
例:任务打开外卖App→搜奶茶→点第一家→看到商品/价格列表,Verifier 在末帧确认到了店铺页,判:Y=success,置信度 c=0.9。步骤奖励校准给每一步权重(示意):
- 关键推进步(打开App/点搜索框/输入/点结果):w_t≈1.0
- 无效步(点空白、被遮挡点击无变化):w_t≈0
- 必要但贡献小的等待:w_t≈0.3
于是成功轨迹的逐步奖励可以是:r_t = c · w_t → r ≈ [0.9, 0.9, 0.9, 0.0, 0.9, 0.27]。这样训练后模型会强化真正推动成功的步骤,不会因为成功轨迹里出现过误触,就学会误触。
4.4 Step 权重来源(可叠加)
- 进度信号:距离目标更近了吗?(例如:首页→搜索页→结果页→店铺页,阶段推进就加权)
- 状态变化有效性:动作后界面有实质变化?(键盘出现/页面跳转/loading消失)无变化则降权
- 风险/偏航惩罚:误入无关页面、触发高风险页面、循环卡死则降权或直接负权
- 自一致性:同一状态下多次 rollout 的动作是否稳定?(不稳定往往是瞎蒙,可降权)
reward 分配:
- 成功轨迹:r_t = + w_t
- 失败轨迹:r_t = - w_t
- 用置信度缩放:r_t ← c · r_t
5 思维链合成注入
5.1 先想再做:最小思考骨架
✅Action 本质是一次工具调用
每一步从截图到 action,等价于基于状态做决策并调用工具。行业共识:先CoT(想清楚)再执行通常更稳,尤其长任务能减少冲动点击。
例:看到加载中就先等待并设验证点,而不是立刻狂点搜索框位置。
✅推荐的最小思考骨架
任务是什么 → 当前在哪个页面 → 下一步子目标 → 为什么是这个动作 → 执行后如何验证
例:任务:搜奶茶;当前:首页但有权限弹窗;子目标:清掉弹窗;动作:点允许/拒绝;验证:弹窗消失且搜索框可交互。
5.2 人类难标注,必须合成
✅人类下意识正确但难写出来
你在美团点外卖不会想半天为什么点搜索框,但让标注员写高质量推理链不现实。
例:标注员可能只记录点这里,但不会写如果键盘没出来说明没点中,需要换策略/处理遮挡。
✅合成思维链的思路
用规则+模型生成,把应该怎么想注入训练数据:
- 状态摘要(屏幕关键元素/弹窗/可操作入口) 例:顶部有搜索框;中间弹出定位权限对话框。
- 目标分解(本步子目标) 例:先让输入框可用,再输入关键字。
- 候选动作对比(为什么选A不选B) 例:点‘允许’比点背后搜索框更可靠,因为搜索框当前不可点击。
- 风险提示与验证点(执行后看什么算成功) 例:若弹窗未消失则不要继续输入,先重试或 BACK。
5.3 Thinking Model抽取字段
✅让模型更会想
Progress Track:我做到任务哪一步了(阶段/里程碑);State Summary:当前界面关键元素摘要(控件、弹窗、入口);Effect Predict:执行候选动作会发生什么(因果预测);Self-Reflect:是否偏航/风险/不确定,需要换策略吗;State Verify:执行后怎么判断生效(验证点)
✅让模型更会做
把当前意图拆成短子目标(本步要达成什么),给出可执行原子动作(tap/swipe/type/launch)
✅每步统一输出 schema
- state_summary:屏幕摘要(短文本)
- plan:本步子目标
- action:TAP/SWIPE/TYPE/BACK/LAUNCH…
- arguments:坐标/文本/包名/方向/距离
- expectation:预期UI变化
- check:验证规则(关键字/页面区域/控件存在性)
6 训练与迭代变强
6.1 加权SFT(最关键、最稳)
✅用步骤奖励校准的 step 权重做样本加权
- 主任务:预测 action + args
- 辅助任务:预测 state_summary / progress / verify(多头监督)
例:搜奶茶中,TAP(搜索框)、TYPE(“奶茶”) 这类关键步 w_t 更高,训练时梯度更大;随手滑一下的低贡献步权重更低,避免学到噪声。
✅一个常用损失形式
L=∑twt⋅(Laction+λ1Lintent+λ2Lverify) L = \sum_t w_t \cdot \big( L_{\text{action}} + \lambda_1 L_{\text{intent}} + \lambda_2 L_{\text{verify}} \big)L=t∑wt⋅(Laction+λ1Lintent+λ2Lverify)
对每一步同时学习做什么动作本步意图是什么做完如何验证,并用步骤奖励校准得到的权重把关键步骤放大、噪声步骤缩小,从而提升长任务稳定性。
例:失败类型是弹窗未处理,则弹窗出现后仍点背后区域的那一步权重应最高
6.2 偏好学习/对比学习(把失败用起来)
✅从失败轨迹构造偏好对
同一状态 s 下,成功动作 a^+ 优于失败动作 a^-。DPO / Pairwise Ranking 都可(实现简单,效果常很好)。
例:同样看到定位权限弹窗,a^+=TAP(允许/拒绝) 明显优于 a^-=TAP(弹窗背后搜索框坐标)。
✅失败轨迹也能变黄金
失败类型画像:元素不存在误点、弹窗未处理、等待不足、跳转错误、焦点丢失、滚动过头、循环卡死…
负样本可训练:自检/回退(BACK、重新定位、换入口)、后果预测(点这里会跑偏)、终止与求助(超时/循环检测触发)
6.3 轻量RL(可选:最后细抛光)
GUI 环境昂贵、噪声大。通常先靠rollout + 分拣 + CSRS + 加权SFT/偏好学习把成功率拉上来;RL(PPO风格等)用于最后抛光。
例:当模型已基本会搜奶茶并进店铺,PPO 再去优化什么时候等待、什么时候重试、什么时候回退这类细节策略)
6.4 迭代飞轮:N → N+1
✅一轮迭代流水线
冷启动 → Rollout → 轨迹级验证 →步骤奖励校准 → 合成思维链 → 训练 → 进入下一轮
✅为什么会越训越强
- 成功轨迹比例提升 → 正样本质量更高
- 失败更高级(更接近决策边界)→ 负样本更有教学价值
- 数据分布越来越像线上真实环境 → 泛化更稳
llout + 分拣 + CSRS + 加权SFT/偏好学习把成功率拉上来;RL(PPO风格等)用于最后抛光。
例:当模型已基本会搜奶茶并进店铺,PPO 再去优化什么时候等待、什么时候重试、什么时候回退这类细节策略)
6.4 迭代飞轮:N → N+1
✅一轮迭代流水线
冷启动 → Rollout → 轨迹级验证 →步骤奖励校准 → 合成思维链 → 训练 → 进入下一轮
✅为什么会越训越强
- 成功轨迹比例提升 → 正样本质量更高
- 失败更高级(更接近决策边界)→ 负样本更有教学价值
- 数据分布越来越像线上真实环境 → 泛化更稳
例:第一轮失败多在不会点搜索框;第二轮失败多在弹窗/等待;越往后越接近真实边界问题