「Agent随笔」01 为什么agent能操控Chrome
Chrome 给外部程序留了个操控自己的口子

缘起
使用 Claude Code 这类 agent 已经有半年多的时间了,早期使用也是慢慢摸索,更多的是以用促学。什么 MCP 啊,Skills 啊,也就一点点地知道是怎么回事了。现在想想,竟然只有半年多的时间啊,不可思议。
半年多后的现在,我安装的 Skills 已经有大几十个了,这还是中间删过一回。其实常用的 Skills 好像不超过10个,算上偶尔用用的应该也不超过20个。可有些 Skills 就是那种,你虽然从没用过,现在也不用,但你总感觉哪天会用上,于是总也删不掉。
而当下 Skills 的管理方式实在是混乱不堪,想把常用的和不怎么用的分开管理还得自己想办法,也包括 Skills 的更新问题,自建 Skills 的版本管理等,于是我就开始整理各种 Skills,然后打算自己写一个 Skills 的管理程序。
在这个过程中出于职业习惯,我也要进一步了解下各个 Skills 的底层原理,那就顺便把我学到的东西写出来吧。我也不知道能写多少,就看到哪写到哪吧。
首当其冲的就是浏览器相关的 Skills。我的 Claude Code 之前用的可能是 Playwright,但是也装过 chrome-devtools-mcp,所以我也不确定它实际用的是哪个,怎么就能操作我的浏览器了?借这个梳理 Skills 的机会,也一起搞明白吧。
CDP
浏览器的 Skills,就是帮助 agent 操纵浏览器,来完成各种自动化操作的 Skills。从 agent 到操纵浏览器,这中间还有好多环节,我主要用 Chrome,所以就拿 Chrome 来说明,其他浏览器应该也是类似的逻辑,我就不单独说了。
Agent 之所以能操纵 Chrome,是因为 Chrome 提供了让其它软件操纵自己的功能,叫做 remote debugging,也就是远程调试。
不过,因为 Chrome 是通过 Chrome DevTools Protocol 来暴露这个功能的,所以一般在讨论这个话题时,你更常见到的名词是它的简称 CDP。也就是说,包括我们今天要说的 agent,想要操作 Chrome 一般都是要通过 CDP 来操作的。
CDP 提供了两种传输通道,WebSocket 和 Pipe。外部程序就是通过这两种通道来向 Chrome 发送指令的。
WebSocket 是基于 HTTP 进行握手而建立的长连接,它会启用一个端口,所以你本地的任何程序都有可能通过这个端口来建立 WebSocket 连接,进而操作你的浏览器。如果你通过配置把端口暴露在网络上,那网络中其它节点也能操作你的浏览器。如今有很多云托管的浏览器就是这样使用的。
不过这两种通道都不是默认开启的,需要你在启动 Chrome 时附带参数,--remote-debugging-pipe 表示开启 Pipe,--remote-debugging-port={端口号} 表示开启 WebSocket。
考虑到自动化操作浏览器的安全问题,在 Chrome 136 之后的版本,使用这两个参数时必须同时使用--user-data-dir参数来指定一个非默认的用户数据目录,以避免你的个人数据被轻易泄露。
既然如此,那是不是意味着,你正在使用的 Chrome,也就是使用默认的用户数据目录的 Chrome,就不会被外部程序操控了呢?也不是。
在 Chrome 144 之后的版本,你可以在 Chrome 中打开 Chrome://inspect/#remote-debugging 这个地址,你会发现这里是可以手动开启 remote debugging 的,如下图。

既然使用默认的用户数据目录后,也可以再开启 remote debugging,那跟 Chrome 136之前版本那样,通过参数开启的有什么区别呢?其实,想要通过 WebSocket 连接 CDP,在连接之前是先要调用 CDP 的一些接口的,如/json/version来获取连接所必需的信息。
而上面说的这种在页面上后开启 remote debugging 的场景,它是不会提供这些接口的。这样就阻断了网络上其它节点的程序按照正常流程连接 CDP 的可能。
而如果你是在本地使用,那本地的程序是可以通过DevToolsActivePort这个文件来获取这些信息的,然后就可以连到你这个后开启 remote debugging 的 Chrome 上了。并且每次连接时,你的浏览器都会弹出一个确认提示,你必须点击「允许」,它才能操作你的 Chrome,这也是进一步的安全措施吧。

然后点击允许后,你还会在浏览器最上方还会看到一个横幅,提示你 Chrome 正在被操控。

所以,如果你正在使用的 Chrome 遇到什么问题了,需要 agent 当场接管来帮你处理,就可以通过上面这种方式进行。
而 Pipe 则是进程间的一种通信方式,当一个进程启动了 Chrome,Chrome 就成了这个父进程的子进程,它们之间便可以通过 Pipe 进行通信,所以 Pipe 通道适用于本地场景,但不适用于你正在使用的 Chrome,因为它的父进程是你的操作系统,它也没有开启 Pipe 通道。
Agent 浏览器工具
Agent 浏览器工具,这个名字有点儿长,后面我就简称它为 ABT(Agent Browser Tooling) 吧,我自己起的名字哈,业内似乎还没有一个统一的名称。
那我们的 agent 是如何连接到 CDP 来操纵浏览器的呢?它自己是没有连接能力的,它就是通过各种 ABT 来连接的。那 agent 又是如何使用 ABT 的呢,就是通过 MCP 和 CLI。现在的 agent 应该都支持 MCP 了,而 CLI 一般是搭配 Skills 来使用,Skills 里会说明 CLI 的具体使用方法。
也就是说,agent 首先要接入 ABT 提供的 MCP 或 CLI,通过 MCP 或 CLI 调用 ABT,ABT 再连接到 CDP,才能实现对浏览器的操控。
ABT 也是通过前面说的 CDP 的两种通道来操作浏览器的。大多数情况下 ABT 会启动一个 daemon,然后由它来启动 Chrome 子进程,再通过 Pipe 或 WebSocket 连接 CDP 来操作浏览器。
MCP server 就相当于我说的 daemon,它会创建 Chrome 子进程。很多 CLI 也是如此,虽然看上去是一个个独立的命令,当首次执行时会启动一个 daemon,由 daemon 来创建 Chrome 子进程,然后每个命令也只是在给自己的 daemon 发消息,连 CDP 的还是持续运行的 daemon。
不过当使用 WebSocket 时,Chrome 不一定是子进程,也可以是已经启动的或云端托管的 Chrome,所以理论上可以不需要一个持续运行的 daemon,而只需要一个临时进程,操作浏览器时就启动进程连接 CDP,操作完就可以结束进程。有些 CLI 是可以这样使用的。
我这次梳理的 ABT 有 Playwright、Chrome-Devtools、Browser Use 和 agent-browser,这几个是我经常在网上看到的 ABT。它们也分别提供了 MCP 和 Skills + CLI 两种接入方式,咱们下篇文章就先看看共性的部分,看看这两种方式要如何选择吧。也讲讲究竟什么是 MCP,什么是 Skills。