Doramagic 项目包 · 项目说明书

stagehand 项目

生成时间:2026-05-10 12:00:15 UTC

Stagehand 简介

Stagehand 是一个基于浏览器自动化框架的工具,通过命令行接口(CLI)提供对浏览器的精细控制能力。该项目由 Browserbase 开发,支持本地和远程两种运行模式,能够执行页面导航、元素交互、网络请求捕获、截图等多种浏览器自动化任务。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 守护进程基础设施

继续阅读本节完整说明和来源证据。

章节 导航与页面操作

继续阅读本节完整说明和来源证据。

章节 元素交互

继续阅读本节完整说明和来源证据。

核心架构

Stagehand 采用客户端-守护进程(Client-Daemon)架构设计,CLI 作为客户端与守护进程通信,守护进程负责实际管理浏览器实例。

graph TD
    A[CLI Client] --> B[Socket Communication]
    B --> C[Daemon Process]
    C --> D[Browser Instance]
    C --> E[Local CDP Discovery]
    D --> F[Network Capture]
    E --> C

守护进程基础设施

守护进程管理依赖 Socket 文件进行进程间通信,关键路径配置如下:

const SOCKET_DIR = os.tmpdir();

function getSocketPath(session: string): string {
  return path.join(SOCKET_DIR, `browse-${session}.sock`);
}

function getLockPath(session: string): string {
  return path.join(SOCKET_DIR, `browse-${session}.lock`);
}

守护进程实现了基于文件锁的并发控制机制,使用 O_EXCL 标志确保原子性文件创建,防止竞争条件:

async function acquireLock(
  session: string,
  timeoutMs: number = 10000,
): Promise<boolean> {
  const lockPath = getLockPath(session);
  // O_EXCL ensures atomic creation - fails if file exists
  const handle = await fs.open(lockPath, "wx");
  await handle.write(String(process.pid));
  await handle.close();
  return true;
}

资料来源:packages/cli/src/index.ts:1-50

CLI 命令系统

Stagehand CLI 提供丰富的命令集,覆盖浏览器自动化的各个方面。

导航与页面操作

命令说明关键选项
open <url>导航到指定 URL--wait, --timeout, --context-id, --persist
goto <url>open 的别名同上
pages列出所有打开的页面-
newpage [url]创建新页面/标签页-

导航命令支持多种等待状态:

  • load(默认)
  • domcontentloaded
  • networkidle
program
  .command("open <url>")
  .alias("goto")
  .description("Navigate to URL")
  .option(
    "--wait <state>",
    "Wait state: load, domcontentloaded, networkidle",
    "load",
  )
  .option("-t, --timeout <ms>", "Navigation timeout in milliseconds", "30000")

资料来源:packages/cli/src/index.ts:80-130

元素交互

元素操作命令支持通过选择器精确定位和操作 DOM 元素:

命令说明选项
click <selector>点击元素--button, --delay, --xpath
dblclick <selector>双击元素--button, --delay
fill <selector> <value>填充输入框--no-press-enter
select <selector> <values...>选择下拉选项-
hover <x> <y>悬停在坐标位置--xpath
scroll <x> <y> <deltaX> <deltaY>滚动操作--xpath
drag <fromX> <fromY> <toX> <toY>拖拽操作--delay, --xpath
program
  .command("fill <selector> <value>")
  .description("Fill input element (presses Enter by default)")
  .option("--no-press-enter", "Don't press Enter after filling")
  .action(async (selector: string, value: string, cmdOpts) => {
    const pressEnter = cmdOpts.pressEnter !== false;
    const result = await runCommand("fill", [selector, value, { pressEnter }]);
  });

资料来源:packages/cli/src/index.ts:150-200

键盘与输入

命令说明选项
type <text>输入文本-d, --delay, --mistakes
press <key>按下按键-
key <key>press 的别名-
program
  .command("type <text>")
  .description("Type text")
  .option("-d, --delay <ms>", "Delay between keystrokes")
  .option("--mistakes", "Enable human-like typing with mistakes")

页面信息获取

命令说明获取类型
get <what> [selector]获取页面信息url, title, text, html, markdown, value, box, visible, checked
program
  .command("get <what> [selector]")
  .description(
    "Get page info: url, title, text, html, markdown, value, box, visible, checked",
  )

截图功能

截图命令支持多种输出格式和裁剪选项:

program
  .command("screenshot [path]")
  .description("Take screenshot")
  .option("-f, --full-page", "Full page screenshot")
  .option("-t, --type <type>", "Image type: png, jpeg", "png")
  .option("-q, --quality <n>", "JPEG quality (0-100)")
  .option("--clip <json>", "Clip region as JSON")
  .option("--no-animations", "Disable animations")
  .option("--hide-caret", "Hide text caret")

资料来源:packages/cli/src/index.ts:220-280

可访问性快照

获取页面的可访问性树快照:

program
  .command("snapshot")
  .description("Get accessibility tree snapshot")
  .option("-c, --compact", "Output tree only (no xpath map)")

快照返回结构包含:

  • tree: 格式化的可访问性树
  • xpathMap: 元素到 XPath 的映射
  • urlMap: URL 映射

资料来源:packages/cli/src/index.ts:290-310

等待与状态检查

命令说明状态值
wait <type> [arg]等待条件load, selector, timeout
is <check> <selector>检查元素状态visible, checked
program
  .command("wait <type> [arg]")
  .description("Wait for: load, selector, timeout")
  .option("-t, --timeout <ms>", "Timeout", "30000")
  .option("-s, --state <state>", "Element state: visible, hidden, attached, detached", "visible")

资料来源:packages/cli/src/index.ts:50-80

视口控制

program
  .command("viewport <width> <height>")
  .description("Set viewport size")
  .option("-s, --scale <n>", "Device scale factor", "1")

本地浏览器策略

Stagehand 支持多种本地浏览器启动策略,可通过配置灵活选择。

策略类型

策略说明适用场景
isolated启动独立的 Chromium 实例完全隔离的自动化任务
cdp连接到指定的 Chrome DevTools Protocol 端点复用已有浏览器会话
auto自动检测本地浏览器,无则启动隔离实例通用场景
export async function resolveLocalStrategy({
  localConfig,
  headless,
  defaultViewport,
  discoverLocalCdp,
  resolveWsTarget,
}: ResolveLocalStrategyOptions): Promise<ResolvedLocalStrategy> {
  if (localConfig.strategy === "isolated") {
    return {
      localLaunchOptions: { headless, viewport: defaultViewport },
      localInfo: { localSource: "isolated" },
    };
  }

  if (localConfig.strategy === "cdp") {
    const cdpUrl = await resolveWsTarget(localConfig.cdpTarget);
    return {
      localLaunchOptions: { cdpUrl },
      localInfo: { localSource: "attached-explicit", resolvedCdpUrl: cdpUrl },
    };
  }
}

资料来源:packages/cli/src/local-strategy.ts:1-80

CDP 自动发现

系统支持自动发现本地运行的 Chrome 实例:

async function probeFallbackPort(port: number): Promise<string | null> {
  const jsonVersionUrl = await probeJsonVersion(port);
  if (jsonVersionUrl) {
    return jsonVersionUrl;
  }
  // WebSocket fallback...
}

资料来源:packages/cli/src/local-cdp-discovery.ts:1-50

本地模式提示

根据不同的启动策略,系统会提供相应的使用提示:

const ISOLATED_MODE_HINT = "Hint: Run `browse env local --auto-connect` to reuse your local browsing credentials and cookies.";
const ATTACHED_EXISTING_HINT = "Hint: Run `browse env local` without `--auto-connect` to switch back to an isolated Chromium browser.";

网络捕获功能

Stagehand 提供完整的网络请求捕获和持久化能力。

数据模型

interface PendingRequest {
  id: string;
  timestamp: string;
  method: string;
  url: string;
  headers: Record<string, string>;
  body: string | null;
  resourceType: string;
}

文件系统组织

捕获的网络请求按以下结构存储:

{networkDir}/
  {counter}-{method}-{domain}-{pathSlug}/
    request.json   # 请求详情
    response.json  # 响应详情

文件名生成逻辑:

function getRequestDirName(
  counter: number,
  method: string,
  url: string,
): string {
  const parsed = new URL(url);
  const domain = sanitizeForFilename(parsed.hostname, 30);
  const pathPart = parsed.pathname.split("/").filter(Boolean)[0] || "root";
  const pathSlug = sanitizeForFilename(pathPart, 20);
  return `${String(counter).padStart(3, "0")}-${method}-${domain}-${pathSlug}`;
}

function sanitizeForFilename(str: string, maxLen: number = 30): string {
  return str
    .replace(/[^a-zA-Z0-9.-]/g, "-")
    .replace(/-+/g, "-")
    .replace(/^-|-$/g, "")
    .slice(0, maxLen);
}

资料来源:packages/cli/src/index.ts:55-100

全局配置选项

CLI 支持通过全局选项配置会话和行为:

interface GlobalOpts {
  ws?: string;              // WebSocket 连接地址
  headless?: boolean;       // 无头模式
  headed?: boolean;         // 有头模式(优先于 headless)
  json?: boolean;           // JSON 输出格式
  session?: string;         // 会话标识
  connect?: string;         // 连接目标
  proxies?: boolean;        // 启用代理(远程模式)
  advancedStealth?: boolean; // 高级隐身模式
  solveCaptchas?: boolean;   // 自动解决验证码
  region?: string;          // 区域设置
  keepAlive?: boolean;       // 保持连接
  sessionTimeout?: number;   // 会话超时时间
  blockAds?: boolean;       // 屏蔽广告
}

守护进程生命周期管理

启动与停止

program
  .command("start")
  .description("Start browser daemon")
  .action(async () => {
    if (await isDaemonRunning(session)) {
      console.log(JSON.stringify({ status: "already running", session }));
      return;
    }
    await ensureDaemon(session, isHeadless(opts));
  });

program
  .command("stop")
  .description("Stop browser daemon")
  .option("--force", "Force kill Chrome processes if daemon is unresponsive")

自动重连机制

CLI 实现了智能重连策略,在连接失败时自动尝试恢复:

// Attempt 0: Brief wait and retry
if (attempt === 0) {
  await new Promise((r) => setTimeout(r, 200));
  continue;
}

// Attempt 1: Try to restart daemon without cleanup
if (attempt === 1) {
  await ensureDaemon(session, headless);
  continue;
}

// Final attempt: Full cleanup and restart
await killChromeProcesses(session);
await cleanupStaleFiles(session);
await ensureDaemon(session, headless);

资料来源:packages/cli/src/index.ts:350-450

命令执行流程

sequenceDiagram
    participant CLI
    participant Daemon
    participant Browser
    
    CLI->>Daemon: sendCommand(command, args)
    Daemon->>Browser: Execute action
    Browser-->>Daemon: Result
    Daemon-->>CLI: Return result
    CLI->>CLI: output(result, json?)

命令执行的核心逻辑:

async function runCommand(
  command: string,
  args: unknown[],
  retries: number = 3,
): Promise<unknown> {
  for (let attempt = 0; attempt < retries; attempt++) {
    try {
      return await sendCommand(session, command, args);
    } catch (err) {
      // Connection error handling...
      if (attempt === 0) {
        await new Promise((r) => setTimeout(r, 200));
        continue;
      }
      await ensureDaemon(session, headless);
    }
  }
}

快速入门

基本导航

# 打开网页
stagehand open https://example.com

# 带等待状态
stagehand open https://example.com --wait networkidle

元素交互

# 点击按钮
stagehand click "#submit-button"

# 填充表单
stagehand fill "input[name=email]" "[email protected]"
stagehand fill "input[name=password]" "secret123"

页面信息

# 获取页面标题
stagehand get title

# 获取元素文本
stagehand get text ".article-content"

# 获取可访问性树
stagehand snapshot

截图

# 普通截图
stagehand screenshot output.png

# 全页截图
stagehand screenshot full-page.png --full-page

# 指定区域
stagehand screenshot cropped.png --clip '{"x":0,"y":0,"width":800,"height":600}'

总结

Stagehand 提供了一套完整的浏览器自动化解决方案,具有以下特点:

  1. 丰富的命令集:覆盖导航、交互、信息获取、网络捕获等各方面
  2. 灵活的运行模式:支持本地和远程两种部署方式
  3. 智能的策略选择:可根据需求选择隔离、CDP 或自动模式
  4. 可靠的守护进程:实现了进程锁和自动重连机制
  5. 详细的网络捕获:支持完整的请求/响应持久化

资料来源:[packages/cli/src/index.ts:1-50]()

快速开始

Stagehand 是一个由 Browserbase 开发的浏览器自动化框架,提供 CLI 工具和 Core SDK 两种使用方式,支持本地浏览器和远程 Browserbase 云端浏览器的自动化操作。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 前置要求

继续阅读本节完整说明和来源证据。

章节 安装步骤

继续阅读本节完整说明和来源证据。

章节 环境变量配置

继续阅读本节完整说明和来源证据。

环境准备

前置要求

要求说明
Node.js版本 20+
Playwright用户需自行安装 playwrightplaywright-core
系统依赖Chrome/Chromium 浏览器(本地模式)

安装步骤

# 安装核心包
npm install @browserbasehq/stagehand

# 安装 Playwright(必须)
npm install playwright

# 安装浏览器驱动
npx playwright install chromium

环境变量配置

在项目根目录创建 .env 文件,配置必要的环境变量:

# Browserbase 云端模式必需
BROWSERBASE_API_KEY=your_api_key
BROWSERBASE_PROJECT_ID=your_project_id

# 本地模式可选配置
BROWSERBASE_API_KEY=    # 留空则使用本地模式

资料来源:.env.example

CLI 快速上手

Stagehand CLI 提供丰富的浏览器自动化命令,安装后可通过 npx stagehand 或直接安装为全局命令使用。

基础命令结构

# 基本语法
stagehand [options] [command] [arguments]

# 常用全局选项
# --session <name>    指定会话名称
# --headless         以无头模式运行
# --connect <url>    连接到远程 Browserbase 会话
# --json             以 JSON 格式输出结果

资料来源:packages/cli/src/index.ts:1-50

导航与页面操作

# 打开网页
stagehand open https://example.com

# 等待页面加载完成
stagehand open https://example.com --wait load

# 页面加载超时设置(毫秒)
stagehand open https://example.com --timeout 30000

# 创建新标签页
stagehand newpage https://example.com

# 切换标签页(按索引)
stagehand tab_switch 1

# 关闭标签页
stagehand tab_close

资料来源:packages/cli/src/index.ts:200-260

元素交互

常见交互命令

命令说明示例
fill填写输入框stagehand fill "#search" "关键词"
select选择下拉选项stagehand select "#dropdown" "选项1"
click点击元素stagehand click "#submit"
type模拟键盘输入stagehand type "Hello World"
press按下特定按键stagehand press "Enter"

填写表单示例

# 填写输入框(默认会按 Enter)
stagehand fill "#username" "myuser"
stagehand fill "#password" "mypass"

# 不自动按 Enter
stagehand fill "#input" "value" --no-press-enter

# 输入延迟(模拟人工打字)
stagehand type "Hello" --delay 100

# 启用人工化输入(含随机错误)
stagehand type "Hello" --mistakes

资料来源:packages/cli/src/index.ts:150-200

键盘操作

# 按下按键
stagehand press Enter
stagehand press Tab
stagehand press Escape
stagehand press "Cmd+A"

# 组合键示例
stagehand press "Cmd+C"

资料来源:packages/cli/src/index.ts:140-160

元素状态检查

检查命令

# 检查元素是否可见
stagehand is visible "#submit-button"

# 检查复选框是否选中
stagehand is checked "#agree-checkbox"

获取页面信息

# 获取页面 URL
stagehand get url

# 获取页面标题
stagehand get title

# 获取元素文本内容
stagehand get text "#heading"

# 获取元素 HTML
stagehand get html "#container"

# 获取元素 Markdown 格式内容
stagehand get markdown "#article"

# 获取元素值
stagehand get value "#input"

# 获取元素边界框
stagehand get box "#image"

# 获取可见性状态
stagehand get visible "#hidden-element"

资料来源:packages/cli/src/index.ts:280-320

等待与同步

等待命令

# 等待页面加载
stagehand wait load

# 等待选择器出现
stagehand wait selector "#content"

# 等待超时(毫秒)
stagehand wait selector "#loading" --timeout 10000

# 等待元素状态
stagehand wait selector "#modal" --state visible
stagehand wait selector "#tooltip" --state hidden
stagehand wait selector "#element" --state attached

状态选项:visiblehiddenattacheddetached

资料来源:packages/cli/src/index.ts:50-80

截图与快照

截图功能

# 基础截图
stagehand screenshot

# 保存到指定路径
stagehand screenshot ./screenshot.png

# 全页面截图
stagehand screenshot ./full-page.png --full-page

# 指定图片格式和质量
stagehand screenshot ./output.jpg --type jpeg --quality 85

# 裁剪区域(JSON 格式)
stagehand screenshot ./cropped.png --clip '{"x":0,"y":0,"width":800,"height":600}'

# 禁用动画和隐藏光标
stagehand screenshot ./clean.png --no-animations --hide-caret

资料来源:packages/cli/src/index.ts:340-380

辅助功能树快照

# 获取完整快照(含 XPath 映射)
stagehand snapshot

# 紧凑输出(仅树结构)
stagehand snapshot --compact

快照输出包含:

  • tree: 格式化的辅助功能树
  • xpathMap: 元素到 XPath 的映射
  • urlMap: URL 引用映射

资料来源:packages/cli/src/index.ts:380-420

坐标操作

鼠标操作

# 悬停到坐标位置
stagehand hover 100 200

# 悬停并返回元素 XPath
stagehand hover 100 200 --xpath

# 滚动页面
stagehand scroll 0 500 0 100

# 拖拽操作
stagehand drag 100 200 300 400

资料来源:packages/cli/src/index.ts:220-250

视图控制

# 设置视口大小
stagehand viewport 1920 1080

# 设置缩放比例
stagehand viewport 1920 1080 --scale 2

资料来源:packages/cli/src/index.ts:420-440

Daemon 模式

Stagehand 使用守护进程模式管理浏览器实例,支持会话复用。

进程管理命令

# 启动守护进程
stagehand start

# 停止守护进程
stagehand stop

# 强制停止(杀掉 Chrome 进程)
stagehand stop --force

# 检查状态
stagehand status

会话管理

# 使用指定会话
stagehand --session my-session open https://example.com

# 远程模式会话超时
stagehand --session my-session --session-timeout 300 open https://example.com

资料来源:packages/cli/src/index.ts:450-520

本地模式策略

Stagehand CLI 支持多种本地浏览器模式:

graph TD
    A[开始解析本地策略] --> B{strategy 配置}
    B -->|isolated| C[启动独立 Chromium 实例]
    B -->|cdp| D[连接到指定 CDP 目标]
    B -->|auto| E[自动检测本地浏览器]
    E -->|发现调试端口| F[附加到现有浏览器]
    E -->|未发现| G[回退到隔离模式]
    
    C --> H[返回 localSource: isolated]
    D --> I[返回 localSource: attached-explicit]
    F --> J[返回 localSource: attached-existing]
    G --> K[返回 localSource: isolated-fallback]
策略说明localSource 值
isolated启动独立的 Chromium 实例isolated
cdp通过 CDP URL 连接到指定浏览器attached-explicit
auto自动检测或回退attached-existing / isolated-fallback

资料来源:packages/cli/src/local-strategy.ts:20-80

CDP 发现机制

本地模式支持自动发现 Chrome DevTools 协议端点:

graph LR
    A[扫描用户数据目录] --> B{读取 DevToolsActivePort}
    B -->|成功| C{检查端口可达性}
    B -->|失败| D[尝试备用端口]
    C -->|可达| E[构建 WebSocket URL]
    C -->|不可达| D
    D --> E
    E --> F[返回 cdpUrl]

资料来源:packages/cli/src/local-cdp-discovery.ts:1-60

输出格式

JSON 输出模式

# 启用 JSON 输出
stagehand get title --json

# 示例输出
{
  "success": true,
  "data": {
    "title": "Example Domain"
  }
}

基础输出

默认情况下,CLI 以人类可读格式输出结果:

stagehand get url
# 输出: https://example.com

stagehand snapshot --compact
# 输出: 辅助功能树结构

网络请求捕获

Stagehand 支持捕获和分析网络请求:

graph TD
    A[请求进入] --> B{网络捕获启用?}
    B -->|否| C[跳过]
    B -->|是| D[记录请求元数据]
    D --> E[等待响应]
    E --> F[写入文件系统]
    F --> G[生成目录结构]
    
    G --> H[request.json]
    G --> I[response.json]

请求数据存储结构:

<networkDir>/
  └── <counter>-<METHOD>-<domain>-<path>/
      ├── request.json   # 请求详情
      └── response.json  # 响应详情

资料来源:packages/cli/src/index.ts:80-150

常见工作流示例

自动化测试流程

# 1. 启动并导航
stagehand open https://example.com --wait load

# 2. 获取初始快照
stagehand snapshot --compact > initial-state.txt

# 3. 执行交互
stagehand fill "#search" "test query"
stagehand press Enter

# 4. 等待结果加载
stagehand wait selector ".results" --timeout 10000

# 5. 截图保存
stagehand screenshot ./search-results.png

# 6. 验证结果
stagehand is visible ".result-item"

数据采集流程

# 1. 导航到目标页面
stagehand open https://news.example.com

# 2. 截图存档
stagehand screenshot ./news-$(date +%Y%m%d).png --full-page

# 3. 获取内容
stagehand get markdown "article"

# 4. 关闭浏览器
stagehand stop

故障排除

问题解决方案
连接被拒绝运行 stagehand start 启动守护进程
浏览器未响应使用 stagehand stop --force 强制停止
超时错误增加 --timeout 参数值
CDP 连接失败检查 Chrome 是否以 --remote-debugging-port 启动

查看详细日志

# 启用详细输出
stagehand --verbose open https://example.com

资料来源:[.env.example](https://github.com/browserbase/stagehand/blob/main/.env.example)

系统架构总览

Stagehand 是一个基于 Chrome DevTools Protocol (CDP) 的浏览器自动化 CLI 工具和 SDK,提供命令行界面和编程 API 两种交互方式。该项目采用 monorepo 结构,使用 pnpm workspace 管理多个包。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 3.1 命令分类表

继续阅读本节完整说明和来源证据。

章节 3.2 命令执行流程

继续阅读本节完整说明和来源证据。

章节 4.1 Socket 通信机制

继续阅读本节完整说明和来源证据。

1. 项目概述

Stagehand 是一个基于 Chrome DevTools Protocol (CDP) 的浏览器自动化 CLI 工具和 SDK,提供命令行界面和编程 API 两种交互方式。该项目采用 monorepo 结构,使用 pnpm workspace 管理多个包。

核心项目括:

  • 浏览器页面导航与交互
  • 元素定位与操作
  • 截图与 DOM 快照
  • 网络请求捕获
  • 本地浏览器发现与连接
  • Daemon 进程管理

资料来源:packages/cli/src/index.ts:1-50

2. 整体架构

Stagehand 采用客户端-服务器架构,核心组件包括:

graph TD
    subgraph CLI层
        A[CLI Commands] --> B[runCommand]
        B --> C[runDaemon]
    end
    
    subgraph 通信层
        D[Unix Socket] <--> E[WebSocket Bridge]
        F[Lock File] 
    end
    
    subgraph Daemon层
        G[Browser Daemon] --> H[Playwright Page]
        H --> I[Accessibility Snapshot]
    end
    
    subgraph 本地发现
        J[CDP Discovery] --> K[DevTools Active Port]
        K --> L[WebSocket Target]
    end
    
    A --> D
    C --> D
    G --> J

资料来源:packages/cli/src/index.ts:200-300

3. CLI 命令体系

CLI 使用 commander.js 框架实现,所有命令都通过 runCommand 函数统一路由到 Daemon 进程执行。

3.1 命令分类表

类别命令功能描述
导航open, goto页面导航
元素click, dblclick, hover元素交互
输入fill, select, type, press文本与选择操作
查询get, is, snapshot页面信息获取
截图screenshot页面截图
坐标scroll, drag视口操作
网络network网络请求捕获
多页pages, newpage多标签页管理
守护start, stop, statusDaemon 控制

资料来源:packages/cli/src/index.ts:50-200

3.2 命令执行流程

sequenceDiagram
    participant CLI as CLI Client
    participant Socket as Unix Socket
    participant Daemon as Browser Daemon
    participant Browser as Playwright Browser
    
    CLI->>Socket: sendCommand(command, args)
    Socket->>Daemon: 转发请求
    Daemon->>Browser: 执行操作
    Browser-->>Daemon: 返回结果
    Daemon-->>Socket: 序列化响应
    Socket-->>CLI: 返回结果
    CLI->>CLI: output(result, json)

资料来源:packages/cli/src/index.ts:300-400

4. Daemon 基础设施

Daemon 是长期运行的浏览器进程管理服务,通过 Unix Socket 与 CLI 通信。

4.1 Socket 通信机制

// Socket 路径: /tmp/browse-{session}.sock
function getSocketPath(session: string): string {
  return path.join(os.tmpdir(), `browse-${session}.sock`);
}

资料来源:packages/cli/src/index.ts:350-360

4.2 文件锁机制

系统使用 O_EXCL 模式的原子文件创建实现互斥锁,防止多进程竞争:

async function acquireLock(
  session: string,
  timeoutMs: number = 10000
): Promise<boolean> {
  const lockPath = getLockPath(session);
  // O_EXCL 确保原子创建
  const handle = await fs.open(lockPath, "wx");
  await handle.write(String(process.pid));
}
参数类型默认值说明
sessionstring"default"会话标识符
timeoutMsnumber10000锁等待超时

资料来源:packages/cli/src/index.ts:380-420

4.3 连接重试策略

当连接失败时,系统自动执行三级重试:

重试次数策略动作
0短暂等待等待 200ms 后重试
1重启 Daemon不清理状态重启
2完全清理杀死进程、清理文件、重启

资料来源:packages/cli/src/index.ts:450-500

5. 本地模式策略系统

本地模式支持三种策略:autoisolatedcdp

5.1 策略决策流程

graph TD
    A[resolveLocalStrategy] --> B{strategy === isolated?}
    B -->|是| C[使用 headless + viewport]
    B -->|否| D{strategy === cdp?}
    D -->|是| E[解析 cdpTarget]
    D -->|否| F[discoverLocalCdp]
    F --> G{发现浏览器?}
    G -->|是| H[连接现有浏览器]
    G -->|否| I[isolated-fallback]

资料来源:packages/cli/src/local-strategy.ts:30-70

5.2 策略类型对比

策略说明使用场景
auto自动发现或创建默认模式,智能选择
isolated启动独立浏览器实例完全隔离的环境
cdp连接到指定 CDP 端点复用现有浏览器会话

资料来源:packages/cli/src/local-strategy.ts:10-50

5.3 本地信息源

来源类型说明提示信息
attached-existing连接已存在的调试浏览器提示使用 --auto-connect
attached-explicit显式指定 CDP 目标-
isolated独立启动的浏览器提示切换到 attached 模式
isolated-fallback无法发现时的回退-

资料来源:packages/cli/src/local-strategy.ts:50-80

6. CDP 发现机制

CDP (Chrome DevTools Protocol) 发现用于自动找到本地运行的 Chrome 浏览器实例。

6.1 发现流程

graph LR
    A[读取 DevToolsActivePort] --> B{端口可达?}
    B -->|否| C[cleanup 过期文件]
    B -->|是| D[构建 WebSocket URL]
    E[探测 JSON Version] --> F{成功?}
    F -->|是| D
    F -->|否| G[探测 WebSocket]

资料来源:packages/cli/src/local-cdp-discovery.ts:100-150

6.2 WebSocket 探测

系统通过低层 TCP 握手检测 WebSocket 支持:

async function probeWebSocket(port: number): Promise<boolean> {
  // 发送 HTTP Upgrade 请求
  const response = await performWebSocketHandshake(port, wsKey);
  // 检查 101 Switching Protocols 响应
  return /^HTTP\/1\.[01] 101(?:\s|$)/.test(response);
}

资料来源:packages/cli/src/local-cdp-discovery.ts:50-80

7. 全局配置选项

CLI 全局选项通过 GlobalOpts 接口定义:

选项类型说明
--sessionstring会话标识
--headlessboolean无头模式
--headedboolean窗口模式
--jsonbooleanJSON 输出
--wsstringWebSocket 地址
--connectstring远程连接地址
--proxiesboolean启用代理 (远程)
--regionstring区域设置 (远程)
--block-adsboolean广告拦截 (远程)

资料来源:packages/cli/src/index.ts:500-550

8. 网络捕获系统

系统提供完整的网络请求/响应捕获能力。

8.1 捕获数据结构

interface PendingRequest {
  id: string;
  timestamp: string;
  method: string;
  url: string;
  headers: Record<string, string>;
  body: string | null;
  resourceType: string;
}

8.2 文件存储结构

每个请求保存到独立目录:

{networkDir}/
  └── {counter:3d}-{method}-{domain}-{pathSlug}/
      ├── request.json   # 请求元数据
      └── response.json  # 响应元数据

资料来源:packages/cli/src/index.ts:100-180

9. 页面信息获取

snapshot 命令提供可访问性树快照功能:

case "snapshot": {
  const snapshot = await page!.snapshot();
  return {
    tree: snapshot.formattedTree,
    xpathMap: snapshot.xpathMap,
    urlMap: snapshot.urlMap,
  };
}
输出字段类型说明
treestring格式化的可访问性树
xpathMapRecord选择器到 XPath 的映射
urlMapRecord元素到 URL 的映射

资料来源:packages/cli/src/index.ts:250-280

10. 关键技术栈

组件技术选型
CLI 框架commander.js
浏览器自动化Playwright
通信协议Unix Socket + WebSocket
包管理pnpm workspace

资料来源:pnpm-workspace.yaml()

资料来源:[packages/cli/src/index.ts:1-50]()

act() 操作执行

act() 是 Stagehand 框架中的核心操作执行机制,负责将高级自然语言指令转换为具体的浏览器自动化操作。该系统通过 Handler-工具(Tool)架构模式,实现了操作意图识别、目标元素定位、操作执行和结果验证的完整流程。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 核心组件关系

继续阅读本节完整说明和来源证据。

章节 执行流程

继续阅读本节完整说明和来源证据。

章节 元素交互操作

继续阅读本节完整说明和来源证据。

概述

act() 是 Stagehand 框架中的核心操作执行机制,负责将高级自然语言指令转换为具体的浏览器自动化操作。该系统通过 Handler-工具(Tool)架构模式,实现了操作意图识别、目标元素定位、操作执行和结果验证的完整流程。

在 Stagehand 的 CLI 工具 packages/cli/src/index.ts 中,操作执行通过 runCommand 函数与浏览器守护进程通信,守护进程接收指令后调用相应的 Handler 处理逻辑。资料来源:packages/cli/src/index.ts:1-100

架构设计

核心组件关系

graph TD
    A[Agent / LLM] --> B[actHandler]
    B --> C[工具选择]
    C --> D[click.ts]
    C --> E[type.ts]
    C --> F[其他工具]
    D --> G[ElementHandler]
    E --> G
    F --> G
    G --> H[Playwright Page API]
    H --> I[浏览器执行]
    I --> J[结果验证]
    J --> K[状态反馈]

执行流程

阶段组件职责
意图解析actHandler.ts解析 LLM 生成的指令,提取操作类型和参数
工具路由act.ts根据操作类型选择对应的工具模块
元素定位各工具模块使用 XPath/CSS 选择器定位目标元素
操作执行ElementHandler调用 Playwright API 执行具体操作
结果验证Handler 返回值返回执行结果和状态信息

资料来源:packages/core/lib/v3/handlers/actHandler.ts:1-50 资料来源:packages/core/lib/v3/agent/tools/act.ts:1-50

操作类型

元素交互操作

#### click 点击操作

click 操作用于模拟鼠标点击事件,支持多种变体:

命令描述参数
click点击元素selector, button, count, force
click_xy坐标点击x, y, button, count
dblclick双击selector
rightclick右键菜单selector

CLI 实现示例:

program
  .command("click <selector>")
  .description("Click element")
  .option("-b, --button <btn>", "Mouse button", "left")
  .option("-c, --count <n>", "Click count", "1")
  .option("--force", "Force click")
  .option("--xpath", "Return XPath of clicked element")
  .action(async (selector: string, cmdOpts) => {
    const opts = program.opts<GlobalOpts>();
    const result = await runCommand("click", [
      selector,
      {
        button: cmdOpts.button,
        clickCount: parseInt(cmdOpts.count),
        force: cmdOpts.force,
      },
    ]);
    output(result, opts.json ?? false);
  });

资料来源:packages/core/lib/v3/agent/tools/click.ts:1-80 资料来源:packages/cli/src/index.ts:200-250

#### type 输入操作

type 操作用于模拟键盘输入,支持人类化输入模式:

选项类型描述
textstring要输入的文本内容
delaynumber击键间隔(毫秒)
mistakesboolean启用类人错误模拟
program
  .command("type <text>")
  .description("Type text")
  .option("-d, --delay <ms>", "Delay between keystrokes")
  .option("--mistakes", "Enable human-like typing with mistakes")
  .action(async (text: string, cmdOpts) => {
    const result = await runCommand("type", [
      text,
      {
        delay: cmdOpts.delay ? parseInt(cmdOpts.delay) : undefined,
        mistakes: cmdOpts.mistakes,
      },
    ]);
  });

资料来源:packages/core/lib/v3/agent/tools/type.ts:1-60 资料来源:packages/cli/src/index.ts:350-400

#### fill 填充操作

fill 专门用于填充表单输入框,默认在填充完成后按 Enter 键:

参数类型描述
selectorstring输入框选择器
valuestring填充值
pressEnterboolean是否按 Enter 确认

资料来源:packages/cli/src/index.ts:280-330

#### select 下拉选择

select 用于选择下拉菜单选项:

program
  .command("select <selector> <values...>")
  .description("Select option(s)")
  .action(async (selector: string, values: string[]) => {
    const result = await runCommand("select", [selector, values]);
  });

坐标操作

#### hover 悬停

悬停到指定坐标位置:

program
  .command("hover <x> <y>")
  .description("Hover at coordinates")
  .option("--xpath", "Return XPath of hovered element")
  .action(async (x: string, y: string, cmdOpts) => {
    const result = await runCommand("hover", [
      parseFloat(x),
      parseFloat(y),
      { returnXPath: cmdOpts.xpath },
    ]);
  });

#### scroll 滚动

在指定位置执行滚动操作:

program
  .command("scroll <x> <y> <deltaX> <deltaY>")
  .description("Scroll at coordinates")
  .option("--xpath", "Return XPath of scrolled element")
  .action(async (x: string, y: string, dx: string, dy: string, cmdOpts) => {
    const result = await runCommand("scroll", [
      parseFloat(x),
      parseFloat(y),
      parseFloat(dx),
      parseFloat(dy),
      { returnXPath: cmdOpts.xpath },
    ]);
  });

资料来源:packages/cli/src/index.ts:420-470

键盘操作

#### press 按键

模拟按下指定键盘按键:

参数示例值
修饰键Enter, Tab, Escape
组合键Cmd+A, Ctrl+C, Shift+Tab
功能键F1-F12
program
  .command("press <key>")
  .alias("key")
  .description("Press key (e.g., Enter, Tab, Escape, Cmd+A)")
  .action(async (key: string) => {
    const result = await runCommand("press", [key]);
  });

资料来源:packages/cli/src/index.ts:380-410

状态检查操作

#### is 检查元素状态

验证元素的可见性或选中状态:

program
  .command("is <check> <selector>")
  .description("Check element state: visible, checked")
  .action(async (check: string, selector: string) => {
    const result = await runCommand("is", [check, selector]);
  });
检查类型描述
visible元素是否可见
checked复选框/单选框是否选中

#### get 获取页面信息

获取页面或元素的各类信息:

program
  .command("get <what> [selector]")
  .description(
    "Get page info: url, title, text, html, markdown, value, box, visible, checked",
  )
  .action(async (what: string, selector?: string) => {
    const result = await runCommand("get", [what, selector]);
  });

资料来源:packages/cli/src/index.ts:490-520

导航操作

#### open 页面导航

program
  .command("open <url>")
  .alias("goto")
  .description("Navigate to URL")
  .option("--wait <state>", "Wait state: load, domcontentloaded, networkidle")
  .option("-t, --timeout <ms>", "Navigation timeout", "30000")
  .action(async (url: string, cmdOpts) => {
    // 支持等待状态配置和超时控制
  });

资料来源:packages/cli/src/index.ts:560-620

特殊操作

#### wait 等待操作

等待特定条件满足:

program
  .command("wait <type> [arg]")
  .description("Wait for: load, selector, timeout")
  .option("-t, --timeout <ms>", "Timeout", "30000")
  .option("-s, --state <state>", "Element state: visible, hidden, attached, detached")
  .action(async (type: string, arg: string | undefined, cmdOpts) => {
    const result = await runCommand("wait", [
      type,
      arg,
      { timeout: parseInt(cmdOpts.timeout), state: cmdOpts.state },
    ]);
  });
等待类型参数描述
load-等待页面加载
selector选择器等待元素出现
timeout毫秒等待指定时间

#### eval JavaScript 执行

在页面上下文中执行任意 JavaScript:

program
  .command("eval <expression>")
  .description("Evaluate JavaScript in page")
  .action(async (expr: string) => {
    const result = await runCommand("eval", [expr]);
  });

资料来源:packages/cli/src/index.ts:540-560

执行上下文管理

Session 隔离机制

Stagehand 使用 Session 来隔离不同的浏览器上下文:

interface GlobalOpts {
  ws?: string;
  headless?: boolean;
  headed?: boolean;
  json?: boolean;
  session?: string;
  connect?: string;
  proxies?: boolean;
  advancedStealth?: boolean;
  solveCaptchas?: boolean;
  region?: string;
  keepAlive?: boolean;
  sessionTimeout?: number;
  blockAds?: boolean;
}

function getSession(opts: GlobalOpts): string {
  return opts.session ?? process.env.BROWSE_SESSION ?? "default";
}

资料来源:packages/cli/src/index.ts:700-750

守护进程架构

CLI 与浏览器守护进程之间通过 Unix Socket 进行通信:

graph LR
    A[CLI Client] -->|runCommand| B[Unix Socket]
    B --> C[Daemon Process]
    C --> D[Chrome DevTools Protocol]
    D --> E[Browser Instance]

守护进程管理包括:

功能描述
start启动浏览器守护进程
stop停止守护进程
status检查守护进程状态
daemon以守护进程模式运行
program
  .command("start")
  .description("Start browser daemon")
  .action(async () => {
    const session = getSession(opts);
    if (await isDaemonRunning(session)) {
      console.log(JSON.stringify({ status: "already running", session }));
      return;
    }
    await ensureDaemon(session, isHeadless(opts));
  });

资料来源:packages/cli/src/index.ts:620-680

操作执行重试机制

当命令执行失败时,系统会自动进行重试:

async function sendCommand(
  session: string,
  command: string,
  args: unknown[],
  headless: boolean,
  retries: number = 3,
): Promise<unknown> {
  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
      return await sendCommandOnce(session, command, args);
    } catch (err) {
      if (attempt === retries || command === "stop") {
        throw err;
      }

      const errMsg = err instanceof Error ? err.message : String(err);
      const isConnectionError =
        errMsg.includes("ENOENT") ||
        errMsg.includes("ECONNREFUSED") ||
        errMsg.includes("Connection failed");

      if (!isConnectionError) {
        throw err;
      }

      // 重试策略
      if (attempt === 0) {
        await new Promise((r) => setTimeout(r, 200));
        continue;
      }
      if (attempt === 1) {
        await ensureDaemon(session, headless);
        continue;
      }

      await killChromeProcesses(session);
      await cleanupStaleFiles(session);
      await ensureDaemon(session, headless);
    }
  }
}
重试次数策略原因
0短暂等待后重试Socket 可能暂时不可用
1重启守护进程守护进程可能无响应
2清理并完全重启清理残留状态

资料来源:packages/cli/src/index.ts:800-870

输出格式

操作执行结果支持两种输出格式:

模式触发方式输出内容
标准输出默认格式化的人类可读输出
JSON 模式--json 选项结构化 JSON 数据
function output(result: unknown, asJson: boolean): void {
  if (asJson) {
    console.log(JSON.stringify(result, null, 2));
  } else {
    // 格式化输出
    console.log(result);
  }
}

总结

act() 操作执行系统是 Stagehand 自动化能力的核心,它通过以下设计实现:

  1. 分层架构:Handler 层负责指令解析,工具层负责具体操作
  2. 灵活的命令路由:CLI 通过统一的 runCommand 接口支持多种操作类型
  3. 健壮的错误处理:自动重试机制和详细的错误信息
  4. 会话隔离:通过 Session 实现多任务并行执行
  5. 丰富的操作类型:覆盖点击、输入、导航、状态检查等常见自动化场景

资料来源:[packages/core/lib/v3/handlers/actHandler.ts:1-50]()

extract() 数据提取

extract() 是 Stagehand 框架中的核心数据提取方法,允许开发者从网页内容中提取结构化数据。该功能通过 Zod schema 定义期望的数据结构,由 AI 模型智能解析页面内容并返回类型安全的提取结果。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 1. Schema 驱动的数据提取

继续阅读本节完整说明和来源证据。

章节 2. 指令引导的提取

继续阅读本节完整说明和来源证据。

章节 支持的 Zod 类型

继续阅读本节完整说明和来源证据。

概述

extract() 是 Stagehand 框架中的核心数据提取方法,允许开发者从网页内容中提取结构化数据。该功能通过 Zod schema 定义期望的数据结构,由 AI 模型智能解析页面内容并返回类型安全的提取结果。

核心功能

1. Schema 驱动的数据提取

Stagehand 使用 Zod 作为数据验证和类型定义的基础。开发者可以通过 Zod schema 定义期望的输出结构:

import { extract } from "@browserbase/stagehand";
import { z } from "zod";

const schema = z.object({
  title: z.string(),
  price: z.number(),
  inStock: z.boolean(),
});

const result = await extract(page, { 
  instruction: "提取产品名称、价格和库存状态",
  schema 
});

2. 指令引导的提取

extract() 方法支持自然语言指令,让 AI 模型理解需要提取哪些信息:

参数类型描述
instructionstring描述需要提取的数据的自然语言指令
schemaZodSchema定义提取数据的 Zod schema
maxTokensnumber最大响应 token 数(可选)

工作流程

graph TD
    A[调用 extract] --> B[解析 Zod Schema]
    B --> C[生成 AI Prompt]
    C --> D[执行 DOM 解析]
    D --> E[提取匹配数据]
    E --> F[Schema 验证]
    F --> G{验证通过?}
    G -->|是| H[返回结构化数据]
    G -->|否| I[抛出验证错误]

与 Zod 的集成

Stagehand 通过 zodCompat.ts 实现与 Zod 的深度集成:

// packages/core/lib/zodCompat.ts
import { z } from "zod";

// 支持的 Zod 类型映射
const typeMap = {
  string: "text",
  number: "number", 
  boolean: "boolean",
  array: "array",
  object: "object",
};

支持的 Zod 类型

Zod 类型提取行为说明
z.string()文本提取返回 DOM 元素的文本内容
z.number()数值提取解析文本中的数字
z.boolean()布尔提取识别 yes/no、true/false 等
z.array(z.string())列表提取返回多个匹配元素
z.object({...})嵌套对象递归提取复杂结构

错误处理

graph LR
    A[提取操作] --> B{执行结果}
    B -->|成功| C[返回数据]
    B -->|验证失败| D[ZodError]
    B -->|超时| E[TimeoutError]
    B -->|无匹配| F[NoMatchError]

常见错误类型

错误类型描述处理方式
ValidationErrorSchema 验证失败检查 schema 定义
TimeoutError操作超时增加 timeout 参数
NoMatchError未找到匹配元素调整选择器或指令

使用示例

基本用法

import { stagehand } from "@browserbase/stagehand";
import { z } from "zod";

const page = await stagehand.init();

const product = await page.extract({
  instruction: "提取产品标题和价格",
  schema: z.object({
    title: z.string(),
    price: z.number(),
  }),
});

console.log(product);
// { title: "iPhone 15 Pro", price: 999 }

复杂嵌套结构

const schema = z.object({
  products: z.array(z.object({
    name: z.string(),
    price: z.number(),
    variants: z.array(z.object({
      color: z.string(),
      inStock: z.boolean(),
    })),
  })),
  totalResults: z.number(),
});

const data = await page.extract({
  instruction: "提取所有产品及其变体信息",
  schema,
});

与其他模块的协同

graph TD
    subgraph 核心模块
        A[extractHandler.ts]
        B[extract.ts]
        C[zodCompat.ts]
    end
    
    subgraph 依赖关系
        D[snapshot 获取 DOM]
        E[AI 模型解析]
        F[Zod 验证]
    end
    
    A --> B
    B --> C
    A --> D
    A --> E
    A --> F
    
    style A fill:#e1f5fe
    style B fill:#fff3e0
    style C fill:#e8f5e9

配置选项

选项默认值描述
timeout30000操作超时时间(毫秒)
retries3失败重试次数
modelgpt-4o使用的 AI 模型
verbosefalse启用详细日志

最佳实践

  1. 使用精确的指令:指令越具体,提取结果越准确
  2. 选择合适的 Schema:避免过于复杂的嵌套结构
  3. 处理可选字段:使用 z.string().optional() 处理可能缺失的字段
  4. 设置合理的超时:复杂页面需要更长超时时间
  5. 验证返回数据:即使通过 schema 验证,也建议进行运行时检查

相关资源

来源:https://github.com/browserbase/stagehand / 项目说明书

observe() 页面观察

observe() 是 stagehand 框架中的核心页面观察功能,用于实时捕获和分析网页的当前状态。该功能通过无障碍树(Accessibility Tree)快照机制,获取页面的 DOM 结构、元素属性、位置信息等关键数据,为 AI 代理提供理解和操作网页的能力。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 Snapshot 快照系统

继续阅读本节完整说明和来源证据。

章节 Observe Handler 处理层

继续阅读本节完整说明和来源证据。

章节 CLI 集成接口

继续阅读本节完整说明和来源证据。

概述

observe() 是 stagehand 框架中的核心页面观察功能,用于实时捕获和分析网页的当前状态。该功能通过无障碍树(Accessibility Tree)快照机制,获取页面的 DOM 结构、元素属性、位置信息等关键数据,为 AI 代理提供理解和操作网页的能力。

页面观察功能在架构中扮演着"感知层"的角色,是连接浏览器渲染结果与 AI 决策引擎的关键桥梁。通过标准化的快照格式,系统能够将复杂的 DOM 结构转化为 AI 可理解的语义化表示。

核心组件

Snapshot 快照系统

快照系统负责从浏览器页面提取无障碍信息,生成结构化的页面描述。

组件职责输出格式
capture.ts底层无障碍数据采集原始 AX 节点树
index.ts快照格式化与映射构建树形结构 + XPath/URL 映射

快照系统的工作流程:

graph TD
    A[浏览器页面] --> B[Playwright snapshot API]
    B --> C[原始 AX 节点]
    C --> D[格式化处理]
    D --> E[formattedTree 树形输出]
    D --> F[xpathMap XPath 映射]
    D --> G[urlMap URL 映射]

Observe Handler 处理层

observeHandler.ts 实现了页面观察的核心逻辑,负责协调快照采集、结果处理和错误管理。

处理流程包含以下关键阶段:

  1. 初始化阶段 - 验证观察上下文和目标页面
  2. 采集阶段 - 调用底层快照 API 获取无障碍树
  3. 处理阶段 - 格式化节点数据,构建引用映射
  4. 返回阶段 - 输出结构化的观察结果

CLI 集成接口

在 CLI 工具中,snapshot 命令提供了直接调用观察功能的入口:

program
  .command("snapshot")
  .description("Get accessibility tree snapshot")
  .option("-c, --compact", "Output tree only (no xpath map)")

输出格式示例:

{
  "tree": "页面元素的树形文本表示",
  "xpathMap": {
    "elementRef1": "/html/body/div[1]/button[2]",
    "elementRef2": "/html/body/main/section[1]"
  },
  "urlMap": {
    "linkRef1": "https://example.com/page",
    "linkRef2": "https://api.example.com/data"
  }
}

快照数据模型

节点结构

每个无障碍树节点包含以下核心属性:

属性类型描述
rolestring元素的 ARIA 角色
namestring元素的辅助名称
valuestring元素的值属性
descriptionstring元素的描述信息
statestring[]元素状态数组
childrenNode[]子节点列表

引用映射机制

系统维护两套关键映射表:

XPath 映射 - 将内部引用转换为 XPath 表达式,支持精确定位 DOM 元素 URL 映射 - 记录页面中的链接和资源 URL,便于后续操作

graph LR
    A[snapshot 结果] --> B[tree 格式化文本]
    A --> C[xpathMap 引用表]
    A --> D[urlMap URL 表]
    C --> E[click 元素定位]
    C --> F[fill 输入定位]
    D --> G[navigate 链接访问]

配置选项

紧凑模式 (Compact Mode)

使用 --compact-c 选项时,快照输出仅包含树形结构,不包含映射表:

browse snapshot --compact

适用于需要快速查看页面结构的场景,减少输出量。

全局输出控制

选项说明默认值
--json输出完整 JSON 格式false
紧凑模式仅输出树形文本false

与其他模块的协作

无障碍树采集 (capture.ts)

底层采集模块通过 Playwright 的 snapshot() 方法获取页面的无障碍表示。该模块负责:

  • 遍历 DOM 节点构建 AX 树
  • 提取元素的语义化信息
  • 过滤无关的内部节点

本地模式发现 (local-cdp-discovery.ts)

页面观察功能依赖于与浏览器实例的 CDP 连接。CDP 发现机制确保 CLI 能够正确连接到本地或远程浏览器实例:

graph TD
    A[启动浏览器] --> B[CDP 端口检测]
    B --> C{发现可用端口}
    C -->|成功| D[建立 WebSocket 连接]
    C -->|失败| E[使用默认端口]
    D --> F[初始化 snapshot 服务]

本地策略解析 (local-strategy.ts)

根据配置策略,系统选择合适的浏览器连接方式:

策略说明适用场景
isolated独立 Chromium 实例默认隔离环境
cdp连接到已有浏览器复用已有会话
auto自动发现可用浏览器智能选择

使用场景

1. 页面状态分析

在执行自动化操作前,先观察页面结构:

browse open https://example.com
browse snapshot

2. 元素定位验证

通过 XPath 映射验证元素选择器的正确性:

browse snapshot --json
# 检查 xpathMap 中的路径是否与预期匹配

3. AI 代理感知

为 AI 代理提供页面状态的语义化表示,支持决策制定:

{
  "tree": "form\n  input[name='username']\n  input[name='password']\n  button[type='submit']",
  "xpathMap": {
    "username": "/html/body/form/input[1]",
    "password": "/html/body/form/input[2]",
    "submit": "/html/body/form/button"
  }
}

技术规格

性能特性

指标典型值说明
采集延迟< 100ms中等复杂度页面
树深度限制完整遍历
输出大小10KB - 500KB取决于页面复杂度

错误处理

观察功能实现了完善的错误处理机制:

  • 连接失败 - 自动重试并清理陈旧状态
  • 超时处理 - 默认 30 秒超时保护
  • 部分失败 - 返回已采集的部分数据

总结

observe() 页面观察功能是 stagehand 框架的感知核心,通过标准化的无障碍树快照机制,将网页的视觉和语义信息转化为 AI 可理解的结构化数据。该功能与 CLI 工具、CDP 连接层、本地策略模块紧密协作,为浏览器自动化操作提供了稳定、可靠的页面状态感知能力。

来源:https://github.com/browserbase/stagehand / 项目说明书

Agent 代理系统

Stagehand 的 Agent 代理系统是浏览器自动化操作的核心引擎,负责管理浏览器会话、执行自动化命令、处理网络请求捕获以及维护浏览器状态。该系统通过 CLI 接口和核心库协同工作,为 AI 代理提供可观测、可控制的浏览器操作能力。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 整体架构图

继续阅读本节完整说明和来源证据。

章节 1. CLI 命令接口层

继续阅读本节完整说明和来源证据。

章节 2. Daemon 守护进程管理

继续阅读本节完整说明和来源证据。

概述

Stagehand 的 Agent 代理系统是浏览器自动化操作的核心引擎,负责管理浏览器会话、执行自动化命令、处理网络请求捕获以及维护浏览器状态。该系统通过 CLI 接口和核心库协同工作,为 AI 代理提供可观测、可控制的浏览器操作能力。

系统架构

整体架构图

graph TD
    subgraph CLI层 ["CLI 接口层"]
        CLI[packages/cli/src/index.ts]
        Cmds[命令定义]
    end
    
    subgraph Agent核心 ["Agent 核心系统"]
        AC[AgentClient]
        AP[AgentProvider]
        Handler[v3AgentHandler]
        ActionMap[actionMapping]
    end
    
    subgraph Daemon层 ["Daemon 守护进程"]
        Socket[Unix Socket]
        Chrome[Chrome Browser]
        CDP[CDP Protocol]
    end
    
    CLI --> Cmds
    Cmds --> AC
    AC --> AP
    AP --> Handler
    Handler --> ActionMap
    ActionMap --> Socket
    Socket --> Chrome
    Chrome --> CDP
    
    LocalCDP[local-cdp-discovery] --> AP
    LocalStrat[local-strategy.ts] --> AP

核心组件

1. CLI 命令接口层

CLI 层定义了所有与浏览器交互的命令,通过 runCommand 函数与 Agent 系统通信。

主要命令类别

类别命令功能
导航open, goto打开 URL
元素操作click, fill, select, upload操作页面元素
状态检查is, wait, get检查元素状态
键盘鼠标type, press, hover, scroll, drag模拟输入
网络network on/off, network path网络请求捕获
截图screenshot页面截图
快照snapshot辅助功能树快照
标签页pages, newpage, closepage多标签管理

2. Daemon 守护进程管理

Daemon 系统负责管理浏览器实例的生命周期。

// Socket 路径配置
const SOCKET_DIR = os.tmpdir();

function getSocketPath(session: string): string {
  return path.join(SOCKET_DIR, `browse-${session}.sock`);
}

function getLockPath(session: string): string {
  return path.join(SOCKET_DIR, `browse-${session}.lock`);
}

Daemon 启动流程

sequenceDiagram
    participant CLI
    participant Lock
    participant Daemon
    participant Chrome
    
    CLI->>Lock: acquireLock(session)
    Lock-->>CLI: 锁定成功
    CLI->>Daemon: ensureDaemon(session, headless)
    Daemon->>Chrome: 启动/连接 Chrome
    Chrome-->>Daemon: WebSocket Ready
    Daemon->>Lock: releaseLock(session)

关键特性

  • 基于 O_EXCL 的原子文件锁机制防止竞态条件
  • 自动检测并清理陈旧的锁文件
  • 支持会话超时自动清理

3. 本地模式策略

Agent 支持多种本地浏览器连接策略,由 local-strategy.ts 定义。

策略说明用途
isolated启动独立 Chromium 实例完全隔离的浏览器环境
cdp连接到指定 CDP 目标复用已有浏览器会话
auto自动检测可用浏览器优先复用,无则创建新实例
// 资料来源:packages/cli/src/local-strategy.ts:18-37
export async function resolveLocalStrategy({
  localConfig,
  headless,
  defaultViewport,
  discoverLocalCdp,
  resolveWsTarget,
}: ResolveLocalStrategyOptions): Promise<ResolvedLocalStrategy>

4. CDP 发现机制

通过 local-cdp-discovery.ts 实现 Chrome DevTools Protocol 端点的自动发现。

graph LR
    A[读取 DevToolsActivePort] --> B{端口可达性检查}
    B -->|可达| C[构建 WebSocket URL]
    B -->|不可达| D[清理陈旧文件]
    C --> E[返回 CDP URL]
    D --> E

发现流程

  1. 扫描用户数据目录(userDataDirs
  2. 读取 DevToolsActivePort 文件获取端口信息
  3. 验证端口可达性
  4. 构建 WebSocket 连接 URL

网络捕获系统

网络请求监控

Agent 系统提供完整的网络请求捕获能力,捕获结果写入文件系统供 Agent 检查。

// 资料来源:packages/cli/src/index.ts:88-103
interface PendingRequest {
  id: string;
  timestamp: string;
  method: string;
  url: string;
  headers: Record<string, string>;
  body: string | null;
  resourceType: string;
}

目录结构

{networkDir}/
├── {counter}-{method}-{domain}-{pathSlug}/
│   ├── request.json    # 请求详情
│   └── response.json   # 响应详情

文件名生成规则

// 资料来源:packages/cli/src/index.ts:95-120
function getRequestDirName(
  counter: number,
  method: string,
  url: string,
): string {
  const parsed = new URL(url);
  const domain = sanitizeForFilename(parsed.hostname, 30);
  const pathPart = parsed.pathname.split("/").filter(Boolean)[0] || "root";
  const pathSlug = sanitizeForFilename(pathPart, 20);
  return `${String(counter).padStart(3, "0")}-${method}-${domain}-${pathSlug}`;
}

命令执行流程

runCommand 执行机制

graph TD
    Start[CLI Command] --> Check{连接状态检查}
    Check -->|正常| Execute[发送命令]
    Check -->|连接错误| Retry{重试策略}
    
    Retry -->|Attempt 0| Wait[等待 200ms]
    Wait --> Execute
    
    Retry -->|Attempt 1| Restart[重启 Daemon]
    Restart --> Execute
    
    Retry -->|Attempt 2| Cleanup[清理并重启]
    Cleanup --> Execute
    
    Execute --> Success[返回结果]
    Execute --> Error[抛出错误]

错误重试策略

重试次数策略条件
0短暂等待ENOENT, ECONNREFUSED, Connection failed
1重启 Daemon同上
2完整清理杀死 Chrome 进程 + 清理文件 + 重启

会话管理

GlobalOpts 配置选项

选项类型说明
wsstringWebSocket 端点
headlessboolean无头模式
headedboolean显示浏览器窗口
jsonbooleanJSON 格式输出
sessionstring会话标识符
connectstring连接模式
proxiesboolean启用代理 (远程)
advancedStealthboolean高级隐身模式
solveCaptchasboolean自动解验证码
regionstring区域设置 (远程)
keepAliveboolean保持连接活跃
sessionTimeoutnumber会话超时秒数
blockAdsboolean广告拦截

会话参数构建

// 资料来源:packages/cli/src/index.ts:520-545
function buildSessionParamsFromOpts(
  opts: GlobalOpts,
): Record<string, unknown> | null {
  const params: Record<string, unknown> = {};
  const browserSettings: Record<string, unknown> = {};

  if (opts.proxies) params.proxies = true;
  if (opts.region) params.region = opts.region;
  if (opts.keepAlive) params.keepAlive = true;
  if (opts.sessionTimeout !== undefined) params.timeout = opts.sessionTimeout;

  if (opts.advancedStealth) browserSettings.advancedStealth = true;
  if (opts.blockAds) browserSettings.blockAds = true;
  // ...
}

命令实现示例

元素操作命令

// 资料来源:packages/cli/src/index.ts:185-200
program
  .command("fill <selector> <value>")
  .description("Fill input element (presses Enter by default)")
  .option("--no-press-enter", "Don't press Enter after filling")
  .action(async (selector: string, value: string, cmdOpts) => {
    const pressEnter = cmdOpts.pressEnter !== false;
    const result = await runCommand("fill", [
      selector,
      value,
      { pressEnter },
    ]);
    output(result, opts.json ?? false);
  });

截图命令

// 资料来源:packages/cli/src/index.ts:320-345
program
  .command("screenshot [path]")
  .description("Take screenshot")
  .option("-f, --full-page", "Full page screenshot")
  .option("-t, --type <type>", "Image type: png, jpeg", "png")
  .option("-q, --quality <n>", "JPEG quality (0-100)")
  .option("--clip <json>", "Clip region as JSON")
  .option("--no-animations", "Disable animations")
  .option("--hide-caret", "Hide text caret");

辅助功能快照

// 资料来源:packages/cli/src/index.ts:360-380
program
  .command("snapshot")
  .description("Get accessibility tree snapshot")
  .option("-c, --compact", "Output tree only (no xpath map)")
  .action(async (cmdOpts) => {
    const result = (await runCommand("snapshot", [cmdOpts.compact])) as {
      tree: string;
      xpathMap?: Record<string, string>;
      urlMap?: Record<string, string>;
    };
    // ...
  });

错误处理机制

Daemon 状态管理

stateDiagram-v2
    [*] --> Stopped
    Stopped --> Starting: start 命令
    Starting --> Running: 启动成功
    Starting --> Failed: 启动失败
    Running --> Stopping: stop 命令
    Running --> Failed: 连接断开
    Stopping --> Stopped: 清理完成
    Failed --> Starting: 重试
    Stopped --> [*]

锁机制实现

// 资料来源:packages/cli/src/index.ts:490-520
async function acquireLock(
  session: string,
  timeoutMs: number = 10000,
): Promise<boolean> {
  const lockPath = getLockPath(session);
  
  while (Date.now() - startTime < timeoutMs) {
    try {
      // O_EXCL 确保原子性创建
      const handle = await fs.open(lockPath, "wx");
      await handle.write(String(process.pid));
      await handle.close();
      return true;
    } catch (err: unknown) {
      if ((err as NodeJS.ErrnoException).code === "EEXIST") {
        // 检查持有者是否存活
        try {
          const holderPid = parseInt(await fs.readFile(lockPath, "utf-8"));
          process.kill(holderPid, 0);
          await new Promise((r) => setTimeout(r, 100));
        } catch {
          // 清理陈旧锁
          await fs.unlink(lockPath);
        }
      }
    }
  }
  return false;
}

扩展命令系统

Agent 系统支持通过命令模式(Command Pattern)轻松扩展新功能:

program
  .command("<command> <args...>")
  .description("命令描述")
  .option("-x, --option <value>", "选项说明")
  .action(async (args, cmdOpts) => {
    const result = await runCommand("command_name", [
      args,
      { /* options */ }
    ]);
    output(result, opts.json ?? false);
  });

总结

Stagehand 的 Agent 代理系统提供了完整的浏览器自动化能力:

  1. Daemon 管理:基于 Unix Socket 的进程间通信,支持多会话隔离
  2. 命令系统:统一的命令接口,覆盖所有浏览器操作
  3. 网络监控:完整的请求/响应捕获机制
  4. 本地策略:灵活的浏览器连接策略(隔离/CDP/自动)
  5. 错误恢复:多级重试和自动清理机制
  6. 会话管理:完善的会话状态和配置管理

来源:https://github.com/browserbase/stagehand / 项目说明书

Handler 处理系统

Handler 处理系统是 Stagehand 框架的核心组件,负责在浏览器环境中执行自动化操作。该系统通过统一的命令调度机制,将高层操作指令(如 click、type、hover 等)转换为底层的浏览器 CDP (Chrome DevTools Protocol) 调用。资料来源:[packages/cli/src/index.ts:1-50]()

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 组件层次

继续阅读本节完整说明和来源证据。

章节 命令处理流程

继续阅读本节完整说明和来源证据。

章节 生命周期管理

继续阅读本节完整说明和来源证据。

概述

Handler 处理系统是 Stagehand 框架的核心组件,负责在浏览器环境中执行自动化操作。该系统通过统一的命令调度机制,将高层操作指令(如 clicktypehover 等)转换为底层的浏览器 CDP (Chrome DevTools Protocol) 调用。资料来源:packages/cli/src/index.ts:1-50

Stagehand 的 Handler 架构采用分层设计,上层 CLI 通过 Unix Domain Socket 与本地守护进程通信,守护进程负责维护浏览器会话并执行实际的 DOM 操作。这种设计允许命令行工具、API 和其他客户端共享同一个浏览器实例,同时保持操作的原子性和一致性。资料来源:packages/cli/src/index.ts:200-280

核心架构

组件层次

┌─────────────────────────────────────────────────────────────┐
│                      CLI Client                             │
│  (Commands: click, type, hover, fill, screenshot, etc.)    │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                  Socket Command Router                      │
│              (runCommand / sendCommand)                     │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    Daemon Process                            │
│           (Browser Session Management)                       │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                  Handler Execution Layer                     │
│    ┌─────────────┬─────────────┬──────────────────┐        │
│    │ Action      │ Navigation  │ State Check      │        │
│    │ Handlers    │ Handlers    │ Handlers         │        │
│    └─────────────┴─────────────┴──────────────────┘        │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│               Chrome DevTools Protocol (CDP)                │
└─────────────────────────────────────────────────────────────┘

命令处理流程

当 CLI 收到一个命令(如 click <selector>)时,系统按以下流程处理:

graph TD
    A[CLI Command: click] --> B[Parse Options & Arguments]
    B --> C[Call runCommand]
    C --> D[Acquire Session Lock]
    D --> E{Socket Available?}
    E -->|Yes| F[Send Command via Socket]
    E -->|No| G[ensureDaemon]
    G --> F
    F --> H[Daemon Router Switch]
    H --> I[Execute Handler]
    I --> J[CDP: Runtime.evaluate]
    J --> K[Return Result]
    K --> L[Release Lock]

资料来源:packages/cli/src/index.ts:300-400

守护进程管理

生命周期管理

Stagehand 使用 Unix Domain Socket 进行进程间通信,并通过文件锁机制确保同一会话的互斥访问:

功能实现方式文件路径模式
Socket 通信Unix Domain Socket/tmp/browse-{session}.sock
进程锁O_EXCL 原子文件创建/tmp/browse-{session}.lock
锁超时10秒默认超时-

资料来源:packages/cli/src/index.ts:250-320

自动重试机制

命令执行失败时,系统提供智能重试:

重试次数策略说明
0短暂等待200ms 后重试(socket 暂时不可用)
1重启守护进程保持现有文件状态
2完全清理重启杀死 Chrome 进程并清理残留文件
// 重试条件判断
const isConnectionError =
  errMsg.includes("ENOENT") ||
  errMsg.includes("ECONNREFUSED") ||
  errMsg.includes("Connection failed");

资料来源:packages/cli/src/index.ts:380-420

操作处理器

元素操作命令

命令功能关键参数
click点击元素selector, button, clickCount, delay, xpath
dblclick双击元素selector, button
hover悬停x, y, returnXPath
fill填写输入框selector, value, pressEnter
type模拟打字text, delay, mistakes
select选择下拉选项selector, values
drag拖拽操作fromX, fromY, toX, toY

资料来源:packages/cli/src/index.ts:450-550

键盘操作

program.command("press <key>")
  .alias("key")
  .description("Press key (e.g., Enter, Tab, Escape, Cmd+A)")

支持组合键和特殊键位,包括 modifier keys (Cmd, Ctrl, Shift, Alt)。

资料来源:packages/cli/src/index.ts:580-600

导航操作

命令选项默认值
open <url>--wait (load/domcontentloaded/networkidle)load
-t, --timeout30000ms
--context-id-
--persistfalse

资料来源:packages/cli/src/index.ts:620-680

状态检查处理器

元素状态验证

program.command("is <check> <selector>")
  .description("Check element state: visible, checked")

支持的检查类型:

检查类型说明
visible元素在视口内可见
checked复选框/单选框选中状态
attached元素存在于 DOM
detached元素已从 DOM 移除

资料来源:packages/cli/src/index.ts:700-720

等待机制

program.command("wait <type> [arg]")
  .option("-t, --timeout <ms>", "Timeout", "30000")
  .option("-s, --state <state>", "visible, hidden, attached, detached", "visible")
等待类型参数说明
load-页面加载完成
selectorCSS 选择器元素出现
timeout毫秒数自定义超时

资料来源:packages/cli/src/index.ts:50-80

信息获取处理器

GET 命令

program.command("get <what> [selector]")
  .description("Get page info: url, title, text, html, markdown, value, box, visible, checked")
属性说明返回格式
url当前页面 URLstring
title页面标题string
text元素文本内容string
html元素 HTMLstring
markdown元素 Markdownstring
value表单元素值string
box元素边界框{x, y, width, height}
visible可见性状态boolean
checked选中状态boolean

资料来源:packages/cli/src/index.ts:720-750

截图处理器

program.command("screenshot [path]")
  .option("-f, --full-page", "Full page screenshot")
  .option("-t, --type <type>", "png, jpeg", "png")
  .option("-q, --quality <n>", "JPEG quality (0-100)")
  .option("--clip <json>", "Clip region")
  .option("--no-animations", "Disable animations")
  .option("--hide-caret", "Hide text caret")

资料来源:packages/cli/src/index.ts:750-800

网络捕获

请求/响应记录

系统可以捕获并保存网络请求到文件系统:

interface PendingRequest {
  id: string;
  timestamp: string;
  method: string;
  url: string;
  headers: Record<string, string>;
  body: string | null;
  resourceType: string;
}

保存目录结构:

{networkDir}/
├── 000-GET-example-com-api/
│   ├── request.json
│   └── response.json
└── 001-POST-example-com-login/
    ├── request.json
    └── response.json

资料来源:packages/cli/src/index.ts:850-920

配置与超时

全局选项 (GlobalOpts)

interface GlobalOpts {
  ws?: string;
  headless?: boolean;
  headed?: boolean;
  json?: boolean;
  session?: string;
  connect?: string;
  // Remote session options
  proxies?: boolean;
  advancedStealth?: boolean;
  solveCaptchas?: boolean;
  region?: string;
  keepAlive?: boolean;
  sessionTimeout?: number;
  blockAds?: boolean;
}

超时配置

操作默认超时配置方式
页面导航30000ms--timeout 选项
截图10000ms硬编码
Socket 连接28000mswaitForSocketReady
守护进程锁10000msacquireLock 参数

资料来源:packages/cli/src/index.ts:280-340

错误处理

错误分类

错误类型处理策略重试行为
ENOENT连接错误重试
ECONNREFUSED连接错误重试
Connection failed连接错误重试
其他直接抛出不重试
// 优雅关闭处理
process.on("SIGTERM", () => shutdown());
process.on("SIGINT", () => shutdown());
process.on("uncaughtException", (err) => {
  console.error("Uncaught exception:", err);
  shutdown();
});

资料来源:packages/cli/src/index.ts:420-450

本地浏览器策略

策略类型

策略说明使用场景
auto自动检测已存在的浏览器或启动隔离浏览器默认策略
isolated强制使用独立的 Chrome 实例干净环境
cdp连接到指定的 CDP 目标调试已有浏览器
if (localConfig.strategy === "isolated") {
  return {
    localLaunchOptions: { headless, viewport: defaultViewport },
    localInfo: { localSource: "isolated" },
  };
}

资料来源:packages/cli/src/local-strategy.ts:1-50

CDP 发现机制

系统通过以下方式发现本地浏览器:

  1. 读取 DevToolsActivePort 文件获取端口
  2. 探测 /json/version 端点获取 WebSocket URL
  3. 验证端口可达性
  4. 建立 WebSocket 连接
async function probeJsonVersion(port: number): Promise<string | null> {
  const res = await fetch(`http://127.0.0.1:${port}/json/version`);
  const json = await res.json();
  return json.webSocketDebuggerUrl;
}

资料来源:packages/cli/src/local-cdp-discovery.ts:1-100

会话管理

多会话支持

通过 --session 参数或 BROWSE_SESSION 环境变量指定会话:

function getSession(opts: GlobalOpts): string {
  return opts.session ?? process.env.BROWSE_SESSION ?? "default";
}

每个会话维护独立的:

  • Socket 通信通道
  • 浏览器实例
  • 状态文件

模式覆盖

支持动态切换执行模式(local/remote):

await fs.writeFile(getModeOverridePath(session), mapped);

资料来源:packages/cli/src/index.ts:180-220

参考映射 (Ref Map)

系统缓存最近一次 snapshot 的引用映射,支持 @ref 语法:

let refMap: {
  xpathMap: Record<string, string>;
  urlMap: Record<string, string>;
} = {
  xpathMap: {},
  urlMap: {},
};

program.command("refs").description("Show cached ref map from last snapshot");

资料来源:packages/cli/src/index.ts:820-850

总结

Handler 处理系统是 Stagehand 实现浏览器自动化的核心抽象层。它通过以下设计原则提供可靠的操作执行:

  1. 进程隔离:守护进程与 CLI 分离,支持多客户端共享
  2. 幂等性:通过锁机制和幂等命令确保安全并发
  3. 自动恢复:智能重试和状态清理保证鲁棒性
  4. 灵活配置:支持多种执行模式和详细参数调优
  5. 统一接口:命令行、API 复用同一 Handler 逻辑

资料来源:[packages/cli/src/index.ts:300-400]()

DOM 与无障碍树

Stagehand 中的 DOM 与无障碍树(Accessibility Tree,简称 A11y Tree)是核心的页面元素抽象层。该系统将网页的 DOM 结构转换为结构化的无障碍树表示,使 AI 代理能够理解页面内容并执行自动化操作。无障碍树不仅包含元素的可见性信息,还包含语义角色、ARIA 属性和可访问性标签等关键元数据。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 组件关系

继续阅读本节完整说明和来源证据。

章节 核心模块

继续阅读本节完整说明和来源证据。

章节 CLI 接口

继续阅读本节完整说明和来源证据。

概述

Stagehand 中的 DOM 与无障碍树(Accessibility Tree,简称 A11y Tree)是核心的页面元素抽象层。该系统将网页的 DOM 结构转换为结构化的无障碍树表示,使 AI 代理能够理解页面内容并执行自动化操作。无障碍树不仅包含元素的可见性信息,还包含语义角色、ARIA 属性和可访问性标签等关键元数据。

Stagehand 通过 snapshot 命令获取无障碍树快照,该快照包含格式化的树形结构、元素 XPath 映射和 URL 映射。这些数据被用于元素定位、坐标解析和交互操作。

架构设计

组件关系

graph TD
    A[Browser Page] --> B[DOM Tree]
    A --> C[Accessibility Tree]
    B --> D[snapshot command]
    C --> D
    D --> E[xpathMap]
    D --> F[urlMap]
    D --> G[formattedTree]
    E --> H[Coordinate Resolver]
    F --> I[Element Locator]
    G --> J[AI Agent]

核心模块

模块文件路径职责
A11yTreepackages/core/lib/v3/understudy/a11y/snapshot/a11yTree.ts无障碍树生成与格式化
DomTreepackages/core/lib/v3/understudy/a11y/snapshot/domTree.tsDOM 树结构处理
CoordinateResolverpackages/core/lib/v3/understudy/a11y/snapshot/coordinateResolver.ts屏幕坐标解析
FocusSelectorspackages/core/lib/v3/understudy/a11y/snapshot/focusSelectors.ts焦点元素选择器

Snapshot 命令

CLI 接口

packages/cli/src/index.ts 中定义的 snapshot 命令提供两种输出模式:

program
  .command("snapshot")
  .description("Get accessibility tree snapshot")
  .option("-c, --compact", "Output tree only (no xpath map)")
  .action(async (cmdOpts) => {
    const result = (await runCommand("snapshot", [cmdOpts.compact])) as {
      tree: string;
      xpathMap?: Record<string, string>;
      urlMap?: Record<string, string>;
    };
    // ...
  });

资料来源:packages/cli/src/index.ts:1-500

输出格式

参数类型描述
treestring格式化的无障碍树结构
xpathMapRecord<string, string>元素引用到 XPath 的映射
urlMapRecord<string, string>元素引用到 URL 的映射

紧凑模式下仅返回纯文本树结构,适合日志记录和快速调试:

if (cmdOpts.compact && !opts.json) {
  console.log(result.tree);
} else {
  output(result, opts.json ?? false);
}

Snapshot 实现

后端处理逻辑

在 CLI 的命令处理器中,snapshot case 从 Playwright Page 对象获取快照数据:

case "snapshot": {
  const [compact] = args as [boolean?];
  const snapshot = await page!.snapshot();

  refMap = {
    xpathMap: snapshot.xpathMap ?? {},
    urlMap: snapshot.urlMap ?? {},
  };

  if (compact) {
    return { tree: snapshot.formattedTree };
  }
  return {
    tree: snapshot.formattedTree,
    xpathMap: snapshot.xpathMap,
    urlMap: snapshot.urlMap,
  };
}

资料来源:packages/cli/src/index.ts

引用映射管理

全局 refMap 变量存储最新的快照映射,供后续元素操作使用:

let refMap: {
  xpathMap: Record<string, string>;
  urlMap: Record<string, string>;
} = {
  xpathMap: {},
  urlMap: {},
};

元素状态检查

is 命令

is 命令用于检查元素的可访问性状态:

case "is": {
  const [check, selector] = args as [string, string];
  const locator = page!.deepLocator(resolveSelector(selector));
  switch (check) {
    case "visible":
      return { visible: await locator.isVisible() };
    case "checked":
      return { checked: await locator.isChecked() };
    default:
      throw new Error(`Unknown check: ${check}`);
  }
}

资料来源:packages/cli/src/index.ts

检查类型方法描述
visibleisVisible()元素是否可见
checkedisChecked()复选框/单选框是否选中

CLI 接口

program
  .command("is <check> <selector>")
  .description("Check element state: visible, checked")
  .action(async (check: string, selector: string) => {
    const result = await runCommand("is", [check, selector]);
    output(result, opts.json ?? false);
  });

页面信息获取

get 命令

get 命令提供多种页面和元素信息查询:

program
  .command("get <what> [selector]")
  .description(
    "Get page info: url, title, text, html, markdown, value, box, visible, checked",
  )
  .action(async (what: string, selector?: string) => {
    const result = await runCommand("get", [what, selector]);
    output(result, opts.json ?? false);
  });

资料来源:packages/cli/src/index.ts

支持的信息类型

类型参数返回内容
url当前页面 URL
title页面标题
textselector元素文本内容
htmlselector元素 HTML
markdownselector转换后的 Markdown
valueselector表单元素值
boxselector元素边界框 {x, y, width, height}
visibleselector元素可见性
checkedselector复选框状态

坐标与交互

坐标操作命令

命令描述选项
click_xy <x> <y>坐标点击--button, --count, --xpath
hover <x> <y>坐标悬停--xpath
scroll <x> <y> <dx> <dy>滚动--xpath
drag <fromX> <fromY> ...拖拽延迟、坐标选项
program
  .command("click_xy <x> <y>")
  .description("Click at exact coordinates")
  .option("-b, --button <btn>", "Mouse button: left, right, middle", "left")
  .option("-c, --count <n>", "Click count", "1")
  .option("--xpath", "Return XPath of clicked element")
  .action(async (x: string, y: string, cmdOpts) => {
    const result = await runCommand("click_xy", [
      parseFloat(x),
      parseFloat(y),
      {
        button: cmdOpts.button,
        clickCount: parseInt(cmdOpts.count),
        returnXPath: cmdOpts.xpath,
      },
    ]);
  });

资料来源:packages/cli/src/index.ts

坐标解析器

CoordinateResolver 模块负责将无障碍树中的元素引用转换为屏幕坐标。该解析器基于快照中的 xpathMap 和元素边界框信息计算精确的交互坐标。

本地浏览器策略

策略类型

packages/cli/src/local-strategy.ts 定义了三种本地浏览器连接策略:

export async function resolveLocalStrategy({
  localConfig,
  headless,
  defaultViewport,
  discoverLocalCdp,
  resolveWsTarget,
}: ResolveLocalStrategyOptions): Promise<ResolvedLocalStrategy> {
  if (localConfig.strategy === "isolated") {
    return {
      localLaunchOptions: { headless, viewport: defaultViewport },
      localInfo: { localSource: "isolated" },
    };
  }
  // ...
}

资料来源:packages/cli/src/local-strategy.ts

策略说明
isolated启动独立的 Chromium 实例
cdp连接到指定的 Chrome DevTools Protocol 目标
auto自动检测本地调试端口

CDP 发现机制

packages/cli/src/local-cdp-discovery.ts 实现了自动发现本地 Chrome 调试端口的功能:

async function resolveDevToolsActivePortUrl(
  port: number,
  userDataDirs: string[],
): Promise<string | null> {
  for (const dir of userDataDirs) {
    const info = await readDevToolsActivePort(dir);
    if (!info || info.port !== port) {
      continue;
    }
    // 验证端口可达性
    if (!(await isPortReachable(info.port))) {
      await cleanupStaleDevToolsActivePort(dir);
      continue;
    }
    return buildDevToolsWsUrl(info.port, info.wsPath);
  }
  return null;
}

资料来源:packages/cli/src/local-cdp-discovery.ts

网络捕获集成

快照与网络数据

无障碍树快照可以与网络捕获功能结合使用:

interface PendingRequest {
  id: string;
  timestamp: string;
  method: string;
  url: string;
  headers: Record<string, string>;
  body: string | null;
  resourceType: string;
}

每个网络请求保存到文件系统时,使用 URL 解析生成目录名:

function getRequestDirName(
  counter: number,
  method: string,
  url: string,
): string {
  try {
    const parsed = new URL(url);
    const domain = sanitizeForFilename(parsed.hostname, 30);
    const pathPart = parsed.pathname.split("/").filter(Boolean)[0] || "root";
    const pathSlug = sanitizeForFilename(pathPart, 20);
    return `${String(counter).padStart(3, "0")}-${method}-${domain}-${pathSlug}`;
  } catch {
    return `${String(counter).padStart(3, "0")}-${method}-unknown`;
  }
}

资料来源:packages/cli/src/index.ts

使用场景

基本工作流程

graph LR
    A[open <url>] --> B[snapshot]
    B --> C[xpathMap + urlMap]
    C --> D[click <element>]
    D --> E[verify state]
    E --> F[screenshot]

多页面管理

Stagehand 支持多标签页操作:

program
  .command("pages")
  .description("List all open pages")
  .action(async () => {
    const result = await runCommand("pages", []);
    output(result, opts.json ?? false);
  });

program
  .command("tab_close [index]")
  .alias("close")
  .description("Close tab by index (defaults to last tab)")
  .action(async (index?: string) => {
    const result = await runCommand("tab_close", [
      index ? parseInt(index) : undefined,
    ]);
    output(result, opts.json ?? false);
  });

总结

DOM 与无障碍树系统是 Stagehand 实现浏览器自动化的基础。通过将复杂 DOM 结构转换为语义化的无障碍树,结合 XPath 映射和坐标解析,AI 代理能够准确理解和操作网页元素。该系统与 CLI 工具深度集成,提供 snapshotgetis 等命令支持灵活的页面检查和交互操作。

资料来源:[packages/cli/src/index.ts:1-500](https://github.com/browserbase/stagehand/blob/main/packages/cli/src/index.ts)

Deep Locator 深层定位器

Deep Locator(深层定位器)是 Stagehand 框架中负责精确定位和操作 DOM 元素的核心子系统。它通过多层次的定位策略、跨帧(iframe)处理、以及智能选择器解析,实现了对复杂网页结构中元素的可靠访问。该系统是 Stagehand 能够实现高精度自动化控制的关键技术基础。

章节 相关页面

继续阅读本节完整说明和来源证据。

概述

Deep Locator(深层定位器)是 Stagehand 框架中负责精确定位和操作 DOM 元素的核心子系统。它通过多层次的定位策略、跨帧(iframe)处理、以及智能选择器解析,实现了对复杂网页结构中元素的可靠访问。该系统是 Stagehand 能够实现高精度自动化控制的关键技术基础。

核心职责:

  • 提供多种元素定位策略(CSS 选择器、XPath、坐标等)
  • 处理跨 iframe/跨帧的元素定位
  • 管理浏览器上下文和帧的注册与追踪
  • 解析和优化选择器以提高定位准确性
  • 与 CLI 命令层紧密集成,支持交互式定位操作

资料来源:packages/cli/src/index.ts:1-50

资料来源:[packages/cli/src/index.ts:1-50]()

失败模式与踩坑日记

保留 Doramagic 在发现、验证和编译中沉淀的项目专属风险,不把社区讨论只当作装饰信息。

medium 仓库名和安装名不一致

用户照着仓库名搜索包或照着包名找仓库时容易走错入口。

medium 能力判断依赖假设

假设不成立时,用户拿不到承诺的能力。

medium 维护活跃度未知

新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。

medium 下游验证发现风险项

下游已经要求复核,不能在页面中弱化。

Pitfall Log / 踩坑日志

项目:browserbase/stagehand

摘要:发现 7 个潜在踩坑项,其中 0 个为 high/blocking;最高优先级:身份坑 - 仓库名和安装名不一致。

1. 身份坑 · 仓库名和安装名不一致

  • 严重度:medium
  • 证据强度:runtime_trace
  • 发现:仓库名 stagehand 与安装入口 create-browser-app 不完全一致。
  • 对用户的影响:用户照着仓库名搜索包或照着包名找仓库时容易走错入口。
  • 建议检查:在 npm/PyPI/GitHub 上确认包名映射和官方 README 说明。
  • 复现命令:npx create-browser-app
  • 防护动作:页面必须同时展示 repo 名和真实安装入口,避免用户搜索错包。
  • 证据:identity.distribution | github_repo:776908852 | https://github.com/browserbase/stagehand | repo=stagehand; install=create-browser-app

2. 能力坑 · 能力判断依赖假设

  • 严重度:medium
  • 证据强度:source_linked
  • 发现:README/documentation is current enough for a first validation pass.
  • 对用户的影响:假设不成立时,用户拿不到承诺的能力。
  • 建议检查:将假设转成下游验证清单。
  • 防护动作:假设必须转成验证项;没有验证结果前不能写成事实。
  • 证据:capability.assumptions | github_repo:776908852 | https://github.com/browserbase/stagehand | README/documentation is current enough for a first validation pass.

3. 维护坑 · 维护活跃度未知

  • 严重度:medium
  • 证据强度:source_linked
  • 发现:未记录 last_activity_observed。
  • 对用户的影响:新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
  • 建议检查:补 GitHub 最近 commit、release、issue/PR 响应信号。
  • 防护动作:维护活跃度未知时,推荐强度不能标为高信任。
  • 证据:evidence.maintainer_signals | github_repo:776908852 | https://github.com/browserbase/stagehand | last_activity_observed missing

4. 安全/权限坑 · 下游验证发现风险项

  • 严重度:medium
  • 证据强度:source_linked
  • 发现:no_demo
  • 对用户的影响:下游已经要求复核,不能在页面中弱化。
  • 建议检查:进入安全/权限治理复核队列。
  • 防护动作:下游风险存在时必须保持 review/recommendation 降级。
  • 证据:downstream_validation.risk_items | github_repo:776908852 | https://github.com/browserbase/stagehand | no_demo; severity=medium

5. 安全/权限坑 · 存在评分风险

  • 严重度:medium
  • 证据强度:source_linked
  • 发现:no_demo
  • 对用户的影响:风险会影响是否适合普通用户安装。
  • 建议检查:把风险写入边界卡,并确认是否需要人工复核。
  • 防护动作:评分风险必须进入边界卡,不能只作为内部分数。
  • 证据:risks.scoring_risks | github_repo:776908852 | https://github.com/browserbase/stagehand | no_demo; severity=medium

6. 维护坑 · issue/PR 响应质量未知

  • 严重度:low
  • 证据强度:source_linked
  • 发现:issue_or_pr_quality=unknown。
  • 对用户的影响:用户无法判断遇到问题后是否有人维护。
  • 建议检查:抽样最近 issue/PR,判断是否长期无人处理。
  • 防护动作:issue/PR 响应未知时,必须提示维护风险。
  • 证据:evidence.maintainer_signals | github_repo:776908852 | https://github.com/browserbase/stagehand | issue_or_pr_quality=unknown

7. 维护坑 · 发布节奏不明确

  • 严重度:low
  • 证据强度:source_linked
  • 发现:release_recency=unknown。
  • 对用户的影响:安装命令和文档可能落后于代码,用户踩坑概率升高。
  • 建议检查:确认最近 release/tag 和 README 安装命令是否一致。
  • 防护动作:发布节奏未知或过期时,安装说明必须标注可能漂移。
  • 证据:evidence.maintainer_signals | github_repo:776908852 | https://github.com/browserbase/stagehand | release_recency=unknown

来源:Doramagic 发现、验证与编译记录