用 Stagehand 自动化浏览器并连接 BotBrowser
通过 CDP 使用 Stagehand 的 act、extract、observe;由启动器选择 Chromium 可执行文件和 BotBrowser profile。
BotBrowser Team
Stagehand 浏览器自动化分成两个层次:Stagehand 提供页面操作原语,浏览器启动器选择 Chromium 可执行文件及启动参数。Stagehand v4 文档定义了 act、extract 和 observe,并通过 Chrome DevTools Protocol(CDP)连接已运行的 Chromium。BotBrowser 支持由操作者指定 Chromium 可执行文件并通过 --bot-profile 选择 profile,Stagehand v4 可以通过 CDP 连接该浏览器。BotBrowser 不提供 Stagehand 适配器、不制定 agent 策略,也不保证第三方网站如何分类。browser-use 配置文章介绍另一种 agent framework,Playwright 入门文章介绍另一种浏览器控制 API。
连接成功不代表启动的是预期进程。CDP URL 标识的是浏览器端点,不能证明可执行文件路径、profile 参数、网络路由或页面状态。应分开记录这些观察,并参考 BotBrowser 命令行参数说明了解支持的 profile 参数;Stagehand 方法调用不会配置浏览器启动。
TL;DR
observe()发现候选操作,act()执行操作,extract()按 schema 返回页面数据。- Stagehand v4 通过 CDP 连接 Chromium;当前文档说明它不依赖 Playwright 或 Puppeteer。
- 由启动器选择可执行文件并在浏览器启动前传入
--bot-profile,而不是通过 Stagehand 的页面原语设置。 - CDP 有响应只能说明端点响应了,不能证明使用了哪个二进制或 profile。
- 将端点限制在 loopback,单独核对启动进程;端点或进程不匹配就停止。
目录
- 各原语的职责
- 启动与连接是不同操作
- 先启动受控浏览器,再连接 Stagehand
- 用 fixture 运行三个原语
- 核对进程与 CDP 目标
- 故障表与恢复顺序
- Stagehand 不负责的部分
- 实践结论
各原语的职责
Stagehand 的三个命名原语负责不同工作。observe() 发现页面上的可操作元素并返回结构化动作;act() 执行一条指令或 observe() 返回的动作;extract() 按请求的形状读取页面内容。observe、act 和 extract 官方参考页分别描述了输入与结果。它们是 Stagehand 操作,不是选择浏览器二进制的参数。
这一区分限定了运行记录可以证明什么。observe() 的结果表示 Stagehand 根据当前页面和指令找到了候选动作,不代表用户已授权。act() 的结果表示发起了一次页面操作,远端应用仍可能拒绝它、延迟处理或应用不同状态。extract() 的结果是符合请求 schema 的页面数据,不是服务器数据库副本,也不能独立证明页面内容为真。
| 原语 | 页面层工作 | 值得保留的证据 |
|---|---|---|
observe() | 为范围明确的指令发现候选元素 | 页面 origin、指令标识、候选数和选定动作 |
act() | 执行指令或选定动作 | 动作标识和返回结果,不把它描述为远端已接受 |
extract() | 按请求的 schema 读取内容 | schema 版本和工作流需要的最少字段 |
这些操作可以组成一条流程,但不应压成一个 success 状态。浏览器连接可能成功而提取失败;提取也可能成功,但后续提交仍未确认。分开保存状态,才能判断需要检查哪一层。页面导航、对话框或 origin 变化也可能让先前发现的元素失效。
Stagehand 还提供导航和 locator 等熟悉的页面方法,但这不等于 Stagehand v4 是 Playwright。其当前介绍说明它通过 CDP 控制浏览器,并且没有 Playwright 或 Puppeteer 依赖。项目如果需要 Playwright 的 BrowserType.launch() 生命周期,应使用 Playwright 配置指南和独立启动代码。需要 Stagehand 原语时,则应按其浏览器连接约定操作。
启动与连接是不同操作
启动会创建操作系统进程。启动器向进程传递可执行文件路径、profile 参数、用户数据目录、调试端点和网络选项。之后的 CDP 连接则把客户端接到已经运行的浏览器上。Stagehand 浏览器配置参考分别介绍了面向本地进程的 localBrowser.launch() 和面向现有 Chromium 的 localBrowser.connect()。连接示例使用 CDP URL,再把返回的 browser 交给 Stagehand.create()。
在 BotBrowser 中,profile 通过 --bot-profile 在进程启动时选择。它不是 Stagehand 指令、page.evaluate() 的值,也不是 observe() 能在启动后修改的设置。BotBrowser 的 Playwright 文档展示了 executable 和 profile 参数如何传入 Playwright 启动;其他启动器也遵循同一职责边界:启动参数必须在 Stagehand 连接前交给浏览器进程。profile 管理文章说明 profile 的选择和文件处理。
| 选择项 | 启动路径 | 连接路径 |
|---|---|---|
| 谁创建 Chromium? | Stagehand 的本地浏览器工厂或外部启动器 | 由操作者管理的启动器先创建 |
| 在哪里选择可执行文件? | 固定版本所支持的启动配置 | 在外部进程启动参数中、Stagehand 连接之前 |
在哪里传入 --bot-profile? | 仅当支持的启动 API 会转发浏览器参数时 | 在进程启动参数中、CDP 端点开放之前 |
| Stagehand 收到什么? | 本地启动调用返回的 browser | 连接到指定 CDP URL 的 browser |
| 哪些内容需要单独核对? | 实际启动进程及参数 | 进程身份、端点和 profile 参数 |
如果环境中已有受控 Chromium 进程,或由单独的 supervisor 管理进程创建,连接路径会更合适。它也带来一个明确前提:端点开始监听后 Stagehand 才能连接。如果固定版本的本地启动选项能够满足全部配置,直接启动可能更简单。不要因为本地 helper 能启动 Chrome,就假设它支持任意 executable 或 profile 参数;应核对对应版本的类型定义。
先启动受控浏览器,再连接 Stagehand
由操作者管理启动配置,可以明确显示关键参数。下面的 shell 形状使用占位环境变量,不是一次真实运行记录,也不证明某个 BotBrowser 二进制已接受这些参数。应从部署配置读取变量,为每次运行分配一个空闲的 loopback 端口,并将同一个 STAGEHAND_CDP_PORT 传给启动器和客户端:
"$BOTBROWSER_EXEC_PATH" \
--user-data-dir="$STAGEHAND_USER_DATA_DIR" \
--remote-debugging-port="$STAGEHAND_CDP_PORT" \
--bot-profile="$BOT_PROFILE_PATH"
--bot-profile 选择 BotBrowser profile 文件。--user-data-dir 是独立的 Chromium 存储目录,不是 BotBrowser 加密 profile 的替代品。当浏览器版本要求远程调试使用独立目录时,应新建专用目录,不要把客户数据目录用于测试。CDP 端点允许高权限浏览器控制,因此应限制为 loopback,不对共享网络开放,并在运行结束后关闭进程。Chrome 的远程调试说明介绍了该接口及其安全边界。
进程启动后,Stagehand 文档中的连接形状如下:
import { localBrowser, Stagehand } from '@browserbasehq/stagehand';
import { z } from 'zod';
const cdpPort = process.env.STAGEHAND_CDP_PORT;
if (!cdpPort || !/^\d+$/.test(cdpPort)) {
throw new Error('The launcher must assign a numeric CDP port');
}
const browser = await localBrowser.connect({
cdpUrl: `http://127.0.0.1:${cdpPort}`,
});
const stagehand = await Stagehand.create({ browser });
const { data: actions } = await stagehand.observe('Find the read-only build marker');
if (actions.length !== 1) {
throw new Error(`Expected one build-marker action; found ${actions.length}`);
}
await stagehand.act(actions[0]);
const { data } = await stagehand.extract({
instruction: 'Read the build marker from the current page',
schema: z.object({ build: z.string() }),
});
console.log(data.build);
当前 Stagehand v4 文档给出了连接方式和原语调用;端点、fixture 与依赖安装仍取决于具体环境。应固定 Stagehand 版本,并对照相应 API 参考核实 import 和返回字段。示例读取自有 fixture,不提交表单、不登录,也不修改远端记录。代码仅展示本地集成形状,不表示此处实际运行过。
每次运行都应分配自己的端点,不要使用可能被其他浏览器占用的固定端口。如果端口已占用,不要直接连上去:地址可能指向另一个 Chromium。让启动器分配并记录端点,同时限制在 localhost。不要把 CDP 暴露到公网;任何能访问端点的客户端都可能控制浏览器。
用 fixture 运行三个原语
自有 fixture 可以让各层结果可见,不必依赖公开网站。它可以显示固定 build 标识和一个无副作用控件。第一条 observe() 指令应明确指向该控件,所选动作只读取标识,然后 extract() 按小型 schema 返回它。不要在 fixture 中放入登录凭据、真实购买、账号修改或删除操作,否则测试会变成授权流程。
只保留少量预期值和观察值。例如 fixture 返回 build: "stagehand-fixture-7",记录可以保存该字符串、fixture 版本、origin 和 schema 名称。无需保存整个 DOM、与任务无关的截图、cookies、本地存储或完整模型 trace。较小的记录能减少意外暴露,也更方便定位问题。
fixture 要足够稳定,才能区分连接错误和页面错误。固定 build 标记可核对导航是否抵达预期本机 origin;只读按钮能在不修改服务端状态的情况下测试 observe() 和 act();小型 JSON 对象可以测试 extract() 与 schema。应将这些元素放在同一版本中管理。如果标记属性变化而 Chromium 不变,变化来自 fixture 合同,而不是启动器。
不要用页面可见的 user-agent 证明选择了哪个进程或 profile。页面值可用于单独检查,但无法识别打开标签页的可执行文件。反过来,进程路径也不能证明 fixture 已按预期渲染。启动器进程、CDP 端点和 fixture 返回值回答的是不同问题,应保存在不同字段中。
fixture 还可以区分页面状态与动作是否可用。如果标记只有在用户同意后才显示,observe() 指令不应把该决定包装成普通准备点击。应记录需要操作者批准,或创建独立只读路径。稳定的测试合同并不能成为自动修改权限或数据的理由。
核对进程与 CDP 目标
三个检查分别确认不同事实。首先检查启动器进程,将其可执行文件和脱敏后的参数与声明值比较。其次请求 loopback CDP 端点,确认预期调试服务正在响应。最后进入自有 fixture 并读取 build 标记。一项检查通过不能代替其他两项。
| 检查 | 通过时的观察 | 不能据此证明 |
|---|---|---|
| 启动器进程 | 进程路径与声明的 BotBrowser 路径一致,并且存在所需 profile 参数 | 加密 profile 已被接受或产生了预期页面值 |
| CDP 端点 | 声明的本机 URL 返回浏览器元数据 | 该端点属于第一行检查的进程 |
| fixture 页面 | Stagehand 看到预期 origin 和 build 标记 | 第三方服务会显示相同页面或做出相同决定 |
发现不匹配就停止。当启动由 MCP 服务器而不是脚本负责时,同样的检查依然适用,承载可执行文件的配置键见为自定义 Chromium 构建配置 Playwright MCP。如果进程路径不一致,不能因为 CDP 端点健康就继续。如果路径一致但端点报告了另一个浏览器,应检查端口分配和进程归属。如果前两项一致但缺少标记,则检查 fixture 路由和页面生命周期,而不是修改 profile 值。这个顺序可以避免页面重试掩盖启动错误。
简短回执可以包括 runId、固定的 Stagehand 版本、脱敏后的 executable 名称、非敏感 profile 标识、本机端点、fixture build、使用的原语和首个错误。不要记录加密 profile 内容、访问令牌、代理凭据或含客户标识的绝对路径。回执的用途是区分配置值与观察值。
CDP 元数据不是浏览器身份凭证。如果多个进程可以监听或转发端点,应将 URL 与进程 ID 关联,并检查本机进程参数。隧道、容器端口映射、开发代理或残留浏览器,都可能让客户端连到不同进程。每次运行使用独立端点,复用端口前先结束过期进程。
浏览器生命周期也需要明确。只读检查完成后关闭 Stagehand 连接;只有本次运行创建并拥有该进程时,才关闭浏览器进程。Stagehand 浏览器文档区分它启动的浏览器和调用方连接的浏览器。连接客户端不能假定自己拥有结束进程的权利;其他受控任务可能仍由 supervisor 使用该进程。
故障表与恢复顺序
| 观察 | 可能出错的层 | 下一项检查 | 停止条件 |
|---|---|---|---|
| CDP 拒绝连接 | 浏览器启动或端口分配 | 确认启动器状态和分配的端口 | 不要隐式启动另一个浏览器 |
| CDP 已连接但进程路径不符 | 端点错误或残留进程 | 将端点与登记的进程 ID 对照 | 页面操作前结束本次运行 |
| 进程正确但缺少 profile 参数 | 启动器配置 | 启动前检查参数构造 | 不要声称已选择 profile |
| 存在 profile flag 但 fixture 不一致 | profile、fixture 或应用 | 分别比较 profile 兼容性和 fixture 版本 | 不要为匹配而改页面值 |
observe() 没有返回候选项 | 页面状态或指令 | 检查 fixture 和可见控件 | 不要把指令扩大成其他操作 |
act() 有返回但 fixture 未变化 | 动作结果或应用状态 | 再读标记并检查返回结果 | 没有证据时不要报告远端变更 |
extract() 未通过 schema 验证 | 页面输出或 schema 合同 | 比较 fixture 可见数据与请求 schema | 保持数据最少,不要静默放宽 schema |
| 动作期间浏览器断开 | 进程生命周期或远端结果 | 重试前核对 fixture 或幂等状态 | 可能产生副作用时绝不盲目重放 |
恢复顺序应从首个失败层开始。连接被拒绝不是改 extraction schema 能解决的问题。端点指向错误进程,也不是扩大页面指令可以修复的。缺少 profile 参数要改启动器配置,并以新的运行标识重启进程。若连接中断前可能已提交远端修改,应查询幂等状态或交给责任操作者,不要自动重放。
browser-use 指南讲解另一种 agent loop 的审批点;自动化检测概览讨论通用信号。它们都不能替代 Stagehand 场景中的三层区分:原语结果、CDP 连接,以及连接前选择的 executable 和 profile。
Stagehand 不负责的部分
Stagehand 提供浏览器自动化 API;应用仍负责任务授权、账号选择、网络路由、密钥、数据留存,以及重要操作的审批。浏览器可能已连接就绪,但任务本身仍可能不合适,或目标已发生变化。策略应写在调用 Stagehand 的应用中,而不是放在 agent 可自行改写的指令里。
profile 选择应保留在经过审核的启动器配置中。agent 不应枚举任意 profile 文件、打印文件内容,或根据页面文字选择账号。启动器可以解析已批准的 profile 标识并将路径传给浏览器。回执可保存非敏感标签和配置版本,不复制加密文件或其内容。
网络路由是另一项独立配置。profile 参数不能证明请求使用了哪条出口路径,CDP 端点也不会告诉你应用层权限判断。如果工作流需要指定路线,应在外部启动层配置,并通过该工作流允许的诊断方式确认。除非外围系统提供相应值并注明来源,否则不要把网络路线、语言或账号状态归因于 Stagehand。
第三方分类也遵循相同边界。act() 成功表示一次浏览器交互返回了结果,extract() 成功表示返回了符合请求形状的数据。两者都不保证 session 被接受、检测器给出某种分类,或应用完成业务交易。BotBrowser 提供受控浏览器选项,不承诺第三方结果。
当工作流涉及凭据、同意、付款、删除、权限变更、发布或不可逆提交时,应在应用层暂停,由操作者决定是否继续。暂停信息应包括目标 origin、动作和可见参数,但不要把密钥放进模型 trace。这是应用策略边界,不是 observe()、act() 或 extract() 自带的行为。
常见问题
Stagehand v4 内部使用 Playwright 吗?
Stagehand v4 当前介绍说明其浏览器控制基于 CDP,并称没有 Playwright 或 Puppeteer 依赖。它的页面 API 可能让 Playwright 用户感到熟悉,但这不等于使用 Playwright 的 BrowserType 启动生命周期。
Stagehand 连接后能选择 BotBrowser profile 吗?
不能。profile 在浏览器进程启动时传入。Stagehand 页面原语在连接后工作,不能追溯修改进程参数。应在打开 CDP 端点前,把已批准的 --bot-profile 放入启动器配置。
CDP 连接成功能证明启动的是预期浏览器吗?
不能。它只证明端点有响应。应将端点与进程 ID 和 executable 路径对照,再检查脱敏参数,并独立读取自有 fixture。
这种连接能保证检测器或网站结果吗?
不能。Stagehand 动作和受控浏览器配置都不保证第三方的分类、接受或业务结果。这些结果不属于启动检查的结论。
来源
- Stagehand v4 介绍
- Stagehand
act() - Stagehand
extract() - Stagehand
observe() - Stagehand 浏览器配置
- Chrome 远程调试
- Chrome DevTools Protocol
- BotBrowser Playwright 配置
- BotBrowser 命令行参数
实践结论
将 Stagehand 原语与浏览器进程创建分开处理。由启动器选择 BotBrowser executable 和 profile,只开放本机 CDP 端点,再让 Stagehand 连接已运行浏览器。把进程、端点和自有 fixture 作为三项独立观察来核验;任何一项不匹配都停止。这只能说明集成检查的边界,不能证明 profile 有效、任务获准或第三方网站会如何判断。
本文属于指南: 浏览器部署与发布:运维决策地图