本地双语复刻页 · 非官方 · 仿 browser-use.com 原版风格重建 · 译文:claude-fable-5 · effort xhigh · 260723 ·
英文原文 · 本文是你机器上 browser-use CLI 3.0 的"出生证明"
← All Posts / 目录
Engineering · April 19, 2026(260419)
The Bitter Lesson of Agent Harnesses
智能体 Harness 的苦涩教训
Don't wrap the LLM. Don't wrap its tools either.
别包装大模型。也别包装它的工具。
Gregor Zunic · Co-founder, CTO(联合创始人 & CTO)
术语 · harness 本义是"马具/挽具"——套在马身上让人驾驭马的那套皮带,引申为"让模型能驾驭外部世界的那层装置":连接、辅助函数、说明书的总和。上一篇说"别造框架",这一篇更进一步:连 harness 里的辅助函数都别多造。
A SKILL.md and some Python helpers. The LLM has complete freedom. If something's missing, it writes it.
一份 SKILL.md,加几个 Python 辅助函数。大模型拥有完全的自由。缺什么,它自己写。
The learning / 学到的东西
A few months ago we wrote The Bitter Lesson of Agent Frameworks. The argument: don't wrap the LLM in abstractions. Maximal action space, then restrict.
几个月前我们写了《智能体框架的苦涩教训》。论点:别用抽象包裹大模型。先给最大动作空间,再限制。
We were still wrapping its tools.
可我们当时还在包装它的工具。
Every click(), type(), scroll() helper is an abstraction you decided the model needs. Every one of them is a constraint the RL'd model has to fight around.
每一个 click()、type()、scroll() 辅助函数,都是你替模型决定"它需要这个"的抽象。每一个都是经过强化学习的模型不得不绕着打的枷锁。
Why raw CDP / 为什么用裸 CDP
When we built the first version of Browser Use, we shipped thousands of lines of element extractors, DOM indexers, click wrappers.
造第一版 Browser Use 时,我们写了几千行元素提取器、DOM 索引器、点击包装器。
LLMs know CDP. They were trained on millions of tokens of Page.navigate, DOM.querySelector, Runtime.evaluate.
可是大模型认识 CDP。它们在数百万 token 的 Page.navigate、DOM.querySelector、Runtime.evaluate 代码上训练过。
CDP is the lowest level Chrome exposes. Give it directly to the model: Cross-origin iframes — attach to the target directly, no frame abstraction to fight. Shadow DOM — walk shadowRoot.querySelectorAll like the model has seen ten thousand times. Anti-bot injection — it's Chrome talking to itself.
CDP 是 Chrome 对外暴露的最底层。直接把它交给模型:跨域 iframe——直接挂到目标上,没有中间层的"框架抽象"要搏斗;Shadow DOM(网页组件的"暗盒"结构)——像模型见过一万次的那样遍历 shadowRoot.querySelectorAll;反机器人检测——这是 Chrome 在和自己说话,网站分辨不出来。
解读 · 这三条是浏览器自动化的三大老大难。传统工具(Playwright/Selenium)为它们各造一套解决方案;CDP 直控的答案是:这些难题在底层根本不存在,是包装层自己制造的。
What we got wrong / 我们错在哪
A few months ago on this blog we wrote Closer to the Metal: Leaving Playwright for CDP. The conclusion of that post: "Our agents shouldn't have to know the nuances of CDP Targets in order to Get Stuff Done." Turns out we were wrong.
几个月前我们还在博客写过《贴近金属:离开 Playwright 拥抱 CDP》,当时的结论是:"我们的 agent 不应该为了干活而去了解 CDP Target 的细枝末节。"事实证明,我们错了。
That post listed ten ways a Chrome tab can crash. We built watchdog services to catch each one — tab crashes, target detach, renderer OOM, zygote death, GPU process crash. Each got a handler. Each handler had to be kept in sync with Chrome's internals.
那篇文章列了 Chrome 标签页崩溃的十种方式。我们为每一种造了看门狗服务——标签崩溃、目标脱离、渲染进程内存耗尽、zygote 进程死亡、GPU 进程崩溃。每种配一个处理器,每个处理器都得跟着 Chrome 内部实现的变动持续维护。
Give the LLM direct CDP access and the ability to edit its own harness, and it handles all of that itself. Pages dying, targets wrongly attached, Chrome stalling — the agent reads the error, reattaches to a fresh target, retries. It doesn't need a watchdog. It's read ten thousand threads about Chrome crashes. It already knows what to do.
给大模型直接的 CDP 访问权,再给它编辑自己 harness 的能力,这一切它自己就能搞定。页面死掉、目标挂错、Chrome 卡死——agent 读报错,重新挂上新目标,重试。它不需要看门狗。它读过一万个关于 Chrome 崩溃的论坛帖子,早就知道该怎么办。
The "complexities of CDP" we were trying to hide weren't something to hide. They were something to let the model see.
我们想藏起来的"CDP 的复杂性",根本不该藏,而应该让模型看见。
Four files / 四个文件
That's the whole harness:
整个 harness 就这些:
run.py(13 行)——跑普通 Python,辅助函数预先加载好
helpers.py(192 行)——CDP 的薄包装,而且 agent 可以改它
daemon.py(220 行)——维持 CDP websocket 连接不断线
SKILL.md——告诉 agent 上面这些怎么用
~600 lines total. The agent writes Python. The Python imports helpers. The helpers speak CDP. Chrome does what it's told. Everything above Chrome is rewriteable.
总共约 600 行。agent 写 Python,Python 导入辅助函数,辅助函数说 CDP,Chrome 照办。Chrome 之上的一切都是可重写的。
The self-heal loop / 自愈循环
Here's what happens when a tool is missing. When a helper is missing, the agent does what any Claude Code user would do: greps helpers.py, adds the function, reruns.
缺工具时会发生什么:缺一个辅助函数时,agent 做的事和任何 Claude Code 用户一样——grep 一下 helpers.py,把缺的函数加上,重跑。
We didn't tell it to do this. We gave it Claude Code's normal Read/Edit/Write plus CDP access. Coding agents already know how to fix a missing import.
我们没教过它这么做。我们只是给了它 Claude Code 常规的读/改/写能力,外加 CDP 访问权。编程 agent 本来就知道怎么修一个缺失的导入。
The key: the agent isn't writing new code from first principles. It's writing the one function that was missing, the same way it'd fix a missing import on any codebase.
关键在于:agent 不是从第一性原理凭空造新系统,它只是补上那一个缺失的函数——就像在任何代码库里修一个缺失的 import 一样。
Magical moments / 神奇时刻
Upload. We forgot to add upload_file(). Mid-task, the agent hit a file input, grepped helpers.py, saw nothing, wrote the function using raw DOM.setFileInputFiles, and uploaded the file. We found out when we read the git diff.
上传。我们忘了写 upload_file()。任务进行到一半,agent 撞上一个文件上传框,grep 了 helpers.py 发现没有,当场用底层的 DOM.setFileInputFiles 写了这个函数,把文件传了上去。我们是事后看 git diff 才发现的。
Chunked upload. After writing upload_file, the agent tried to upload a 12MB file. CDP websocket payloads cap around 10MB. It hit the limit, read the error, switched to a chunked upload pattern.
分块上传。写完 upload_file 之后,agent 尝试上传一个 12MB 的文件。CDP websocket 单次载荷上限约 10MB。它撞上限制,读了报错,自己改成分块上传的方案。
Gusto to calendar. Task: put every employee's birthday in our shared calendar. Required navigating Gusto's employee tab, extracting dates from the DOM, then creating Google Calendar events.
Gusto 到日历。任务:把每个员工的生日录入共享日历。需要在 Gusto(人事系统)的员工页导航、从 DOM 抽出日期、再逐个创建 Google 日历事件。
Azure admin roles. Azure's admin portal is a pile of blades inside iframes. Raw CDP, via coordinate-level Input.dispatchMouseEvent, passes through at the compositor level.
Azure 管理员角色。Azure 的管理后台是一堆嵌在 iframe 里的面板。裸 CDP 通过坐标级的 Input.dispatchMouseEvent,在合成器(compositor)层直接穿透点击。
Try it / 试试
Setup prompt for Claude Code or Codex: Set up https://github.com/browser-use/browser-harness for me.
给 Claude Code 或 Codex 的安装提示词就一句:"Set up https://github.com/browser-use/browser-harness for me."
First person to find a task it fails on (not captcha/2FA) gets a Mac Mini. Seriously. I've been trying to break it for a week and can't.
第一个找到它搞不定的任务(验证码/两步验证除外)的人,奖一台 Mac Mini。认真的。我自己试着弄坏它一个星期了,没成功。
The bitter lesson of agent harnesses: your helpers are abstractions too. Delete them. Let the agent write what it needs.
智能体 harness 的苦涩教训:你的辅助函数也是抽象。删掉它们。让 agent 自己写它需要的。