
简介Stagehand 是一个面向 AI 工程师与 Web 自动化开发者的生产级框架专为自然语言驱动的浏览器自动化设计可替代 Playwright 实现更轻量、可配置、模块化的 AI 浏览器操作。它提供三个简洁 API支持多模型后端如 OpenAI、Anthropic 等显著降低 Web 自动化脚本编写门槛适用于智能测试、RPA 场景及 AI 代理开发等中高级实践需求。资源包共 153 个文件主体为 124 个 TypeScript 源码文件含核心引擎与工具链、9 个 Markdown 文档含快速入门与 API 说明、6 个 JSON 配置文件如 evals.config.json、settings.json辅以 HTML 示例页、YML 工作流及环境模板等整体仅 1.11MB结构清晰、开箱即用。已有 436 人学习下载读者可直接获取完整框架源码、本地可运行示例cart.html、peeler.html、多环境配置模板.env.example、config.json及标准化开发规范prettierignore、gitignore快速集成至现有 AI 应用或自动化流水线。1. Stagehand 不是又一个 Selenium 封装而是专为 LLM 驱动浏览器交互设计的生产级调度中枢当你用大模型生成“打开京东搜索显卡”这类指令后真正卡住的不是模型推理而是如何让这条自然语言指令在真实浏览器中稳定、可追溯、可重放地执行——Selenium 太底层Playwright 缺少语义层LangChain 的 BrowserTool 又太轻量、难调试、无状态管理。Stagehand 正是为解决这个断层而生它不替代底层驱动而是构建在 Playwright 之上的一套面向 AI Agent 的浏览器操作协议栈把“点击搜索框→输入关键词→等待结果加载→提取前三个商品标题”这一连串动作抽象成可版本化、可审计、可回滚的声明式任务单元。它面向的是需要长期运行、多模型协同、需人工复核与干预的生产环境比如电商比价 Agent、金融报表抓取服务、政务网站表单自动填报系统。如果你正在用 Dify 或自研 Agent 框架接入网页操作能力却反复陷入 selector 失效、异步等待不可靠、失败日志无法定位到具体步骤的问题Stagehand 提供的不是语法糖而是一套带上下文快照、操作溯源、失败重试策略和可观测埋点的基础设施。2. 为什么 Stagehand 选择 Playwright 作为底座而非 Puppeteer 或 Selenium2.1 底层驱动选型Playwright 的三重确定性优势Stagehand 并未从零实现浏览器控制而是深度绑定 Playwrightv1.40其核心依据来自三方面硬性指标跨浏览器一致性Chrome/Firefox/WebKit 在同一套 API 下行为偏差 3%而 Puppeteer 仅支持 ChromiumSelenium 在 Firefox 上常因 GeckoDriver 版本错配导致ElementNotInteractableError原生等待机制Playwright 的page.locator().click()内置智能等待默认 5s自动检测元素是否attached、visible、enabled无需手动写wait_for_selector或expected_conditions网络层可控性通过routeAPI 可拦截并 mock 任意请求如屏蔽广告 JS、注入 mock 数据这对测试 LLM 对页面结构的理解鲁棒性至关重要——例如模拟“搜索接口返回空数组”时Agent 是否会 fallback 到滚动查看更多。提示Stagehand 显式禁用 Playwright 的headless: new模式即 Chromium 的 headless 新模式因其在部分 Linux 环境下触发 GPU 进程崩溃改用headless: old或headless: false配合 xvfb更稳定。2.2 Stagehand 的协议层设计将自然语言指令编译为可执行动作图Stagehand 的核心创新不在驱动层而在动作编译器Action Compiler。它接收 LLM 输出的 JSON 结构如{action: fill, target: search-input, value: RTX 4090}将其映射为 Playwright 原生调用并插入三类中间件Selector 解析器将语义描述如搜索框转为 CSS/XPath支持 fallback 链input[nameq]→input#kw→textarea[aria-labelSearch]上下文快照器每次动作前自动保存 DOM 快照 截图可配置为仅失败时保存路径格式为run_20240615_142301/action_03_fill_search_input.png可观测钩子在click()后自动注入performance.getEntriesByType(navigation)记录跳转耗时在fill()后调用element.screenshot()验证输入是否可见。2.2.1 动作编译器的典型输入输出对照LLM 输出JSONStagehand 编译后的 Playwright 调用关键参数说明{action:click,target:登录按钮}await page.locator(button:text(登录), button:has-text(Login)).first().click({ timeout: 10000 })timeout由 Stagehand 全局策略设定默认 10sfirst()防止多匹配误点{action:scroll_to,target:商品列表底部}await page.locator(div.product-list).evaluate(el el.scrollIntoView({ behavior: smooth }))使用evaluate执行原生 JS避免page.mouse.wheel()的精度问题{action:extract,target:价格标签,output_key:price_list}await page.locator(span.price).allInnerTexts()allInnerTexts()自动处理span¥/spanspan8999/span的文本拼接2.3 初始化 Stagehand 实例的最小可行配置from stagehand import Stagehand # 生产环境推荐配置启用快照、超时控制、失败重试 agent Stagehand( browser_typechromium, # 支持 chromium/firefox/webkit headlessTrue, # 生产环境设为 True本地调试可设 False screenshot_on_failureTrue, # 失败时自动截图存档 dom_snapshot_on_actionTrue, # 每步保存 DOM 快照JSON 格式 timeout12000, # 全局动作超时ms覆盖 Playwright 默认 30000 retry_strategy{ # 失败重试策略 max_retries: 2, backoff_factor: 1.5, # 第二次重试延迟 第一次 * 1.5 retryable_errors: [TimeoutError, TargetClosedError] } )screenshot_on_failure和dom_snapshot_on_action生成的文件默认存于./stagehand_runs/目录结构按时间戳分层便于 CI/CD 流水线归档retry_strategy中retryable_errors仅包含明确可重试的异常排除ValueError逻辑错误和AssertionError校验失败避免掩盖真实 Bugtimeout12000是经验阈值多数电商页面首屏渲染 ≤ 3sJS 执行 ≤ 2s网络请求 ≤ 5s留 2s 缓冲应对 CDN 波动。3. 用 Stagehand 实现一个可审计的电商比价 Agent3.1 定义结构化任务从自然语言到 Stagehand Action Schema假设 LLM 输出以下结构化指令序列符合 Stagehand 的ActionListSchema[ { action: navigate, url: https://www.jd.com, description: 打开京东首页 }, { action: fill, target: 搜索框, value: RTX 4090, description: 输入显卡型号 }, { action: click, target: 搜索按钮, description: 触发搜索 }, { action: wait_for, target: 商品列表容器, state: visible, timeout: 15000, description: 等待商品列表出现 }, { action: extract, target: 商品标题, limit: 3, output_key: top_titles, description: 提取前三个商品标题 } ]Stagehand 将此 JSON 加载为ActionList对象每个动作执行后自动记录元数据字段示例值说明action_idaction_004全局唯一动作序号用于日志关联timestamp_start2024-06-15T14:23:01.123Z动作开始精确时间selector_usedinput#key实际匹配到的 selector非 LLM 描述的“搜索框”duration_ms247.8从开始到结束耗时含等待screenshot_path./stagehand_runs/run_20240615_142301/action_004_click_search_button.png失败时必存成功时按配置存3.2 执行任务并捕获结构化结果# 加载任务定义 with open(jd_search_task.json) as f: task_definition json.load(f) # 执行并获取结果 result agent.run(task_definition) # result 是 dict包含 # - success: bool全链路是否成功 # - actions: list[dict]每步详细记录 # - outputs: dictextract 动作的 output_key → 值 # - run_id: str本次运行唯一 ID if result[success]: print(Top titles:, result[outputs][top_titles]) # [华硕 TUF GAMING RTX 4090 O24G, 七彩虹 iGame RTX 4090 ADL OC, 微星 SUPRIM X RTX 4090 24G] else: # 查看失败详情 failed_action next(a for a in result[actions] if not a[success]) print(fFailed at {failed_action[action_id]}: {failed_action[error_message]}) print(fScreenshot saved to: {failed_action[screenshot_path]})3.2.1 关键参数wait_for动作的三种 state 模式state值触发条件适用场景注意事项visible元素在视口内且getBoundingClientRect().height 0等待按钮显示若元素被overflow: hidden父容器裁剪可能误判attached元素已挂载到 DOM 树即使display: none等待 JS 动态插入节点需配合is_visible()二次校验stable元素位置/尺寸 500ms 内无变化防动画抖动等待轮播图停止开销略高仅在动画密集页启用注意wait_for的timeout参数优先级高于全局timeout允许对关键步骤单独延长等待如支付页加载可能需 30s。3.3 本地调试可视化执行流与 DOM 快照对比Stagehand 提供stagehand serveCLI 命令启动本地 Web 服务自动解析./stagehand_runs/下最新运行记录# 启动调试服务默认端口 8080 stagehand serve --runs-dir ./stagehand_runs/ # 输出 # Serving runs from ./stagehand_runs/ # Open http://localhost:8080/run/run_20240615_142301 to view execution trace访问该 URL 后界面呈现左侧时间轴按action_id排序的动作列表绿色/红色标识成功/失败中部 DOM 对比点击任一动作左右分屏显示「动作前快照」vs「动作后快照」高亮差异节点如新增的li classproduct-item右侧截图画廊所有动作截图缩略图失败动作自动置顶并加红框标注错误区域。此功能直接解决“为什么 LLM 说点击了但没反应”的经典问题——你不再需要复现整个流程只需加载快照用浏览器 DevTools 检查#search-button是否被z-index遮挡或是否存在pointer-events: none。4. Stagehand 的生产就绪配置环境隔离、并发控制与失败熔断4.1 多环境隔离通过 BrowserContext 实现资源硬隔离Stagehand 默认为每次run()创建独立的BrowserContext而非复用Page这是生产环境的关键设计Cookie/Storage 隔离A 任务登录京东B 任务登录淘宝互不污染内存泄漏防护Context 销毁时自动释放所有关联资源包括 Service Worker并发安全每个 Context 绑定独立的 WebSocket 连接避免 Playwright 的page实例跨线程使用报错。# 生产环境建议为高频任务预创建 Context 池 from stagehand import Stagehand agent Stagehand( context_pool_size5, # 预创建 5 个空闲 Context context_reuse_timeout300, # 5 分钟内未使用则销毁 # 其他参数同前... )context_pool_size5表示最多同时运行 5 个任务超出请求排队context_reuse_timeout300防止空闲 Context 占用内存过久平衡启动开销与资源复用。4.2 并发执行用 asyncio 控制 QPS 与资源水位Stagehand 原生支持async run()但需主动控制并发度避免浏览器进程爆炸import asyncio from stagehand import Stagehand agent Stagehand(...) async def run_with_rate_limit(task_def, semaphore): async with semaphore: # 限制并发数 return await agent.arun(task_def) # 限制最大并发为 3防止 CPU/内存过载 semaphore asyncio.Semaphore(3) tasks [ run_with_rate_limit(task1, semaphore), run_with_rate_limit(task2, semaphore), run_with_rate_limit(task3, semaphore), run_with_rate_limit(task4, semaphore), # 此任务将等待前 3 个完成 ] results await asyncio.gather(*tasks)4.2.1 并发参数与硬件资源映射表并发数推荐 CPU 核心数推荐内存典型瓶颈现象应对措施1~22 核2GB无明显瓶颈无需调整3~54 核4GBPlaywright 进程 CPU 占用 90%启用--single-process启动参数6~108 核8GB页面加载超时率上升降低timeout至 8000ms增加retry_strategy.max_retries1提示在 Docker 中部署时务必设置--shm-size2g否则高并发下 Playwright 的共享内存不足会导致Failed to create OpenGL context错误。4.3 失败熔断基于历史成功率的动态降级Stagehand 内置熔断器Circuit Breaker当某类任务如navigate到特定域名连续失败 3 次自动触发降级一级降级切换备用 selector如京东搜索框从#key切换到input[namekeyword]二级降级跳过该动作注入人工审核队列生成review_required.json文件含截图与 DOM 快照路径三级降级返回预设 fallback 值如{status: unavailable, message: 目标站点暂不可达}。启用方式只需在初始化时传入策略from stagehand.circuit_breaker import DomainBasedCircuitBreaker breaker DomainBasedCircuitBreaker( failure_threshold3, # 连续失败阈值 reset_timeout300, # 5 分钟后重置计数器 fallback_domains[jd.com, taobao.com] # 仅对这些域名启用 ) agent Stagehand( circuit_breakerbreaker, # 其他参数... )熔断状态实时写入./stagehand_runs/circuit_breaker_state.json格式为{ jd.com: { failure_count: 0, last_failure_time: 2024-06-15T14:20:00Z, state: CLOSED // CLOSED / OPEN / HALF_OPEN } }5. 验证 Stagehand 的稳定性用 pytest 构建可回放的回归测试套件5.1 编写可回放的测试用例锁定 DOM 快照而非 selector传统 UI 测试常因 selector 变更而失效。Stagehand 测试的核心是用 DOM 快照哈希值代替硬编码 selector# test_jd_search.py import pytest from stagehand import Stagehand pytest.fixture def stagehand_agent(): return Stagehand(headlessTrue, dom_snapshot_on_actionTrue) def test_jd_search_returns_titles(stagehand_agent): # 1. 预先录制基准快照人工确认正确后存档 baseline_snapshot snapshots/jd_search_results_v1.json # 2. 执行任务 result stagehand_agent.run({ actions: [ {action: navigate, url: https://www.jd.com}, {action: fill, target: 搜索框, value: RTX 4090}, {action: click, target: 搜索按钮}, {action: wait_for, target: 商品列表容器, state: visible} ] }) # 3. 验证提取当前快照哈希与基线比对 current_hash result[actions][-1][dom_snapshot_hash] # 由 Stagehand 自动生成 with open(baseline_snapshot) as f: assert current_hash json.load(f)[hash]dom_snapshot_hash是对 DOM 快照内容剔除动态属性如>def test_network_failure_recovery(stagehand_agent): # 注入 10s 延迟到搜索接口 stagehand_agent.mock_network( url_patternhttps://search.jd.com/.*, delay_ms10000, status_code503 ) result stagehand_agent.run({...}) # 同上任务 # 验证重试生效且最终成功 assert result[success] is True assert len([a for a in result[actions] if a[action] wait_for]) 2 # 重试一次5.2.1 Stagehand 测试覆盖率关键指标指标目标值测量方式说明动作成功率≥ 99.5%success_count / total_actions统计所有arun()调用中action.success为 True 的比例平均恢复时间MTTR≤ 8smean(action.duration_ms for action in failed_actions)仅统计重试后成功的失败动作快照哈希漂移率≤ 0.1%(changed_snapshots / total_runs) * 100DOM 结构变更需人工审核并更新基线执行测试时添加--stagehand-report参数生成 HTML 报告pytest test_jd_search.py --stagehand-report./reports/jd_test_20240615.html报告包含每步截图、DOM 差异高亮、性能火焰图按动作耗时排序、失败根因分类网络超时/JS 错误/selector 失效。5.3 生产环境监控将 Stagehand 日志对接 PrometheusStagehand 内置MetricsExporter可推送指标至 Prometheus Pushgatewayfrom stagehand.metrics import MetricsExporter exporter MetricsExporter( pushgateway_urlhttp://pushgateway.example.com:9091, job_namestagehand-prod ) # 在 agent.run() 后调用 exporter.export_metrics(result)导出的指标包括stagehand_action_duration_seconds{actionclick,target搜索按钮,statussuccess}直方图stagehand_context_pool_utilization{envprod}当前 Context 使用率stagehand_circuit_breaker_state{domainjd.com}1CLOSED, 0OPEN配合 Grafana 面板可设置告警规则当rate(stagehand_action_duration_seconds_sum[1h]) / rate(stagehand_action_duration_seconds_count[1h]) 15平均耗时突增当avg_over_time(stagehand_circuit_breaker_state{domainjd.com}[1h]) 0熔断持续 1 小时这些信号直接关联业务 SLA若京东比价任务平均耗时超过 15 秒意味着用户等待体验跌破阈值需立即介入。本文还有配套的精品资源点击获取