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(默认)domcontentloadednetworkidle
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 提供了一套完整的浏览器自动化解决方案,具有以下特点:
- 丰富的命令集:覆盖导航、交互、信息获取、网络捕获等各方面
- 灵活的运行模式:支持本地和远程两种部署方式
- 智能的策略选择:可根据需求选择隔离、CDP 或自动模式
- 可靠的守护进程:实现了进程锁和自动重连机制
- 详细的网络捕获:支持完整的请求/响应持久化
资料来源:[packages/cli/src/index.ts:1-50]()
快速开始
Stagehand 是一个由 Browserbase 开发的浏览器自动化框架,提供 CLI 工具和 Core SDK 两种使用方式,支持本地浏览器和远程 Browserbase 云端浏览器的自动化操作。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
环境准备
前置要求
| 要求 | 说明 |
|---|---|
| Node.js | 版本 20+ |
| Playwright | 用户需自行安装 playwright 或 playwright-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
状态选项:visible、hidden、attached、detached
资料来源: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 管理多个包。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
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, status | Daemon 控制 |
资料来源: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));
}
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| session | string | "default" | 会话标识符 |
| timeoutMs | number | 10000 | 锁等待超时 |
资料来源:packages/cli/src/index.ts:380-420
4.3 连接重试策略
当连接失败时,系统自动执行三级重试:
| 重试次数 | 策略 | 动作 |
|---|---|---|
| 0 | 短暂等待 | 等待 200ms 后重试 |
| 1 | 重启 Daemon | 不清理状态重启 |
| 2 | 完全清理 | 杀死进程、清理文件、重启 |
资料来源:packages/cli/src/index.ts:450-500
5. 本地模式策略系统
本地模式支持三种策略:auto、isolated 和 cdp。
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 接口定义:
| 选项 | 类型 | 说明 |
|---|---|---|
--session | string | 会话标识 |
--headless | boolean | 无头模式 |
--headed | boolean | 窗口模式 |
--json | boolean | JSON 输出 |
--ws | string | WebSocket 地址 |
--connect | string | 远程连接地址 |
--proxies | boolean | 启用代理 (远程) |
--region | string | 区域设置 (远程) |
--block-ads | boolean | 广告拦截 (远程) |
资料来源: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,
};
}
| 输出字段 | 类型 | 说明 |
|---|---|---|
tree | string | 格式化的可访问性树 |
xpathMap | Record | 选择器到 XPath 的映射 |
urlMap | Record | 元素到 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 操作用于模拟键盘输入,支持人类化输入模式:
| 选项 | 类型 | 描述 |
|---|---|---|
text | string | 要输入的文本内容 |
delay | number | 击键间隔(毫秒) |
mistakes | boolean | 启用类人错误模拟 |
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 键:
| 参数 | 类型 | 描述 |
|---|---|---|
selector | string | 输入框选择器 |
value | string | 填充值 |
pressEnter | boolean | 是否按 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 自动化能力的核心,它通过以下设计实现:
- 分层架构:Handler 层负责指令解析,工具层负责具体操作
- 灵活的命令路由:CLI 通过统一的
runCommand接口支持多种操作类型 - 健壮的错误处理:自动重试机制和详细的错误信息
- 会话隔离:通过 Session 实现多任务并行执行
- 丰富的操作类型:覆盖点击、输入、导航、状态检查等常见自动化场景
资料来源:[packages/core/lib/v3/handlers/actHandler.ts:1-50]()
extract() 数据提取
extract() 是 Stagehand 框架中的核心数据提取方法,允许开发者从网页内容中提取结构化数据。该功能通过 Zod schema 定义期望的数据结构,由 AI 模型智能解析页面内容并返回类型安全的提取结果。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
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 模型理解需要提取哪些信息:
| 参数 | 类型 | 描述 |
|---|---|---|
instruction | string | 描述需要提取的数据的自然语言指令 |
schema | ZodSchema | 定义提取数据的 Zod schema |
maxTokens | number | 最大响应 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]常见错误类型
| 错误类型 | 描述 | 处理方式 |
|---|---|---|
ValidationError | Schema 验证失败 | 检查 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配置选项
| 选项 | 默认值 | 描述 |
|---|---|---|
timeout | 30000 | 操作超时时间(毫秒) |
retries | 3 | 失败重试次数 |
model | gpt-4o | 使用的 AI 模型 |
verbose | false | 启用详细日志 |
最佳实践
- 使用精确的指令:指令越具体,提取结果越准确
- 选择合适的 Schema:避免过于复杂的嵌套结构
- 处理可选字段:使用
z.string().optional()处理可能缺失的字段 - 设置合理的超时:复杂页面需要更长超时时间
- 验证返回数据:即使通过 schema 验证,也建议进行运行时检查
相关资源
来源:https://github.com/browserbase/stagehand / 项目说明书
observe() 页面观察
observe() 是 stagehand 框架中的核心页面观察功能,用于实时捕获和分析网页的当前状态。该功能通过无障碍树(Accessibility Tree)快照机制,获取页面的 DOM 结构、元素属性、位置信息等关键数据,为 AI 代理提供理解和操作网页的能力。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
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 实现了页面观察的核心逻辑,负责协调快照采集、结果处理和错误管理。
处理流程包含以下关键阶段:
- 初始化阶段 - 验证观察上下文和目标页面
- 采集阶段 - 调用底层快照 API 获取无障碍树
- 处理阶段 - 格式化节点数据,构建引用映射
- 返回阶段 - 输出结构化的观察结果
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"
}
}
快照数据模型
节点结构
每个无障碍树节点包含以下核心属性:
| 属性 | 类型 | 描述 |
|---|---|---|
role | string | 元素的 ARIA 角色 |
name | string | 元素的辅助名称 |
value | string | 元素的值属性 |
description | string | 元素的描述信息 |
state | string[] | 元素状态数组 |
children | Node[] | 子节点列表 |
引用映射机制
系统维护两套关键映射表:
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 代理提供可观测、可控制的浏览器操作能力。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
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发现流程:
- 扫描用户数据目录(
userDataDirs) - 读取
DevToolsActivePort文件获取端口信息 - 验证端口可达性
- 构建 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 配置选项
| 选项 | 类型 | 说明 |
|---|---|---|
ws | string | WebSocket 端点 |
headless | boolean | 无头模式 |
headed | boolean | 显示浏览器窗口 |
json | boolean | JSON 格式输出 |
session | string | 会话标识符 |
connect | string | 连接模式 |
proxies | boolean | 启用代理 (远程) |
advancedStealth | boolean | 高级隐身模式 |
solveCaptchas | boolean | 自动解验证码 |
region | string | 区域设置 (远程) |
keepAlive | boolean | 保持连接活跃 |
sessionTimeout | number | 会话超时秒数 |
blockAds | boolean | 广告拦截 |
会话参数构建
// 资料来源: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 代理系统提供了完整的浏览器自动化能力:
- Daemon 管理:基于 Unix Socket 的进程间通信,支持多会话隔离
- 命令系统:统一的命令接口,覆盖所有浏览器操作
- 网络监控:完整的请求/响应捕获机制
- 本地策略:灵活的浏览器连接策略(隔离/CDP/自动)
- 错误恢复:多级重试和自动清理机制
- 会话管理:完善的会话状态和配置管理
来源:https://github.com/browserbase/stagehand / 项目说明书
Handler 处理系统
Handler 处理系统是 Stagehand 框架的核心组件,负责在浏览器环境中执行自动化操作。该系统通过统一的命令调度机制,将高层操作指令(如 click、type、hover 等)转换为底层的浏览器 CDP (Chrome DevTools Protocol) 调用。资料来源:[packages/cli/src/index.ts:1-50]()
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
Handler 处理系统是 Stagehand 框架的核心组件,负责在浏览器环境中执行自动化操作。该系统通过统一的命令调度机制,将高层操作指令(如 click、type、hover 等)转换为底层的浏览器 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, --timeout | 30000ms | |
--context-id | - | |
--persist | false |
资料来源: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 | - | 页面加载完成 |
selector | CSS 选择器 | 元素出现 |
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 | 当前页面 URL | string |
title | 页面标题 | string |
text | 元素文本内容 | string |
html | 元素 HTML | string |
markdown | 元素 Markdown | string |
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 连接 | 28000ms | waitForSocketReady |
| 守护进程锁 | 10000ms | acquireLock 参数 |
资料来源: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 发现机制
系统通过以下方式发现本地浏览器:
- 读取
DevToolsActivePort文件获取端口 - 探测
/json/version端点获取 WebSocket URL - 验证端口可达性
- 建立 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 实现浏览器自动化的核心抽象层。它通过以下设计原则提供可靠的操作执行:
- 进程隔离:守护进程与 CLI 分离,支持多客户端共享
- 幂等性:通过锁机制和幂等命令确保安全并发
- 自动恢复:智能重试和状态清理保证鲁棒性
- 灵活配置:支持多种执行模式和详细参数调优
- 统一接口:命令行、API 复用同一 Handler 逻辑
资料来源:[packages/cli/src/index.ts:300-400]()
DOM 与无障碍树
Stagehand 中的 DOM 与无障碍树(Accessibility Tree,简称 A11y Tree)是核心的页面元素抽象层。该系统将网页的 DOM 结构转换为结构化的无障碍树表示,使 AI 代理能够理解页面内容并执行自动化操作。无障碍树不仅包含元素的可见性信息,还包含语义角色、ARIA 属性和可访问性标签等关键元数据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
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]
核心模块
| 模块 | 文件路径 | 职责 |
|---|---|---|
| A11yTree | packages/core/lib/v3/understudy/a11y/snapshot/a11yTree.ts | 无障碍树生成与格式化 |
| DomTree | packages/core/lib/v3/understudy/a11y/snapshot/domTree.ts | DOM 树结构处理 |
| CoordinateResolver | packages/core/lib/v3/understudy/a11y/snapshot/coordinateResolver.ts | 屏幕坐标解析 |
| FocusSelectors | packages/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
输出格式
| 参数 | 类型 | 描述 |
|---|---|---|
tree | string | 格式化的无障碍树结构 |
xpathMap | Record<string, string> | 元素引用到 XPath 的映射 |
urlMap | Record<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
| 检查类型 | 方法 | 描述 |
|---|---|---|
visible | isVisible() | 元素是否可见 |
checked | isChecked() | 复选框/单选框是否选中 |
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 | 无 | 页面标题 |
text | selector | 元素文本内容 |
html | selector | 元素 HTML |
markdown | selector | 转换后的 Markdown |
value | selector | 表单元素值 |
box | selector | 元素边界框 {x, y, width, height} |
visible | selector | 元素可见性 |
checked | selector | 复选框状态 |
坐标与交互
坐标操作命令
| 命令 | 描述 | 选项 |
|---|---|---|
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 工具深度集成,提供 snapshot、get、is 等命令支持灵活的页面检查和交互操作。
资料来源:[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]()
失败模式与踩坑日记
保留 Doramagic 在发现、验证和编译中沉淀的项目专属风险,不把社区讨论只当作装饰信息。
用户照着仓库名搜索包或照着包名找仓库时容易走错入口。
假设不成立时,用户拿不到承诺的能力。
新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
下游已经要求复核,不能在页面中弱化。
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 发现、验证与编译记录