它缺了一个函数,Agent自己写了三行补上:592行代码背后的CDP直连实验
2025 年 Manus 爆火,2026 年 AI Agent 应用遍地开花,好些人都想让 AI 帮自己操作浏览器。现实是当你拿起 Playwright,要先学选择器、等元素、处理异常;换成 Puppeteer,API 是一层套一层;Selenium 更不用说,光配环境就能耗掉一个下午。更扎心的是,等你终于把脚本跑通了,网站改了个 UI,选择器全废。
就在大家习惯了框架越强越好的时候,browser-use 团队做了一个反直觉的实验:把所有框架层都拆掉,只留一根 WebSocket 直连 Chrome,让 AI 自己决定怎么操作。结果就是开源了 Browser Harness,只有 592 行 Python,6 天斩获 5.1K Star。
极简上手
先安装工具:
uv tool install browser-harness
uv 是一个 Python 包管理器,比 pip 快很多。然后要让 Chrome 允许外部连接,打开 Chrome,地址栏输入 chrome://inspect/#remote-debugging,勾选远程调试即可。这个设置跟 Chrome 配置文件绑定,勾一次以后每次启动都自动生效。
现在可以跑第一个任务了:
browser-harness <<'PY'
new_tab("https://github.com")
wait_for_load()
print(page_info())
PY
按回车等几秒钟,如果一切正常,你会看到 Chrome 自动打开一个新标签页跳到 GitHub,终端输出一串页面信息——URL、标题、视口大小、滚动位置。
这几行代码在做什么?new_tab() 打开新标签页,wait_for_load() 等页面加载完,page_info() 打印信息。没有选择器,没有等待元素可见,没有异常处理,就是"打开、等、看"三步。这就是 Browser Harness 的风格——极简到近乎粗暴,但也正因为简单,你一眼就能看懂它在干什么。
叫 harness 不叫 framework
这个项目叫 harness,不叫 framework。Framework 是框架——它替你做决策,你只能在它画好的框里行动;Harness 是挽具——它只负责把你和浏览器连上,怎么跑是你的事。Browser Harness 就是一根 WebSocket 连上 Chrome,剩下的全交给 Agent 自己判断怎么搞。
不封装,直接暴露 CDP
这里要提到 CDP——Chrome DevTools Protocol。你打开 Chrome 的开发者工具,看到的元素审查、网络请求、控制台,底层都是通过 CDP 通信的。Playwright、Puppeteer 这些框架,本质上也是在 CDP 上面包了一层 API。
问题就出在这层包装上。Playwright 的 click() 只暴露了 3 个参数,但 CDP 的 Input.dispatchMouseEvent 有 14 个参数——button、clickCount、modifiers、pointerType、force、tangentialPressure 等。对写脚本的人来说 3 个参数够用了,但对 AI Agent 来说,你封装得越多它反而越受限。模拟拖拽中间状态?处理触摸屏事件?发送特定按键组合?这些 Playwright 做不到的事,CDP 原生就支持,只是那层封装把路堵死了。
Browser Harness 的做法很简单:不包。一个 cdp() 函数,直接调任意 CDP 方法,Chrome 支持什么 Agent 就能用什么,不用等框架更新。来看 helpers.py 里 click() 的真实实现,就两行:
cdp("Input.dispatchMouseEvent", type="mousePressed", x=x, y=y, button=button, clickCount=clicks)
cdp("Input.dispatchMouseEvent", type="mouseReleased", x=x, y=y, button=button, clickCount=clicks)
没有等待元素可见,没有自动滚动,没有异常处理,这些"智能"封装全被砍掉了——Agent 自己决定要不要等、要不要滚、要不要重试。
自愈式设计:Agent 自己写代码
砍掉封装之后,缺了功能怎么办?这是整个项目最出圈的点。作者 Gregor 在 Hacker News 上亲口说的,他在跑任务时忘了实现 upload_file() 函数,Agent 发现缺了这个功能,直接编辑 helpers.py,自己写了一个文件上传函数,然后继续把文件上传了。Gregor 是事后看 git diff 才发现的。
来看 Agent 自己写的代码:
def upload_file(selector, path):
"""Set files on a file input via CDP DOM.setFileInputFiles."""
doc = cdp("DOM.getDocument", depth=-1)
nid = cdp("DOM.querySelector", nodeId=doc["root"]["nodeId"], selector=selector)["nodeId"]
cdp("DOM.setFileInputFiles", files=[path] if isinstance(path, str) else list(path), nodeId=nid)
仅仅三行代码,直接调 CDP 的 DOM.setFileInputFiles,绕过了文件选择对话框。Agent 读 CDP 文档、写 Python 函数、继续任务,一气呵成。这不是简单的"缺啥补啥",而是一种根本性的架构选择:不替 Agent 做决策,让它自己发现和解决问题。
知识用 Markdown 传,不用代码绑
项目里有两套技能文件。第一个 interaction-skills 是通用交互技巧,里面有 16 个 Markdown 文件,覆盖对话框、Shadow DOM、拖拽、iframe、Cookie 等常见处理技巧。假如 Agent 发现需要处理 Shadow DOM,它直接读 interaction-skills 里的 Markdown 文档,自己组合 CDP 调用。
另外一个 domain-skills 是站点特定知识,里面有多达 50 多个文件,覆盖了 GitHub、LinkedIn、Amazon、TikTok 等热门网站。遇到没见过的网站怎么办?它会截图、观察、尝试,成功了就把经验记到 domain-skills 里,下次直接用,也就是越用越智能。
这些技能并不是写死的脚本,是一份文档,作用是告诉 Agent"碰到这类问题可以这么处理",而不是"你必须调用这个函数"。更有意思的是这些领域技能还是 Agent 自己生成的,官方也说了别自己手写技能文件,只有 Agent 自己生成的才最贴合浏览器里的真实场景。
把这三点设计放一起看思路就清晰了:连接给自由度,知识给方向,扩展给能力。
局限
安全性是要考虑的问题——Agent 能自己写代码、自己执行,效率高了,但如果它写了不该写的代码呢?目前的做法是遇到登录页面就停下来问用户,但这只是最基础的防护,更复杂的安全边界还没有。其次是可靠性,Agent 自己写的代码质量怎么保证?它可能写出一个能跑但很脆弱的函数。还有一个更根本的问题,这种设计能规模化吗?一个人跑自己的任务自愈机制很好用,但如果 100 个 Agent 同时跑,helpers.py 被并发编辑怎么办?
Browser Harness 用 592 行代码做了一个思想实验:框架越薄,Agent 才越强。
项目基于 MIT 协议开放。
项目地址:https://github.com/browser-use/browser-harness