数据STUDIO

又一个 AI 浏览器自动化神器!

Image

全文速览

  • browser-use不是又一个Selenium封装——你描述目标,LLM自己决定每一步怎么操控浏览器。v0.13最大变化:扔掉万行DOM处理代码,换成600行CDP直连的Browser Harness,因为LLM本来就会CDP协议。
  • 最意外的涌现行为:Agent发现缺upload_file函数→读helpers.py源码→写DOM.setFileInputFiles实现→保存→继续任务。不是预设容错,是LLM+薄抽象层的自然结果。
  • 该用:复杂多步操作、页面结构不确定、需适应改版的场景。不该用:简单爬虫、低延迟要求、成本敏感的批量任务。薄抽象不是银弹,是方向盘——只在你需要判断时才有优势。
Image
Image

0155k Star的浏览器Agent

如果你让一个AI帮你"从GitHub下载browser-use的README,截个图保存到桌面",传统做法是什么?你得先写个Python脚本:driver.find_element(...)找按钮、time.sleep(2)等加载、处理各种弹窗、调试选择器……折腾半小时,只为完成一个5秒就能手工干完的活。

browser-use做的事很简单:你给它一句话,它自己操控浏览器去完成。

Image

从2024年开源到现在,103k+ Star、318位贡献者、2600+项目依赖它。6月刚发的v0.13.2,用Rust重写了核心运行时,同时推出了一个叫Browser Harness的东西——只有约600行代码。

这个项目在PyPI上已经迭代了129个版本,最新版安装只需要一行:

uv add "browser-use[core]"

然后5行代码就能跑起来:

from browser_use.beta import Agent, BrowserProfile, ChatBrowserUse
import asyncio

async def main():
    agent = Agent(
        task="Find the number of stars of the browser-use repo",
        llm=ChatBrowserUse(),
        browser_profile=BrowserProfile(
            headless=False,
            allowed_domains=["*.github.com"],
        ),
    )
    history = await agent.run()
print(history.final_result())

asyncio.run(main())

02它怎么就不是又一个Selenium封装?

第一眼看browser-use,"这不就是Playwright上面套了个LLM调用吗?"

但55k人Star它不是因为它"封装得好"。是因为它解决了一个真实到令人发指的痛点:写浏览器自动化脚本太烦了。

Image

用过Selenium或Playwright的人都知道这个循环:打开DevTools → 找元素选择器 → 写find_element → 跑一下 → 报错(元素找不到/页面还没加载/iframe挡住了/Shadow DOM进不去)→ 加time.sleep和WebDriverWait → 再跑 → 又报错 → 调试到怀疑人生。

你花90%的时间不是在实现业务逻辑,而是在跟网页的DOM结构斗智斗勇。更致命的是——页面一改版,你的脚本就挂了。XPath变了,class名改了,元素层级调整了——每个都是定时炸弹。

browser-use 的底层洞察是:LLM不需要你帮它找选择器。 它在训练数据里已经见过几百万字的网页结构、HTML语义、常见的按钮文案和表单逻辑。你说"点击搜索按钮",它自己知道去找什么。传统工具的抽象层不是帮助,是障碍。

这不是Selenium的升级版。是范式切换——从"人类写代码操控浏览器"变成"人类描述目标,AI自己决定每一步怎么操控浏览器"。你不再是程序员,你是任务描述者。

03扔掉一万行代码,换六百行CDP直连

如果你去看browser-use v0.12和之前的版本,你会看到一个"正常"的开源项目——上万行Python代码,包含DOM元素提取器、元素索引器、点击包装器、目标管理器、看门狗、跨域iframe处理器……每一行都是为了解决"网页太复杂,LLM搞不定"这个问题。

然后v0.13来了。他们把这一万行全扔了。换成了一个只有约600行的东西——Browser Harness。

Image

这四文件分别是:

文件
行数
干什么的
run.py
~13行
入口点。预加载helpers,执行用户代码。零配置。
helpers.py
~192行
一组薄薄的CDP包装函数:goto_url()、click_at_xy()、type_text()、capture_screenshot()、js()、new_tab()……可以在运行时被Agent编辑。
daemon.py
~220行
维护CDP WebSocket长连接。处理崩溃检测、重连、多实例命名空间(Unix Domain Socket /tmp/bu-<NAME>.sock)。
SKILL.md
—
LLM的运行时指令:怎么用helpers、优先坐标点击而非选择器、跨域iframe怎么处理……

为什么这个改动是"架构转向"而不是"代码精简"?核心洞察就一句话——LLM already knows CDP。

Chrome DevTools Protocol是Chrome自己的调试协议。Page.navigate、DOM.querySelector、Runtime.evaluate、Input.dispatchMouseEvent——这些命令名、参数格式、调用方式,LLM在训练数据里已经见过几百万字了。你不需要再给它包装一层Playwright的page.click(selector)。它"本来就会"操作CDP。你给它的抽象层越厚,它的"原生能力"被遮蔽得越多。

这种"薄抽象"还有一个额外的好处:坐标点击天然穿透iframe和Shadow DOM。 传统工具里,点击iframe里的按钮要手动switch_to.frame(),遇到Shadow DOM要shadow_root.querySelector(),跨域iframe直接是死胡同。但CDP的Input.dispatchMouseEvent在浏览器的合成器层(compositor layer)执行坐标点击——根本不关心目标元素在哪个frame、哪个shadow root、哪个跨域边界里。点就完了。

04Agent怎么把自己修好的?

如果上面那段"架构转向"听起来还像PPT——下面这个故事会让你感觉到这东西到底有多不一样。

browser-use团队在开发Browser Harness的时候,有一天发现了一个git diff:

# 这个函数不在原始代码里。
# Agent在执行任务时自己写的。

def upload_file(file_path):
"""Upload a file using CDP DOM.setFileInputFiles"""
# Agent读了helpers.py,发现没有upload_file
# 然后它写了这个实现
    ...

完整过程是这样的:

  1. Agent在执行一个"上传文件到XX网站"的任务
  2. 它扫描了helpers.py里的可用函数——click_at_xy、type_text、capture_screenshot、scroll……
  3. 发现没有upload_file
  4. 它没有报错,没有卡住,没有输出"抱歉我做不到"
  5. 它读了helpers.py的代码风格
  6. 写了一个使用DOM.setFileInputFiles的upload_file()函数
  7. 保存到helpers.py
  8. 调用它
  9. 继续执行任务

团队是在review git diff的时候才发现这段代码的。

还有一次,一个12MB的文件超过了CDP WebSocket的消息大小限制(约10MB)。Agent的输出不是"文件太大传不了"——它重写了上传逻辑,改成了分块传输。

这些不是预编程的容错机制。没有人在代码里写"如果缺upload_file就生成一个"。这是LLM + 薄抽象层的涌现行为——当Agent可以直接看到和修改它自己的工具代码时,它的"调试能力"被释放了出来。

Image

注意:以上两个案例均来自browser-use团队官方博客自述,是单一案例而非系统验证。不是说"每个Agent都会自愈"——而是说这种架构设计让自愈变成了可能。

05Agent循环操作背后的全部步骤

说完了架构和案例,来看看Agent到底是怎么工作的。

browser-use的核心是一个四步循环:

Image

每一步拆开来看:

1. Observe(观察):Agent获取当前浏览器状态。默认模式是截图+页面信息。use_vision参数控制行为:auto(只在需要时截图)、true(每步都截图,最准但token消耗大)、false(只看DOM文本,最快但可能漏信息)。另外可以用page_extraction_llm指定一个小模型专门做页面文本提取,省token。

2. Decide(决策):LLM根据当前状态、任务目标、历史动作记录,决定下一步做什么。可以一次输出最多3个动作(max_actions_per_step默认3),比如填表单时一次填3个字段。动作类型覆盖导航(搜索/打开/后退)、页面交互(点击/输入/滚动/按键)、JS执行、标签页管理、内容提取、文件操作、下拉菜单、截图、宣布任务完成。

3. Act(执行):Browser Harness通过CDP执行动作。坐标点击用Input.dispatchMouseEvent在合成器层完成——这句话翻译成人话就是:不管目标元素藏得多深,点得上去。 动作一个接一个执行,"直到页面发生变化"才进入下一步观察。

4. Verify(验证):截图确认结果。paint_order_filtering做DOM树优化——把被其他元素遮挡的节点移除,避免Agent"看到"不存在的东西。

有人会问:每步都要调一次LLM,是不是很慢很贵?是的——一个30步的任务可能耗时2-5分钟,token消耗也不是小数目。所以模型选择很关键。官方给出的benchmark数据:

模型
WebVoyager成功率
备注
ChatBrowserUse (bu-ultra)
78.0%
云端最强,速度快3-5倍
OSS + BU LLM 混合
63.3%
开源模型+云端模型
Claude Opus 4.6
62.0%
—
Gemini 3.1 Pro
59.3%
—
Claude Sonnet 4.6
59.0%
—
GPT-5
52.4%
—
GPT-5 Mini
37.0%
—

差距很大。或者说,14个百分点在复杂任务里就是"一次成功 vs 多次重试"的区别。

06什么时候该用,什么时候不该折腾?

坦率地说,browser-use不是银弹。

Image

该用的场景:

  • 复杂多步Web操作:跨页面填表、提交审批、数据提取+整理
  • 页面结构不确定的任务:你不知道目标网站长什么样,LLM去探索
  • 需要适应页面变化:网站经常改版,维护选择器脚本成本太高
  • 探索性调研:竞品网站信息搜集、价格监控、内容聚合

不该用的场景:

  • 简单爬虫或固定表单提交——传统Playwright脚本更快、更便宜、结果100%确定
  • 低延迟要求的场景——每步LLM调用1-5秒,实时交互不现实
  • 成本敏感的批量任务——如果你要跑10万次同样的操作,写一个稳定脚本跑一次的成本远低于Agent跑10万次

开源还是云端:

方面
开源版
云端版 (ChatBrowserUse)
成功率
取决于你用的LLM
bu-ultra: 78%,目前最高
隐蔽性
需要自己配代理
内置代理轮换 + 反检测
验证码
需要第三方集成
内置处理能力
集成
自己开发
1000+ 预置集成(Gmail/Slack/Notion等)
扩展性
自己管理浏览器实例
自动扩展
价格
只付LLM API费
输入$0.20/百万token,输出$2.00/百万token

如果你是个人开发者想试试,开源版足够你跑通大多数自动化任务。如果你要给客户交付一个生产级服务——云端版省掉的反检测、验证码、扩展性维护成本可能值回票价。

最后,这里是官方Quickstart改编的一个完整示例——帮你理解一个实际任务从头到尾长什么样:

from browser_use.beta import Agent, BrowserProfile, ChatBrowserUse
import asyncio

async def main():
# 创建一个Agent:告诉它任务、给它模型、配置浏览器
    agent = Agent(
        task="打开GitHub trending页面,找到今天Star最多的Python项目,把项目名和Star数提取出来",
        llm=ChatBrowserUse(),
        browser_profile=BrowserProfile(
            headless=False,           # 能看到浏览器操作过程
            allowed_domains=["*.github.com"],  # 只允许访问GitHub
        ),
    )

# 跑!
    history = await agent.run()

# 打印最终结果
print(history.final_result())

# 还可以看每一步的详细信息
for step in history.steps():
print(f"Step {step.step_num}: {step.action_name}")
# step.screenshot 有每一步的截图
# step.model_output 有LLM的完整输出

asyncio.run(main())

这个Agent实际执行时会经过:导航到GitHub Trending → 滚动找Python标签 → 点进去 → 提取第一个项目信息 → 返回结果。大约5-8步,取决于页面加载速度和模型决策。

07browser-use里的代码工程美学

Image

browser-use真正的价值比一个"好用的浏览器Agent工具"更大。

它的设计选择相当于做了一个实验——实验结果挑战了AI工具设计领域一个常见的假设。

那个假设是:更好的AI工具 = 更完善的API封装。 LLM能力有限?给它封装更多helper函数。网页太复杂?给它做DOM预处理。可能出错?给它加错误处理和重试。每多一层封装,LLM就少犯一点错。

browser-use的实验结果指向相反方向:在browser-use这个案例范围内,抽象层越薄,LLM表现越好。

为什么?因为LLM的训练数据里有海量的CDP协议文档。Page.navigate怎么用、Input.dispatchMouseEvent的参数格式是什么——它"本来就会"。你给它Playwright的page.click(selector),它反而要经过一道"翻译":这行Playwright代码对应的是哪个CDP命令来着?参数怎么映射?多了一道翻译,就多了一出错的可能。

Browser Harness的600行代码之所以能替代一万行,不是因为它"更聪明"。是因为它信任LLM的能力。它把LLM当作一个已经懂浏览器协议的工程师——你不需要替它找选择器、不需要替它封装命令、不需要替它处理异常。你给它最原始的操控权,它自己知道怎么做。

那个"Agent自己写upload_file"的案例,就是这种信任的最好证明。你不是在给LLM建一个游乐场——你是在给它一个方向盘。

Image

当然,方向盘哲学不是万能的。简单、重复、确定性的任务,传统脚本仍然更可靠、更便宜、更可预测。但如果你面对的任务多少需要一点"判断"——这个按钮是不是我要点的那个、这个表单应该先填哪一栏、数据提取出来应该怎么归类——那么薄抽象层会越来越有优势。

因为当LLM越来越强时,你不需要预测它需要什么。它自己会告诉你。

Image