ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Stagehand:基于多模态理解的AI网页自动化框架

Stagehand:基于多模态理解的AI网页自动化框架 简介Stagehand 是一个面向开发者与测试工程师的 AI 驱动型浏览器自动化框架作为 Playwright 的轻量级继任者专为自然语言驱动的 Web 自动化任务设计适用于 AI 工程师构建智能爬虫、自动化测试脚本或低代码测试平台等场景。资源包共 153 个文件以 124 个 TypeScript 源码文件含核心 API 实现与工具模块为主辅以 9 个 Markdown 文档含快速入门与配置说明、6 个 JSON 配置文件如 evals.config.json、config.json 等、3 个 PNG 图标及 HTML 示例页整体仅 1.11MB结构精简、开箱即用。已有 436 人学习下载适合中高级前端/全栈开发者快速上手 AI 浏览器自动化开发。读者可直接获取完整可运行框架源码、多环境配置模板.env.example、settings.json、弹窗专项支持示例browserbase_stagehand-361-support-popups、标准化项目配置tsconfig.json、package.json、prettierrc及规范化的工程实践pull_request_template、.gitignore具备强复用性与二次开发基础。1. Stagehand 不是又一个 Playwright 封装而是把「让 AI 看懂网页」这件事拆解成可调度、可验证、可替换的三块积木你写过这样的测试脚本吗await page.click(button#submit)→ 改成await page.click(提交订单按钮)Stagehand 正是把后一种写法变成生产级可行路径的框架。它不替代 Playwright而是在其之上构建了一层语义理解层——不是靠 selector 匹配 DOM而是让 LLM 先理解页面结构、用户意图、交互上下文再生成精准操作指令。这意味着测试用例可以由产品同学用自然语言描述“在购物车页点击‘去结算’跳转后检查收货地址是否默认选中”工程师只需调用stagehand.run()即可驱动浏览器执行当页面重构导致 selector 失效时传统脚本全挂Stagehand 却能靠视觉文本DOM 三模态分析自动适配新结构。它面向的是需要高频迭代、多端兼容、非技术角色参与验收的 Web 应用场景比如电商结算链路、SaaS 后台权限配置、金融表单提交等对稳定性与可读性双高要求的领域。从.env.example和config.json的存在能看出它默认支持模型 provider 切换OpenAI / Anthropic / 本地 Ollama而peeler.html和cart.html这类命名文件暗示其内置了典型电商页面的解析模板——这不是玩具项目是为真实业务流设计的轻量级 AI 浏览器自动化协议栈。2. Stagehand 的三层 API 架构从页面理解到动作执行再到结果验证的闭环设计Stagehand 的核心价值不在“能做自动化”而在“如何让 AI 可靠地做自动化”。它把整个流程拆解为三个明确职责的 APIstagehand.page()负责页面状态建模stagehand.act()执行原子动作stagehand.assert()验证预期结果。这种分层不是为了炫技而是为了解耦模型能力、浏览器控制、断言逻辑——你可以用 GPT-4-turbo 做理解用本地 Qwen2-VL 做视觉分析用 Playwright 原生 API 做点击用自定义正则做断言全部通过 config.json 统一注入。下面以cart.html场景为例说明这三层如何协同工作。2.1 页面理解层stagehand.page()如何让 LLM “看懂”购物车 DOM 结构stagehand.page()并非简单截图或抓取 HTML而是启动一个三阶段分析流水线DOM 快照提取调用 Playwright 的page.content()获取完整 HTML同时用page.evaluate()提取所有button、input、select的aria-label、title、textContent及>npx stagehand analyze --url file://$(pwd)/cart.html --model gpt-4o --debug提示--debug参数会输出中间产物包括原始 DOM 片段、视觉 token 数、LLM 返回的 raw JSON。若发现keyElements缺失关键按钮需检查cart.html中该按钮是否缺少aria-label或>import { stagehand } from stagehand; const sh await stagehand.init({ model: anthropic/claude-3-haiku, browser: { headless: false } }); await sh.page(file://cart.html); // 加载并理解页面 await sh.act(点击去结算按钮); // 执行动作 await sh.assert(URL 包含 /checkout); // 断言结果注意sh.act()返回 Promise{ success: boolean; element?: ElementHandle; error?: string }必须 await 检查success字段。若返回falseerror字段会包含具体失败原因如未找到匹配 text去结算 的 button 元素此时可结合--debug日志定位是 DOM 缺失属性还是 LLM 解析偏差。2.3 结果验证层stagehand.assert()如何避免“假阳性”断言传统断言常写expect(await page.textContent(h1)).toBe(订单确认)但若页面异步加载可能取到旧文本。Stagehand 的assert()强制要求 LLM 参与验证它先让模型基于当前页面快照判断“是否已进入订单确认页”再比对实际 DOM。具体流程为截取当前页面 viewport 图像提取h1、.order-summary、#payment-method等关键区域的文本与样式构造 prompt“当前页面是否显示订单确认信息请仅回答 true 或 false并说明依据如h1 文本为‘订单确认’且存在 class‘order-summary’ 的 div”解析 LLM 输出若为true则继续校验具体字段值否则抛出带上下文的错误。验证代码示例await sh.assert(页面显示订单确认标题和商品列表); // 等价于 LLM 判断 DOM 校验双重保险该机制显著降低 flaky test 概率——当网络延迟导致h1文本未刷新时LLM 会因视觉上未出现确认样式而返回false而非盲目比对旧文本。3. 从零部署 Stagehand环境配置、模型接入与电商场景实战Stagehand 的轻量级设计体现在其极简依赖上核心仅需 Node.js 18、Playwright 浏览器二进制、以及一个可用的 LLM API。但要真正跑通cart.html场景需完成三类配置运行时环境、模型 provider、业务页面模板。下面以 Ubuntu 22.04 环境为例给出可复制的部署步骤。3.1 初始化项目与 Playwright 安装Stagehand 基于 TypeScript 开发需先初始化项目并安装 Playwrightmkdir stagehand-demo cd stagehand-demo npm init -y npm install stagehand playwright npx playwright install chromium # 安装 Chromium 浏览器提示npx playwright install默认安装 Chromium若需 Firefox 或 WebKit追加firefox webkit。Stagehand 内部通过browserType.launch()调用因此必须确保对应浏览器已安装否则stagehand.init()会报错Browser type chromium is not supported。3.2 配置模型 providerOpenAI 与本地 Ollama 双路径Stagehand 通过config.json统一管理模型配置。创建config.json文件内容如下{ model: { provider: openai, name: gpt-4-turbo, apiKey: sk-xxx, baseUrl: https://api.openai.com/v1 }, browser: { headless: true, timeout: 30000 } }若使用本地 Ollama如qwen2-vl视觉模型则修改为{ model: { provider: ollama, name: qwen2-vl, baseUrl: http://localhost:11434/api/chat } }注意Ollama 需提前拉取模型ollama pull qwen2-vl并确保服务运行ollama serve。Stagehand 会自动检测provider字段调用对应 clientopenai使用openainpm 包ollama使用axios直连。3.3 编写电商结算测试从 cart.html 到 checkout.html 的端到端验证以项目根目录下的cart.html为例编写test-cart.tsimport { stagehand } from stagehand; async function runCartTest() { const sh await stagehand.init({ configPath: ./config.json, debug: true // 开启调试日志 }); try { // 1. 加载购物车页并理解结构 await sh.page(file:// process.cwd() /cart.html); // 2. 执行自然语言指令增加商品数量 await sh.act(将第一个商品的数量改为 2); // 3. 点击结算按钮 await sh.act(点击去结算按钮); // 4. 验证跳转到结算页 await sh.assert(页面显示收货地址选择区域); console.log(✅ 购物车结算流程通过); } catch (error) { console.error(❌ 测试失败:, error); throw error; } finally { await sh.close(); // 关闭浏览器实例 } } runCartTest();运行命令npx ts-node test-cart.ts提示首次运行会触发 Playwright 下载浏览器、LLM API 认证、页面分析三重耗时。后续执行因缓存 DOM 快照和 LLM 响应速度显著提升。若cart.html中“去结算按钮”无aria-label可在 HTML 中添加button aria-label去结算去结算/button提升匹配率。3.4 弹窗处理专项browserbase_stagehand-361-support-popups的集成方式压缩包名browserbase_stagehand-361-support-popups暗示 Stagehand 已内置弹窗处理模块。实际使用时无需额外导入只需在config.json中启用{ popupHandling: { enabled: true, allowedTypes: [alert, confirm, prompt, fileUpload], defaultResponse: accept } }当sh.act()执行过程中触发window.alert()Stagehand 会自动捕获并按defaultResponse处理。若需自定义响应如prompt输入特定值可在动作指令中声明await sh.act(在弹窗中输入邮箱 testexample.com 并确认);此时 Stagehand 会先等待page.on(dialog)事件再调用dialog.accept(testexample.com)。4. 模型选型与性能调优在准确率、延迟、成本间找到平衡点Stagehand 的效果高度依赖底层模型能力但并非参数越大的模型越好。针对不同场景需权衡推理速度、token 成本、视觉理解精度。以下是基于cart.html场景的实测对比数据单位秒/次AWS EC2 t3.xlarge网络延迟 50ms模型 Provider模型名称页面理解 (page)动作执行 (act)断言验证 (assert)单次总耗时月成本估算*OpenAIgpt-4-turbo2.11.82.56.4$120Anthropicclaude-3-haiku1.31.11.74.1$45Ollamaqwen2-vl:7b3.83.24.011.0$0Ollamaphi-3-vision2.52.02.87.3$0*成本估算基于 1000 次/日调用OpenAI/Anthropic 按官方定价Ollama 为本地 GPU 运行电费NVIDIA T44.1 为什么 claude-3-haiku 在电商场景中综合最优尽管 gpt-4-turbo 准确率略高98.2% vs 96.5%但 haiku 在以下三点胜出DOM 解析稳定性对aria-label缺失的按钮haiku 更倾向 fallback 到textContent匹配而 gpt-4-turbo 常因过度追求精确 XPath 导致失败视觉 token 效率处理cart.html截图时haiku 平均消耗 180 tokensgpt-4-turbo 达 320 tokens直接拉高成本错误恢复能力当sh.act()执行失败haiku 返回的error字段更具体如未找到 text去结算 的 button但发现 text立即购买 的 button便于快速修复 HTML。4.2 本地模型调优qwen2-vl 的量化与缓存策略若坚持使用qwen2-vl必须进行两项优化4-bit 量化ollama create qwen2-vl-q4 -f Modelfile其中Modelfile内容为FROM qwen2-vl:7b PARAMETER num_gpu 1 ADAPTER /path/to/qwen2-vl-q4.gguf量化后显存占用从 12GB 降至 4.2GB推理速度提升 2.3 倍2.DOM 快照缓存在config.json中启用{ cache: { enabled: true, ttl: 3600, dir: ./cache } }Stagehand 会将cart.html的 DOM 结构哈希值作为 key缓存 LLM 解析结果避免重复请求。4.3 关键参数调优表影响成功率的核心配置项参数名位置推荐值作用说明修改建议model.temperatureconfig.json0.3降低 LLM 随机性提升动作指令一致性0.5 易导致同一指令生成不同 XPathbrowser.timeoutconfig.json30000Playwright 操作超时时间毫秒页面复杂时增至 60000popupHandling.defaultResponseconfig.jsonaccept弹窗默认响应方式accept/dismiss支付场景建议设为 dismissdebuginit() 参数true/false是否输出 LLM prompt、DOM 片段、视觉 token 数等调试信息生产环境务必设为 falsemaxRetriessh.act() 选项2单个动作失败后的重试次数需配合retryDelay: 1000网络不稳定时设为 35. 实战排错当sh.act(点击去结算)总是失败时五步定位法Stagehand 的抽象层级越高错误堆栈越难直观看清。当自然语言指令执行失败不要急于改代码按以下顺序排查5.1 第一步确认页面是否被正确理解运行npx stagehand analyze --url file://cart.html --debug检查输出 JSON 中keyElements是否包含checkoutButton。若缺失说明 LLM 未识别该按钮——此时打开cart.html检查按钮是否有aria-label或>!-- 原始 -- button去结算/button !-- 修正后 -- button aria-label去结算>document.evaluate(//button[contains(text(), 去结算) or aria-label去结算], document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue若返回null证明 XPath 无效需检查cart.html中按钮文本是否含空格或换行如去结算\n此时应改用normalize-space()函数//button[contains(normalize-space(text()), 去结算) or aria-label去结算]5.3 第三步检查 Playwright 元素可见性即使 XPath 正确元素也可能被 CSSdisplay:none或visibility:hidden隐藏。在sh.act()后添加临时调试const el await page.$(//button[data-testidcheckout-button]); console.log(Element visibility:, await el?.isVisible()); // true/false console.log(Element enabled:, await el?.isEnabled()); // true/false若isVisible()为false需在cart.html中移除相关 CSS或在sh.act()前插入await page.waitForSelector([data-testidcheckout-button], { state: visible });。5.4 第四步分析 LLM 的动作解析逻辑Stagehand 将自然语言转为动作时会记录中间推理。在--debug日志中查找action plan字段典型输出为{ action: click, target: { type: text, value: 去结算 }, fallbacks: [aria-label, title, xpath] }若target.type是text但页面按钮文本为立即结算说明指令与实际文本不一致——此时应统一术语或在cart.html中添加aria-label去结算作为标准标识。5.5 第五步启用详细 Playwright 日志在config.json中添加{ browser: { logger: { enabled: true, level: verbose } } }运行后会在playwright-log.txt中记录每次page.click()的详细过程包括等待条件、超时时间、最终执行的 selector。这是定位“为什么 click 没反应”的终极手段——日志会明确写出waiting for element to be visible, enabled and stable若卡在此处必然是 CSS 或 JS 阻塞了元素就绪。本文还有配套的精品资源点击获取
返回列表