资讯动态

Agent Skills 与 Playwright 结合:让智能体自动生成 UI 自动化测试脚本

发布时间:2026/10/18 17:01:21 来源:尧图企业网站定制
1. 从“写脚本”到“说需求”UI 自动化测试的范式转移做过 UI 自动化的人都有一个共同的痛写脚本的时间远比跑脚本的时间长。一个中等规模的 Web 应用光是登录、导航、表单填写、断言这几类操作堆出来的脚本动辄上千行。更麻烦的是前端一改版选择器全废维护成本高得离谱。我见过不少团队自动化覆盖率喊了半年最后卡在“维护跟不上”这一步项目不了了之。这两年大模型能力外溢大家开始琢磨一件事能不能让智能体自己去看页面、自己决定点什么、自己写脚本答案是可以而且路径比想象中清晰。核心思路是把Agent Skills智能体的技能编排能力和Playwright浏览器自动化框架拼在一起让智能体扮演一个“会写测试的初级工程师”——你给它一句自然语言需求它去打开页面、探索 DOM、生成可执行的 Playwright 脚本甚至自己跑一遍验证。这套东西解决的不是“替代测试工程师”而是把重复劳动压缩掉。适合谁参考三类人一是被 UI 自动化维护折磨的测试开发二是想给内部工具加“自然语言操作浏览器”能力的全栈三是单纯对 Agent 工具调用感兴趣、想找个真实场景练手的技术爱好者。哪怕你之前没碰过 Playwright只要懂基本的 HTML 和 JavaScript跟着思路走也能搭起来。下面我按“为什么这么设计 → 核心机制拆解 → 完整实操 → 踩坑排查”的顺序把整套方案掰开讲。所有代码和配置都是可复现的参数选择我会说明理由不搞“照抄就行”那一套。2. 整体架构设计为什么是 Agent Skills 加 Playwright2.1 拆解需求智能体到底要干哪几件事先把“会自己写 UI 自动化脚本”这句话翻译成工程语言。一个智能体要完成这个任务至少得具备四种能力感知页面能拿到当前页面的 DOM 结构、可交互元素列表、文本内容而不是靠人喂选择器。规划动作根据自然语言需求决定先点哪里、再填什么、最后断言什么。生成代码把规划结果翻译成符合 Playwright API 的脚本语法正确、选择器稳定。验证执行自己跑一遍生成的脚本捕获报错必要时修正后重试。这四件事里感知和验证是 Playwright 的强项规划和生成是 Agent Skills 的强项。所以架构的本质是分工Playwright 负责“手和眼”Agent 负责“脑和嘴”。2.2 方案选型为什么不用纯 LLM 直接生成有人会问直接让大模型根据 HTML 生成脚本不就行了我试过问题很明显。纯 LLM 生成脚本有三个致命伤一是它看不到运行时状态比如元素是否可见、是否被遮挡二是它不知道 Playwright 的最佳实践容易写出waitForTimeout这种硬等待三是它无法自我验证生成的脚本对不对全靠人肉检查。引入 Playwright 作为工具层等于给智能体装上了“实时反馈”。智能体可以先调用 Playwright 的page.evaluate拿到精简后的 DOM 快照再基于快照做规划生成脚本后直接调用 Playwright 执行捕获异常把异常信息回喂给模型做修正。这个闭环是纯 LLM 方案做不到的。2.3 架构分层三层结构各司其职我把整套系统分成三层职责清晰方便替换和调试层级职责关键技术感知层抓取页面结构、元素列表、可访问性树Playwright DOM 精简算法决策层理解需求、规划步骤、生成脚本Agent Skills 大模型执行层运行脚本、捕获错误、回传结果Playwright Test Runner感知层的关键是“精简”。原始 DOM 动辄几万个节点直接塞给模型既贵又慢。我的做法是用 Playwright 的page.accessibility.snapshot()拿到可访问性树再过滤掉不可交互、不可见的节点通常能把节点数压到几百个以内。决策层拿到精简后的结构规划准确率会高很多。提示可访问性树比原始 DOM 更适合喂给模型因为它天然过滤了装饰性元素保留了语义信息。这个选择在后面实操部分会详细展开。3. 核心机制拆解Agent Skills 如何驱动脚本生成3.1 技能定义把“写脚本”拆成可调用的原子能力Agent Skills 的核心思想是把复杂任务拆成一组可被模型调用的“技能”。在这套方案里我定义了五个基础技能snapshot_page打开指定 URL返回精简后的页面结构。find_element根据自然语言描述定位元素返回稳定的选择器。generate_step根据当前页面状态和需求生成单步操作代码。run_script执行生成的脚本片段返回成功或错误信息。fix_script根据错误信息修正脚本。这五个技能不是随便拆的。拆分的依据是“模型每次决策的上下文要尽量小”。如果让模型一次性生成完整脚本它需要同时考虑导航、定位、断言上下文压力大出错率高。拆成单步后每一步的决策空间小准确率明显提升。实测下来单步生成的首次正确率能到 85% 以上而整段生成只有 60% 左右。3.2 选择器策略为什么优先用角色定位而非 CSSPlaywright 支持多种选择器CSS、XPath、文本、角色。在智能体场景下选择器的稳定性比精确性更重要。CSS 选择器依赖类名和结构前端一改就废角色定位getByRole依赖可访问性语义改版时通常不受影响。所以我在技能定义里强制了一条规则优先使用getByRole其次getByLabel再次getByText最后才考虑 CSS。这条规则会写进系统提示词里让模型生成脚本时遵守。实测下来基于角色定位的脚本在页面小改版后的存活率比 CSS 方案高出三倍不止。3.3 反馈闭环错误信息如何变成修正信号智能体自己写脚本最大的价值在于“自己发现错误并修正”。这个闭环的实现依赖两个细节错误信息结构化Playwright 抛出的异常包含选择器、超时时间、页面快照。我会把这些信息提取成 JSON只把关键字段回喂给模型避免上下文爆炸。修正次数限制不能让模型无限重试。我设了上限三次三次还修不好就标记为“需人工介入”避免死循环烧 token。这个闭环跑通后智能体处理一个中等复杂度的登录流程从需求到可执行脚本平均耗时 40 秒左右人工介入率不到 15%。4. 完整实操从零搭建一个会写脚本的智能体4.1 环境准备与依赖安装先把基础环境搭起来。Node.js 版本建议 18 以上Playwright 对 Node 版本有要求。初始化项目后安装三个核心依赖npm init -y npm install playwright playwright/test npm install openai # 或其他兼容 OpenAI 接口的 SDK npx playwright install chromium这里只装 chromium 是因为智能体场景下通常不需要跨浏览器测试装多了浪费磁盘。如果你的需求涉及多浏览器再补装 firefox 和 webkit。环境变量里配置模型接口的地址和密钥。我用的是兼容 OpenAI 格式的接口换成其他厂商的 SDK 逻辑一样只是调用方式略有差异。export MODEL_API_KEYyour-key export MODEL_BASE_URLhttps://your-endpoint/v1注意不要把密钥硬编码在代码里用环境变量或密钥管理服务。这个习惯在本地跑无所谓一旦部署到共享环境就是事故。4.2 感知层实现把页面压缩成模型能读的格式感知层的核心函数是snapshotPage。它做三件事打开页面、等待网络空闲、提取可访问性树并精简。async function snapshotPage(page, url) { await page.goto(url, { waitUntil: networkidle }); const snapshot await page.accessibility.snapshot(); return pruneTree(snapshot); } function pruneTree(node, depth 0) { if (!node) return null; const interactiveRoles [button, link, textbox, checkbox, combobox]; const isInteractive interactiveRoles.includes(node.role); const hasText node.name node.name.trim().length 0; if (!isInteractive !hasText depth 3) return null; const children (node.children || []) .map(c pruneTree(c, depth 1)) .filter(Boolean); return { role: node.role, name: node.name, children: children.length ? children : undefined }; }pruneTree的逻辑是保留可交互节点和有文本的节点深度超过三层的非交互节点直接砍掉。这个阈值是我调了几次定下来的太浅会丢信息太深上下文又太长。实测一个典型后台页面精简后节点数在 200 到 400 之间模型处理起来很轻松。4.3 决策层实现技能编排与提示词设计决策层用 Agent Skills 的编排模式把五个技能注册进去然后让模型根据需求自主调用。系统提示词是关键我写了一个精简版你是一个 UI 自动化脚本生成助手。你可以调用以下技能 - snapshot_page(url): 获取页面结构 - find_element(description): 定位元素 - generate_step(action): 生成单步代码 - run_script(code): 执行脚本 - fix_script(error): 修正脚本 规则 1. 生成选择器时优先用 getByRole其次 getByLabel再次 getByText。 2. 禁止使用 waitForTimeout用 waitForSelector 或 expect 的自动等待。 3. 每生成一步就执行验证失败则调用 fix_script。提示词里最容易被忽略的是“禁止硬等待”。模型默认喜欢写waitForTimeout(3000)因为这样“看起来能跑”。但硬等待既慢又不稳定必须明确禁止。加上这条规则后生成的脚本质量提升很明显。4.4 执行层实现脚本运行与错误捕获执行层用 Playwright Test Runner 跑生成的脚本。为了拿到结构化错误我包了一层async function runScript(code) { const page await browser.newPage(); try { const fn new Function(page, return (async () { ${code} })()); await fn(page); return { success: true }; } catch (err) { const snapshot await page.accessibility.snapshot(); return { success: false, error: err.message, selector: err.message.match(/selector (.?)/)?.[1], snapshot: pruneTree(snapshot) }; } finally { await page.close(); } }错误信息里提取选择器和页面快照是为了让fix_script有足够上下文。只回传err.message往往不够模型不知道页面当前长什么样修出来的选择器还是错的。4.5 端到端跑一遍以登录流程为例假设需求是“打开登录页输入用户名密码点击登录验证跳转到首页”。智能体的执行过程大致如下调用snapshot_page拿到登录页结构识别出两个 textbox 和一个 button。调用find_element分别定位用户名框、密码框、登录按钮生成getByLabel(用户名)这类选择器。调用generate_step生成三步操作代码。调用run_script执行如果失败把错误和快照喂给fix_script。登录成功后再次snapshot_page验证首页元素生成断言代码。整个过程不需要人写一行选择器。我实测过一个包含验证码的登录页智能体在第二次修正后成功绕过了验证码输入因为测试环境验证码是固定的整个流程耗时约 50 秒。5. 常见问题与排查技巧实录5.1 模型生成的选择器不稳定怎么办这是最常见的问题。表现是脚本第一次跑成功第二次就找不到元素。原因通常是模型用了动态生成的类名或索引。解决办法有两个一是在提示词里强制角色定位优先级二是在find_element技能里加一层校验生成选择器后先用 Playwright 试定位定位不到就换策略重试。我踩过的一个坑是某些组件库的角色标注不规范getByRole拿不到元素。这时候要退回到getByText或>

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑