Agent Skills + Playwright:让 AI 自己写 E2E 测试并执行的架构拆解
发布时间:2026/10/15 15:00:11 锦皓数字建站

1. 为什么 E2E 测试需要 Agent Skills 而不是写死脚本E2E 测试最让人头疼的地方不是 Playwright 的 API 有多难而是页面永远在变。今天登录框的name属性是email明天前端重构变成user-email今天下单流程三步走完明天加了个优惠券弹窗。你辛辛苦苦维护的测试用例集两周不跑就红一片改起来比写新的还累。Agent Skills 这套机制解决的正是这个矛盾。它不是一个预定义的工具函数库而是一个文件夹里面放一份SKILL.md指令文件、若干执行脚本和参考资源。Agent 读到SKILL.md里的description字段后自己判断当前任务该不该加载这个技能加载后根据自然语言描述现场写代码、执行、读结果、失败重试。换句话说测试脚本不再由人提前写死而是 Agent 每次针对具体页面现场生成。这跟 MCP Tool 的定位完全不同。MCP Tool 是可枚举的原子操作——搜索、发请求、读文件参数固定调用即用。但 E2E 测试是无法穷举的任务页面千变万化交互路径组合爆炸你不可能为每个场景预置一个工具。Skill 给的是自由度和上下文效率MCP 给的是确定性和低延迟。两者不是替代关系而是分层协作。我试过把 Playwright 的完整 API 文档塞进 system prompt结果上下文直接爆掉Agent 反而抓不住重点。而 Skill 的渐进式披露设计——SKILL.md只放高频工作流完整 API 参考下沉到API_REFERENCE.md按需加载——上下文占用差了十倍不止。这就是为什么让 AI 自己写 E2E 测试这件事必须用 Skill 模式来做而不是堆工具定义。本文面向需要 AI 自主生成并执行端到端测试的研发场景交付一套可跟做的架构从SKILL.md配置模板、Playwright 执行脚本到通过 TaoToken 统一 Key/API 通道接入 Claude Code 的完整闭环。核心检索词是 Agent Skills 与 Playwright 协作的 E2E 自动化架构适合测试开发、前端工程和正在搭 Agent 工具链的同学。2. TaoToken 前置统一 Key 与 API 通道接入 Claude Code在让 Agent 跑起来之前得先解决Agent 怎么连上模型这件事。Claude Code 默认走 Anthropic 官方通道但很多团队需要统一管理 Key、统一计费、统一审计。TaoToken 在这里扮演的是 API 网关角色你拿到一个 Base URL 和一个 Key就能把 Claude Code、Cline、Codex 等工具全部指向同一个通道不用每个工具单独配一套凭证。先拿 Key。打开 https://taotoken.net/api-keys 登录后创建一个新 Key复制出来。注意这个 Key 只在创建时完整显示一次丢了就得重建。拿到后先存到环境变量里别硬编码进脚本export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api接下来配置 Claude Code。Claude Code 读取的是~/.claude/settings.json你需要把env段指向 TaoToken 的 Base URL。这里有个关键点Anthropic 兼容接口的路径是/api不要多加/v1否则会 404。配置文件长这样{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: sk-你的key, ANTHROPIC_MODEL: claude-sonnet-4-20250514 } }如果你用的是 Cline 或 Roo Code 这类 VS Code 插件配置项名字不同但逻辑一样Base URL 填https://taotoken.net/apiAPI Key 填你的 KeyModel ID 填claude-sonnet-4-20250514。这三件套——Base URL、Key、Model ID——缺一不可任何一项写错都会导致 401 或模型不存在。Codex 用户走的是~/.codex/auth.json结构略有差异{ OPENAI_API_KEY: sk-你的key, OPENAI_BASE_URL: https://taotoken.net/api }配好之后先别急着跑 E2E用一次最简单的对话验证通道是否通。打开 https://taotoken.net/api 对应的模型对话页面或者直接在 Claude Code 里发一句你好能正常返回就说明 Key 和 Base URL 没问题。这一步很重要因为后面 E2E 失败时你要能区分是模型通道挂了还是是测试脚本写错了。关于 Coding Plan如果你打算长期用 Agent 做编码和测试任务可以了解 https://taotoken.net/coding-plan 它针对高频调用场景做了额度优化。但本文的重点是架构通道配通即可不必纠结套餐细节。3. 可复制配置SKILL.md 模板与 Playwright 执行脚本这一节是全文的核心直接给可复制的文件。目录结构建议这样组织playwright-skill/ ├── .claude-plugin/ │ └── plugin.json └── skills/ └── playwright-skill/ ├── SKILL.md ├── run.js ├── API_REFERENCE.md ├── package.json └── lib/ └── helpers.js先写SKILL.md。它的 YAML frontmatter 是 Agent 召回的关键description要写具体能力和触发条件别写空话--- name: playwright-skill description: Complete browser automation with Playwright. Auto-detects dev servers, writes clean test scripts to /tmp. Test pages, fill forms, take screenshots, check responsive design, validate UX, test login flows, check links, automate any browser task. Use when user wants to test websites, verify UI behavior, or run end-to-end checks. --- # Playwright Browser Automation ## 工作流 1. 检测本地 dev server调用 require(./lib/helpers).detectDevServers() - 命中 1 个直接用多个让用户选0 个询问 URL 2. 测试脚本一律写到 /tmp/playwright-test-*.js不污染项目目录 3. 默认可见浏览器headless: false slowMo: 100 4. URL 参数化检测到的地址放进 TARGET_URL 常量 5. 断言只用条件等待waitForURL / waitForSelector / waitForLoadState禁止固定 sleep ## 执行方式 cd $SKILL_DIR node run.js /tmp/playwright-test-xxx.js ## 禁止事项 - 禁止直接 node /tmp/xxx.js必须走 run.js 统一入口 - 禁止在脚本里硬编码 URL - 禁止使用 page.waitForTimeout() 做断言run.js是零模块解析错误的关键。它做三件事统一代码输入方式、防御性包装 LLM 生成的代码、chdir 到 skill 目录保证require(playwright)能解析。核心逻辑如下const fs require(fs); const path require(path); function getCodeToExecute() { const args process.argv.slice(2); if (args.length 0 fs.existsSync(args[0])) { return fs.readFileSync(path.resolve(args[0]), utf8); } if (args.length 0) return args.join( ); if (!process.stdin.isTTY) return fs.readFileSync(0, utf8); process.exit(1); } function wrapCodeIfNeeded(code) { const hasRequire code.includes(require(); const hasAsyncIIFE code.includes((async () {); if (hasRequire hasAsyncIIFE) return code; if (!hasRequire) { return const { chromium, firefox, webkit, devices } require(playwright); const helpers require(./lib/helpers); (async () { try { ${code} } catch (error) { console.error(Automation error:, error.message); process.exit(1); } })();; } return code; } function cleanupOldTempFiles() { const files fs.readdirSync(__dirname); files .filter(f f.startsWith(.temp-execution-) f.endsWith(.js)) .forEach(f fs.unlinkSync(path.join(__dirname, f))); } process.chdir(__dirname); cleanupOldTempFiles(); const code wrapCodeIfNeeded(getCodeToExecute()); const tempFile path.join(__dirname, .temp-execution-${Date.now()}.js); fs.writeFileSync(tempFile, code, utf8); require(tempFile);package.json只需要声明 Playwright 依赖{ name: playwright-skill, version: 1.0.0, dependencies: { playwright: ^1.48.0 } }lib/helpers.js里放 dev server 检测逻辑扫描常见端口const net require(net); const COMMON_PORTS [3000, 3001, 5173, 8080, 8000, 4200]; function checkPort(port) { return new Promise(resolve { const socket new net.Socket(); socket.setTimeout(300); socket.on(connect, () { socket.destroy(); resolve(true); }); socket.on(timeout, () { socket.destroy(); resolve(false); }); socket.on(error, () resolve(false)); socket.connect(port, 127.0.0.1); }); } async function detectDevServers() { const results await Promise.all(COMMON_PORTS.map(async p ({ port: p, alive: await checkPort(p) }))); return results.filter(r r.alive).map(r http://localhost:${r.port}); } module.exports { detectDevServers };装依赖cd skills/playwright-skill npm install npx playwright install chromium。这一步会下载 Chromium 二进制国内网络可能需要几分钟耐心等。4. 验证请求一次完整的 Agent 测试会话与成功结果配置就绪后验证流程分三步先确认通道通再确认 Skill 被召回最后确认测试脚本能跑通并返回截图。第一步在 Claude Code 里发一句自然语言任务测一下本地登录流程看看能不能正常跳转到 dashboard。Agent 应该先读取SKILL.md然后调用detectDevServers()检测本地服务。如果检测到http://localhost:3001它会生成一个测试脚本写到/tmp/playwright-test-login.js内容大致如下const { chromium } require(playwright); const TARGET_URL http://localhost:3001; (async () { const browser await chromium.launch({ headless: false, slowMo: 100 }); const page await browser.newPage(); await page.goto(${TARGET_URL}/login); await page.fill(input[nameemail], testexample.com); await page.fill(input[namepassword], password123); await page.click(button[typesubmit]); await page.waitForURL(**/dashboard); console.log(Login successful, redirected to dashboard); await page.screenshot({ path: /tmp/login-result.png }); await browser.close(); })();注意断言方式waitForURL(**/dashboard)是条件等待不是固定 sleep。这保证了脚本天然具备 Playwright 的自动等待语义不会因为网络慢就 flaky。第二步Agent 执行cd $SKILL_DIR node run.js /tmp/playwright-test-login.js。run.js会 chdir 到 skill 目录、清理上次的临时文件、把脚本包装后写入.temp-execution-*.js、用require()执行。控制台应该输出Login successful, redirected to dashboard同时/tmp/login-result.png生成截图。Agent 读到这个输出后判断测试通过返回结论给你。第三步故意制造失败验证自我修正能力。把登录密码改成错的Agent 执行后会捕获到waitForURL超时退出码非零。Agent 读到错误栈后会重新分析页面结构可能发现是选择器写错了改写脚本重试。这个生成-执行-读错-重写的循环就是 Agent Skills 相比固定脚本的核心优势。如果你在验证时遇到模型不返回先检查 TaoToken 通道打开 https://taotoken.net/api 的模型对话页面发一条消息能回就说明通道没问题问题在 Skill 配置。如果模型返回了但 Skill 没被召回检查SKILL.md的description是否写得太泛Agent 需要明确的触发词才能加载。5. 本篇常见错误排查401、local proxy failed、reading choices、OAuth排障这节按真实报错来每个都给出定位路径和修复动作。401 Unauthorized。最常见的原因是 Key 写错或 Base URL 路径不对。先确认ANTHROPIC_BASE_URL是https://taotoken.net/api不要写成https://taotoken.net/api/v1。然后确认ANTHROPIC_AUTH_TOKEN是完整的sk-开头字符串没有多余空格。如果用的是 Cline检查它读的是settings.json还是插件自己的配置面板两处可能不一致。修复后重启 Claude Code环境变量不会热加载。local proxy failed / connection refused。这个报错通常出现在你本地配了代理但代理没启动或者ANTHROPIC_BASE_URL指向了localhost而本地没有服务。检查settings.json里有没有残留的http://127.0.0.1:xxxx配置全部清掉换成 TaoToken 的地址。另外确认系统环境变量里没有HTTP_PROXY/HTTPS_PROXY指向失效的本地端口。reading choices / Cannot read properties of undefined。这是 Playwright 脚本层面的错误不是通道问题。典型场景是 Agent 生成的代码里page.click()的选择器匹配不到元素或者waitForURL的 glob 写错了。定位方法看run.js输出的错误栈找到具体行号然后在浏览器里手动验证选择器。修复方式是让 Agent 重新生成或者在SKILL.md里补充该页面的选择器约定。OAuth / authentication_error。如果你之前用 Claude Code 登录过 Anthropic 官方账号它可能缓存了 OAuth token跟settings.json里的ANTHROPIC_AUTH_TOKEN冲突。解决方法是清掉~/.claude/下的凭证缓存文件只保留settings.json里的 Key 配置。Codex 用户同理检查~/.codex/auth.json里有没有旧的OPENAI_API_KEY残留。MODULE_NOT_FOUND: playwright。这是run.js的 chdir 没生效或者node_modules没装。确认执行命令是cd $SKILL_DIR node run.js 脚本而不是在项目根目录直接node run.js。然后确认skills/playwright-skill/node_modules/playwright存在不存在就重新npm install。Skill 没被召回。Agent 完全没读SKILL.md直接开始瞎写。检查.claude-plugin/plugin.json是否存在且格式正确Claude Code 靠它发现插件。然后检查SKILL.md的description是否包含用户任务里的关键词比如用户说测登录description 里最好有 login flows。排障时记住一个原则先分层再定位。通道层TaoToken Key/Base URL、召回层SKILL.md/plugin.json、执行层run.js/Playwright三层分开验证不要混在一起猜。6. 从技能定义到测试落地的闭环CTA 与长期实践把整条链路串起来看TaoToken 提供统一的 Key 和 API 通道Claude Code 作为 Agent 运行时SKILL.md定义技能边界和触发条件run.js作为统一执行入口消除环境不确定性Playwright 负责真实浏览器操作。四层各司其职任何一层出问题都能独立排查。这套架构的价值不在于用 AI 写测试这个表象而在于三个可复用的工程模式。第一渐进式披露控制上下文成本——SKILL.md只放高频工作流完整 API 参考按需加载比把整本文档塞进 prompt 高效得多。第二防御性代码包装兜底 LLM 输出——Agent 生成的代码经常缺require、缺 async 包装run.js自动补全并加 try-catch把未捕获异常转成退出码Agent 能据此自我修正。第三统一执行入口消除环境不确定性——process.chdir(__dirname)解决模块解析临时文件延迟清理避免竞态这些细节决定了 Agent 能不能稳定跑通。实践建议本地探索用headless: falseslowMo: 100执行过程肉眼可见截图返回结果人机都能确认发生了什么跑 CI 时切headless: true让脚本以退出码表达结果截图归档做失败排查。TARGET_URL一律参数化否则脚本无法复用。给自动化流量打标通过自定义 Header 让后端识别这是测试流量还能要求返回纯文本响应降低 Agent 解析成本。如果你要把这套模式接进 CI把 Agent 生成的脚本沉淀为固定用例headless 模式跑进流水线。视觉回归方面API_REFERENCE.md里覆盖了截图对比配合像素级 diff 可以做无人值守的 UI 回归。多 Agent 协作场景下Skill 模式会是标准化底座——给 Agent 装技能比给它一堆工具参数更接近它真正需要的自由度和可靠性。通道配置和 Key 管理走 https://taotoken.net/api-keys 接入文档在 https://taotoken.net/doc 模型验证用 https://taotoken.net/api 的对话页面长期编码和 Agent 任务可以看 https://taotoken.net/coding-plan 。先把通道配通再把SKILL.md和run.js落地最后用一次真实登录测试跑通闭环这套架构就能在你自己的项目里复用了。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。