☰
多模态Agent如何重构UI测试路径规划:从脚本固化到边看边决策
2026/10/22 5:05:59 网站建设 项目流程

1. 为什么 UI 测试路径规划让人头大,以及多模态 Agent 带来了什么转机

做 UI 测试的同学应该都有过这种体验:需求文档写得明明白白,按钮跳转链路来回捋了三四遍,一到临门一脚却发现界面上多了个弹窗,或者某个元素在窄屏下被折叠隐藏,提前录制好的脚本当场作废。传统录制回放、脚本固化这条路,对付静态页面还行,一旦碰上动态 UI、复杂业务流、跨端适配,维护成本高到让人想转行。这也是为什么近两年多模态 Agent 成了 UI 测试路径规划这个方向的热门解法——让 AI 像人一样"看一眼屏幕再决定点哪里",而不是把每一步操作死写在脚本里。

先把我对这件事的理解说清楚。UI 测试路径规划,本质上要解决一个问题:给定一个测试目标(比如"完成一笔订单退款"),系统需要自动生成并执行一条从入口到目标状态的可操作路径,路径上每一步都要在正确的元素上做正确的动作,还要能处理中途出现的异常状态。传统做法靠人在脚本里把路径"硬编码"出来,代价是每次界面改版、文案调整、流程增加分支,脚本都要跟着返工。而多模态 Agent 的思路是把路径规划变成一个"边看边决策"的在线决策问题:模型读入屏幕截图或者界面结构,结合自然语言描述的测试目标,在自己的工作记忆里维护"我现在在哪一步、下一步该做什么",每次只输出一个原子动作,执行后再继续观察反馈,直到目标达成。

这里面的关键转变,是把"测试路径的编写者"从人变成了 AI。人只需要用自然语言描述测试意图,路径探索、分支处理、状态验证都交给 Agent 完成。我在几个模拟的跨平台业务系统上跑过这类方案,体会很深:路径规划的复杂度从 O(操作步数) 的维护负担,变成了 O(意图数量) 的定义负担。界面怎么改不敏感了,新增分支也只是多一句自然语言描述的事。

那么这套东西适合谁来参考?如果你是测试开发工程师,正在为 UI 自动化脚本的维护成本头疼;或者你在做自动化测试平台,想引入 AI 能力但不确定从哪里下手;又或者你只是对 Agent 应用落地感兴趣,想看看视觉语言模型在真实业务里怎么干活——这篇东西都能给你一个从思路到落地的完整参考。我下面的所有内容,都基于我自己在一个模拟电商业务系统上做的多模态 Agent 测试路径规划实验,代码风格偏向工程实践,直接可抄。

2. 方案选型与整体架构:先想清楚三个问题再动手

2.1 先回答一个核心问题:为什么必须是多模态

我在最早做这个实验的时候,也走了一段弯路。先试过纯文本方案——把 UI 的结构树(比如页面元素的层级、标签、坐标)导出来,拼成 JSON 喂给大语言模型,让它自己规划路径。结果比较惨:结构树信息太"干净",丢掉了太多视觉线索。一个按钮在截图上是灰的还是亮的,一个区块是折叠还是展开,提示文案是红色错误还是灰色占位,这些在结构树里往往没有直接体现,但对测试路径决策至关重要。模型看不到这些信息,就会出现"规划出一条看起来很合理、实际点在错误元素上"的路径。

多模态模型的优势就在于,它能把截图里的像素信息和结构树里的语义信息融合起来。我在实验里对比过同一套任务在纯文本 Agent 和多模态 Agent 下的表现:纯文本 Agent 在简单线性流程上的成功率大概七成左右,一旦遇到弹窗遮挡、元素状态变化、多个相似按钮并列这类场景,成功率会掉到五成以下;而多模态 Agent 依靠"截图 + 结构树"双路输入,在同样的场景里成功率能稳定在八成五以上,遇到异常界面时还能自己判断"这里跳过了什么,要不要绕路"。这个差距不是模型聪明不聪明的问题,而是信息维度的问题——屏幕本身就是视觉的,你强行把它翻译成文本,翻译过程就丢了信息。

2.2 Agent 的四层架构:感知、规划、记忆、执行

这套系统的整体架构,我拆成四个层次来设计,每一层都有明确职责,便于单独调试。

感知层负责把屏幕状态变成 Agent 可理解的上下文。我这里用了双通道:一条通道是截图,通过视觉模型处理成图像特征;另一条通道是 UI 结构信息,在移动端可以拿到层级树,在 Web 端可以拿到 DOM 结构和可访问性属性。双通道信息会在提示词里拼合成描述块,按区域组织:每个可交互元素给一个编号、类型、位置描述、可用状态,再配一份整屏的语义概览。

规划层是 Agent 的"大脑",由一个大模型扮演,输入感知层给的屏幕描述、系统当前状态、用户的测试目标,输出下一步决策。决策格式是结构化的,必须包含动作类型和参数,比如"点击 id=12 的按钮"或者"在输入框 id=5 中输入文本'测试账号'"。我要求模型在每次输出动作之前,先输出一小段"思考摘要",说明为什么选这个动作、预期发生什么变化。这个设计后来帮了大忙——一旦路径执行出错,看思考摘要就能快速定位是推理错了还是执行环境错了。

记忆层维护测试任务的上下文。这里不是简单地把对话历史全部塞给模型,而是维护一个精简的运行状态记录器:当前页面签名、已完成步骤、待验证断言、发现过的异常。每次决策时,我把"最近几次观察结果 + 当前状态摘要 + 测试目标"拼进提示词,这样就避免了上下文过长导致的注意力分散。

执行层负责把 Agent 输出的动作翻译成真实的 UI 操作。这一层我接的是成熟的自动化框架,在移动端用 UI 自动化驱动,在 Web 端用浏览器自动化方式驱动。执行层会把操作结果(成功、超时、元素未找到、页面无变化)反馈给记忆层,形成闭环。

2.3 模型选型和成本控制:不是越强越好,而是够用就好

模型选型上,我两类都试了。闭源视觉语言模型在复杂界面理解上确实更强,特别是那种元素密集、视觉层次复杂的页面,识别准确率明显高,但成本也高——一次测试跑几十步,每一步都要输截图,token 消耗很可观。开源视觉语言模型成本低、可以本地部署,隐私性也好,但在小目标元素识别、模糊界面理解上会弱一些。我的折中方案是"主力用开源模型 + 关键节点用闭源模型兜底":平时走开源模型省钱,当 Agent 连续两次决策置信度都很低,或者执行出错需要从异常中恢复时,再升级到闭源模型重新理解界面。这个方案跑下来,成本大概是全闭源方案的六成,成功率没有明显下降。

注意:这里的成本策略只针对我自己的实验场景。如果你所在公司的业务系统对数据安全要求极高,优先考虑本地部署开源模型方案;如果追求最优效果且预算充足,闭源方案体验更完整。

3. 核心细节解析与实操要点:决定成败的三个细节点

3.1 把屏幕变成 Agent 能稳定读懂的"结构化描述"

多模态 Agent 规划 UI 测试路径,第一步不是想路径,而是把屏幕"翻译"清楚。翻译质量直接决定了后续所有决策的质量,这一步我反复调了三轮才稳定下来。

第一轮我直接让模型端到端看图输出动作,效果不稳定。模型有时能看懂,有时会把一个 banner 图误认为按钮,甚至替用户做了不该做的操作。后来我把感知层改成了两阶段:先做"屏幕切片理解",再做"动作决策"。第一阶段专门让模型把屏幕里的可交互元素识别出来,输出一个按坐标和分层组织的描述文本,不涉及任何动作决策;第二阶段才是决策模型拿着这份描述去规划下一步。两阶段分离的好处是,第一阶段可以用更专注于视觉理解的提示词模板,误判率明显下降。

结构化描述的具体格式,我最终确定为这样:

{ "screen_summary": "订单确认页,顶部是收货地址卡,中部是商品清单,底部是合计金额和提交按钮。", "interactive_elements": [ { "id": 1, "type": "input", "label": "收货人姓名", "state": "filled", "location": [120, 240, 480, 280], "hint": "张三" }, { "id": 2, "type": "button", "label": "提交订单", "state": "enabled", "location": [160, 920, 440, 970], "hint": "底部主按钮" } ], "non_interactive_clues": [ "页面顶部有一个倒计时组件,剩余 14:32", "商品图片显示正常,无加载占位" ] }

这个结构有三个细节值得注意。其一,location用的是标准化坐标,统一把屏幕分成 100x100 的网格,不同分辨率设备跑到同一网格坐标系下,模型不用重新学坐标尺度。其二,state字段必须显式标注,很多模型会把 disabled 状态的按钮当可用按钮去规划路径,显式标注后这类错误少了很多。其三,non_interactive_clues是我后来加上的——把倒计时、提示横幅这类不参与交互但影响判断的视觉信息也描述进去,因为有几次模型看到倒计时以为是个可点的控件,加了这一项就没再犯。

3.2 动作空间的设计直接决定 Agent 的"可操作性"

动作空间是 Agent 可以执行的原子动作集合。这一步看起来很基础,实际上是最容易踩坑的地方。动作空间设计得太细,模型决策难度大、容易出错;设计得太粗,又无法覆盖真实 UI 操作的需要。我在实验里逐步收敛到六个原子动作:

点击明确坐标区域、在输入框中输入文本、滑动屏幕指定方向、等待界面元素出现或消失、返回上一级界面、以及标记断言失败并终止本轮测试。

这个动作空间少而精,覆盖了我在模拟业务系统里遇到的大部分测试操作需求,而且每个动作的参数都很简单。我特意没有加入"长按""双击""手势缩放"这类低频动作——它们会显著增加模型决策空间的复杂度,而实际测试场景里很少用到。如果真碰上需要这些动作的测试任务,我会用"等待 + 点击"的组合动作绕过去。

动作空间的提示词定义也要讲究。简单罗列"可选动作:click, input, swipe, wait, back, assert"太粗糙,模型不知道每个动作的具体约束。我给每个动作配了使用场景说明和参数约束,比如点击动作明确"只能点击 interactive_elements 中列出的元素坐标,非交互区域不可点击",等待动作明确"等待时间一般不超过 10 秒,超过视为超时异常"。有了这些约束,模型在决策时就很少输出"点击某个图片的空白区域"这种无效动作。

3.3 记忆机制:别让 Agent 走两步就失忆

一开始我做的是一个无记忆版本,模型每次只看当前屏幕和测试目标,直接输出动作。跑下来发现一个典型问题:模型在多层页面上反复进入退出,完全记不住自己是"从哪个入口进来的",常常陷入点击-返回-再点击的原地循环。原因在于,UI 测试路径是有状态依赖的,决策不仅要看当前屏幕,还要结合"这个界面是从哪一步来的""前面已经完成过什么操作"这些历史信息。

我给系统加了一个轻量记忆模块,维护三个维度的状态。页面栈记录从入口到当前界面的页面序列,用来支持返回操作和避免重复进入;步骤进度记录测试任务拆解成步骤序列后的执行情况,标记哪些已完成、哪些待执行;节点摘要记录最近五步的动作和观察结果,用来支撑决策上下文。这三个状态会拼成一段简短的"运行状态",和感知层的屏幕描述一起放进提示词。

这里要特别提醒一下,记忆不是越长越好。我试过保留全部对话历史和完整页面栈,结果模型被大量无关信息干扰,决策反而变差,token 成本也上去了。精简记忆 + 结构化状态才是正确姿势。关于 token 成本,放个实测参考:一个 20 步左右的测试任务,完整跑一遍大约消耗 6 万到 10 万 token,其中截图输入占了主要部分,精简记忆后能省下三成左右。

4. 实操过程与核心环节实现:从零搭起一个能跑的路径规划 Agent

4.1 环境准备与依赖清单

工程实现上我用的是 Python 生态,核心依赖分三块。模型调用层,接视觉语言模型的推理接口,同时封装了一个本地开源模型的推理框架作为备用;在自动化执行层,接成熟的 Web 浏览器自动化工具和移动端驱动,用来操作被测应用;中间状态管理则用结构化存储组织运行状态和上下文。

搭建环境唯一要提醒的是,把模型推理和 UI 执行分开部署,尽量别放在同一台机器。模型推理偶尔会占满显存和 CPU,一旦和 UI 执行抢资源,截图加载就会变慢,Agent 的时序判断会乱。我一开始图省事放同一台机器上,跑长流程时出现了"截图还没加载完就点了下一步"的乌龙,分开部署后才稳定下来。

4.2 最小可跑通的核心循环:观察-决策-执行

先给你看整个流程最核心的循环,代码长度不长,逻辑也很直白:

for step in range(max_steps): screenshot = take_screenshot() ui_tree = extract_ui_tree() screen_desc = perceive(screenshot, ui_tree) action = plan(screen_desc, memory, test_goal) result = execute(action) memory.update(action, result) verify_progress(screen_desc, test_goal) if is_goal_reached(screen_desc): print("测试路径规划完成,已到达目标状态") break

整个循环里最关键的plan函数,本质上是一次构造好的提示词调用。我最常用的提示词模板长这样:

def build_planning_prompt(screen_desc, memory, goal): return f""" 你是 UI 测试路径规划 AI。你的任务是在给定界面上规划并执行测试路径,直到达成目标状态。 测试目标:{goal} 当前运行状态:{memory.status_summary()} 屏幕结构描述: {screen_desc.to_text()} 请严格遵循以下规则: 1. 只能从动作空间中选择一个动作输出,不要同时执行多个动作。 2. 优先参考屏幕描述中的 interactive_elements,不要在非交互区域上浪费时间。 3. 如果当前屏幕与测试目标无关,检查运行状态中的页面栈,选择返回或点击可跳转的元素。 4. 如果连续三步没有推进任何状态,输出 assert_fail 终止本轮,并附上失败原因。 现在请输出你的决策,JSON 格式,包含思考摘要和动作: {{ "thinking_summary": "<简要说明决策依据>", "action": {{ "type": "<click|input|swipe|wait|back|assert_fail>", "params": {{...}}, "expected_outcome": "<执行后预期看到的界面变化>" }} }} """

这个提示词我迭代过很多版,最值得分享的经验是最后那句expected_outcome。让模型在每次动作前预测"点击之后应该看到什么",执行结果会和这个预期做对比,不一致就说明路径偏离了预期,需要重新评估。这个机制相当于给 Agent 加了一层想象力校验,能拦截大量"动作执行成功但没朝着目标方向走"的隐性错误。

4.3 执行层的适配与异常路由

执行层我封装了一个统一的函数,把不同端(Web、移动端)的差异收敛到同一个接口后面。这个函数会处理元素查找、动作执行、结果归一化三类事情。其中结果归一化主要是把各种执行结果(成功、超时、元素不存在、弹窗遮挡、网络错误)映射成统一的状态码,方便记忆层识别。

有一个执行层的设计我特别想强调,就是"动作结果别直接信框架的成功标记"。我遇到过很多次,框架层面报告点击成功,但实际没有触发任何界面变化——原因可能是页面 JS 报错、元素被遮挡、或者点击区域被其他控件劫持。所以我在执行层加了一条额外规则:动作执行后,感知层会重新采样屏幕,对比动作前后的截图差分。如果界面没有任何变化,而预期里明确写了"应该发生变化",那这个步骤会被标记为suspicious_success,推动 Agent 进入异常处理分支。

4.4 从任务解析到路径输出的完整跑通示例

链路全打通之后,整个流程是这样的。我拿"模拟用户完成一笔订单退款"来举例。

任务解析阶段,模型把"完成一笔订单退款"的目标拆解成一组里程碑检查点:进入订单列表、定位目标订单、进入订单详情、发起退款申请、选择退款原因、提交申请、确认成功提示。这些里程碑不是具体路径,而是功能不变量——只要最终能到达"退款申请成功"的状态即可,具体点哪个按钮、怎么绕路,Agent 自行决策。

感知规划阶段,Agent 从订单列表页开始,感知当前屏幕,发现自己正处于订单列表页,记忆里可以看到退款流程的里程碑进度是"第一步存在"。模型决策"点击'全部订单'筛选标签,再点击目标订单条目",这个决策依据主要是屏幕描述里"全部订单"标签的高亮状态和订单条目的文本内容。执行层收到指令后完成点击,重新采样屏幕,确认界面跳转到订单详情页,匹配到第二个里程碑。

行程中间会遇到意外。一次实测里,点击提交申请按钮后弹出的不是退款原因选择框,而是"当前订单商品已超过退货时效"的提示。Agent 的思考摘要显示出它识别到了这个异常,检查了记忆里"当前处于退款申请第 4 步"的状态,决策"返回订单详情,重新读取退款政策提示,调整路径为'申请售后'入口"。这个调整花了两步,但最终仍然到达了成功提交的里程碑。这种"路径计划失效时能重新规划绕行"的能力,正是传统的录制回放永远不具备的。

最终 Agent 输出一份测试路径报告,包括完整的操作序列、每个操作前后的屏幕关键变化、异常处理记录、以及最终是否到达目标状态。这份报告可以直接归档为测试资产,也可以反哺给自动化测试平台,作为回归测试的候选路径。

5. 常见问题与排查技巧实录:我在实际跑测中踩过的坑

5.1 截图看得见,但模型就是"读不懂",怎么排查

最典型的故障是模型输出一份看起来合理但完全没依据的路径——它"脑补"了屏幕上不存在的元素。我排查下来,绝大多数情况是截图质量不过关。截图分辨率太低、界面有缩放、或者部分区域被系统状态栏遮挡,都会让模型看不清细节。我的建议是三个动作一起做:截图前先把屏幕分辨率固定到逻辑分辨率,不要用物理像素直接喂给模型;图片统一缩放到模型擅长的输入尺寸,保持宽高比,不要过度压缩;截图后叠加一层元素框选标注,把检测到的关键元素用矩形框画出来再输入给视觉模型,这个技巧能显著提升元素定位准确率。

如果框选后模型还是读不懂,那就要看截图里是不是有"渲染后才加载"的内容。某些页面元素是异步加载的,截图时机太早画面还是白屏或骨架屏。处理方式很简单:截图前先做一次静置等待加上元素出现检测,等页面真正渲染完全了再进入感知阶段。

5.2 弹窗遮挡和动态元素导致的点击错位

测试路径执行中最常见的翻车点是弹窗。Agent 规划好了要点击某个按钮,结果执行时屏幕上突然多了个优惠券弹窗,把按钮盖得严严实实,点击落在了弹窗上。这类问题光靠截图是发现不了的,因为规划时弹窗还没出现。

我的处理办法分两层。感知层在每次重新采样屏幕时,增加一个"阻塞组件检测"——把全屏弹窗、半屏弹窗、底部动作条这几类盖层单独标注出来,并判定它们是否覆盖了目标元素。规划层在拿到阻塞层信息后,优先决策"关闭弹窗"而不是"继续点击目标",关闭动作一般通过点击关闭按钮、遮罩区域或使用返回键实现。这个机制加上之后,弹窗场景的成功率从原来的不到一半提升到了接近正常水平。

动态元素(列表滚动加载、轮播图、倒计时按钮)是另一个高发问题。处理动态元素的核心思路是"等待 + 重采样":当决策依赖某个元素但该元素当前不稳定或不存在时,让 Agent 先执行等待,重新采样屏幕获取元素的最新坐标,再继续。我在实验里给等待动作加了两个约束,等待不超过 10 秒,且每步只能等待一次。这两个约束能有效防止 Agent 把等待当作万能策略无限等待下去。

5.3 长流程中途翻车:状态丢失与"原地打转"

30 步以上的长流程,最容易出现"原地打转"现象:Agent 反复执行点击-返回-再点击,看起来一直在工作,实际进度完全没有前进。我定位到的根因是记忆模块没有正确更新——动作执行成功了,但是新的屏幕状态和里程碑进度没有记录到记忆里,模型每次决策都以为还停留在旧状态。

解决方法是加入"进度脉冲检查"。每完成一次动作循环,就做一次状态摘要对比,把"里程碑状态"序列化成短签名,一旦执行前后签名完全一致且连续重复三次,就触发"原地打转"预警,强制 Agent 放弃当前分支,回到最近一个成功的里程碑重新探索。这个机制在我的实验里基本消灭了无限循环问题。

5.4 成本失控:token 烧得太快怎么办

多模态 Agent 规划的每一步都要输入截图,token 消耗确实不小。我在实际跑测中整理了几个有效的止损手段。第一,截图不是每步都要全量输入——只有界面发生显著变化时才需要完整输入新的截图,界面未变化的步骤可以只输入"界面未变化,沿用上一帧的屏幕描述",这一步能省掉大量重复的图像 token。第二,里程碑状态在记忆中以"短文本摘要"形式保存,而不是截图副本——需要追溯时再重新截屏,不需要每时每刻保留全量截图。第三,为感知层和规划层分配不同的模型大小,感知层(屏幕切片理解)用小模型足够,规划层(决策大模型)用大模型,两种模型配合使用比全程大模型省钱省得明显。

我把几种方案的 token 成本对比了一下,给你一个直观参考:

方案每步平均 token 消耗(含截图)20 步任务总成本(相对值)
全量截图 + 大模型全程约 10000100%
发生变化才截图 + 大模型全程约 550055%
发生变化才截图 + 大小模型分层约 380038%
问题特征推荐解决动作
模型"脑补"不存在元素加元素框选标注后再输入;检查截图分辨率与异步加载
弹窗遮挡目标元素感知层加阻塞层检测;规划层优先决策关闭弹窗
长流程原地打转清理页面栈,落到最近里程碑重新探索;触发进度脉冲检查
步骤执行成功但无界面变化标记为可疑成功;重新采样并修正记忆
连续决策置信度都很低切换更强的模型兜底,完成后切回常规模型

我在实际操作中最后想说的几点

整个多模态 Agent 路径规划系统跑下来,我最大的体会有两个。第一,不要神化 Agent 的"规划"能力,它本质上还是一个逐步决策器,它的可靠性来自每一步的观察-决策-验证闭环,而不是什么全局路径推演。第二,这套方案真正的价值不在"省掉写脚本的人",而在"把测试路径变成可解释、可维护、可演进的知识资产"——每次 Agent 跑出来的路径和决策依据都在报告里,回归测试时可以复用,产品改版后可以用旧路径作为参考快速生成新路径。

最后分享一个我认为值得后续扩展的方向:把 Agent 在测试路径规划中产生的决策报告,和历史版本的产品需求文档、缺陷记录做联想分析,让测试路径的变更原因可以直接追溯到产品逻辑变更的源头。我在实验里已经初步验证了这条路是可行的,只是数据沉淀还少,跑出的结论还不够扎实。如果你也在做类似的事,多留一份决策报告,后面会发现这些数据的价值远超预期。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询