资讯详情

资讯详情

LLM智能体下沉移动端测试:通用Agent框架改造实践

1. 先说个现象再说怎么解决最近一段时间大家都在往一个大方向使劲让 LLM 直接操作手机把原来靠脚本堆出来的移动端自动化测试变成一个会自动想、自动做、自动判断结果的智能体。听起来很美好但真上手之后你会发现拿一个通用 Agent 框架直接跑测试跑出来的结果基本是灾难。它会把一个登录按钮当成广告关掉会在弹窗面前反复横跳甚至会把崩溃弹窗当成正常业务页面继续点下去。这套内容是“LLM 驱动的自动化测试”系列的第 9 篇聚焦移动端自动化的第 5 个话题当通用 Agent 框架下沉到测试场景。要聊的不是那些概念层面的畅想而是我自己在把一套通用智能体框架改造成移动端测试工具时实际踩过的坑、调过的参、改过的结构。适合正在做移动端自动化、又对 LLM 有想法但不知道从哪下手的同学参考。如果你已经把脚本自动化玩得很熟这篇文章会帮你梳理一条从“脚本执行”走向“意图驱动”的相对平滑的落地路径。先声明本文不涉及任何具体大模型品牌和评测结论只聊通用的工程改造思路。2. 通用 Agent 框架到底是什么它原本不是干这个的2.1 通用框架的本职完成一段“用户任务”现在大家讨论的通用 Agent 框架核心能力是三步理解一个自然语言目标 → 拆解成步骤 → 通过调用工具逐步执行。比如你说“帮我把相册里最近十张照片按时间排序”框架会识别出意图规划一个路径然后调用相册应用、模拟点击、滑屏、确认结果。这套设计思路原本不是为测试准备的。它是为了“代替人完成操作”所以它的成功标准是目标是否达成。操作错了没关系只要最终用户看到想要的结果就算成功。它没有“主动发现异常”的动机也不关心执行过程中有没有出现与目标无关的坏状态。这跟测试的核心诉求天然是错位的测试不是要“完成业务”而是要“找出业务没被正确完成的地方”。2.2 框架下沉到测试场景等于换了三种约束把通用 Agent 框架搬到测试环境相当于给一个本来就复杂的系统临时追加了三条它没设计过的约束错误不再是噪声而是信号。通用框架遇到弹窗、异常页通常会尝试绕过去或者重试因为它的目标是完成任务。测试工具恰恰相反遇到一个不该出现的弹窗就是一次失败的证据应该停下来报告而不是绕过。过程必须可以被观察和回放。用户操作错了可以接受但测试操作错了如果没记录出了问题根本没法定位。Agent 框架默认的“只记录结果”和测试要求的“完整记录过程”差距很大。结果不是“完成”而是“断言”。通用框架判断“做完没做完”测试要判断“对不对、是否符合预期”。这个转变不是改一句 prompt 就能解决的而是要从执行逻辑上拆出来一层独立的校验机制。说直白一点通用 Agent 框架是“想办法把事情干成”测试工具是“想办法把事情干坏并记录下来”。这两种思维方式要在同一个系统里共存不改造是不可能的。3. 下沉改造的核心在框架里硬切出测试专属的四个模块3.1 感知层不能只看截图还要读结构移动端 Agent 第一件要做的事是“看懂当前屏幕”。通用框架通常的做法是把当前屏幕截图丢给多模态模型让模型描述画面内容。这对一般操作勉强够用但对测试来说有三个致命短板第一截图理解有延迟且不稳定。大模型看一张截图再返回描述通常需要几百毫秒到几秒。这个延迟在“用户随便点两下”的场景里无所谓但在自动化测试里每步操作之间如果都卡 2 秒一条 30 步的用例跑下来直接奔着一分钟以上去整批用例回归的成本完全不可控。第二某些 UI 细节光靠截图根本看不清。列表项的状态、按钮是否可点、文字的精确内容多模态模型经常“猜”。通用框架可以猜个大概测试框架不能猜。我当时的处理方式是给模型喂的不只是一张截图而是截图上叠加了 UI 层级信息的标注图像。就是先把 Android 的控件树或者是 iOS 的可访问性树 dump 出来然后在内存里把每个控件的 bounding box、文案、控件类型画到截图上面让模型同时感知“视觉面貌”和“结构定义”。第三屏幕变化需要有一个“发生了什么”的判断。测试场景下Agent 需要知道静态页面没变、有弹窗出现、页面跳转了、软键盘弹起来了……这些状态判断如果全丢给大模型做成本高且不稳定。我的做法是在感知层里加一个轻量级“屏幕差异检测模块”对相邻两步的截图像素特征做比对如果差异极小就直接判定“页面未变化”不再调用大模型。这个模块能把大概 40% 的无效模型调用直接滤掉对压成本非常有帮助。3.2 规划层把“完成任务”改成“覆盖风险路径”规划层是通用 Agent 框架里被描述得最玄乎的部分但落地到测试场景你会发现它其实只需要做一件事将用户给的测试意图翻译成一条可执行的 UI 操作路径。比如用户说“我要测登录功能”框架要能自己规划出打开登录页 → 输入正确的账号密码 → 点击登录 → 校验页面跳转 → 回到登录页 → 输入错误的账号密码 → 点击登录 → 校验错误提示。这个规划能力用大模型的推理来做并不复杂真正复杂的反而是约束。通用框架的规划是“自由的”因为它要应对真实用户的随意表达测试框架的规划必须加两类硬约束反例路径必须离得开主流程。测试不光要能走通正常路径还要能意识到“正常路径之外有哪些容易被忽略的异常分支”。这个可以用提示词约束模型强制列出至少一条反例路径而不能让它只按“完成任务”的惯性走。规划必须可裁剪、可重试。如果规划出的第 3 步在真实屏幕根本不存在框架要能放弃这条路径重新规划一条平行的替代路径而不是卡死在整个计划上。我给规划层设计了“分步规划局部重规划”的机制模型只规划下一步执行后根据屏幕反馈再次规划下一步而不是一口气生成一个全程计划。实操下来分步规划比一次性规划稳定非常多。主要原因是移动端 UI 的不确定性太大任何一步的实际运行都可能和预期不符。一次性规划碰到一次意外就得全部推翻分步规划只损失一步的代价。3.3 执行层从“点击坐标”到“动作安全校验”执行层负责把规划出的步骤真正落到手机上。通用框架在这个层面基本就是两件事找到控件执行点击或输入。测试框架则必须在动作发生之前加一道安全闸门。我加了一个叫“动作预校验”的模块原理特别简单大模型决定要点击某一个控件时先在 UI 树里查出这个控件的可见性和可点击属性只有两个属性都是 true才允许把点击指令发给设备。这个闸门虽然蠢但非常有效它直接解决了 GPT 幻觉导致的“点到不存在的按钮”问题。执行层还有另外一个测试独有的需求输入必须可控可回放。通用框架在输入框里敲字无所谓测试框架必须记录每次输入的内容、输入的位置、输入的时间点。原因很简单测试用例需要可复现如果这次跑输入的是“abc”下次跑输入的是“abcd”那条用例就废了。顺便说一句移动端自动化测试里最坑爹的输入问题是输入法。第三方输入法弹出的候选词框、联想条会遮住按钮还会改变屏幕布局。我当时的处理方式很粗暴在测试设备上全局禁用第三方输入法只保留一个无候选词的英文键盘并在执行前强制关闭软键盘。3.4 校验层Agent 自己判断“通过/失败”本身就是个伪命题终于说到校验层。这是通用 Agent 框架下沉到测试场景时最别扭的地方也是我认为所有想这么做的人必须想明白的一个点不要指望大模型自己做测试断言。原因是LLM 的特性是“生成合理的内容”而测试断言要求的是“判断对错”。大模型判断对错的准确率在对 UI 描述不明确时会大幅下降。比如一个“保存成功”的提示模型可能因为截图上这个 Toast 很小、被状态栏遮挡就给出“不确定”的判断。这时候如果让模型自己决定用例是否通过结果会非常不稳定。正确做法是大模型只负责“提出断言建议”和“提供当前页面状态的结构化描述”真正的断言逻辑由测试框架用确定性代码执行。我自己是把校验层拆成了三类文本校验明文比对页面是否出现某个文案、toast、错误提示。控件状态校验直接查 UI 树中某个控件的 enabled、selected、checked 等属性。视觉差异校验拿测试截图和基线截图算相似度。这个适合判断“页面是否发生了预期跳转”。这样设计的最大好处是模型的判断误差被隔离在执行链路之外。模型说错话没关系最终拍板的是确定性代码。4. 实操落地的完整过程与关键参数配置4.1 环境搭建和框架选型思路改造基础设施时我没有从零手写 Agent 框架而是直接在一套开源的通用 Agent 脚手架基础上改把它的“操作执行器”换成了移动端设备控制通道。这里有个重要的经验不要选那种把“感知—规划—执行”紧紧耦合在代码里的框架要选模块之间有清晰接口的。否则后面每一层改造都会变成在别人的逻辑里打补丁越打越乱。具体环境上我用的配置如下移动端设备两台 Android 真机 一台 iOS 真机用模拟器跑测试传感器和弱网问题会失真设备连接通过 ADB 与 iOS 的 WebDriverAgent 通道控制截图获取Android 用系统截屏iOS 用 WebDriverAgent 的截图接口统一做分辨率压缩到 720p 再送模型UI 树获取Android 用 AccessibilityService 的节点 dumpiOS 用 XCUITest 的可访问性快照大模型调用统一封装成一个异步接口支持超时与重试不绑定具体模型厂商这套组合跑下来我最大的感受是Android 端的 UI 树获取比 iOS 容易得多但 Android 真机的碎片化问题比 iOS 更严重。比如同一套 UI 树解析代码在原生应用上是正常的换到某个深度定制的国产 ROM 上控件的可见性字段会丢失。遇到这种情况别浪费时间调解析逻辑直接查看该 ROM 的厂商文档或者用截图叠加方式兜底。4.2 关键参数配置与实测数值参考大模型参数这一块我给几个自己反复调过之后相对稳定的参考值注意这是参考不是标准答案temperature0.2 ~ 0.4。测试场景要尽力降低随机性。高于 0.7 时规划结果经常飘操作步骤会出现莫名其妙的抖动。max_tokens单步规划控制在 500 tokens 以内多步完整规划控制在 1500 tokens 以内。给太多 token 空间模型容易输出长篇大论的说明而不是直接给结构化动作解析时还容易出错。超时设置单次模型调用设置 15 秒超时超过就重试一次再失败就切换到“降级路径”——放弃当前用例并标记为“环境异常”而不是继续傻等。重试机制动作执行失败重试 2 次后仍失败的将屏幕截图和 UI 树摘要塞给模型让它给一个新的局部计划。如果新计划和旧计划完全相同判定为“模型自身无法解决”直接结束用例并标注失败原因。这里要给个重要提醒移动端自动化里的等待问题比网页自动化严重得多。页面转场、网络请求、动画效果都会导致执行一步之后屏幕还没稳定。我的做法是在每一步动作执行后固定等待 0.8 秒再采一次截图做差异检测如果两次截图基本相同说明页面稳定了再进入下一步。4.3 移动端独有的提示词结构设计给移动端测试 Agent 设计提示词和给通用 Agent 设计提示词最大的不同在于必须让模型摆脱“完成任务”的惯性转向“主动找茬”的思维。我调整后的提示词结构大概是这样{当前任务描述} {当前屏幕截图} {当前屏幕UI结构摘要} {上一步执行的操作记录} 请你做两件事 1. 判断当前页面状态是否正常。如果存在异常弹窗、报错文案、崩溃页面 直接在结果中给出异常标记和异常描述不要再执行任何操作。 2. 如果页面状态正常基于任务目标规划下一步动作输出格式为 动作类型: 点击/滑动/输入/返回 目标控件: 控件描述 输入内容: 仅输入类动作需要 动作理由: 一句话说明为什么执行这个动作 注意你是在做测试不是在使用应用。不要绕过异常异常是你需要报告的结果。实测下来加最后一段“注意”的约束非常关键。不加这句话模型碰到弹窗会习惯性地尝试点击“确定”或者“关闭”把它当成正常交互的一部分。加了之后模型会先把异常情况结构化地输出出来然后停在原地等待框架决策。4.4 一个真实的执行日志片段为了让大家直观感受执行过程我摘一段脱敏后的实际运行日志[Step 04] 感知层: 页面截图已采集, UI树节点数127 [Step 04] 感知层: 屏幕差异检测与上一步截图核心区域差异度0.311, 判定页面已变化 [Step 04] 规划层: 大模型返回结果动作类型点击, 目标登录按钮, 理由进入登录流程 [Step 04] 执行层: 预校验通过与位置推算完成, target_nodebtn_login, 坐标(356, 788) [Step 04] 执行层: ADB点击指令已下发, 耗时213ms [Step 05] 感知层: 页面截图已采集, 发现弹出键盘, 键盘区域占用屏幕底部约30% [Step 05] 规划层: 大模型返回结果动作类型输入, 目标账号输入框, 内容testuser01 [Step 05] 执行层: 输入前安全校验通过你可以看到整个链路里模型只在“规划层”出现其余环节全部是确定性代码在干活。这就是下沉改造的核心思路让模型做最擅长的事理解和规划把最不擅长的事精确执行和判断对错交给工程逻辑兜底。4.5 稳定性测试结果改造完成后我在一个模拟项目X的 Android 应用上跑了 3 轮完整回归每轮包含 12 条用例。结果大致是用例首次执行通过率约 85%重试一次后通过率提升到 96%剩余 4% 失败的原因集中在两类弱网导致的页面超时、ROM 的控件树信息缺失平均每条用例执行时间从手工脚本的 4 分钟降到了 1 分半以内大模型调用成本按 1 轮回归计算12 条用例约调用 220 次整体费用在可接受范围内这个结果说不上惊艳但作为从“脚本测试”到“意图驱动测试”转换的第一步稳定性已经达到可以日常使用的水平。5. 移动端测试场景下的典型问题与排查实录5.1 大模型误点“关闭”按钮导致测试中断这是最常见的坑。弹窗的“关闭”按钮在截图里特别显眼模型很容易把它当成正常操作的一部分点击掉。排查时发现根本原因是规划层拿到的 UI 树摘要里弹窗按钮和页面正常控件的属性没有做区分。解决方式在 UI 树摘要把弹窗、广告、引导蒙层这些特殊节点的类型标记提取出来纳入提示词的前置说明里。如果界面上检测到弹窗节点就在给模型的信息里加一条“当前存在弹窗请优先判断是否需要报告异常不要点击任何关闭按钮以外的元素”。5.2 坐标定位偏移点击永远偏 10 像素某些 ROM 的 UI 树给出的控件坐标和真实点击区域存在偏移。这个问题的诡异之处在于它不是均与偏移而是部分控件偏移部分控件不偏移。排查后发现是某些控件带了透明边框或者圆角裁切而系统给出的 bounding box 包含了这些不可见区域。我的处理方式执行层点击前取控件 bounding box 的中心点并对中心点做小幅向内收缩比如收缩 5%降低落到边缘的概率。另外点击操作下发后增加一步“点击后截图对比”如果点击后屏幕和点击前完全相同判定为“点击可能未生效”触发重试。5.3 软键盘遮挡下一步按钮输入完账号后弹出的软键盘经常挡住登录按钮。通用框架通常不会管这个问题但测试执行到这一步会卡住。解决方式输入完成后执行层主动向设备发送“返回”指令收起键盘如果键盘没有收起则通过系统接口强制隐藏输入法这两个操作合在一起基本能解决 90% 的遮挡问题。5.4 页面加载过慢导致的误判断移动端页面加载不像网页有明确的 loading 状态很多页面是“白的、有部分控件、然后逐步渲染完整”。前两步的截图差异检测会把这种加载误判为“页面变化”导致模型过早规划下一步。我在感知层加了一个“页面稳定判定”机制连续采集 3 次截图两两计算差异度如果差异度都小于阈值才判定页面进入稳定状态。等待策略改成轮询式超过 10 秒仍未稳定直接标记该步骤为“可能超时”。5.5 问题速查表现象排查重点处理建议模型点击消失按钮UI 树摘要中按钮不可见但未标记强制性要求 UI 树摘要输出可见性字段输入内容丢失第三方输入法干扰设备层面禁用第三方键盘页面判断混乱动画过渡导致截图差异过大执行后固定等待连续截图稳定判定用例失败但无日志执行过程未保存过程截图每步操作强制保存“前置截图后置截图”模型规划重复失败规划无新意一直尝试同一路径失败 2 次后强制换一个动作策略6. 关于下沉改造我想多说几句实在话把通用 Agent 框架改造到移动端测试场景并不是一个“拿来即用”的过程。它更像是在一个成熟的通用底座上围绕测试的三件事重新打磨让感知更结构化、让规划更受约束、让校验更可编程。我个人在实际操作中最深的体会是不要被“Agent 很聪明”这个说法迷惑。一个通用的 LLM Agent 能在开放世界里完成很多事但在测试这种“必须精确、必须可复现、必须可解释”的场景里它的自由度必须被刻意压制。你加的约束越多系统越像测试工具你放任它自由发挥系统越像一个偶尔靠谱的玩具。最后给大家一条实在建议如果条件允许先从一个小应用的冒烟测试开始做下沉改造别一上来就追求全流程回归。先跑通“感知—规划—执行—校验”的最小闭环再逐步扩大范围。这个领域目前最大的问题不是模型不够强而是工程侧如何把模型的强项和测试的确定性要求结合得更好。我在后续的系列文章里还会继续拆这一块的具体细节。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →