在探索生成式 AI (GenAI) 创新应用的过程中,我偶然发现了一个名为 Stagehand 的自动化框架。它利用 大模型 (LLM) 的强大能力,以自然语言为驱动,无缝地与 Web 用户界面进行交互。这个概念立刻吸引了我,迫不及待地想深入了解。本文将带你了解 Stagehand 的安装过程、第一印象,以及使用过程中遇到的“期望 vs. 现实”时刻。
Stagehand 是 Browserbase 开发的一款强大的高级自动化测试工具。它旨在帮助开发人员和测试人员构建稳定、基于浏览器的 workflow 和测试用例,摆脱 flaky 测试设置或复杂配置的困扰。传统自动化测试常常面临元素定位困难、环境依赖性强等问题,导致测试结果不稳定,维护成本高昂。而 Stagehand 另辟蹊径,通过大模型的理解和执行能力,尝试解决这些痛点。
Stagehand 的核心价值:给测试套件赋予 “手”、“眼” 和 “大脑”
想象一下,你的测试套件拥有了一双“手”和“眼睛”,以及一个“大脑”(大模型),它能够完全通过自然语言来导航和与网页交互。这就是 Stagehand 为你的自动化测试套件带来的力量:将智能、类人的自动化叠加在传统的测试流程之上。 换句话说, Stagehand 将复杂的测试脚本简化为更易于理解和维护的自然语言指令,极大地降低了自动化测试的门槛。
核心功能:page.act()
,page.extract()
和 page.observe()
Stagehand 的核心是三个基本功能,也就是它的 “三剑客”:page.act()
,page.extract()
和 page.observe()
。这些功能使其能够与网页进行交互、理解网页内容并验证网页行为。
-
page.act()
: 让 Stagehand 与网页交互把
act()
想象成赋予 Agent “双手”,它解释自然语言指令并执行相应的操作 (例如,点击、键入)。例如:
await page.act(“click on ‘add to cart’ icon”)
你还可以屏蔽变量,以避免将它们传递给 LLM,正如文档中提到的。这在处理敏感数据或需要精确控制输入内容时非常有用。
-
page.observe()
: 帮助 Stagehand 检查当前页面并识别可能的动作observe()
特别适用于检查元素是否已加载。一个简单的
observe
(没有参数) 返回 LLM 基于当前窗口上下文的建议操作,包括一个 XPath 选择器和一个简短的描述。//Response from the page.observe() { "description": "A brief description of the component", "method": 'click', "arguments": [], "selector": 'xpath=/html/body[1]/div[1]/main[1]/button[1]' }
observe()
的实际应用:const [action] = await page.observe( "Type 'Tell me in one sentence why I should use Stagehand' into the search box", ); await drawObserveOverlay(page, [action]); // Highlight the search box await page.waitForTimeout(1_000); await clearOverlays(page); // Remove the highlight before typing await page.act(action); // Take the action
这段代码首先使用
page.observe()
找到搜索框并建议输入内容。然后,它高亮显示搜索框,短暂等待后清除高亮,最后使用page.act()
执行输入操作。 -
page.extract()
: 使用 Zod 模式从页面提取结构化数据你定义要提取的内容和你期望的格式,Stagehand 将其作为结构化输出返回。
const item = await page.extract({ instruction: "extract the price of the item", schema: z.object({ price: z.number(), }), });
这对于断言或条件流程以验证应用程序是否按预期运行非常有用。例如,你可以提取商品价格并与预期价格进行比较,从而验证价格是否正确显示。
从使用角度来看,Stagehand 充当一个 Orchestrator (编排器),管理 Playwright 脚本的执行,同时利用 LLM 来确定如何与网页交互并推动流程前进。简而言之,Stagehand 提供了一个更高级的抽象层,让测试人员能够专注于测试逻辑,而无需过多关注底层实现细节。
快速上手: Stagehand 安装与配置
要开始使用 Stagehand,可以按照官方的快速入门指南进行安装。首先,你需要使用以下命令创建一个新的 Browserbase 应用:
$ npx create-browser-app
在安装过程中,它会要求你提供一些输入,例如项目名称和选择使用的 大模型。安装完成后,导航到你的项目目录并按照以下步骤开始使用 Stagehand。
-
在项目根目录下创建一个
.env
文件,并添加你的 LLM API 密钥,然后安装 Node 模块。GOOGLE_API_KEY="<YOUR GEMINI API KEY>" //If you are using Gemini Model OPENAI_API_KEY="<YOUR OPENAI API KEY>" //If you are using Open AI Model
$ npm install
-
执行以下命令触发流程:
npm run start
这将在浏览器中打开一个网页并执行开箱即用的测试用例。
Stagehand 的内部机制: LLM 与 WebPage Data 的协同
由于 Stagehand 利用了 LLM 的能力,因此内部会将提供的自然语言连同 WebPage 数据(他们称之为可访问性树数据)一起发送给 LLM,以识别需要执行的操作。通过分析 WebPage 的结构和内容,LLM 可以理解用户意图,并生成相应的操作指令。
在执行测试用例时,终端输出会显示 Stagehand 内部执行的一系列事件,这有助于理解 Stagehand 的工作原理。
将 Stagehand 集成到测试框架 (例如,Jest)
为了更好地理解 Stagehand 的实际应用,我将其集成到了一个测试框架中。
-
首先,使用以下命令安装 Jest 和相关模块 (针对 TypeScript):
$ npm install --save-dev jest ts-jest @types/jest typescript
-
在根目录下创建一个名为
tests
的新文件夹,并添加你的第一个测试文件。添加一个基本测试,如下所示:import { describe, test, expect } from '@jest/globals'; function add(a: number, b: number): number { return a + b; } describe('basic test using stagehand', () => { test('adds two numbers correctly', () => { expect(add(2, 3)).toBe(5); }); });
-
创建一个
jest.config.ts
文件并添加以下内容:import type { Config } from 'jest'; const config: Config = { preset: 'ts-jest', testEnvironment: 'node', testMatch: ['**/__tests__/**/*.test.ts'], //make sure to add the test folder created above here moduleFileExtensions: ['ts', 'js', 'json'], verbose: true, }; export default config;
-
更新你的
package.json
文件,添加以下 npm 命令:package.json > "scripts": { ... //add below "test": "jest", "test:watch": "jest --watch" ... }
-
更新
tsconfig.json
文件,添加以下内容以避免任何问题:{ "compilerOptions" : { ... "allowImportingTsExtensions": true, //use this value "noEmit": true, //use this value ... } }
-
执行基本测试:
$ npm test
如果遇到 ESM 与 CommonJS 问题,可以使用 Copilot 或 ChatGPT 等工具进行调试。
-
编写并执行一个使用 Stagehand 驱动的测试用例。
在设置好测试框架后,我们将 Stagehand 的核心功能分解为三个部分:
- 初始化 — 在
beforeAll
hook 中处理 - 操作 — 直接在测试用例中执行
- 清理 — 在
afterAll
hook 中管理
我们将使用 Stagehand 的默认初始化作为参考,并在
beforeAll
hook 中进行设置。在
tests
文件夹中创建一个名为roombooking.test.ts
的新文件,并将以下代码粘贴到其中:import { describe, test, expect } from '@jest/globals'; import { drawObserveOverlay, clearOverlays } from "../utils.ts"; import { z } from "zod"; import { Stagehand, Page, BrowserContext } from "@browserbasehq/stagehand"; import StagehandConfig from "../stagehand.config.ts"; describe('Automation Test Suite Basic Tests', () => { let stagehand: any; let page: any; let context: any;
beforeAll(async () => { console.log('Setting up the stuff...'); stagehand = new Stagehand({ ...StagehandConfig, }); await stagehand.init(); page = stagehand.page; context = stagehand.context; }); afterAll(async () => { // Cleanup after all tests console.log('🧹 Cleaning up the stuff..'); await stagehand.close(); }); test( 'Open the website, book the suite for the dates provided, book the available suite and verify the booking confirmation', async () => { try { console.log('🔍 Running test :'); await page.setViewportSize({ width: 1280, height: 800 }); //for a full screen view await page.goto(`https://automationintesting.online/`); await page.waitForTimeout(3000); await page.act("scroll 10% down to view the 'Check In' and 'Check Out' fields under the 'Check Availability' section"); await page.act("click on the 'Check In' field and select the date '18/06/2025'"); await page.act('click enter on the "Check In" field'); await page.act("click on the 'Check Out' field and select the date '19/06/2025'"); await page.act('click enter on the "Check Out" field'); await page.act("click on the button 'Check Availability'"); await page.act("scroll down to the next chunk"); const availableOptions = await page.extract({ instruction: "extract the Heading text from each Room card as an array", schema: z.object({ rooms: z.array(z.string()), }), }); console.log("🚀 ~ availableOptions:", availableOptions) expect(availableOptions.rooms).toContain('Suite'); await page.waitForTimeout(2000); await page.act("click on the 'Book Now' button of the room with title 'Suite'"); await page.act("click on the 'Reserve Now' button"); await page.act("Provide input 'John Doe' in the 'Firstname' field"); await page.act("Provide input 'Doe' in the 'Lastname' field"); await page.act("Provide input 'john.doe@example.com' in the 'Email' field"); await page.act("Provide input '+11234567890' in the 'Phone' field"); await page.waitForTimeout(3000); const [action2] = await page.observe( "Identify the button with text 'Reserve Now' and click on it", ); await drawObserveOverlay(page, [action2]); await clearOverlays(page); await page.act(action2); await page.waitForTimeout(2000); const item = await page.extract({ instruction: "extract the text from the card 'Booking Confirmed' on the screen", schema: z.object({ bookingStatus: z.string(), dates: z.string(), }), }); console.log("Extracted items :", item); expect(item.bookingStatus).toBeDefined(); expect(item.bookingStatus).toContain('Your booking has been confirmed for the following dates:'); expect(item.dates).toBeDefined(); expect(item.dates).toContain('2025-06-18 - 2025-06-19'); console.log("✅ Test passed: Booking confirmation verified successfully"); await page.waitForTimeout(5000); } catch (error) { console.error("❌ Test failed:", error); throw error; } }, 100000);
});
上述测试用例将执行以下操作:
- 访问
https://automationintesting.online/
网页。 - 输入入住和退房日期,并检查给定日期内是否有 Suite 房间可用。
- 如果可用,则提供所有必需的信息并预订房间。否则,测试用例将失败。
- 验证预订后是否显示预期的文本。
- 初始化 — 在
我的看法: 大模型 前提条件 & 有限的本地模型支持
我最初的期望是可以使用任何模型,但事实并非如此。Stagehand 最适合支持结构化输出的模型。目前,不建议使用本地模型(例如,Ollama)(尽管它们受到支持),这对于专注于隐私、性能和成本的团队来说可能是一个障碍。由于本地模型在隐私性和成本方面具有优势,因此对本地模型的支持是未来的发展方向之一。
我相信未来的进步将使 Stagehand 更容易与本地模型一起使用。
act
步骤中的静默失败
“act” 步骤中未捕获的错误不会停止执行,除非显式处理。这可能导致大量使用 try-catch 块,从而使脚本更难管理。但是,Stagehand 团队已经意识到了这一点,并且正在努力改进。因此,在编写 Stagehand 测试用例时,需要格外注意错误处理,以确保测试的可靠性。
高度响应的团队
由于 Stagehand 仍处于发展初期,因此团队制定了一些使用它的“推荐方式”。也就是说,Stagehand 团队在 Slack 上能够快速响应,通常会提供切实可行的解决方案。这种积极的支持增强了用户的信心。
尚未完全替代传统的测试套件
Stagehand 对于 AI 驱动的测试自动化很有用,但尚未准备好替代传统的、生产级的测试套件,尤其是在关键系统方面,因为它们需要一致性,而 大模型 有时可能会带来挑战(至少目前是这样)。由于 大模型 的推理过程存在一定的不确定性,因此 Stagehand 在需要高度确定性的场景下可能表现不佳。
准确性取决于模型和步骤表述
该工具在测试步骤明确表述时效果最佳。但是,这引入了波动性,因为解释可能因人而异(这可以通过标准化整个项目中指令的编写方式来缓解)。准确性还取决于模型如何解释每个步骤,这可能导致偶尔的不一致。因此,在编写 Stagehand 测试用例时,需要尽可能清晰和简洁地描述测试步骤。
Stagehand 的未来:走向智能自动化
-
亲身体验 Stagehand Agent
我们可以尝试将 Stagehand Agent 与 LangChain 结合使用。它承诺提供更结构化和流畅的自动化体验。“软件一直都是确定性和可重复性的,但使用 AI Agent 时,很难复制一个 workflow。Stagehand 结合了两者最好的部分:智能和确定性。”——Stagehand
-
将 Stagehand 与 Browserbase 集成
我们可以探索 Browserbase(Stagehand 背后的平台)以了解它如何增强浏览器自动化和高级 workflow。
-
使用 Stagehand 进行 Web Scraping
我们可以测试 Stagehand 的结构化数据提取能力,作为传统 Web Scraping 工具的一种更轻便、更直观的替代方案。
总而言之,Stagehand 通过 大模型 技术为自动化测试带来了新的可能性。它简化了测试脚本的编写,降低了测试门槛,并有望解决传统测试中的 flaky 问题。虽然目前还存在一些局限性,但随着 大模型 技术的不断发展和 Stagehand 自身的不断完善,它有望成为下一代自动化测试的重要组成部分。