agent-browser 与 CDP:浏览器自动化的底层原理
本文基于对 vercel-labs/agent-browser v0.26.0 源码的完整分析,深入剖析其架构设计与 Chrome DevTools Protocol 的交互原理。
一、agent-browser 是什么
agent-browser 是 Vercel Labs 开源的浏览器自动化 CLI 工具,专为 AI Agent 设计。与传统工具不同,它的定位是"AI 友好的浏览器自动化"------输出 accessibility tree 而非原始 DOM,让 LLM 能直接理解页面结构。
关键特性:
- Rust 原生实现:核心约 36,000 行 Rust 代码,Node.js 仅作为跨平台 wrapper
- CDP 直连:通过 WebSocket 与 Chrome DevTools Protocol 通信,无需 Playwright/Puppeteer 中间层
- Accessibility Tree :
snapshot命令输出可交互元素的层级结构,自带@ref标记 - 多 Backend 支持:CDP(Chrome)、WebDriver、Appium、Lightpanda
二、CDP 基础:Chrome DevTools Protocol
2.1 什么是 CDP
CDP 是 Chrome 内置的远程调试协议。当你打开 Chrome 的「开发者工具」时,DevTools 前端就是通过 CDP 与 Chrome 后端通信的。agent-browser 本质上就是用一个程序替代了 DevTools 前端。
2.2 CDP 的两种通信方式
| 方式 | 协议 | 用途 |
|---|---|---|
| HTTP | HTTP/JSON | 发现可用页面 |
| WebSocket | JSON-RPC | 实时命令和事件传输 |
关键发现 :agent-browser 与 Chrome 的通信全是 WebSocket ,没有 HTTP REST API。这意味着每个命令都是异步的------发一个请求,通过 id 字段匹配响应。
2.3 CDP 消息格式
// 命令(Client → Chrome)
{"id": 1, "method": "Runtime.evaluate", "params": {"expression": "document.title"}}
// 响应(Chrome → Client)
{"id": 1, "result": {"value": "Example Domain"}}
// 事件(Chrome → Client,无 id)
{"method": "Page.loadEventFired", "params": {"timestamp": 1234567890}}
三、agent-browser 架构总览
3.1 分层架构
┌─────────────────────────────────────────┐
│ CLI 层(Rust) │
│ - 命令解析(open/snapshot/click/fill) │
│ - 状态管理(DaemonState) │
├─────────────────────────────────────────┤
│ Action 层(actions.rs, 9000+ 行) │
│ - handle_navigate / handle_evaluate │
│ - handle_snapshot / handle_click │
│ - handle_screenshot / handle_route │
├─────────────────────────────────────────┤
│ Browser 管理层(browser.rs) │
│ - Chrome 进程启动/发现/清理 │
│ - 页面导航(含 waitUntil 策略) │
│ - 多 Target/Session 管理 │
├─────────────────────────────────────────┤
│ CDP 客户端层(cdp/client.rs) │
│ - WebSocket 连接管理 │
│ - 命令/响应 id 匹配 │
│ - 事件广播(broadcast channel) │
│ - Keepalive(30s Ping) │
├─────────────────────────────────────────┤
│ WebSocket(tokio-tungstenite) │
│ ↕ │
│ Chrome DevTools Protocol │
└─────────────────────────────────────────┘
3.2 为什么用 Rust 而非 Node.js
package.json 中有一行关键说明:"Fast native Rust CLI"。
agent-browser 的 bin/agent-browser.js 实际上只是一个跨平台 wrapper------检测操作系统和架构,然后调用对应的原生二进制。核心逻辑全部在 Rust 中。
Rust 带来的优势:
- 零依赖运行:不需要 Node.js 环境,单个二进制文件即可运行
- 原生性能:WebSocket 通信、JSON 解析、Accessibility Tree 处理都在原生层面完成
- 进程管理:启动/监控/清理 Chrome 进程树更可靠
四、核心能力深度解析
4.1 Chrome 进程管理
agent-browser 不只是「连接到一个已运行的 Chrome」,它还负责完整的 Chrome 生命周期管理。
启动流程 (cdp/chrome.rs):
- 检测系统中已有的 Chrome(Chrome/Edge/Brave/Playwright/Puppeteer)
- 构建启动参数(headless、user-data-dir、window-size、proxy 等)
- 启动 Chrome 子进程
- 从
DevToolsActivePort文件读取端口和 WebSocket 路径 - 验证 WebSocket 端点是否可用
进程清理:
- Unix 系统使用
kill(-pgid, SIGKILL)杀掉整个进程组 - 包括 GPU 进程、渲染进程、工具进程、crashpad 等
- 防止孤儿进程阻塞用户的正常 Chrome
4.2 WebSocket CDP 客户端
cdp/client.rs 是 agent-browser 最底层的模块,约 360 行 Rust 代码实现了一个生产级的 CDP WebSocket 客户端。
核心设计:
next_id: AtomicU64------ 自增命令 IDpending: HashMap<u64, oneshot::Sender>------ 等待响应event_tx: broadcast::Sender------ 事件广播
命令发送流程:
- 生成唯一
id(AtomicU64 自增) - 将
id→oneshot::Sender存入pendingHashMap - 通过 WebSocket 发送 JSON
- 在 Reader 任务中,收到响应后根据
id找到对应的 Sender,发送结果 - 调用方通过
oneshot::Receiver等待结果(30 秒超时)
Keepalive:每 30 秒发送 WebSocket Ping 帧,防止中间代理(nginx/Envoy/云 LB)断开空闲连接。
4.3 Accessibility Tree 快照
snapshot 是 agent-browser 最核心的 AI 友好特性。
传统工具的痛点 :Playwright 的 page.content() 返回原始 HTML,LLM 难以理解;querySelector 需要精确的 CSS 选择器,对动态生成内容无效。
agent-browser 的方案:
agent-browser snapshot
输出格式:
[3] link "登录"
[7] textbox "用户名"
[12] button "提交"
实现原理 (snapshot.rs, 1586 行):
- 启用 Accessibility Domain:
Accessibility.enable - 获取完整 AX 树:
Accessibility.getFullAXTree - 构建树结构:Chrome 返回的 AX 节点是扁平数组,需要通过
childIds重建层级 - 生成 Ref 映射:每个可交互元素分配一个
@ref,后续click @3时通过 RefMap 解析为具体 DOM 操作 - iframe 穿透:遍历所有 iframe,为每个 frame 创建独立的 CDP session
4.4 交互能力
点击 (interaction.rs):
agent-browser 的点击不是简单的 element.click(),而是模拟真实的鼠标事件序列:
- 解析 ref/selector,获取元素中心坐标(考虑 iframe 偏移)
- 发送鼠标移动事件(
Input.dispatchMouseEventmouseMoved) - 发送鼠标按下事件(
Input.dispatchMouseEventmousePressed) - 发送鼠标释放事件(
Input.dispatchMouseEventmouseReleased)
这比 JavaScript 的 click() 更真实,能触发 CSS :hover、鼠标事件监听器等。
输入 (fill / type):先聚焦元素,清空现有内容(Ctrl+A + Delete),然后逐字符输入。
4.5 Network 拦截
agent-browser 支持通过 CDP 的 Fetch domain 拦截和修改网络请求:
agent-browser route "**/api/**" --mock '{"status": 200, "body": "{}"}'
实现原理:
Fetch.enable启用拦截(urlPattern: "*")- 监听
Fetch.requestPaused事件 - 匹配 URL 模式,决定是放行、mock 响应还是 abort
- 通过
Fetch.fulfillRequest或Fetch.continueRequest响应
4.6 多 Backend 架构
agent-browser 不仅支持 CDP,还抽象了 WebDriver 和 Appium 后端:
pub enum BrowserBackend {
Cdp(CdpBackend),
WebDriver(WebDriverBackend),
Appium(AppiumManager),
}
设计意义:
- CDP:本地 Chrome,功能最全(snapshot、network 拦截、录屏)
- WebDriver:远程浏览器(Selenium Grid、BrowserStack)
- Appium:移动端浏览器(iOS Safari、Android Chrome)
所有 backend 实现相同的 BrowserBackend trait,上层 actions 代码无需关心底层协议差异。
五、为什么不是 shell 脚本
很多开发者看到 CDP 是 JSON over WebSocket,会直觉地认为:"用 curl + websocat + jq 就能搞定"。实际上 agent-browser 做了大量 shell 难以实现的事情:
| 能力 | shell 难度 | agent-browser 实现 |
|---|---|---|
| WebSocket 全双工 | ❌ 极难 | tokio-tungstenite 异步读写 |
| 命令/响应匹配 | ❌ 难 | HashMap<id, oneshot::Sender> |
| 事件持续监听 | ❌ 极难 | broadcast::channel(4096) |
| Chrome 进程管理 | ⚠️ 中等 | 启动→发现→清理全生命周期 |
| Accessibility Tree 解析 | ❌ 极难 | 1586 行专用逻辑 |
| iframe 穿透 | ❌ 难 | 跨 session 递归解析 |
| Network 拦截 | ❌ 难 | Fetch domain + 实时事件处理 |
shell 能做的 :简单的 Runtime.evaluate 执行 JS
WS=$(curl -s http://localhost:9222/json/version | jq -r .webSocketDebuggerUrl)
echo '{"id":1,"method":"Runtime.evaluate","params":{"expression":"document.title"}}' | websocat "$WS"
shell 不能做的:同时监听事件流、匹配异步响应、管理多 session、解析 AX 树。
六、实际应用场景
6.1 AI Agent 网页操作
agent-browser 最初的设计目标就是让 LLM 能「看懂」网页并操作:
LLM ← accessibility tree(带 @ref)
LLM → click @7 / fill @12 "username"
这比让 LLM 直接看 HTML 或截图更高效。
6.2 自动化测试
agent-browser open "https://example.com/login"
agent-browser fill @email "test@example.com"
agent-browser fill @password "secret"
agent-browser click @submit
agent-browser wait-for "Dashboard"
agent-browser screenshot --output login-success.png
6.3 网页数据采集
agent-browser open "https://example.com/products"
DATA=$(agent-browser eval "JSON.stringify([...document.querySelectorAll('.product')].map(...))")
6.4 录屏与回放
agent-browser 支持 Page.screencastFrame CDP 事件,实时捕获页面变化帧,可用于操作回放、视觉回归测试、AI 训练数据采集。
七、总结
agent-browser 的核心价值不在于「能用 CDP」,而在于将 CDP 的底层能力封装成 AI 友好的高层抽象。
| 层级 | 内容 | 复杂度 |
|---|---|---|
| 底层 | WebSocket + JSON-RPC | 中等 |
| 中间层 | CDP 命令封装、事件循环、session 管理 | 高 |
| 上层 | Accessibility Tree、Ref 映射、多 Backend | 很高 |
| 应用层 | AI 友好的 CLI 接口 | 高 |
如果你只是偶尔需要执行一段 JavaScript,shell + websocat 完全够用。但如果你需要让 AI 理解页面结构并操作、处理 iframe、动态内容、network 拦截、跨平台跨后端统一部署,那么 agent-browser 这样的工具是不可替代的。
参考链接: