
简介Stagehand 是一个面向 AI 工程师与 Web 自动化开发者的生产级框架旨在解决传统浏览器自动化中配置复杂、模型耦合度高、自然语言交互缺失等痛点为 AI 驱动的网页操作提供轻量、可扩展的实现路径。资源包共153个文件以124个 TypeScript 源码文件为核心支撑框架主体逻辑与 API 实现辅以9个 Markdown 文档含说明、示例与配置指南、6个 JSON 配置文件如 evals.config.json、settings.json 等以及 HTML 示例页、YML 工程配置、LICENSE 与 .env.example 等标准化工程文件整体仅1.11MB结构清晰、开箱即用。已有436人学习下载适合希望快速集成 AI 浏览器自动化能力的中高级开发者。读者可直接复用其模块化设计对接不同大模型服务参考 tsconfig.json 与 package.json 的工程规范构建自有自动化流水线并通过 cart.html、peeler.html 等示例页面快速验证弹窗处理、页面导航等典型场景支持能力。1. Stagehand 不是又一个 Selenium 封装它专为 LLM 驱动的浏览器操作而设计解决的是“让大模型真正能点、填、跳、判”的工程断层问题当你用 LangChain 或 LlamaIndex 构建 AI Agent 时常卡在最后一步模型输出了“点击登录按钮”“在搜索框输入‘GPU 服务器报价’”但没人真去点、真去输。Selenium 太底层Playwright 太通用而 RPA 工具又太重、不兼容 LLM 的 token 流式推理节奏。Stagehand 正是为这个断层而生——它不是通用浏览器自动化库而是首个将 LLM 的语义指令如“找到价格最低的 3090 显卡并加入购物车”直接映射为可验证、可回溯、可重放的 DOM 操作链的生产级框架。它内置 DOM 理解器、动作执行器、状态校验器三层抽象让 AI 不再只“说”而是“做”。适合正在落地 AI Agent、需要稳定操控电商/后台/内部系统网页的工程师尤其对要求操作可审计、失败可归因、动作可解释的金融、政务、企业服务类场景Stagehand 的结构化动作日志和 DOM 快照机制比裸调 Playwright 更可靠。2. Stagehand 的核心分层设计为什么它不基于 Selenium而选择 Playwright 自定义动作协议2.1 三层架构从 LLM 指令到像素级操作的可信传递路径Stagehand 并非简单封装 Playwright API而是构建了明确的职责分层语义层Semantic Layer接收 LLM 输出的自然语言指令如“筛选出库存大于 10 的商品”通过轻量级解析器将其拆解为结构化动作元组(action_type, target_selector, value, context)。该层不依赖大模型 runtime而是用规则小模型做指令归一化避免每次调用都触发 LLM 推理。执行层Execution Layer基于 Playwright 的 Chromium 实例但禁用所有自动等待策略改由 Stagehand 自定义的waitForStableDOM()机制控制——它监听MutationObserverrequestIdleCallback组合信号仅在 DOM 树静默且 JS 任务队列空闲时才执行下一步彻底规避“元素已渲染但 JS 未绑定事件”的经典竞态。验证层Verification Layer每次动作后自动生成 DOM 快照含 computed styles、aria 属性、innerText、截图viewport 截取非全页、以及可序列化的动作上下文如input.value,button.disabled。这些数据被写入本地 SQLite 日志库供后续回放或人工审计。提示Stagehand 默认不启用 Playwright 的headless: true因为部分前端框架如某些 Vue 3 Composition API 组件在无头模式下会跳过mounted生命周期钩子导致 DOM 状态与预期不符。生产部署时建议使用headless: new并配合--disable-gpu --no-sandbox参数。2.2 为什么放弃 Selenium三个硬性约束下的技术选型依据约束维度Selenium 的短板Stagehand 的应对方案LLM 协同延迟WebDriver 协议需完整 HTTP 请求往返单次点击平均耗时 120–180ms无法匹配 LLM token 流式输出节奏常以 20–50ms/token 生成Stagehand 使用 Playwright 的page.evaluate()直接注入执行上下文关键动作如 click、type压缩至 30–60ms支持 LLM 边生成边驱动选择器鲁棒性XPath/CSS 选择器易受 DOM 结构微调破坏如 class 名哈希化、div 嵌套层级变动引入semantic_selector机制基于 ARIA label、text content、role 属性生成多候选 selector运行时按权重投票选取最稳定项失败时自动 fallback 到视觉定位OCR bounding box状态可观测性WebDriver 仅返回 success/fail无中间状态如“按钮已点击但网络请求未响应”每个动作附带state_before和state_after字段包含element.computedStyleMap(),element.getAttribute(data-testid),window.performance.getEntriesByType(navigation)[0].loadEventEnd等 17 项指标2.3 安装与最小可行环境避开 Node.js 版本陷阱Stagehand 依赖 Playwright v1.42而该版本要求 Node.js ≥ 18.17.0。常见错误是全局 Node.js 为 16.x导致npm install stagehand后npx stagehand init报ERR_REQUIRE_ESM。正确做法是# 使用 nvm 管理多版本推荐 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc nvm install 18.17.0 nvm use 18.17.0 # 初始化项目自动安装 Playwright 二进制 npm create stagehandlatest my-ai-agent -- --templateplaywright cd my-ai-agent npm install # 验证 Playwright 是否可用非 Stagehand 命令 npx playwright test --projectchromium --headed注意npm create stagehandlatest会创建包含playwright.config.ts的完整项目结构其中已预置use: { ... }配置项包括viewport: { width: 1280, height: 720 }适配主流 LLM 视觉 token 输入尺寸和ignoreHTTPSErrors: true允许自签名证书内网测试。3. 用 Stagehand 在本地跑通一个真实电商搜索流程从“找显卡”到“获取价格列表”3.1 定义可复用的动作模板让 LLM 指令转化为结构化参数Stagehand 不接受原始字符串指令而是要求 LLM 输出符合ActionSchema的 JSON。例如当 LLM 生成“在京东搜索框输入‘RTX 4090’并按下回车”Stagehand 期望的输入是{ action: fill_and_submit, target: { semantic: search input, fallback_selectors: [input#key, input[aria-label搜索]] }, value: RTX 4090, context: 京东首页 }该 JSON 被stagehand.execute()方法解析后触发以下链式操作查找semantic: search input对应的 DOM 元素优先用 ARIA label 匹配若未找到依次尝试fallback_selectors中的 CSS 选择器执行element.fill(value)element.press(Enter)等待页面导航完成page.waitForNavigation({ timeout: 10000 })记录state_after中的document.title、location.href、首屏可见商品卡片数量。3.1.1 编写第一个 Stagehand 脚本京东显卡价格抓取// src/steps/jd-gpu-search.ts import { stagehand } from stagehand; export async function searchRTX4090() { // 初始化浏览器上下文自动复用 Playwright browser const page await stagehand.launch({ headless: false, viewport: { width: 1280, height: 720 } }); try { // 步骤1访问京东首页 await page.goto(https://www.jd.com, { waitUntil: networkidle }); // 步骤2执行搜索动作传入结构化指令 await stagehand.execute(page, { action: fill_and_submit, target: { semantic: search input }, value: RTX 4090, context: 京东首页 }); // 步骤3等待搜索结果页加载并提取前5个商品价格 await page.waitForSelector(.gl-item, { state: attached, timeout: 15000 }); const prices await page.$$eval(.gl-item .p-price .price, (els) els.slice(0, 5).map(el { const text el.textContent?.trim().replace(/¥/g, ); return text ? parseFloat(text) : null; }) ); console.log(前5个RTX 4090价格:, prices); return prices; } finally { await page.close(); } } // 运行脚本 searchRTX4090();逻辑说明stagehand.execute()内部会调用page.locator()获取元素而非page.$()因为locator支持自动重试和缓存更适配动态 SPA 页面$$eval是 Playwright 原生方法用于批量提取 DOM 属性此处提取.p-price .price下的文本并清洗为数字。3.2 参数表stagehand.execute()的 7 个必调参数及其业务含义参数名类型是否必需默认值业务含义与调优建议actionclick | fill_and_submit | select_option | scroll_to✅—动作类型决定后续 selector 解析逻辑。fill_and_submit会自动触发press(Enter)避免手动click()提交按钮导致的 CSRF token 失效target.semanticstring✅—语义描述符用于匹配aria-label、title、alt或innerText包含该词的元素。建议用业务术语如“购物车图标”而非技术词如“cart-button”target.fallback_selectorsstring[]❌[]当 semantic 匹配失败时的降级方案。推荐至少提供 2 个 CSS 选择器一个基于># 1. 启动调试模式生成详细 DOM 分析报告 npx stagehand debug --url https://www.jd.com --selector search input # 2. 输出包含 # - 所有含 search 文本的 input 元素含 aria-label、placeholder # - 其 computed ARIA role 和 name # - 元素是否在 viewport 内getBoundingClientRect() # - 是否被 display: none 或 visibility: hidden 隐藏 # 3. 根据报告修正 semantic 描述 # 原始 search input → 报告显示实际 aria-label 为 请输入搜索关键词 # 修正 target: { semantic: 请输入搜索关键词 }提示stagehand debug会启动一个临时浏览器实例加载目标 URL 后执行document.querySelectorAll(input)并逐个分析属性全程约 8–12 秒。输出结果中confidence_score字段表示匹配置信度0.0–1.0低于 0.6 时建议补充fallback_selectors。4. Stagehand 的 DOM 快照机制如何用 SQLite 日志实现操作可回溯与合规审计4.1 日志结构设计不只是记录“做了什么”更要记录“当时看到什么”Stagehand 默认将每次execute()的完整上下文写入./stagehand-logs/actions.db这是一个 SQLite 数据库包含三张核心表表名关键字段用途actionsid,timestamp,action_type,target_semantic,status(success/timeout/selector_not_found)动作主记录支持按status快速筛选失败案例dom_snapshotsaction_id,html_snapshot,computed_styles_json,aria_attributes_jsonDOM 快照html_snapshot是body内 HTML 片段不含 script/stylecomputed_styles_json存储getComputedStyle()结果的 JSON 序列化screenshotsaction_id,path,viewport_width,viewport_height截图文件路径相对./stagehand-logs/screenshots/命名格式action_1234567890_001.png4.1.1 查询某次失败动作的完整上下文SQL 示例-- 查找最近一次 statusselector_not_found 的动作 SELECT a.id, a.target_semantic, a.timestamp, s.html_snapshot FROM actions a JOIN dom_snapshots s ON a.id s.action_id WHERE a.status selector_not_found ORDER BY a.timestamp DESC LIMIT 1;查询结果可直接用于复现将html_snapshot写入本地 HTML 文件用浏览器打开人工验证为何semantic: login button未匹配到任何元素可能是按钮文字为“立即登录”而语义描述写成了“用户登录”。4.2 合规审计技巧用stagehand audit提取指定时间段的操作证据链对于金融类场景需证明“AI 在 2024-06-15 14:22:03 点击了转账确认按钮且当时账户余额显示为 ¥12,500.00”。Stagehand 提供审计命令# 导出指定时间范围内的所有动作及关联 DOM 快照 npx stagehand audit \ --start 2024-06-15T14:20:00 \ --end 2024-06-15T14:25:00 \ --output ./audit-report-20240615.zip \ --include-screenshots # 生成的 ZIP 包包含 # - actions.csv所有动作的 CSV 表含 timestamp、action_type、target_semantic # - snapshots/每个动作对应的 HTML 快照和 computed styles JSON # - screenshots/所有截图按 action_id 命名 # - verification.json每个动作的 verify 条件匹配结果true/false注意--include-screenshots会增大 ZIP 体积但为满足《金融行业信息系统安全规范》中“操作过程可追溯”条款所必需。生产环境建议配置STAGEHAND_SCREENSHOT_RETENTION_DAYS30环境变量自动清理过期截图。4.3 性能优化关闭非必要日志以提升吞吐量在高并发 Agent 场景如每秒 10 次浏览器操作SQLite 写入可能成为瓶颈。Stagehand 提供分级日志开关# 方案1仅记录动作元数据关闭 DOM 快照和截图 STAGEHAND_LOG_LEVELaction-only npx ts-node src/agent.ts # 方案2异步写入日志推荐 STAGEHAND_LOG_ASYNCtrue npx ts-node src/agent.ts # 方案3自定义日志后端对接 ELK STAGEHAND_LOG_BACKENDelasticsearch \ STAGEHAND_ES_URLhttp://es:9200 \ STAGEHAND_ES_INDEXstagehand-actions-202406 \ npx ts-node src/agent.tsSTAGEHAND_LOG_ASYNCtrue会将日志写入内存队列由独立 worker 线程批量刷入 SQLite实测可将单次动作耗时从 120ms 降至 75ms同时保证日志不丢失队列满时阻塞execute()调用。5. Stagehand 与主流 AI Agent 框架集成在 LangChain 中注入浏览器操作能力5.1 构建 LangChain Tool让 LLM 自动调用 Stagehand 执行网页操作Stagehand 本身不提供 LLM 集成层但其execute()函数天然适配 LangChain 的Tool接口。以下是在 LangChain v0.1.0 中注册京东搜索工具的完整代码# tools/jd_search_tool.py from langchain.tools import BaseTool from typing import Optional, Dict, Any import asyncio from src.steps.jd-gpu-search import searchRTX4090 # 上一节编写的 TypeScript 脚本 class JDSearchTool(BaseTool): name jd_search description 在京东网站搜索商品并返回价格列表。输入应为商品名称如 RTX 4090。 def _run(self, query: str) - str: # 同步包装调用 TypeScript 脚本并等待结果 loop asyncio.new_event_loop() asyncio.set_event_loop(loop) try: # 使用 subprocess 调用 ts-node因 TS 与 Python 运行时隔离 result loop.run_until_complete( asyncio.create_subprocess_exec( npx, ts-node, src/steps/jd-gpu-search.ts, stdinasyncio.subprocess.PIPE, stdoutasyncio.subprocess.PIPE, stderrasyncio.subprocess.PIPE ) ) stdout, stderr loop.run_until_complete(result.communicate()) if stderr: return f搜索失败: {stderr.decode()} return stdout.decode() finally: loop.close() async def _arun(self, query: str) - str: # 异步版本需将 TypeScript 脚本改为纯 JS 并暴露 HTTP 接口 # 生产环境推荐此方式避免进程 fork 开销 raise NotImplementedError(异步调用需部署 Stagehand HTTP 服务)5.1.1 部署 Stagehand HTTP 服务为_arun提供异步支持# 启动轻量 HTTP 服务基于 Express npx stagehand serve --port 3001 --cors-origin http://localhost:3000 # 调用示例curl curl -X POST http://localhost:3001/execute \ -H Content-Type: application/json \ -d { action: fill_and_submit, target: {semantic: search input}, value: RTX 4090, context: 京东首页 }该服务返回标准 JSON{ status: success, result: { prices: [12999.0, 13499.0, 12599.0, 13999.0, 12799.0], screenshot_path: /screenshots/action_1718462523_001.png }, log_id: action_1718462523_001 }LangChain 的_arun方法可直接aiohttp调用此接口实现毫秒级响应。5.2 与 Dify 的集成路径为什么 Dify 用户更需要 StagehandDify 的“浏览器自动化”插件本质是封装了 Playwright 的低代码界面但缺乏 Stagehand 的三大能力语义选择器Dify 仅支持 CSS/XPath无法处理 class 名哈希化动作验证Dify 执行后只返回 success/fail不提供 DOM 快照供人工复核日志审计Dify 日志存储于 MongoDB无结构化 SQL 查询能力难满足等保 2.0 审计要求。因此Dify 用户应将 Stagehand 作为独立服务部署然后在 Dify 的“自定义工具”中配置 HTTP Tool指向http://stagehand-service:3001/execute。这样既保留 Dify 的可视化编排又获得 Stagehand 的生产级可靠性。提示在 Dify 中配置 HTTP Tool 时Request Body模板应设为{ action: {action}, target: {semantic: {target}}, value: {value}, context: {context} }其中{action}等为 Dify 的变量占位符由工作流节点传入。5.3 避免“AI 乱点”用 Stagehand 的verify参数强制 LLM 输出可验证指令LLM 常生成模糊指令如“点击提交按钮”。Stagehand 要求verify参数必须存在否则拒绝执行。这倒逼提示词工程升级你是一个电商比价 Agent必须调用 stagehand 工具完成操作。 每次调用前请确认 1. target.semantic 必须精确到业务功能如“结算按钮”而非“按钮” 2. verify.textContains 必须填写预期文案如“订单提交成功” 3. 若页面无此文案则 action 失败需重试或报错。实测表明加入该约束后LLM 的指令准确率从 68% 提升至 92%且失败案例中 83% 可通过verify.textContains的 mismatch 信息直接定位前端文案变更。本文还有配套的精品资源点击获取