AI操作手机三层架构:感知决策执行闭环与工程实践

发布时间:2026/11/3 8:03:08
AI操作手机三层架构:感知决策执行闭环与工程实践 1. 先想清楚一件事让模型“看屏幕点按钮”到底难在哪第一次接触 AI 操作手机这个命题很多人脑子里冒出来的画面是截个图丢给多模态大模型模型吐出一组坐标代码照着点一下完事。我最早也是这么想的写了个几十行的脚本在测试机上点开了设置页然后又点了一次再一次接着就卡在一个系统弹窗上原地转圈。那次之后我才明白AI 操作手机这件事的难点根本不在“模型能不能看懂截图”而在于屏幕这个东西本身极不稳定而你要在这块每秒钟都在变的画布上做一条能收敛到目标的控制回路。iEasyRun AI 这类方案之所以要拆成三层架构不是因为分层听起来高级而是因为它把两类完全不同的不确定性隔开了一类是“我看到的到底是什么”语义问题另一类是“我这一下点到哪去了”执行问题。中间那层负责把前者的模糊输出翻译成后者的精确指令。这三层任何一层偷懒整套东西就跑不起来。1.1 手机界面不是稳定的 API而是一块随时变化的画布桌面端自动化曾经有过一段舒服日子浏览器有 DOM有 CSS 选择器有一整套稳定的元素寻址方式。手机上这套东西基本不存在。你能拿到的只有两样像素和控件树。像素对模型友好但坐标系一旦错位点击就落空控件树对定位友好但很多界面自绘控件、游戏引擎渲染、部分跨平台框架的页面压根不吐节点或者吐出来的节点层级乱成一团。更麻烦的是界面状态的数量级。一个中等复杂度的应用页面组合加上各种弹窗、浮层、加载态、权限请求状态空间轻松上到几十万。你不可能穷举只能在运行时一步步试探。而且试探的成本不低——每走一步都要截一次图跑一次推理注入一次手势单步耗时几百毫秒到几秒。一个十步的任务稍微走岔两次用户体验就从“AI 帮我干活”变成“看 AI 表演”。我在做这类方案的时候有个朴素判断如果单步时延压不到 2 秒以内用户就会开始怀疑它卡死了如果十步里有超过两次走错用户就会关掉它。这两个数字基本决定了架构的所有取舍。1.2 三层拆分的真正动机把“不确定”关进两层之间把架构划成感知、决策、执行三层核心收益是每一层的不确定性都被下一个明确接口兜住了。感知层的输出不是“屏幕上有个登录按钮”而是一份带标识的结构化描述每个可交互元素的归一化边界、文本、控件类型、是否可点击、可点击祖先节点在哪。决策层不需要知道这些信息是从像素里抠出来的还是从控件树里读出来的它只面对一份标准清单。决策层的输出也不是“点那个按钮”而是一条动作指令动作类型、目标标识、参数。执行层不关心为什么要这么做它只负责把这条指令变成系统里一次真实的触摸事件并回传执行结果。这条链路上最关键的契约是目标标识。如果用坐标做标识分辨率一变全线崩如果用模型描述的文本做标识“蓝色的确认按钮”歧义太多。所以实际方案里一定会有一个中间层把元素的多个属性组合成一个稳定的指纹模型只对这个指纹说话。动作指令示例决策层 - 执行层 { action: tap, target_id: e7a3f2c1, fallback_bounds: [512, 1830, 780, 1912], reason: 当前页面需要先关闭升级提示, expect: 弹窗消失回到首页 }expect这个字段很多人会省掉我觉得不该省。它是后面做动作校验和失败恢复的唯一依据没有它执行层交回来的只有“点了”两个字决策层根本不知道自己走对没走对。2. 感知层截图和控件树两条腿走路缺一条都会摔感知层要解决的是“此刻屏幕上有什么、我能操作什么”。这里最常见的误区是二选一——要么纯视觉要么纯控件树。我实测下来两条路单独走都有硬伤必须融合。2.1 视觉通道解决“看得懂”结构通道解决“点得准”纯视觉路线的好处是通用性拉满截一张图任何界面都能处理包括游戏、H5、自绘列表。但它的短板同样明显。多模态模型输出坐标的精度大概在屏幕宽度的 1% 到 3% 之间在 1080 宽的屏幕上就是 10 到 30 像素的误差。按钮够大没事一旦碰上密集的图标栏、小的关闭叉号、间距很窄的列表项这个误差直接导致点错。纯控件树路线刚好相反。节点给的bounds是精确到像素的点哪是哪。但它对节点的覆盖率是真的不稳定——同一个应用主页有节点进了某个自绘的详情页整个树就剩一个根节点加一个 WebView 壳子什么信息都没有。所以实际做法是先用控件树把能拿到的东西全部结构化再把截图和这份结构做交叉验证。举个具体操作控件树里拿到一个节点bounds[60, 420, 1020, 512]同时截图在同一个矩形内做一次文本检测两边文字对得上这个元素的置信度就很高。对不上的时候通常意味着界面正在做动画、截图的坐标系包含状态栏而节点坐标不包含、或者这个位置被一个透明浮层盖住了——三种情况都指向“现在不适合点击”应该等下一帧。通道强项硬伤适用判断视觉截图 多模态模型通用无节点也能理解坐标精度低推理成本高兜底、理解复杂布局、识别图标语义结构无障碍节点树坐标精确带控件类型和可点击性覆盖率不稳自绘界面缺失主力定位优先使用文本OCR 或节点 text定位准可用于校验图像型文字识别慢易受字体干扰交叉验证、列表项识别2.2 归一化坐标系所有坐标漂移问题的总根源我见过最多的线上问题就是坐标漂移而且它特别隐蔽——在你自己的测试机上一切正常换台设备就全乱。根因几乎都出在坐标系没统一。屏幕坐标这件事至少有四个版本同时存在物理像素截图上的坐标、dp 或逻辑像素控件树通常给这个、应用可用区域扣掉状态栏和导航栏、全屏区域含状态栏。一个具体的坑截图如果用AccessibilityService.takeScreenshot()在部分系统版本上拿到的是不含状态栏的裁剪图高度比屏幕物理高度小一截而节点树给的bounds是基于全屏的。两边直接混用纵向偏移几十像素点击就会稳定偏上或偏下。这种偏移在小按钮上就是完全点不中。我的处理方式是所有内部流转的坐标一律用归一化值范围[0, 1000]原点固定在屏幕左上角参照尺寸是包含状态栏和导航栏的完整屏幕。def to_norm(x_px, y_px, screen_w, screen_h): # 统一到 0~1000 的相对坐标系 return round(x_px / screen_w * 1000), round(y_px / screen_h * 1000) def to_px(x_norm, y_norm, screen_w, screen_h): # 还原时用四舍五入避免累积误差 return round(x_norm / 1000 * screen_w), round(y_norm / 1000 * screen_h)用归一化的另一个好处是喂给模型的坐标也统一了。如果不做归一化模型在 1080x2400 的图上训练出来的坐标习惯换到 1440x3200 的折叠屏上就完全失效。归一化之后模型学的其实是“屏幕相对位置”跨设备迁移会稳很多。提醒一句归一化不能解决宽高比差异。同样是(500, 300)在 9:16 的屏幕上和在 4:3 的平板上落在完全不同的视觉位置。如果你的目标设备跨度大喂给模型时最好同时带上宽高比这个标量让模型有个参照。2.3 元素指纹与页面状态哈希让模型有稳定的指代物模型的输出必须能落到一个具体的控件上所以每个可交互元素都要有个稳定 ID。我用的是一个组合指纹再做哈希def fingerprint(node): parts [ node.resource_id or , # 资源 ID最稳定 node.class_name or , strip_digits(node.text or ), # 文本里的数字脱敏防止时间、价格变动导致指纹漂移 strip_digits(node.content_desc or ), node.norm_bounds_key(), # 归一化边界取整到 10 的倍数容忍 1 像素抖动 ] return md5(|.join(parts).encode()).hexdigest()[:8]两个细节值得单独说。第一文本要做数字脱敏。列表里的“3 分钟前”“129.00”每隔一会儿就变一次不脱敏的话同一个按钮每次指纹都不一样模型上一轮记下来的东西这一轮就找不到了。第二归一化边界要取整。浮点数的尾数会引入无意义的差异取整到 10 的倍数能把 1 到 2 像素的渲染抖动吸收掉。有了元素指纹就能顺手算页面状态哈希把当前包名、Activity 名或页面标题加上排序后的元素指纹列表拼起来取哈希。这个值用处很多——判断“页面变没变”、判断“是不是又走回了老地方”、判断“滚动有没有生效”。后面讲无限循环排查的时候会用到它。2.4 采集链路优化帧差、缩放、分块感知层的开销是整套系统的大头尤其是截图。可选通道和各自耗时大致是这样采集方式典型耗时权限要求备注adb exec-out screencap -p200~500 msUSB 或网络调试最通用但慢且大图传输开销大AccessibilityService.takeScreenshot()80~200 ms无障碍服务API 30部分版本不含状态栏MediaProjection ImageReader30~80 ms需用户授权一次流畅适合高频循环控件树实时遍历20~80 ms无障碍服务只需结构时最划算优化上我用了三招。第一招是帧差截图后先算一个低分辨率的感知哈希和上一帧比差异小于阈值就认为界面没变直接跳过推理。这在等待加载、等待动画结束的时候能省掉大量无效调用。第二招是缩放模型侧不需要原图长边缩到 720 到 1080 已经足够识别界面结构token 消耗能降一半以上。第三招是分块只有当需要模型“细看”某个区域时才裁剪局部图而不是每次都送整屏。这三招叠起来一个典型任务的平均单步感知开销能从 400ms 压到 120ms 左右而且模型调用的次数能少三到四成。3. 决策层把“帮我订个外卖”翻译成二十几次点击决策层是整个方案里最像“智能”的部分也是最容易写歪的部分。写歪的典型症状是给模型一句自然语言让它直接输出一大段操作步骤然后照着执行。这条路在 demo 里能跑通在真实环境里第二三步就崩了因为真实界面不会按你的剧本走。3.1 任务拆解与 ReAct 循环的真实骨架我采用的骨架是“先粗排计划再逐帧决策”。第一步让模型把用户指令拆成若干个子目标这个子目标列表是粗粒度的允许粗略、允许不完整。比如“在某本地生活应用里买一杯冰美式”拆出来大概是打开应用 → 进入点单页 → 选规格 → 加购 → 结算 → 确认支付。这六步里后面几步的细节在前面根本看不出来但那不重要计划的作用是给循环一个前进方向避免它在一个页面上反复乱点。真正跑起来的是观察-思考-行动的主循环for step in range(MAX_STEPS): obs perception.capture() # 截图 控件树 if obs.page_hash in visited and last_action_failed: strategy 回到上一层重新规划 decision planner.decide( goalcurrent_subgoal, observationobs.compact(), # 结构化精简后的描述 historymemory.recent(5), # 最近 5 步动作与结果 banned_actionsmemory.blacklist(obs.page_hash), ) result executor.run(decision.action) ok validator.check(decision.expect, perception.capture()) memory.record(obs.page_hash, decision.action, ok) if decision.action.type done: break这里有几个设计决定值得展开说。history只传最近 5 步而不是全量是因为上下文一长模型的注意力就会漂移而且成本也顶不住。banned_actions是按页面哈希记录的失败动作黑名单防止在同一个地方反复踩。validator是独立的校验模块不依赖模型的自我判断——模型说“我点了”不代表点对了必须靠界面变化来验证。3.2 动作空间设计宁可动作少也不要动作模糊动作空间我压到了八个多一个都不要动作参数说明taptarget_id或归一化坐标点击元素中心或指定点long_presstarget_id, 时长长按默认 600msswipe起点、终点、时长滑动用于翻页和拖拽input_texttarget_id, 文本向可编辑控件写入文本back无系统返回wait毫秒数显式等待用于加载态done结果摘要任务完成ask问题文本需要用户决策时中断刻意没做的两个一个是scroll_to滚动到某个元素因为“滚几次能滚到”这个信息模型判断不了拆成 swipe 观察反而更稳另一个是open_app这种高层语义动作因为不同应用内部跳转逻辑差异太大容易变成模型的一厢情愿我宁愿让它老老实实回桌面点图标。动作少的好处是输出格式收敛快。八个动作的 JSON schema 约束在提示词里模型照着填的成功率远高于开放式的自由文本描述。3.3 坐标接地模型给出的是意图代码要给的是像素这是三层架构里最容易被忽略的接口细节。模型输出的往往是“点击右上角的设置入口”而执行层需要的是(978, 136)这样的像素坐标。中间这段翻译就是接地做不好会出现各种玄学问题。我的做法是双路径接地优先走结构结构不行走视觉。如果模型给的是target_id直接在感知层的元素表里查边界取矩形中心如果查不到指纹对不上通常说明界面变了就退回到模型给的归一化坐标再做一次“就近吸附”——在这个坐标周围一个有限半径内找最近的可点击元素把落点修正到它的中心。def resolve_target(decision, elements, screen_w, screen_h): if decision.target_id: node elements.get(decision.target_id) if node: cx (node.bounds[0] node.bounds[2]) / 2 cy (node.bounds[1] node.bounds[3]) / 2 return to_px(*to_norm(cx, cy, screen_w, screen_h), screen_w, screen_h) # 退化路径用模型坐标并做就近吸附 raw to_px(decision.x, decision.y, screen_w, screen_h) near nearest_clickable(elements, raw, radiusint(screen_w * 0.06)) return near.center if near else raw半径取屏幕宽度的 6% 是我试出来的经验值。太小吸收不了模型误差太大又容易吸附到隔壁那个按钮上——尤其是底部那种并排的双按钮“取消 / 确认”半径一大就可能吸附错反而更危险。对这类高危区域我的处理是一旦吸附结果和原始坐标分属不同元素直接不吸附改用原始坐标并要求二次确认。3.4 记忆、校验与失败恢复记忆分三块。短期记忆是最近几步的动作和结果随上下文滚动丢弃。页面记忆记录走过的页面哈希和在上面成功执行过的动作下次遇到同样哈希可以直接复用省一次推理。黑名单记录在某个页面哈希下失败超过两次的动作后续不再尝试。校验这块我定义了几种“成功信号”目标元素从可访问节点里消失点了关闭按钮、页面哈希发生变化跳转成功、目标文本出现在新页面上搜索生效、出现加载指示器至少说明动作被接收了。相反如果动作执行后页面哈希完全没变基本可以判定失败。这里要留一个例外有些动作本来就不改变页面哈希比如点输入框拉起键盘键盘是系统窗口可能不在应用节点树里。这类动作我单独标记为“无状态变化型”不做哈希校验。失败恢复的阶梯是这样的第一次失败原样重试一次很多是时序问题第二次失败换个方式比如从target_id点击改成坐标点击第三次失败执行back回到上一层重新规划back连续三次无效可能回到了桌面就直接终止任务并回报失败原因。这个阶梯能挡掉绝大多数偶发问题又不会陷入死循环。3.5 护栏哪些动作必须先停下来问一句这部分很多方案会跳过但只要真正让 AI 在用户手机上跑过任务就知道护栏不是可选项。需要中断确认的动作我划了这几类涉及金额的操作结算、付款、转账、涉及对外发送的操作发消息、发帖、发邮件、涉及删除的操作清空、卸载、删除聊天记录、涉及账号的操作修改密码、退出登录、授权第三方。判定方式是动作发生前的页面上出现特定语义的按钮文本或者当前应用在白名单里被标记为“高风险”。另外还有一个常被忽略的护栏输入内容不得含敏感信息。用户在任务描述里给出的地址、电话写进输入框之前要过一遍脱敏检查并明确记录进日志方便用户回溯。4. 执行层从一次点击到系统里真正的触摸事件执行层看起来最简单实际上最容易出“看起来成了其实没成”的问题。它的任务就一句话把动作指令变成系统里真实的输入事件并如实回报结果。4.1 无障碍手势注入无 root 情况下的主力通道主力通道是无障碍服务的手势分发API 24 以上可用需要在服务配置里声明canPerformGestures。好处是不需要 root、不需要额外授权走的是系统正规输入通道绝大多数应用都认。fun tap(x: Int, y: Int, durationMs: Long 40L): Boolean { val path Path().apply { moveTo(x.toFloat(), y.toFloat()) } val stroke GestureDescription.StrokeDescription(path, 0L, durationMs) val gesture GestureDescription.Builder().addStroke(stroke).build() return dispatchGesture(gesture, null, null) }durationMs这个参数有讲究。设成 0 或 1 毫秒部分机型会识别为异常事件直接丢弃设成 40 到 60 毫秒最稳接近真实手指的接触时长。滑动同理duration建议不低于 300 毫秒太快会被识别成 fling快速甩动滚动距离超出预期列表直接冲过头。滑动路径我一般用带中间控制点的三次贝塞尔曲线而不是直线。直线滑动在部分应用的列表里会触发“惯性加速”判定滚动距离是曲线的两倍多。加两三个控制点让轨迹稍微弯一点滚动距离的可预测性会好很多。4.2 ADB 通道的能力边界与常见误用ADB 通道我用在三个场景调试期的高频验证、无障碍服务没起来时的兜底、需要跨应用快速跳转的自动化测试。# 点击 adb shell input tap 540 1830 # 滑动起点x 起点y 终点x 终点y 时长ms adb shell input swipe 540 1800 540 600 400 # 按键 adb shell input keyevent KEYCODE_BACK # 文本仅 ASCII 可见字符空格要写成 %s adb shell input text hello%sworld坑主要在三个地方。一是延迟不可控input命令本身启动一个 shell 进程再调系统输入实际耗时 100 到 300 毫秒做高频循环时它会成为瓶颈。二是文本输入只支持 ASCII中文直接失败或者变成乱码。三是无线调试连接一旦抖动命令会静默失败返回码正常但事件没注入这种“假成功”最难查。所以我的策略是ADB 只做兜底不进主循环。主循环永远走无障碍通道无障碍挂了才降级到 ADB并且降级后立刻打日志标记方便定位。4.3 中文输入这块硬骨头输入中文是这类方案绕不过去的坎我试过三种路子。路子一是无障碍的ACTION_SET_TEXT直接给可编辑节点设置文本最干净不弹键盘不依赖输入法。限制是节点必须isEditable且支持这个动作WebView 内的输入框和部分自绘输入框经常不支持调用后文本为空。路子二是装一个无界面的输入法服务通过广播把 Base64 编码的文本传过去输入法解码后提交。这条路通用性最好中文英文都行缺点是用户要手动切换输入法并授权安装成本高。路子三是坐标点击输入框后用 ADB 发文本只对英文有效中文行不通。我实际的组合是优先ACTION_SET_TEXT失败则拉起无界面输入法两者都不行就中断任务提示用户手动输入。第三条路我基本放弃了因为一旦涉及中英混排就彻底没法用。还有一个细节ACTION_SET_TEXT设置的是完整文本会覆盖原有内容。如果输入框里已经有默认文字比如“请输入手机号”这类占位符不算但预填内容是需要先发一个全选加删除的组合动作否则会变成拼接。4.4 手势节奏、异步等待与重试策略真实用户的操作节奏是有停顿的机器操作如果零点几毫秒一个动作连发很多应用的风控会直接判定异常。我在动作之间加了 150 到 400 毫秒的随机延迟滑动结束到下一次截图之间至少等 300 毫秒让渲染和动画有时间跑完。等待这块我不用固定 sleep用的是条件轮询加指数退避每 120 毫秒检查一次目标条件元素出现、页面哈希变化、加载指示器消失最多等 6 秒超过就判定超时。相比固定sleep(2000)这套策略的平均等待时间能降一半以上而任务成功率不降。重试策略上有一条铁律同一个动作在同一个页面状态下最多重试两次。第三次必须换策略否则就是在给无限循环铺路。5. 把三层串成一个能跑通的闭环时延、成本与断点三层拆开看都不复杂真正难的是把它们串成一个能在两秒内转一圈的闭环。这一节说说我调这套东西时最耗时间的几件事。5.1 一次循环的时延预算拆解一个完整的决策循环耗时大致这么分布环节典型耗时优化手段截图80~200 msMediaProjection 替代 ADB 截图控件树遍历20~80 ms限制遍历深度只取可见区域观察构建结构化 压缩10~30 ms元素数量超阈值时按可点击性裁剪模型推理800~4000 ms小模型分流、局部图替代整屏、结果缓存动作注入50~150 ms无障碍手势避免进程启动开销结果校验80~200 ms复用下一轮截图不额外采集合计1.0~4.7 s—能看出来模型推理是绝对的瓶颈占了七成以上。所以优化重点不在别的地方就在推理。我做了三件事把高频页面的“页面哈希 → 动作”映射缓存起来命中就直接跳过推理把简单判断比如“这个弹窗是不是有个明显的关闭叉号”交给规则处理不惊动模型把整屏图换成“结构化文本 少量局部裁剪图”的组合token 数量能砍掉六成。这里有个反直觉的结论给模型的图不是越多越好。我一度把整屏图全送进去想着信息越全模型越准结果发现界面元素一多模型反而开始“挑花眼”选的元素经常不是最该点的那个。改成先给结构化清单让模型指认需要细看时再裁局部图准确率反而上去了。5.2 断线、应用被杀、系统弹窗的兜底真实环境里任务跑到一半被打断是常态。我遇到的打断源大概有这几类系统权限弹窗定位、通知、存储、应用自身的升级提示和活动弹窗、来电或消息横幅、低电量提示、应用被系统回收。统一处理思路是在每轮感知后先做一次“意外检查”如果当前页面的包名不是目标任务的应用或者页面上出现了已知的弹窗特征大面积遮罩 单个或双个按钮 特定关键词就优先执行关闭动作最多三次三次关不掉就回报人工。这个检查放在决策之前比事后回滚成本低得多。断线这块主要针对 ADB 通道。我的做法是每次动作后校验返回码同时保留一个心跳检测连续三次动作无响应就重连重连失败则降级或终止。终止时要把现场信息最后一张截图、最后几个动作、当前页面哈希落盘不然线上出问题完全没法复盘。5.3 任务中断后能不能接着跑这个能力我觉得很值。完整方案的记忆里存了子目标进度任务中断后重新启动可以读回进度接着做而不是从第一屏重来。实现上不用很复杂——存一份{goal, completed_subgoals, last_page_hash, last_screenshot_path}就够了。重启后先感知一次如果页面哈希和上次记录的对得上就接着往下走对不上就回到最近的稳定页面通常是应用首页重新规划。6. 实测踩过的七个坑每个都带排查链路下面这些坑都是我实际遇到并且花时间查过的排查过程比结论更有参考价值所以我尽量把链路写全。6.1 点击落空坐标对了但点在输入法下面现象任务执行到“输入搜索词”这一步点击输入框成功键盘弹出接着要点搜索按钮点了三次都没反应页面哈希一直不变。排查先看动作日志三次点击的坐标完全一样都在(978, 2180)。再调出那一刻的截图发现键盘弹起后整个页面被顶了上去搜索按钮已经移到了(978, 1420)附近而(978, 2180)现在是键盘的字母区。也就是说代码点的是旧的坐标。根因决策用的是键盘弹起前的感知结果而执行发生在键盘弹起之后中间隔了一次界面变化没被感知到。处理给动作注入加了一个前置校验——注入前重新读一次目标节点的当前边界如果和决策时的边界偏差超过 20 像素就作废这次动作重新感知后再决策。多花 60 毫秒能挡掉一大类坐标失效问题。6.2 UI 树为空dump 超时与自绘控件的取舍现象某个应用的商品详情页控件树里只有根节点和一个WebView看不到任何按钮导致所有基于target_id的动作全部失败。排查换成系统命令行 dump等了两秒才返回而且结果一样。确认不是 dump 时机问题是这个页面本身不吐节点。处理这类页面直接切纯视觉模式用截图加模型识别模型输出的坐标做就近吸附时没有节点可用就退化成原始坐标。同时把这类页面的特征包名 Activity 页面标题记进一个“视觉优先”名单下次进这个页面直接跳过控件树省一次无用遍历。6.3 无限循环模型在原地复读现象任务卡在一个列表页日志里同一个坐标被点了十几次每次页面哈希都不变。排查把最近五步的动作和观察拉出来看发现模型每次都自信地认为“点击列表第一项可以进入详情页”但实际点的是一个不可点击的标题区域。失败后模型没收到有效反馈下一轮看到同样的界面做出同样的判断。根因失败信息没有回流到决策层。执行层只回了“已点击”没回“无效”。处理在校验环节加了“哈希未变即失败”的判定把失败结果写进黑名单同一页面哈希下同动作失败两次就禁用。同时给提示词里加了一条硬约束如果最近三步的动作和观察完全重复必须换策略或放弃。6.4 分辨率与折叠屏适配现象同一套流程在小屏手机上正常在折叠屏展开态下点击全部偏移。排查对比两个设备的感知数据发现折叠屏展开后宽高比是 1.1 左右接近方形而提示词里的示例坐标都是按竖屏手机写的模型对“右上角”的理解在方形屏上完全跑偏。处理归一化坐标改成按短边缩放并且在送入模型时显式带上宽高比。更彻底的做法是让模型少输出绝对坐标多输出“点击第几个元素”坐标由代码从元素表里查。这条改完之后跨设备问题的比例降了非常多。6.5 列表到底了没现象任务要“找到列表里某个商品”模型一直在往下滑滑到列表底部后继续滑滑了二十多次才超时结束。排查翻日志看到每次滑动后页面哈希都在变——因为滑动本身会让列表的滚动偏移变化元素边界跟着变指纹就变了。哈希一直在变程序就误判成“还有新内容”。处理滚动到底的判定不能只看哈希要同时满足三个条件中的两个可滚动的列表容器其scrollable属性变成 false、页面出现“没有更多了”之类的文本、连续两次滑动后可见元素指纹集合的重合度超过 80%。第三个条件最关键我用集合重合度替代哈希一下就把误判挡住了。6.6 弹窗与权限请求的干扰现象任务中途被系统的定位权限弹窗挡住模型把这个弹窗当成了目标页面的一部分开始在上面乱点。处理加了一个统一的“中断拦截”前置步骤维护一份常见弹窗的特征规则遮罩层 一到两个按钮 特定关键词 非目标包名的窗口。命中规则就先处理弹窗再重新感知。规则库要持续补因为每个应用的活动弹窗长得都不一样这块只能靠攒。6.7 回归测试与评估怎么做自动化这种东西不改没事一改就可能全线崩所以得有回归集。我的做法是攒一批固定任务20 到 50 个覆盖登录、搜索、下单、设置、跨应用跳转每次改动后跑一遍记录任务成功率、平均步数、平均耗时、人工接管率四个指标。成功率是第一优先但人工接管率同样重要——有些方案成功率不低是靠大量“卡住了就弹给你手动点”撑起来的那不算真本事。跑回归的时候有个细节模拟器和真机结果差异很大。模拟器上跑通了不能说明问题真机上至少得覆盖三种分辨率和两个厂商的定制系统。7. 什么场景下三层架构会扛不住以及怎么兜说了这么多好处也得说清楚边界。这套架构不是万能的有几类场景我试下来效果就是不好提前知道能省很多弯路。强实时交互类比如游戏、直播间的抢购、需要精确时序的操作。三层闭环单步秒级扛不住这类需求。这类场景适合写死的坐标脚本加图像匹配模型只做兜底判断别指望它进主循环。强视觉语义类比如“把这张图里的猫抠出来”或者“找出画面里最显眼的商品”。这类任务的核心是理解图像内容而不是操作界面属于另一个问题域硬塞进操作框架里会很难受。高安全等级场景比如涉及身份的验证、金融确认。这类操作要的不是自动化是无误人工介入的成本远低于出错的成本。信息量极大的长流程比如要在几十个页面里反复比对数据再决策。这类任务不是操作能力的问题是上下文和推理深度的问题最好是拆成“抓取”和“分析”两个独立任务抓取用这套架构分析另起炉灶。反过来这套架构最舒服的场景是步骤在五到三十步之间、界面结构相对标准、允许秒级延迟、失败后可以安全重来的重复性操作。日常的填表、查询、批量设置、内容发布、跨应用数据搬运基本都落在这个区间里。我自己在实际操作中的一个体会是别急着把模型往主循环里塞。先用规则和控件树把能确定的部分做扎实把界面状态摸清楚再把模型放在真正需要判断的地方。我一开始反过来做让模型处理所有环节结果调试成本高得离谱因为一旦出错你根本分不清是感知错了、模型想错了还是执行错了。分层之后每层的日志独立、可复现问题定位时间从几小时压到了几分钟。这个顺序上的经验比任何参数调优都值钱。再补一个实操小技巧给每一轮循环存一张截图和一个 JSON 快照按任务ID/步骤序号归档。跑崩了直接把最后五步的快照翻出来一眼就能看出问题出在哪一层。这个习惯帮我挡掉了无数次“线上复现不了”的扯皮做这类方案的人越早养成越好。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询