浏览器自动化工具横评

43 min read,created at 2026-07-28
浏览器自动化chromeplaywrightpuppeteerego browserbrowser use

浏览器自动化工具横评

主要对比以下工具的功能差异,和用途用法介绍。

  • chrome devtools mcp
  • playwright
  • agent browser
  • browser use
  • ego lite
  • codex chrome插件/内置浏览器

首先从原理上来讲,这些工具都基于CDP (chrome devtools protocol)提供的接口,实现了对浏览器的控制,所以核心能力上不会有太大的区别。

CDP就是chrome最高权限debug权限,右键检查打开的就是devtools的面板,在这里你可以执行任意脚本、修改css、查看网络、查看cookie、模拟设备网络等等。这是人能访问的途径,而CDP代码访问的渠道主要有3种,分别是

进程管道chrome插件远程调试(老)远程调试(新)
交互协议管道自行设计,如codex使用的native messageHttp+WebsocketWebsocket
复用自己profile不可不可
headed
headless一般不可
警告提示有横幅提示每次需手动确认,且有横幅提示

ego lite这种比较特殊,他是基于chromium内核自己写的浏览器,协议是自己的原生定义的事件消息,缺点是要更换浏览器,且目前没有windows。

cdp (chrome devtools protocol)

cdp是可以以开发者模式完全操作chromecdp现在有多达50+domin下,总共600+的函数,数百个可以监听的event。对普通用户来说90%+的函数都用不上。常见的domain有,操作js的Runtime、操作页面的Page、操作网络的Network、操作存储的Storage、获取输入的Input、获取dom的DOM、获取css的CSS等等。官方文档左上角能看到totstable版本。stable 1.3 是 Chrome 64 (现在已经150+)时确定的较小兼容子集;tip-of-tree 则累积了此后多年新增的全部协议能力,其中还包括大量 experimental Domain。

stable 1.3:十几个 Domain
tip-of-tree:50 多个 Domain

默认所有的method的定义也可以查看链接,其中methodevent的定义是分开的。

cdp的基本原则:

  • 调用中需要两个参数methodparam
  • 默认对当前activeTab进行操作

我们这里用两个demo程序来演示最基础的cdp的使用,一个是通过chrome插件,一个是通过websocket远程协议,对应的是上面的chrome插件远程调试(老)或(新)的形式。

两个demo都在当前目录下./cdp-workbenchchrome-extension目录可以直接用chrome插件的开发者模式加载为插件,核心代码就如下:

// 对某个tab启动调试模式,默认就是对当前tab
function attach(debuggee) {
  if (attachedTabs.has(debuggee.tabId)) return Promise.resolve();
  return new Promise((resolve, reject) => {
    chrome.debugger.attach(debuggee, "1.3", () => {
      const error = chrome.runtime.lastError;
      if (error) reject(new Error(error.message));
      else {
        attachedTabs.add(debuggee.tabId);
        resolve();
      }
    });
  });
}
// 开启调试模式后,发送调试指令,指令参数就是{method, params}
function sendCommand(debuggee, method, params) {
  return new Promise((resolve, reject) => {
    chrome.debugger.sendCommand(debuggee, method, params, (result) => {
      const error = chrome.runtime.lastError;
      if (error) reject(new Error(error.message));
      else resolve(result ?? {});
    });
  });
}
// 如果切换页面需要先detach当前,然后attach新的tab
function detach(tabId) {
  if (!attachedTabs.has(tabId)) return Promise.resolve();
  return new Promise((resolve) => {
    chrome.debugger.detach({ tabId }, () => {
      void chrome.runtime.lastError;
      attachedTabs.delete(tabId);
      resolve();
    });
  });
}
// 监听事件,domain.enable之后可以监听该domain的事件
chrome.debugger.onEvent.addListener((source, method, params) => {
  if (!source.tabId) return;
  for (const [port, tabs] of portTabs) {
    if (!tabs.has(source.tabId)) continue;
    try {
      port.postMessage({ type: "cdp-event", tabId: source.tabId, method, params: params ?? {} });
    } catch {}
  }
});

用法: gif

这就是chrome插件版本,申请插件最高级别的权限debugger,就可以操作cdp了,有了cdp的能力,其他的什么tabcookie等权限其实就都有了。另一种接入方式是websocket,分为老和新两个版本的接入方案。老版本是直接启动一个新的浏览器进程,在启动指令中设置远程调试参数,以windows为例,如下指令可以指定9223调试端口,这是一个新的浏览器进程,是新的profile。

& 'C:\Program Files\Google\Chrome\Application\chrome.exe' `
  --remote-debugging-port=9223 `
  --user-data-dir="$env:TEMP\chrome-debug-profile"

如果你不知道你的chrome二进制文件(比如你不是windows是macos或linux)在哪,你可以在chrome://version下找到可执行文件位置和用户数据位置。

img

启动之后,可以打开http://127.0.0.1:9223/json/version会发现里面有个webSocketDebuggerUrl字段记录了cdp-ws协议的交互地址,格式为ws://127.0.0.1:9223/devtools/browser/<browserId>而新版本的协议是没有/json/version这个endpoint,直接在已经运行中的chrome进程中就可以动态开启或关闭(不需要启动指令声明式开启)。方法是在chrome://inspect#remote-debugging勾选开启,会自动监听9222端口的ws地址。

img

但是wsUrl不告诉你,也就是browserId你需要自己去找,他会默认把这个id写到一个DevToolsActivePort文件中,这个文件在chrome://version前面那个截图中的个人资料的..../User Data目录下:

image

对于ws的版本,你可以到websocket-client运行npm i然后npm run start打开http://127.0.0.1:8087这个页面在功能上和chrome插件完全一样,只不过需要先填写url,这里老版本的可以直接输入/json/version的httpendpoint,会自动访问这个enddpoint拿到对应的Websocketurl,新版本则是需要直接输入websocketUrl

image

使用的时候你会发现,如果是走的新版的动态开启的远程调试,会在第一次attach的时候,有个审批的提示,其他功能都是一样的cdp支持的所有函数和事件。

image

chrome devtools mcp (48K⭐)

如果把cdp的600多个method都封装成mcp显然是会浪费大量的上下文,所以谷歌官方挑选了为数不多的二三十个最常用的封装为了mcp工具,这是chrome官方推出的mcp工具,简称chromemcp吧,他支持进程管道远程调试(老+新)两种方式启动,自动化调试用进程管道方式这也是默认的配置方式,而如果要复用自己当前的profile,可以使用远程调试方式。

配置方式:

# 最简单配置,进程管道 + 有头
npx -y chrome-devtools-mcp

# 进程管道 + 无头
npx -y chrome-devtools-mcp --headless

# 远程调试 老 有头 (需要先用remote模型启动进程)
npx -y chrome-devtools-mcp --browser-url=http://127.0.0.1:9222

# 远程调试 新(自动连接9222,需要你打开这个配置,否则方法报错) 有头
npx -y chrome-devtools-mcp --autoConnect

另外一些常见的会开启的feature flag

# 开启视觉检测,开启后可以调用click_at方法
npx -y chrome-devtools-mcp --experimentalVision 

这里说一下为什么新老版本的都是9222端口,这是本质上就只有老版本,他是将进程通信的调试能力通过9222端口开放,以便于远程调试,而这个协议下需要先访问/json/version的http接口,来获取websocket的调试地址。

image

这里介绍下新老版本的前置配置方式,老版本需要用chrome启动一个新的进程,带上如下启动指令remote-debugging-port

& 'C:\Program Files\Google\Chrome\Application\chrome.exe' `
  --remote-debugging-port=9223 `
  --user-data-dir="$env:TEMP\chrome-debug-profile"

然后mcp中配置npx -y chrome-devtools-mcp --browser-url=http://127.0.0.1:9223即可连到这个浏览器上了,这里额外说一下老版本的远程连接的方式和进程管道的方式,在各方面能力和限制是一样的,而进程管道还不占用端口,所以旧版的远程方式的使用场景较少,主要是真的部署了远程托管的浏览器这种场景会用到,进程管道要更实用一些。

而新版本其实主要是允许浏览器启动的时候,不添加remote-debugging-port的启动指令,在设置页面中手动开启,并且新版本手动开启后,并不提供/json/version的endpoint,而是chromemcp通过--autoConnect参数,自己去发现chrome进程,进而找到对应的ws端口和browserid,自己拼出来上图中的websocket url,后续还是走的原来的标准cdp调用的协议交互规范。

新版本的最核心的作用就是不用在启动的时候指定参数,进而使得我们自己用户的profile打开的浏览器也可以在chrome://inspect#remote-debugging中远程调试,这样chromemcp就可以使用用户当前的浏览器进行操作,这应该是新版本远程调试协议的最大用途。

接下来我们来介绍,mcp中的工具列表和功能(chromemcp的返回结构都是纯文本的,可能觉得json太冗余):

类别工具用途
页面list_pages列出可操作页面 (自增数字id + title + url + [selected])
select_page选择页面(用的是上面的自增id,click等操作都是针对选中标签的)
new_page / close_page新开、关闭标签
navigate_page打开 URL、前进后退、刷新
resize_page改页面尺寸
handle_dialog接受/取消 alertconfirmprompt
get_tab_id取得 Chrome 原生 tab id (插件中一般用这个id)
截图take_screenshotviewport 或完整页面截图;可存入 MCP roots 允许的路径
take_snapshot返回可访问性树(AX tree)及稳定 uid (形式:uid=1_3 button "Pages")可根据text确认功能,然后用uid触发click等
DOM/等待wait_for等文字出现或等待指定时间
输入click按 snapshot 的 uid 点击元素
hover悬停指定元素
fill清空后填写单个输入框
type_text在焦点元素输入文字
drag按两个元素的 uid 拖拽
fill_form一次填写多个表单字段
upload_file给文件输入框上传 workspace 内文件
press_key按键或组合键,如 CTRL+AENTER
JavaScriptevaluate_script在页面执行 JavaScript 函数,返回值必须 JSON 可序列化
Consolelist_console_messages获取本次导航以来的 console 日志,可过滤类型、分页
get_console_message按日志 id 获取完整消息
Networklist_network_requests列网络请求,支持过滤和分页
get_network_request查看某请求的完整 headers、body、响应等
性能performance_start_trace开始录制 trace
performance_stop_trace停止并分析 trace,返回性能洞察
performance_analyze_insight查看某一条性能洞察的详细证据和建议
Lighthouselighthouse_audit运行 Lighthouse,返回性能、可访问性、SEO 等审计结果
模拟emulate设备/视口、DPR、UA、地理位置、网络及主题等模拟

feature flags开启的功能:

开关新增工具用途与限制
--experimentalVisionclick_at用 CSS viewport 坐标点击。截图像素与 DPR 不一致时要换算,正是你之前遇到的缩放偏差来源。
--memoryDebuggingtake_heapsnapshot 及 10 个分析工具堆快照摘要、详情、类实例、引用边、retainers、保留路径、dominator、重复字符串、两份快照 diff、关闭已加载快照。用于查内存泄漏。
--experimentalScreencastscreencast_start / screencast_stop录制页面操作视频;需要 ffmpeg 在 MCP 的 PATH,或传 --experimentalFfmpegPath
--categoryExperimentalThirdPartylist_3p_developer_tools / execute_3p_developer_tool调用被测页面通过 window.__dtmcp 暴露的第三方开发者工具。页面自己必须实现它。
--categoryExperimentalWebmcplist_webmcp_tools / execute_webmcp_tool调用页面暴露的 WebMCP 工具;Chrome 149+ 并要带 WebMCP,DevToolsWebMCPSupport feature flags。
--categoryExtensionslist_extensionsinstall_extensionuninstall_extensionreload_extensiontrigger_extension_action管理/调试扩展。1.6.0 的说明中该能力要求 MCP 自己用 pipe 启动 Chrome,不能与 --autoConnect--browser-url--wsEndpoint 合用。

例如一套基本的操作流程,通常会包括:

  • new page url 打开测试的页面
  • wait_for 等待页面完成
  • take_snapshot 获取元素文本和uid
  • fillclick+press_key uid 输入表单
  • click uid 点击提交按钮

对于有些复杂一点的页面,比如按钮上没有明显的提交字样,而是画了个svg,这种snapshot得到的信息就很难推断出这个按钮是提交的作用,这种只能用兜底方案take_screenshot截图,然后大模型识图,找到提交的按钮的坐标,通过click_at进行点击。当然这需要--experimentalVision开启。

此外要说一点的是,截图这个功能在有头模式下,有系统缩放的坑,例如你是windows,你的屏幕分辨率太高,所以你开了缩放125%,那么这个截图大小如果是1250x1250的窗口,那么实际窗口其实是1000x1000css scale大小。

image

image

这会导致,如果右下角有一个按钮,大模型识图之后给你点击的位置是1240,1240,但是实际是按照css取的坐标来触发点击的,1240已经在页面之外了,页面最多就到1000,而这个坑,在无头模式下是没有问题的,无头的截图就是1000x1000,只有有头模式会有问题。那如何避免呢?前面介绍了emulate这个函数,他可以传viewPort参数,来重设当前page的viewport,参数是1000x1000x1这个形式,最后的x1是设备缩放比例,通过这个参数可以把比例调回1,然后再截图,得到的就是1000x1000的了。

用法小结(以登录某个系统的操作为例):

  • 1 调用new_page(url)打开新的页面,并选中
  • 2 调用take_snapshot()获取AXtree拿到元素和uid -> 发送回llm
  • 3 llm找到了user和pass的输入框,调用fill(uid, value)填写用户名密码
  • 4 如果登录按钮在AXtree没有识别,则会用take_screenshot()截图 (如果有设备缩放还要调用emulate()改回100%再截图)->发送回llm
  • 5 llm找到登录按钮的xy坐标,调用click_at(x, y)点击登录按钮

playwright(93K⭐)

playwright是更强大、更完善的浏览器自动化工具,在之前,他就作为前端常用的自动化测试工具了,背靠微软,稳压谷歌puppeteer,AI时代他也迎来了新的增强,不仅有mcp甚至还有更强大的cli工具配合skillplaywright-mcpchromemcp类似。而playwright-cli则是不仅可以执行单步指令,还可以串行传入一段脚本,一次执行脚本中的步骤,后来的工具,基本上将按照脚本批量执行多步操作作为了基本的能力。

playwright不仅支持chrome还支持firefoxwebkit引擎的浏览器,在chrome内核的时候,底层使用cdp,而其他内核则分别用了其他的底层实现,这里不展开。所以playwright整体架构分层上会多一层高度抽象的api层,不同内核的底层实现不同,而且不同的api对不同内核来说支持程度也不同,尤其是一些冷门的功能。

mcp:

npx -y @playwright/mcp@latest --caps=vision # 开启vision

默认开启的有

类别工具用途
页面browser_tabs列出、新建、选择或关闭标签页
browser_navigate打开指定 URL
browser_navigate_back返回上一页
browser_close关闭当前页面
browser_resize修改浏览器窗口大小
输入browser_click点击元素,支持鼠标按键、双击及修饰键
browser_hover悬停到元素
browser_drag在两个页面元素之间拖放
browser_drop把文件或指定 MIME 数据拖放到元素上
browser_type向输入元素写入文本,可选择逐字输入或输入后回车
browser_fill_form一次填写多个表单字段
browser_select_option选择下拉框选项
browser_press_key按键,如 EnterEscapeArrowDown
browser_file_upload上传一个或多个文件
browser_handle_dialog接受或取消 alertconfirmprompt 对话框
等待browser_wait_for等待时间、文本出现或文本消失
截图browser_snapshot获取页面无障碍树;这是定位和操作元素的主要入口
browser_take_screenshot截取当前视口、完整页面或指定元素
browser_find在无障碍树中搜索文本或正则,只返回相关片段,比完整快照省 token
Consolebrowser_console_messages获取浏览器控制台消息
Networkbrowser_network_requests列出页面加载以来的网络请求,并为请求编号
browser_network_request根据编号读取单个请求的 headers、请求体和响应体
JavaScriptbrowser_evaluate在页面或指定元素上下文执行 JavaScript
browser_run_code_unsafe在 MCP 服务进程中执行 Playwright 代码,等同于任意代码执行,风险很高

chrome devtools mcp功能上有非常大的重叠,同样的playwright也有很多ff开启后可以引入更多的功能,比如networkvisionstorage等等,这里只说一下vision,开启后可以使用指定坐标操作:

browser_mouse_click_xy
browser_mouse_move_xy
browser_mouse_drag_xy
browser_mouse_down
browser_mouse_up
browser_mouse_wheel

playwright的截图函数非常好,他有一个scale参数,默认是css,也就是即使你的设备有125%缩放,截图出来的也是原始像素大小,比如原始css的宽度是1000px,前面的chromemcp默认截图是1250px,但playwright可以指定scalecss,得到的是1000px,这对于后续用browser_mouse_click_xy是非常友好的。

不过官方并不建议用mcp而是建议用cli+skill,因为cli具有更好的灵活性,而且支持批量的脚本执行,判断执行,循环执行等,这是原子性的mcp不支持的。通过以下方式安装cli+skill

npm install -g @playwright/cli@latest
playwright-cli install --skills

cli有单独的一套语法,核心功能与mcp无异,且不需要--caps=xxx来开启,默认就支持所有feature。

# tab
playwright-cli tab-list                 # list all tabs
playwright-cli tab-new [url]            # create a new tab
playwright-cli tab-close [index]        # close a browser tab
playwright-cli tab-select <index>       # select a browser tab
# page + input
playwright-cli open [url]               # open browser, optionally navigate to url
playwright-cli goto <url>               # navigate to a url
playwright-cli close                    # close the page
playwright-cli type <text>              # type text into editable element
playwright-cli click <ref> [button]     # perform click on a web page
playwright-cli dblclick <ref> [button]  # perform double click on a web page
playwright-cli fill <ref> <text>        # fill text into editable element
playwright-cli fill <ref> <text> --submit # fill and press Enter
playwright-cli drag <startRef> <endRef> # perform drag and drop between two elements
playwright-cli drop <ref> --path=<file> # drop files onto an element (from outside the page)
playwright-cli drop <ref> --data="k=v"  # drop data onto an element
playwright-cli hover <ref>              # hover over element on page
playwright-cli select <ref> <val>       # select an option in a dropdown
playwright-cli upload <file>            # upload one or multiple files
playwright-cli check <ref>              # check a checkbox or radio button
playwright-cli uncheck <ref>            # uncheck a checkbox or radio button
playwright-cli snapshot                 # capture page snapshot to obtain element ref
playwright-cli snapshot --filename=f    # save snapshot to specific file
playwright-cli snapshot <ref>           # snapshot a specific element
playwright-cli snapshot --depth=N       # limit snapshot depth for efficiency
playwright-cli find <text>              # search the snapshot for text, returns matching nodes
playwright-cli find --regex <pattern>   # search the snapshot with a regexp
playwright-cli eval <func> [ref]        # evaluate javascript expression on page or element
playwright-cli dialog-accept [prompt]   # accept a dialog
playwright-cli dialog-dismiss           # dismiss a dialog
playwright-cli resize <w> <h>           # resize the browser window
playwright-cli press <key>              # press a key on the keyboard, `a`, `arrowleft`
playwright-cli keydown <key>            # press a key down on the keyboard
playwright-cli keyup <key>              # press a key up on the keyboard
playwright-cli mousemove <x> <y>        # move mouse to a given position
playwright-cli mousedown [button]       # press mouse down
playwright-cli mouseup [button]         # press mouse up
playwright-cli mousewheel <dx> <dy>     # scroll mouse wheel
......

最大的不同我个人感觉是在执行的灵活性上,比如运行多个步骤的串行操作的话,mcp需要分别运行->结果->llm->运行->结果->llm,而cli可以直接运行一段脚本,如果是一个循环的,可以直接写在一行,而且可以指定执行顺序,而且支持条件判断,循环执行等等。多步操作可以直接封装成一个shell脚本,运行多个playwright-cli命令,还可以直接用run-code

# 先打开一个空页面
playwright-cli open

# 对这个页面运行code
playwright-cli run-code "async (page) => {
  await page.goto('https://example.com');

  await page.screenshot({
    path: 'example.png',
    fullPage: true
  });

  return {
    title: await page.title(),
    url: page.url()
  };
}"

这里page对象和他所持有的函数goto title等,又是学习的成本,但是好在ai时代,我们不需要记住每一个函数名,而是更多的让ai来帮我们写流程,我们要做的最主要的事情是知道工具的功能边界,知道什么时候用什么工具能解决什么问题,复杂度,可行性这些是我们关注的,而不是记住每一个功能,每一个参数类型。

说回playwright-cli,他相比chromemcp在管理上更复杂,chromemcp比较简单好理解,他就是针对某个浏览器进程进行一个完全操作的模式,而playwright-cli可以操作多个浏览器进程每个进程又可以标记为多个会话session,这里有个会话session的概念。

# open默认会打开一个新的session,不指定的话,会话名是default
playwright-cli open [url1]

# 再次open一个url2的话,还是default这个session,就会把url1关闭替换为url2
playwright-cli open [url2]

# 如果想开一个新的session需要用-s=xxx新的名字
playwright-cli open -s=dev1 [url3]

# 如果想在一个session打开多个页面,在open只能new-tab
playwright-cli new-tab [url4]

# 关闭一个session,默认给default关了
playwright-cli close-session [session]

# 不管是--headed还是--headless,通过show可以看到所有的session
playwright-cli show

无头的也能在这看到:

image

上面的open本质都是进程管道控制的,所以无法复用自己当前的profile,如果想复用某个profile的话,提供了两种方式,一种是chrome插件,搜索插件市场安装插件后,通过如下指令

playwright-cli attach --extension=chrome -s=my-chrome
playwright-cli new-tab -s=my-chrome [url]

chrome插件默认是只能attach一个指定的tab,然后attach之后会把这个tab,和后续如果新开了tab加入到一个Playwright的group里。

image

而如果是通过websocket的话,直接用cdp指定http endpoint即可

# 老版本
playwright-cli attach --cdp=http://127.0.0.1:9222 -s=cdp

# 新版本
playwright-cli attach --cdp=ws://127.0.0.1:9222/devtools/browser/79abd2f7-556a-40fc-aa3f-101ab14b804a -s=cdp

browser-use(108K⭐)

browser-use坐拥108K+star,超过了playwright的93K+star,也是一个AI时代的浏览器自动化工具,在能力上同样基于cdp,接入形式上主要是通过websocket的接入方式。支持的能力这里不再详细列出,与chromemcp能力相近,没有playwright的session的概念,同样是直接通过url来attach到已有的浏览器进程,browser-use主要有3种用法:

  • 1 browser-use本身是个cli工具,与playwright-cli类似,通过cli+skill让agent接入,但是是单session的。
  • 2 另外一个主要的用法是通过python的sdk被引用,本身也是个agent,通过配置LLM供应商自身就是个操作浏览器的agent。
  • 3 提供saas服务,服务形式是浏览器作为worker,可以派发任务给cloud browser

这里我们不介绍第二、三个用法,因为大多数时候我们还是通过综合能力更强的主agent配合cli+skill来操作浏览器比较多。而第一个用法。他不支持进程管道,默认只能通过websocket的方式attach到正在运行的chrome进程中。默认会自己发现chrome/chromium进程,并且尝试attach到其中,如果没有正在运行的chrome/chromium进程,会直接报错警告,而不会自己启动一个自己管理的进程,所以browser-use更倾向于复用用户当前的浏览器,而如果用户当前没有开启remote debugging,那么也会提示用户打开这个选项,然后再尝试attach到进程中。

# 安装
uv tool install browser-use # pip install browser-use也行
# 安装skill
browser-use skill install
# 使用(同样有session的概念,默认不写是default)
browser-use --session agent-1 <<'PY'
new_tab("https://example.com")
print(page_info())
PY

第一次attach会找到当前运行中的浏览器,如果没开启remote debugging,会提示用户打开这个选项,如果已经开了,也会有一个提示,需用户点击同意。

从使用方法上来看,他跟playwright-clirun-code类似,都是输入一段脚本,只不过playwright是js语法,并且默认能操作的返回为page,是run-code之前opennew-tab的page,browser-use是直接在脚本中new_tab然后默认操作的就是新打开的页面,如果要切换多个tab需要tab1=new_tab("url1")tab2=new_tab("url2")switch_tab(tab1)这样的方式切换。

能力上browser-use并没有多出什么,甚至比playwright还要少一些,所以能超过playwright的star数,我觉得有几个原因:

  • 1 命名 + AI时代红利
  • 2 自身就是个浏览器agent有轻量级的用法
  • 3 云端浏览器
  • 4 skill中有其他的一些工具集的组合使用,比如关键步骤截图后,用图片+鼠标轨迹再渲染+敏感信息遮罩生成视频的技巧(虽然remotion等也都能做到),但bu给集成了,整体用法更连贯。

agent browser (39K⭐)

vercelagent browser工具,也是一个浏览器自动化操作的产品,代码基于rust,也是一个cli+skill的工具,与playwright-clibrowser-use-cli类似,同质化非常严重。

# 安装
npm install -g agent-browser
agent-browser install  # Download Chrome from Chrome for Testing (first time only)

# 执行脚本和其他的cli非常类似
agent-browser open https://app.example.com # 默认是headless,--headed可以有头,默认是新的profile
agent-browser snapshot            # accessibility tree with @e1, @e2 refs
agent-browser click @e4
agent-browser fill @e7 "[email protected]"
agent-browser screenshot /tmp/after.png

# 批量操作 对标run-code
agent-browser batch "open https://example.com" "snapshot -i" "screenshot"

连接方式上默认的open和playwright一样是无头的新profile,如果要想有头需要--headed,而如果想要连接已有的浏览器则需要连接Default的profile,需要用

agent-browser --cdp 9222 tab # 这一次要审批
agent-browser --cdp 9222 open https://gmail.com # 这一次复用链接就不需要审批了
agent-browser --cdp 9222 snapshot -i

playwright类似,也有session概念

agent-browser --session agent1 open https://example.com

# 启动一个webui,查看所有session和page,类似playwright show,但是提供了更多信息,感觉更有用一点
agent-browser dashboard start

agent-browser是和playwright-cli用法上最像的,有sessiondashboard,那么vercel为什么会出了一个和playwright类似的工具呢?首先很多功能其实是agent-browser先有的,比如dashboard,后来playwright也补充了。其次ab主要针对agent场景做了不少细节上的优化,比如怎么减少cli返回的数据来减少token,提供必要的函数功能,而不是全部,所以功能上是playwright的子集,但是对于agent来说大而全不见得就是好,精简一下提供最常用的功能反而是好使。

此外ab还有一些独有的特点,依托vercel,有很多专门针对react/nextjs框架的工具,另外是拥抱远端不止可以操作本地或ws协议的远程浏览器。还实现了很多cloud browser provider的协议接入。比如上面提到的browser-use卖云端浏览器的,ab也可以接入,还有aws agentCore, browserlessbrowserbase以及自家的kernel等等。

ego-lite(7K⭐)

ego-browser是一个基于chromium内核的浏览器(目前仅macos)安装后npx skills add citrolabs/ego-lite来添加技能。支持和bu playwright类似的cli能力,语法是js,如下。

# 创建一个新的space,打开login页面,snapshot
ego-browser nodejs <<'EOF'
await useOrCreateTaskSpace('inspect login page')

await openOrReuseTab('https://example.com/login', {
  wait: true,
  timeout: 30,
})

cliLog(await snapshotText())
EOF

# 得到一个类似AXtree的结构,里面每个元素有@12这种ref
ego-browser nodejs <<'EOF'
await useOrCreateTaskSpace('inspect login page')
await fillInput('@12', username)
await fillInput('@15', password)
await click('@18', { label: '点击登录按钮' })
EOF

对于session/进程的管理,ego没有session是在同一个进程中处理任务,但是为了避免tab之间互串的操作,提出了space的概念也就是上面代码每一段运行的时候都会useOrCreateTaskSpace指定特定的space,一般一个任务就单开一个space,如果有多个任务需要并行执行,可以创建多个space,互不影响。

在用户交互上,右上角space按钮点击后查看当前后台被agent操作的space页面,可以预览,完全不影响自己当前的操作。

我个人的评价是,同样还是cdp的能力,ego并没有带来超过playwright的功能,但是在agentic上有了一些新的用户体验,比如全新的浏览器,全新的space概念,减少了用户的心智负担。总之能力上没有突破,惊喜,有,但是不足。尤其想要直接把用户主浏览器给替换的产品策略,很难动摇根深蒂固的用户习惯(虽然已经无缝把各种数据迁移过来了)。我使用过一小段时间,但是还是最终切换回了chrome,期待ego更多的创新吧。

codex的内置浏览器和插件

这里代表了一众harness工具的内置浏览器和chrome插件,在codex中侧边栏是有内置浏览器的,默认可以打开一些调试页面等,此外codex还可以配置chrome插件,直接操作当前的用户浏览器。

前者属于进程管道的方式进行控制,类似playwright的进程管道。

image

他的数据交互流程为

image

image

chrome插件原理则是和playwright的chrome插件类似,这里不展开了。

对于codex我想额外说的事情是,codex本身将各种内置的能力包装到了一个大一统的exec方法中,如果你有留心codex请求体的变化,你会有一个惊人的发现codex的工具越来越少,现在只有3-4个了,这还包括了subagent专用的一个namespace的tool,一个wait,一个询问用户的request_user_input,其他所有的功能工具都包装到了exec这个tool中。也是上面图中看到的,exec本质是运行一段js的语法node_repl,比如写文件会用一段exec函数,运行shell也是,甚至运行iab的操作也是一段nodejs代码。

这也是agent演进的一个方向,前年的时候llm模型上对工具有一波改造,就是对于先读文件搜索关键字,如果有就替换为xx,如果没有,就在最后一行加一行xx,这样的需求,最早的时候是运行多次工具:读文件过滤关键字,判断没有,在最后加xx,这里有两次工具调用,也就是有两次llm调用。而模型变强之后,变成了写一段shell脚本,先grep为空,就执行xxx,不为空就执行xxx,一个if else的语句,将两次工具调用转换为一次,大大降低token消耗。

同样的codex的演进也是这个方向,现在的codex的能力就像是一个nodejs runtime,这个runtime下内置了很多函数,llm可以直接写代码调用这些函数,还可以批量调用,穿插if else,for,while等等,将工具调用批量化,大大降低token消耗。

另外上图中,其实能看到tab=iab.tabs.new(),而chrome插件则是另一个变量,就是把iab换一下,在语法和能力上完全一致,就可以操作用户当前的浏览器,所以这里不做赘述了。

playwright-cli agent-browser等等,已经优化了cli,但终究是三方,codex将很多能力直接内置到codex node runtime了,效率更高,浑然天成,这显然就是最好的使用方式了。

而能力上其实大差不差,接入的形式上codex的iab和chrome插件,应该也是刚好够用,过于复杂的自动化则可以单独引入playwright

隔离性的思考

上面介绍的所有工具基本上,操作的函数都是不需要指定tabIdprocessId的,例如open baidu.com click xxx怎么能保证不会有另外一个agent正好open tabao.com导致前者的click点到了淘宝的页面了?

chromemcp作为stdio的mcp, 靠进程机制隔离,大多数的agentmcp在每个会话中是单独加载的,这也是为啥,经常发现codex指令运行后每次都有mcp初始化的一个等待时间。所以天然的是npx不同的进程,所以activ的page天然是不同的。当然这对于同一个会话中的多subagent之间可能还是有并发互相干扰的问题。这种情况下会遇到锁的问题,如下图,虽然不会出错,但不能支持并发。

image

playwright通过session隔离,不同的session是不同的chrome进程,天然隔离,不同的agent使用不同session名即可,这一点非常好。不过对于attach到Default的进程只有一个,不同agent如果都想操作Default的话怎么办?

首先对于插件来说是只能一个session来attach的,多个的话会卸载第一个,下面方法不可行。

# 这样是不行的!!!
playwright-cli attach --extension=chrome -s=my-chrome1
playwright-cli attach --extension=chrome -s=my-chrome2

然后是ws的

# 这样是可以的,两个session可以分别使用
playwright-cli attach --cdp=ws://127.0.0.1:9222/devtools/browser/79abd2f7-556a-40fc-aa3f-101ab14b804a -s=cdp1
playwright-cli attach --cdp=ws://127.0.0.1:9222/devtools/browser/79abd2f7-556a-40fc-aa3f-101ab14b804a -s=cdp2
playwright-cli -s=cdp1 open https://example.com
playwright-cli -s=cdp2 open https://www.baidu.com

所以playwright是可以完全避免不同会话并发问题的,尤其是Default profile也没问题。

browser-use同样还是sessionbroswer-use --session agent-1 <<'PY'

agent browser默认和playwright一样也有session的概念--session,一般用--session就能隔离,如果是Default的话,有两种方式

# 第一种和playwright一样,指定不同session就好了,后续也是--session agent1的去执行action
agent-browser --session agent1 --cdp 9222 open https://example.com --headed
agent-browser --session agent2 --cdp 9222 open https://example.com --headed

# 第二种方式,指定profile,但是本质是copy了一份原来的用户数据,并不是真的复用
agent-browser profiles # 应该有个Default就是自己的profile
agent-browser --session s1 --profile Default open https://gmail.com --headed
agent-browser --session s1 open https://www.baidu.com
agent-browser --session s2 --profile Default open https://gmail.com --headed
agent-browser --session s1 open https://www.taobao.com

ego lite则是和session类似的有一个space概念,前面介绍了,通过space可以将操作隔离开。

贴一个ego官网的对比图,我觉得对比的不太合理,多任务并发其他的应该也是可以的,ego是taskSpace标记不同任务在不同space中并发进行,但是其他的也可以通过session隔离各干个的,所以这里多任务的标记我没太理解是啥意思,然后独立工作区应该也是指space的隔离,我感觉session也是一样隔离的,只不过没有单独做成space那种ui形式,功能上应该是差不多的。然后就是无摩擦登录,这个也是很模棱两可的词,我觉得大概率指的是attach的时候是否需要用户点允许,感觉不是一个核心的竞争项。

image