agent-browser 与 CDP:浏览器自动化的底层原理

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 Treesnapshot 命令输出可交互元素的层级结构,自带 @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 带来的优势

  1. 零依赖运行:不需要 Node.js 环境,单个二进制文件即可运行
  2. 原生性能:WebSocket 通信、JSON 解析、Accessibility Tree 处理都在原生层面完成
  3. 进程管理:启动/监控/清理 Chrome 进程树更可靠

四、核心能力深度解析

4.1 Chrome 进程管理

agent-browser 不只是「连接到一个已运行的 Chrome」,它还负责完整的 Chrome 生命周期管理

启动流程cdp/chrome.rs):

  1. 检测系统中已有的 Chrome(Chrome/Edge/Brave/Playwright/Puppeteer)
  2. 构建启动参数(headless、user-data-dir、window-size、proxy 等)
  3. 启动 Chrome 子进程
  4. DevToolsActivePort 文件读取端口和 WebSocket 路径
  5. 验证 WebSocket 端点是否可用

进程清理

  • Unix 系统使用 kill(-pgid, SIGKILL) 杀掉整个进程组
  • 包括 GPU 进程、渲染进程、工具进程、crashpad 等
  • 防止孤儿进程阻塞用户的正常 Chrome

4.2 WebSocket CDP 客户端

cdp/client.rs 是 agent-browser 最底层的模块,约 360 行 Rust 代码实现了一个生产级的 CDP WebSocket 客户端

核心设计

  • next_id: AtomicU64 ------ 自增命令 ID
  • pending: HashMap<u64, oneshot::Sender> ------ 等待响应
  • event_tx: broadcast::Sender ------ 事件广播

命令发送流程

  1. 生成唯一 id(AtomicU64 自增)
  2. idoneshot::Sender 存入 pending HashMap
  3. 通过 WebSocket 发送 JSON
  4. 在 Reader 任务中,收到响应后根据 id 找到对应的 Sender,发送结果
  5. 调用方通过 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 行):

  1. 启用 Accessibility Domain:Accessibility.enable
  2. 获取完整 AX 树:Accessibility.getFullAXTree
  3. 构建树结构:Chrome 返回的 AX 节点是扁平数组,需要通过 childIds 重建层级
  4. 生成 Ref 映射:每个可交互元素分配一个 @ref,后续 click @3 时通过 RefMap 解析为具体 DOM 操作
  5. iframe 穿透:遍历所有 iframe,为每个 frame 创建独立的 CDP session

4.4 交互能力

点击interaction.rs):

agent-browser 的点击不是简单的 element.click(),而是模拟真实的鼠标事件序列

  1. 解析 ref/selector,获取元素中心坐标(考虑 iframe 偏移)
  2. 发送鼠标移动事件(Input.dispatchMouseEvent mouseMoved)
  3. 发送鼠标按下事件(Input.dispatchMouseEvent mousePressed)
  4. 发送鼠标释放事件(Input.dispatchMouseEvent mouseReleased)

这比 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": "{}"}'

实现原理

  1. Fetch.enable 启用拦截(urlPattern: "*"
  2. 监听 Fetch.requestPaused 事件
  3. 匹配 URL 模式,决定是放行、mock 响应还是 abort
  4. 通过 Fetch.fulfillRequestFetch.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 这样的工具是不可替代的。

参考链接

相关推荐
守城小轩18 小时前
基于Chrome140的Yahoo自动化(关键词浏览)——需求分析&环境搭建(一)
运维·自动化·chrome devtools·浏览器自动化·指纹浏览器·浏览器开发
时空系1 天前
认识Rust——我的第一个程序 Rust中文编程
开发语言·后端·rust
时空系1 天前
第10篇:归属权与借用——Rust的安全保障 Rust中文编程
开发语言·安全·rust
时空系1 天前
第6篇:数据容器——管理大量数据 Rust中文编程
开发语言·后端·rust
时空系1 天前
第7篇:功能——打造你的工具箱 Rust中文编程
开发语言·网络·rust
qcx231 天前
拆解 Warp AI Agent(五):跨生态联邦——10 种 Skill + MCP + 多 Harness 互操作设计
人工智能·rust·ai agent·skill·warp·mcp·harness
时空系1 天前
第8篇:结构模板——自定义数据类型 Rust中文编程
开发语言·网络·rust