Stagehand x CrewAI 集成实战:基于 MCP/stdio 的 Facade 桥接方案
【免费下载链接】stagehandThe SDK For Browser Agents项目地址: https://gitcode.com/GitHub_Trending/stag/stagehand
导读
本文讲解如何在 Python CrewAI 框架中接入 Stagehand 浏览器 Agent 能力:通过一个保留截图信息的crewai-toolsMCP 适配器子类,将 Stagehand Facade MCP 服务器(基于 stdio 协议)暴露的run、snapshot、screenshot三个工具挂载给 CrewAI Agent。读完本文,你将掌握该集成的完整环境搭建、环境变量配置、运行方式、截图保留机制,以及其背后的安全模型与源码级实现原理。
该集成位于仓库的 packages/integrations/crewai 目录,是 Stagehand 多框架集成矩阵(Claude Code、Codex、Eve、Mastra、Vercel AI SDK 等)中的 Python 一员,详见 packages/integrations/README.md。
一、整体架构:CrewAI 如何驱动 Stagehand Facade
CrewAI 本身是纯文本的工具调用循环,不具备直接操作浏览器的能力。本示例的桥接思路是:
- Facade MCP 服务器:由
@browserbasehq/stagehand-integrations包的stagehand-facadestdio 服务器扮演(入口为 stdio-server.ts),它维护一个持久化浏览器,对外只暴露三个工具:run、snapshot、screenshot; - CrewAI 侧:
agent.py通过mcpadapt的MCPAdapt以 stdio 方式拉起 Node 子进程,再把 MCP 工具转换为 CrewAI 的BaseTool; - 关键适配点:
crewai-tools的CrewAIToolAdapter在转换时会丢弃 MCP 的ImageContent块(文本化转换),因此示例实现了一个ImageSavingToolAdapter子类,在转换前把截图写入临时文件并用文本块替换,从而保住截图能力。
从源码结构看,这套“三个工具 + 一个持久化浏览器”的契约被定义在 core/src/facade/contract.ts 中一次,供所有宿主框架复用,不在各示例中重复维护。
二、环境准备与依赖安装
2.1 前置要求
- Node.js 24+(facade 服务器是 Node 子进程;core 的 package.json 声明
node >=22.18.0,但本示例明确要求 24+); - uv:Python 依赖管理工具,项目通过
uv sync安装; - Python
>=3.11,<3.14(见 pyproject.toml)。
2.2 安装步骤
先在仓库根目录安装依赖并构建 integrations 包,使 facade 服务器入口文件(dist/facade/stdio-server.mjs)存在:
pnpm install pnpm exec turbo run build --filter @browserbasehq/stagehand-integrations然后安装 Python 依赖:
cd packages/integrations/crewai uv sync依赖声明于 pyproject.toml:
crewai>=1.15,<2crewai-tools[mcp]>=1.15,<2- dev 组:
pytest>=9,<10、ruff>=0.15,<1
注意:resolve_server_path()(见 agent.py)硬编码查找core/dist/facade/stdio-server.mjs,若找不到会抛出明确报错并提示先执行上面的 turbo build 命令,因此构建顺序不可省略。
三、环境变量配置:一张表看懂全部变量
| 变量 | 用途 |
|---|---|
STAGEHAND_BROWSER | 浏览器后端。设置了BROWSERBASE_API_KEY时默认为browserbase,否则为local。 |
BROWSERBASE_API_KEY | Browserbase API Key,云端浏览器隔离边界的关键凭据。 |
STAGEHAND_MODEL_NAME/STAGEHAND_MODEL_API_KEY | Facade 服务器使用的模型及其 API Key。 |
CREWAI_MODEL | CrewAI Agent 模型,默认为openai/gpt-5.6-luna。 |
OPENAI_API_KEY | 供宿主进程中 CrewAI Agent 模型使用;不会被转发给 facade 子进程(子进程环境变量是显式的STAGEHAND_*/BROWSERBASE_*白名单)。 |
关于最后一行,源码中的实现非常清晰:facade_env()只透传以STAGEHAND_或BROWSERBASE_开头的环境变量(见 agent.py),并将注释明确说明“模型提供方凭据(例如OPENAI_API_KEY)被刻意不转发”。
3.1 底层配置解析逻辑
Facade 服务器侧的环境变量解析位于 core/src/facade/config.ts,值得展开:
STAGEHAND_BROWSER只接受local或browserbase两个值,其它值直接抛StagehandFacadeConfigError;- 未显式设置时,存在
BROWSERBASE_API_KEY就选browserbase,否则选local(requestedBrowser ?? (browserbaseApiKey ? "browserbase" : "local")); - 选
browserbase却缺 Key 会报错;STAGEHAND_MODEL_API_KEY设置了但没给STAGEHAND_MODEL_NAME也会报错; - 未显式指定模型时,若检测到 Google 系 Key(
GOOGLE_GENERATIVE_AI_API_KEY/GEMINI_API_KEY/GOOGLE_API_KEY),默认模型为google/gemini-3.6-flash; - 模型名采用
provider/name格式,自动从环境推断对应 provider 的 Key(openai / anthropic / groq / cerebras 等); local模式默认headless: false(有头浏览器),browserbase模式透传projectId。
四、运行方式
从packages/integrations/crewai/目录执行:
uv run pytest uv run python agent.py "your instruction"uv run pytest:运行 test_contract.py 中的契约测试;uv run python agent.py "your instruction":把命令行参数拼接为任务描述交给 CrewAI。不给指令时,脚本会打印用法并退出(退出码 2),见 agent.py。
运行后 Crew 会执行kickoff(),Agent 通过三个工具完成浏览器任务,最终输出一份“任务完成情况及结果”的简要报告(expected_output定义见 agent.py)。
4.1 契约测试:三个工具名与提示词被钉死
test_contract.py的三个测试分别验证:
test_facade_tool_contract:会话暴露的工具名排序后必须恰好为["run", "screenshot", "snapshot"],且run工具描述中必须出现never "kind";test_image_saving_tool_adapter_preserves_screenshot:构造一个含TextContent+ImageContent的假CallToolResult,验证适配器把 PNG 字节写入stagehand-screenshot-*.png临时文件、返回文本路径、且原始 base64 不再出现在结果中;test_instructions_match_canonical_constant:当仓库内存在core/src/facade/contract.ts时,校验agent.py中手抄的FACADE_AGENT_INSTRUCTIONS与 TypeScript 端规范常量逐字一致(归一化空行后比较),防止两侧提示词漂移。
也就是说,Agent 的系统提示词并非每个示例各写一份,而是以 contract.ts 中的FACADE_AGENT_INSTRUCTIONS为唯一真源。
五、三个 Facade 工具:契约与用法
5.1 工具清单(来自 contract.ts 的FACADE_TOOLS)
| 工具 | 输入 Schema 要点 | 说明 |
|---|---|---|
run | code(字符串)或actions(数组),二者恰好提供一个 | 导航 + 自动化都在这里:传 JavaScript 走code,传快照动作批量走actions。动作必须用"op"(绝不用"kind")和"id"(绝不用"ref"),ID 需从最新快照复制为字符串。 |
snapshot | includeIframes(布尔,默认true) | 抓取活动页面的可访问性树并注入带括号的元素 ID;每次调用都会替换活动页面的 ID 映射。 |
screenshot | fullPage(布尔)、type(png/jpeg)、quality(0–100) | 截取活动页面。对尺寸受限的 MCP 客户端,推荐{"type":"jpeg","quality":40,"fullPage":false}。 |
run支持的快照动作类型(由RefActionSchema定义,见 contract.ts):
click、hover:只需op+id;fill:额外value(字符串);type:额外text,可选delay(非负数字);press:额外key(字符串);select:额外values(字符串或字符串数组,至少一项)。
运行时,CodeModeRunInputSchema通过.refine强制code与actions二选一(见 contract.ts)。值得一提的实现细节:线缆层刻意不设顶层oneOf互斥,因为基于 AI SDK 的 MCP 客户端(如 Eve、Vercel AI SDK)会拒绝带顶层oneOf的输入 Schema,导致run在客户端就被拒绝——互斥改为描述文本声明 + 运行时校验(该偏差在 contract.ts 的注释中有明确记录)。
5.2 提示词:Agent 被明确要求“只用一个持久化浏览器”
FACADE_AGENT_INSTRUCTIONS原文(contract.ts)规定:
- 恰好三个工具:
snapshot检查页面并注入元素 ID、run执行快照动作或 Playwright 风格pageAPI 的 JavaScript、screenshot视觉检查; - 简单交互用 snapshot 动作,多步工作流用
run code; - 每个动作只用
"op"和"id",ID 仅对活动页面最新一次快照有效,导航或 ID 过期后必须重新 snapshot; - 不要启动另一个浏览器。
5.3 服务端执行链路(tools.ts)
core/src/facade/tools.ts 中的StagehandFacadeTools是三个工具的服务端实现,几个关键点:
- 快照水合:
snapshot()保存pageId → { url, xpathById }映射;runActions()执行前校验:未先快照则报NO_HYDRATED_SNAPSHOT_ERROR,页面 URL 与快照时不一致则报NAVIGATED_SNAPSHOT_ERROR并删除缓存,ID 查不到对应 XPath 则报staleSnapshotIdError(id)——这些错误常量同样定义在 contract.ts; - 动作执行:通过
experimentalBatch在浏览器上下文内运行一段预置的ACTION_RUNNER_SOURCE,用locator依次执行 click/hover/fill/type/press/select,返回{ completed }; - 代码执行:
run(code)把用户代码包进FACADE_PRELUDE/FACADE_EPILOGUE后注入浏览器上下文,提供page、context、browser等 Playwright 兼容运行时(createPlaywrightCompatRuntime,见 runtime.ts),用户代码抛错会以 envelope 形式回传并重新抛出; - 串行队列:所有操作经
enqueue排队,避免并发操作同一个浏览器;截图支持 JPEG quality 取整以及“Base64 预算内压缩”(screenshot-transport.ts)。
六、截图保留机制:为什么需要 ImageSavingToolAdapter
当前版本的 CrewAI 工具循环是纯文本的,且crewai-tools的 MCP 适配器会丢弃 MCP 图片块。为让 Agent 仍能“看到”页面,ImageSavingToolAdapter(agent.py)的做法是:
- 包裹原工具 callable;
- 遍历
CallToolResult.content:非图片块原样保留;ImageContent块则按 MIME 类型(image/jpeg→.jpeg、image/png→.png)解码写入tempfile.mkstemp生成的临时文件(前缀stagehand-screenshot-); - 用一条文本块
Screenshot saved to <path>.替换图片块,再交给父类做文本化转换。
因此每次截图后,Agent 拿到的是一个本地文件路径,打开该文件即可查看截图。
七、安全模型:模型代码跑在哪里
run(code)执行的是模型撰写的 JavaScript,其运行位置是本示例最需要理解的安全边界:
- 代码通过
experimentalBatch在扩展的 service worker 中浏览器侧执行,绝不在宿主进程中运行; - Python 宿主进程只负责拉起 facade 服务器(Node 子进程)并持有 MCP 连接,模型撰写的 JavaScript 不会进入宿主进程;
- Browserbase 是推荐的隔离边界:把浏览器放到 Browserbase 云端(配置
BROWSERBASE_API_KEY),模型代码即使有越界行为也被限制在浏览器沙箱与云端会话内; - 子进程环境变量白名单(
STAGEHAND_*/BROWSERBASE_*)进一步确保宿主侧的模型凭据(如OPENAI_API_KEY)不会被模型代码读到。
服务器侧还有一层防护:sanitizeErrorMessage(stdio-server.ts)在错误回传时对 API Key、Bearer Token、bb_前缀 Browserbase Key、Google AIza Key 等做脱敏,避免凭据泄露到 Agent 上下文。
八、常见排查方向
- 报错“Stagehand facade server not found”:说明
core/dist/facade/stdio-server.mjs尚未构建,回到仓库根目录执行pnpm exec turbo run build --filter @browserbasehq/stagehand-integrations; - 报错“node was not found”:宿主进程通过
shutil.which("node")定位 Node(agent.py),需确保 Node 24+ 在PATH中; - STAGEHAND_BROWSER 取值错误 / browserbase 缺 Key:查看
stagehandFacadeConfigFromEnv的校验分支(config.ts),按 3.1 节约束配置; - Agent 抱怨 ID 失效:快照 ID 只对活动页面的最新快照有效,导航后必须先重新
snapshot。
结语
本示例的价值在于把“浏览器自动化”能力以最小的契约面(三个工具、一个持久化浏览器、纯文本 MCP 传输)接入 CrewAI,同时通过截图适配器弥补crewai-tools的文本化限制。理解其契约定义(contract.ts)、配置解析(config.ts)、服务端工具实现(tools.ts)与安全边界(service worker + 环境白名单)后,你既可以直接照搬运行,也可以把同样的模式迁移到其它 MCP 客户端框架中。
【免费下载链接】stagehandThe SDK For Browser Agents项目地址: https://gitcode.com/GitHub_Trending/stag/stagehand
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考