
写UI自动化的人大概都有过这种体验脚本昨晚还跑得好好的今早一来发现全红了。查了半天原因不过是页面上一个按钮挪了位置或者某个弹窗的文案改了一个字。改完脚本下午又冒出一个新问题。测试代码的维护成本常常比产品迭代本身还高。这正是我想聊的“多模态 Agent 规划 UI 测试路径”要解决的事让机器不再依赖写死的定位器和步骤而是每次运行都像人一样看一眼屏幕、想一下“现在该干什么”、再决定下一步点哪里。这个方向不是概念Demo也不是遥不可及的研究课题在现有工具基础上完全能搭出一套可用的方案。这篇文章适合两类人一类是被繁琐用例维护折磨的测试开发想找一条降低维护成本的路子另一类是关注多模态大模型应用落地的开发者想看看视觉语言模型在端到端测试里到底怎么使。我会把整体思路、系统架构、核心实现细节、模型选型和常见坑都拆开讲过程和代码示例都是可以直接拿去改的。1. 让 Agent “看懂”界面多模态能力解决的根本问题1.1 为什么传统的UI测试自动化会卡在“规划”这一步传统UI自动化有两条主流路径它们都在“规划”这件事上卡过脖子。第一条是录制回放加脚本固化。录制的时候鼠标点了一下“登录”按钮脚本里就记下一个“idlogin-btn”的坐标操作。下次页面改版按钮id变了脚本就废了。第二条是基于图像识别的点击按预先截好的图片模板去匹配屏幕上的位置模板稍微有点偏差就匹配不上而且路径还是死的先点A再等B出现再输入C。这两条路共同的毛病是它们只关心“怎么执行”不关心“为什么执行”。它们不理解当前页面上发生了什么也不理解目标是什么更不具备“这一步走不通就换一条路”的能力。当我们讨论“规划 UI 测试路径”的时候指的正是这种能力给定一个测试目标比如“从首页进入商品详情把商品加入购物车再进入结算页确认金额”Agent 需要把这个目标自动分解成一系列可执行的UI操作同时根据执行过程中的反馈动态调整后续步骤。注意这已经不是“写脚本”而是“做决策”。传统工具给不出这个决策能力因为它们看不到屏幕也没有推理能力。1.2 多模态 Agent 在整个链路里补上了哪几块拼图“多模态 Agent”这个说法里最关键的能力有四个看懂界面、理解目标、拆解任务、适应变化。看懂界面直接读截图识别按钮、输入框、列表项、图标不需要依赖稳定的DOM id或XPath。界面长什么样它就看到什么。理解目标读入自然语言描述的测试目标知道“加入购物车”和“立即购买”是不同的意图。拆解任务把一个高层目标拆成子步骤比如先登录、再搜索、再筛选、再点击价格排序。适应变化页面上多了一个推荐浮层、按钮位置变化、文案从“确认”改成“确定”只要语义不变Agent可以基于视觉判断继续执行。举个例子。目标要“在订单列表里找到昨天那笔金额为128元的订单并查看详情”。传统脚本必须预先定义“昨天的订单在哪一行详情按钮的坐标是多少”多模态 Agent 则可以先看列表截图判断哪个卡片是目标订单再决定点击卡片上的“查看详情”。页面布局怎么改都不影响只要这张卡片上的信息还在。说句实在话不要神话这个能力。它不是通用人工智能本质上是“把感知从结构化的DOM扩展到像素级视觉再用语言模型做推理”。但这种组合恰好补上了UI测试规划环节最缺的感知与推理空白。2. 路径规划系统怎么搭一条可落地的实践架构2.1 感知层截图、UI树、坐标数据怎么合成“统一状态”多模态 Agent 的第一步是感知当前界面状态。最直接的状态来源是截图但只有截图还不够。截图是像素语言模型虽然能看图但不一定精确定位到“这个按钮中心坐标是(520, 340)”。所以在实际工程里我倾向于用“截图 结构化元素信息”双通道合成状态。结构化元素信息从哪来呢常见来源是UI框架的辅助功能树Accessibility Tree、Web端的DOM解析、移动端的view层级。拿到的信息包括元素类型、文本内容、可视状态、边界坐标等。关键步骤是把这些原始数据清洗成Agent可用的格式[ {type: button, text: 登录, bounds: [120, 480, 240, 520]}, {type: input, text: 用户名输入框, placeholder: 请输入用户名, bounds: [120, 300, 400, 340]}, {type: button, text: 注册, bounds: [260, 480, 380, 520]} ]如果拿不到UI树也可以用“可点击区域预测”模型从截图反推出候选元素框但稳定性不如直接解析。双通道的另一个好处是交叉验证当UI树缺文本、截图模糊时两个通道可以互相补位。状态合成之后要给每个可操作元素分配一个可控的ID路径规划模块只操作这些ID不直接跟坐标打交道。坐标是执行层的事。这样设计一来隔离变化二来让Agent的动作空间极小化提升决策准确性。2.2 认知与决策层把大语言模型的推理能力约束在动作空间里决策层是整条链路的核心。它的输入是当前状态、目标描述、历史操作记录输出是下一个动作。但注意不能让模型完全自由输出想做什么必须把输出约束在一个有限的动作空间里。动作空间定义得越小出错率越低。我这里常驻一套动作原语基本够覆盖绝大多数UI操作场景动作原语参数适用场景clickelement_id点击按钮、链接、选项卡inputelement_idtext在输入框填写文本scrolldirectionelement_id可选页面或列表滚动press_keykey_name回车、返回、Esc等waitseconds等待异步渲染、动画结束assertcondition描述校验目标状态是否达成动作空间里还可以加一个“finish”原语表示“我判断目标已达成”。这个原语很关键因为Agent需要显式告诉系统“任务完成了”系统才能对结果做校验而不是无限循环下去。决策层接收这些原语描述之后要做两件事第一判断当前状态和目标之间还有多少距离第二选一个能缩短距离的动作。以登录场景为例Agent看到用户名和密码输入框以及登录按钮结合目标“登录成功”它会判断出“先填账号密码再点登录”。这中间所有的思考过程我建议让它显式输出出来也就是常说的思维链。显式思考不仅能提升推理质量更重要的是出问题的时候你能看到它到底是怎么想的。2.3 记忆与回溯层解决“中间走错一步”的兜底问题规划路径不是一条直线走到底的。执行过程中页面可能出现意外弹窗、点击没有反应、加载超时等情况。如果Agent没有记忆能力它会把同一个动作反复执行形成死循环。所以记忆与回溯层必须有。我在项目里采用的做法是维护一个“状态-动作历史”列表每执行一步就把当前界面摘要、动作、结果追加进去。在做下一步决策时把最近几轮历史信息连同当前状态一起交给模型。这样模型能看到上下文知道“上一步我点了登录但没反应”或者“这个弹窗我刚刚已经关过一次”。回溯逻辑上我设计了三档兜底第一档动作重试。动作执行后界面状态没变化先重试一次排除偶发性的点击未命中。第二档状态恢复。连续两次同一动作无效执行“回退”动作比如按返回键、关闭弹窗、重新进入当前页面再继续尝试。第三档路径重规划。累计无进展超过阈值放弃当前路径重新读一遍目标从当前状态出发规划新的操作序列。这相当于Agent自己给了自己一次“重新考试”的机会。这一层是整个系统能否真正长期跑下去的胜负手。没有记忆与回溯多模态 Agent 就是一个更聪明的哑工具有了它路径规划才变成有反馈闭环的规划。3. 实操拆解从一条需求到一条可执行的测试路径3.1 任务输入把自然语言需求转换成可验证的目标路径规划的第一步不是动作是目标定义。目标定义的质量直接决定最终路径的质量。很多失败案例根源都在于目标太模糊。举个实际例子。需求方说“测试一下下单流程”。这个目标太宽了Agent不知道是PC端还是移动端也不知道要不要登录更不知道下单的商品是哪个。我实践下来推荐把目标拆成三个要素前置条件、操作路径、预期结果。于是“测试一下下单流程”应该改写成这样前置条件用户已登录购物车中有商品「机械键盘」 操作路径进入购物车 → 点击“去结算” → 确认收货地址 → 选择支付方式 → 提交订单 预期结果出现“订单提交成功”页面订单状态为“待支付”Agent拿到这个目标之后需要把它转成一个内部的“状态断言集合”。比如预期结果对应一系列可检查条件页面上存在“订单提交成功”文本、存在“去支付”按钮、没有出现错误提示。这个集合就是路径规划过程中的终止条件。还有一个实用建议目标里的操作路径不要写得太细点到为止。因为Agent自己会根据实际界面调整顺序。写得太细反倒束缚了它的适应能力。比如“进入购物车 → 去结算 → 提交订单”就够了不用指定“先点右上角图标再点第二个Tab”。3.2 状态表示截图 解析树谁为主、谁为辅前面提到过双通道状态实际处理时有一个主次问题。我的默认策略是解析树为主截图兜底加确认。为什么解析树为主因为结构化文本信息对语言模型来说更精确坐标计算不容易出错。“截图坐标输入给模型让它自己找按钮”这种事情我也试过成功率受截图分辨率和模型视觉能力影响很大。模型明明看到了按钮但标出的坐标偏了十几像素点击就落在旁边区域整个路径就断了。所以我把解析树里的元素坐标作为唯一可信坐标截图的作用是语境理解。模型通过截图感知整体布局理解“这是一个确认弹窗”“这是一个筛选面板”但他要点击的坐标统统从解析树的bounds字段来。有一个细节要注意截图的分辨率必须归一化。不同设备的视口尺寸差异很大同一布局下按钮的坐标在1080p和2K屏幕上完全不同。我在工程里统一把截图缩放到宽度1024像素同时把解析树里的bounds按同比例缩放这样模型看到的视觉比例和坐标比例是一致的不会出现“看着在上面坐标在下面”的错位。3.3 动作生成与路径选择规划循环里具体发生了什么理解了状态表示现在进入核心一个规划决策循环到底怎么跑。我用一段伪代码帮你串一遍state get_initial_state() history [] for step in range(max_steps): # 1. 判断是否已完成 if evaluate_goal(state, goal): return PASS # 2. 判断是否卡死 if is_stuck(history): return FAIL # 3. 调用决策模型输出下一个动作 action llm_decision( goalgoal, statestate, historyhistory, action_spaceACTION_SPACE ) # 4. 执行动作 result execute_action(action) # 5. 获取新状态记入历史 state get_state_after_action(action, result) history.append((action, result, state))实际跑起来第3步会多一点细节。我要求决策模型按固定JSON格式输出思考过程和动作{ thought: 当前页面是购物车页目标商品已在列表中需要点击去结算按钮。按钮文本为去结算位于列表底部。, action: { name: click, params: {element_id: btn_checkout} } }固定格式的好处是解析稳定不用写一堆易碎的文本抽取逻辑。模型偶尔会不守规矩输出一大堆解释文字我通常用两条对策一是提示词里给一个严格的few-shot示例二是解析失败时直接让模型重新输出一次。路径选择方面简单的线性任务用贪心策略就够了——每次选一个最像“下一步”的动作执行下去。但多步任务里贪心容易掉进局部陷阱比如为了“点击价格排序”提前退出了筛选页导致后面找不到目标商品。更稳的做法是带一点前瞻的树搜索每一步不是只选一个动作而是让模型生成2到3个候选动作分别模拟执行预测哪个候选更接近目标然后再走一条路。这种策略成本高但复杂任务的成功率明显提升。3.4 多步骤任务的边界会话型操作怎么分解UI测试路径很多不是线性的是带会话状态的。最典型的是“搜索 → 筛选 → 点击结果 → 下单”每一步都依赖上一步的产出。比如购物车里预先放商品这个前置条件本身可能也要通过几步操作来完成。这时候路径规划要区分两类子任务检索型子任务和操作型子任务。检索型子任务的目标是找到某个信息比如“在搜索结果里找出价格低于200元且评论数超过1000的商品”它的输出是一个元素ID。操作型子任务的目标是改变系统状态比如“把该商品加入购物车”它的输出是一组动作序列。我在系统中引入了一个“子目标队列”。Agent拿到总目标之后先拆出一组有序的子目标每个子目标有独立的完成条件。规划循环内层跑单步决策外层循环检查子目标是否达成、决定是否推进到下一个子目标。总目标完成特定商品的下单流程 子目标1确保目标商品在购物车 子目标2进入结算页并确认金额 子目标3提交订单并校验成功页这个分层的意义很大。它把“一口气想完整个路径”的压力降成了“每一步只想当前子目标”。模型不容易因为路径太长而跑偏中途出错也只影响当前子目标的重规划。4. 模型选型、提示词设计与性能调优4.1 模型选型视觉能力、指令遵循能力与成本的平衡模型选型是很多人问的第一件事。多模态大模型主要分两类商用闭源模型和开源模型。商用模型的视觉理解能力强对复杂页面的理解和文本识别都比较稳但按调用量计费跑一个长路径任务可能消耗不少token开源模型可以本地部署成本可控但视觉能力上限普遍低一截在复杂页面上容易看漏元素。我试过几条路结论是这样视觉能力强、指令遵循弱的模型适合做“界面理解”比如从截图中提取页面结构、识别元素文本但不适合做精细决策因为容易输出不合法的动作格式。指令遵循强、视觉弱的模型适合做“决策”前提是给它足够丰富的结构化状态文本别指望它从截图中抠细节。视觉和推理都强的高端模型首选但贵适合跑高频的核心回归场景。更经济的实践是组合方案用轻量视觉模型做界面理解生成结构化元素清单再由强推理模型基于这份清单规划路径。截图给推理模型看是为了了解语境关键的信息抽取和坐标定位交给轻量模型。这个组合把成本压了一半以上成功率并没有明显下降。如果你有四张以上显卡的资源完全可以把头部开源VLM部署起来跑全部环节如果只有一台开发机我建议以API调用为主。选型的时候记得用真实目标界面试跑不要只看榜单指标榜单上那些通用数据集的成绩不能代表它在你们业务页面的表现。4.2 提示词与动作空间约束把Agent的“自由发挥”关进笼子提示词设计决定了Agent行为的上下限。我踩过最大的坑是提示词写得“太开放”。一开始我写的是“请你完成以下UI测试任务”结果模型各种思路发散动不动就输出一些动作空间之外的操作甚至自己发明新的交互方式。后来我改成“你只能从以下动作原语中选择”并且给出严格JSON格式情况立刻好转。这里放一个我常用的提示词模板你可以按需修改你是一个UI测试Agent任务目标{goal} 当前界面元素列表 {elements} 你已经执行过的操作历史 {history} 请判断当前状态和目标之间的距离选择下一个动作。 只允许输出以下动作之一 - click: 点击指定元素 - input: 输入文本到指定元素 - scroll: 滚动页面 - press_key: 按键 - wait: 等待 - finish: 确认任务完成 输出格式要求严格JSON {thought: 你的判断依据, action: {name: 动作名, params: {element_id: 元素ID或参数}}} 注意 1. 动作目标必须是元素列表里真实存在的元素ID。 2. 如果页面弹出非目标相关的弹窗优先关闭它。 3. 如果连续多个动作没有产生状态变化主动报告卡点。 4. 不要执行任何与目标无关的操作。最后那几条注意是实战中积累出来的“护栏”。弹窗问题不写进去Agent就可能被弹窗挡住思路反复点目标元素但永远点不中。卡点不写进去Agent会在一个死局里无限循环直到超时。温度参数建议设为0或接近0。路径规划不需要创造性需要的是稳定可复现的判断。高温度会让同一个任务两次跑出完全不同的路径这在测试领域是致命的测试结果无法归因你分不清是产品Bug还是Agent发挥失常。4.3 评测指标与数据集构建路径规划做得“好”怎么量化规划能力怎么评价不能靠感觉。我日常跟踪四个核心指标任务成功率SR目标达成且校验通过的比例。这是最重要的一票否决项。平均路径长度完成一个任务平均需要的操作步数。步数越少执行越高效。对比人类操作员的标准流程可以直观看出规划有没有绕路。鲁棒性对同一目标重复执行10次成功完成的比例。波动大的系统没法用作回归测试。成本指标每次任务消耗的token数。这个指标直接决定方案能不能长期跑。评测数据集是另一个容易被忽略的工程点。想要客观评价Agent的规划能力必须有覆盖各类场景的测试任务集。我从项目一开始就在维护一个“UI任务语料库”每个任务包含目标描述、入口页面截图、操作步骤标注、预期终态截图。语料库覆盖登录注册、列表筛选、商品交易、设置修改、异常场景五类数量不需要特别大50条跑一轮就能暴露很多问题。特别要强调的是数据里必须包含“变化后的界面”。同一个任务准备两三种不同布局的版本——按钮换位置、文案微调、增加推荐位浮层。只有在这种数据上评测才能看出Agent是不是真的有适应能力而不只是背下了固定步骤。5. 常见问题与排查技巧实录规划系统跑起来之后真正的问题往往不在“模型的聪明程度”而在一些特别琐碎的地方。我把实际运行中碰到的高频问题整理成了一张表每一条都是花过时间换来的。现象可能原因排查思路Agent反复点击同一个元素但无反应元素被遮罩层覆盖点击穿透到遮罩上检查界面是否存在透明浮层在执行click前先检测元素可见性与z-index输入框文本填不进去焦点被抢或者输入框在屏幕外点完输入框后先wait 200ms再输入必要时先scroll到元素可见截图和解析树坐标错位设备像素比DPR不一致统一以逻辑像素为准截图缩放时同步缩放bounds任务在中途崩溃且无任何有效行动目标描述有歧义或页面出现非预期状态回看思维链输出检查提示词第2、3条护栏是否生效Agent把“关闭弹窗”当成主线任务反复执行弹窗关闭后新弹窗又出现形成弹窗循环加入弹窗出现频次统计超过阈值判定环境异常并中止同一个任务两次执行路径差异很大模型温度设置过高或界面存在异步内容温度改0增加wait动作给页面渲染留时间滚动列表时始终滚动不到目标元素加载是懒加载滚动一次未触发把“scroll 状态检查”包成复合动作滚动后判断列表尾部是否变化再决定继续排障方法论上面我自己的心得是先看思维链再看动作记录最后才看模型本身。Agent路径规划链条上任何一个环节出问题都会表现为“任务没完成”但归因路径完全不同。思维链能告诉你它是否理解错了目标动作记录能告诉你它是否选错了元素这两项都正常才值得怀疑模型选型或参数配置。还有一个很容易被忽略的细节日志。Agent 的每一步动作、模型原始输出、执行后的截图、UI树摘要必须全部落日志。不要嫌日志量大一次失败任务的全量日志是定位问题的唯一线索。我习惯给每一步日志打上时间戳和回合号方便失败后按时间线回放现场。此外异步页面是最常见的坑。现代前端大量使用异步渲染点击按钮后数据要几百毫秒才出来。Agent如果立刻截图判断状态会以为点击无效。我的做法是执行动作后强制附带一个动态等待条件——等待页面出现网络请求完成的标志或者等待目标区域DOM变化超时再报错。别用固定sleep一套固定sleep在不同网络环境下根本不可靠。再分享一个我比较喜欢的小技巧给Agent加一个“自检”动作。当它执行完一连串操作准备宣告“finish”时要求它先回答三个问题目标里的每个预期结果我都在当前页面上看到了吗有没有被弹窗挡住还没看到的区域如果答案有任何不确定性不要finish继续探索。这一个自检让我把任务成功率硬提升了差不多8个百分点。路径规划做完一轮之后还有个工程问题结果怎么汇报。我习惯让Agent最终输出一份“路径执行报告”包括成功与否、执行步骤列表、关键节点截图、失败原因。这个报告直接对接现有的测试报告系统领导看一眼就能判断发布是否安全。最后说点个人的体会。做这个方向之前我以为难点全在“规划算法”上做下来才发现真正的难点在感知准确性和工程稳定性。Agent能不能规划出一条漂亮路径前提是它得能准确看到当前界面上有什么。视觉模型识别错了元素推理再好也没用感知对了规划反而是一件顺理成章的事。所以如果你准备上手做我建议先把感知层的截图和UI树融合做扎实把动作原语设计得边界清晰再谈模型选型。倒是模型本身现在市面上的选择已经不少反而不是最稀缺的资源。如果你已经在用类似方案或者正准备搭建希望这篇里的动作空间设计、回溯机制、提示词护栏能帮你少走几步弯路。真跑出问题来欢迎带着日志来交流我很好奇不同业务下的坑长什么样子。