什么时候别用 browser-use:浏览器 Agent 的三道判断题

2026-07-30

本文基于 browser-use 仓库 commit f0aa3a8(2026-07-27)梳理,该项目仍在高频迭代,具体行为以仓库 https://github.com/browser-use/browser-use 最新代码与文档为准。

浏览器 Agent 是兜底手段,不是首选手段。 一件事你之所以要让模型去点页面,通常是因为前面两条路都走不通:拿不到接口,流程也不稳定到能写死。如果这两条里有一条走得通,上 browser-use 只会让你多付一份模型钱、多担一份不确定性。这篇要给的就是这套判断顺序,以及顺序走到最后一档时,你该知道的代价。

顺便说清本篇和站内几篇的分工:硬编码流程与交给模型的取舍讲的是通用的取舍原则,状态机式编排与自由 Agent 的对比讲控制结构,Agent 成本失控的成因讲钱怎么烧掉的;本篇不重复那些通用结论,只落在 browser-use 这个具体项目上,看它的代码结构给你留了哪些档位、哪些事它明确不管。

一、这个项目到底在解决什么问题

仓库根目录的 AGENTS.md 开头一句话说得很直白:它接收一个用户定义的任务,通过 CDP(Chrome DevTools Protocol)驱动 Chromium 浏览网页,处理 HTML,然后反复查询语言模型来决定下一步动作,直到任务完成。

这句话里有三个信息值得你停一下。

一是驱动层用的是 CDP,不是别的浏览器自动化协议栈。这决定了它连的是 Chromium 系浏览器,也决定了你能通过 cdp_url 参数接到一个已经在跑的浏览器实例上。

二是”处理 HTML”。页面要变成模型能读的东西,这一步的开销是实打实的:一个现代页面的 DOM 展开后不小,每一步都要送进模型一次。

三是”反复查询”。这是整个方案的成本结构和不确定性来源——步数不是常量,同一个任务今天三步、明天七步,页面改版一次就可能变成十五步或者原地打转。

所以它解决的是这么一类问题:目标网站没有可用接口,页面结构又不稳定到能写选择器,而任务本身需要临场判断。 只有同时满足这几条,模型逐步决策才是在赚钱而不是在花钱。

二、第一道判断题:有接口就别点页面

这条听起来是废话,但实践里被跳过的频率很高——因为 Agent 版本看起来”五行代码就跑起来了”。examples/getting_started/01_basic_search.py 确实就这么短:

from browser_use import Agent, ChatBrowserUse

async def main():
	llm = ChatBrowserUse(model='bu-2-0')
	task = "Search Google for 'what is browser automation' and tell me the top 3 results"
	agent = Agent(task=task, llm=llm)
	await agent.run()

看着比自己写请求省事。但这五行背后是:启动一个真实浏览器、渲染页面、每一步把页面状态和历史送给模型、模型返回动作、执行、再来一轮。同样一件事如果目标方给了接口,你付出的是一次网络请求、一个确定的 JSON 结构、可预期的失败模式。

判断标准可以粗暴一点:

  • 目标数据有公开 API、开放数据集、RSS、导出功能中的任意一种 → 不要上浏览器 Agent。
  • 目标页面的数据其实来自一个前端能看到的 XHR 接口,且取用方式在对方允许的范围内 → 优先走这条路,失败模式比模拟点击清楚得多,也不必维护选择器。
  • 只有内网系统/老旧后台/第三方平台既没接口也不给导出 → 才进入第二道判断。

值得强调的是,接口路线还有一个隐性优势:它不需要你带着登录态去驱动一个真浏览器。这一点在后面讲边界时会再回来。

三、第二道判断题:结构稳定的流程,写死更划算

假设没有接口。下一个问题是:这个流程每次跑的路径是不是一样的?

如果一样,你要的是”回放”,不是”决策”。browser-use 在这一档给了两样东西。

一是 initial_actions,在主任务开始前先跑一串不经过模型的动作。examples/features/initial_actions.py 里就是一段纯配置:

initial_actions = [
	{'navigate': {'url': 'https://www.google.com', 'new_tab': True}},
	{'navigate': {'url': 'https://en.wikipedia.org/wiki/Randomness', 'new_tab': True}},
]
agent = Agent(
	task='What theories are displayed on the page?',
	initial_actions=initial_actions,
	llm=llm,
)

凡是路径确定的前置步骤——打开哪几个页面、切到哪个标签——都该塞进这里,省下的是实打实的模型调用次数。

二是历史回放。examples/features/rerun_history.py 展示的链路更有意思:先正常跑一遍并落盘历史,再从历史里检测出可替换的变量,最后带着新值重放。

agent = Agent(task=task, llm=llm, max_actions_per_step=1)
await agent.run(max_steps=10)
agent.save_history(history_file)

variables = agent.detect_variables()

results = await substitute_agent.load_and_rerun(
	history_file,
	variables=new_values,
	max_step_interval=20,
	delay_between_actions=1,
)

这套用法的价值在于把”探索”和”执行”分成了两个阶段:探索阶段付一次模型钱,把成功路径固化成一份历史;之后每天跑的是回放,成本和步数都可控。该文件的说明里也点明,回放时 extract 这类动作仍会重新调模型分析当前页面内容,因为页面可能已经变了——也就是说这不是纯粹的机械重播,而是”骨架固定、取数环节仍留一点弹性”。

这一档适合的场景是:每天同一个后台、同一张表单、只有填进去的值不同。相关的工程做法可以参考执行轨迹的复现与回放

但要如实说清它的脆弱点:历史回放绑定的是当时那条路径。目标站点改版、A/B 分流命中不同版本、弹出一个新的同意弹窗,回放就会在某一步落空。你必须给回放配失败告警,而不是当成定时任务扔进 cron 就不管了。

四、第三道判断题:actor 层是那个被忽略的中间档

很多人的认知里只有两档:要么自己写脚本,要么把整件事交给 Agent。browser-use 的 browser_use/actor/ 提供了第三档,它的 README 第一句自我定位是”构建在 CDP 之上的底层浏览器自动化能力”。

这一层暴露出来的是 PageElementMouse 这几个类,方法都是确定式的:gotoreloadget_elements_by_css_selectorclickfillhovercheckselect_optiondrag_topressscreenshotget_urlget_title。你写代码控制每一步,模型完全不参与。

关键在于,这一层还留了两个”只在这一步问模型”的口子:get_element_by_prompt(用自然语言描述找元素,找不到返回 None)和它的强制版本 must_get_element_by_prompt,以及 extract_content(按你给的 Pydantic 模型从当前页面抽结构化数据)。README 里那段 extract_content 的用法就是配一个 BaseModel 子类,把页面上的商品名、价格、描述抽成对象。

browser_use/actor/playground/mixed_automation.py 演示的正是这种混合写法——流程你自己控,只有”找到那个输入框”这一步交给模型:

browser = Browser(keep_alive=True)
await browser.start()

page = await browser.get_current_page() or await browser.new_page()

await page.goto('https://browser-use.github.io/stress-tests/challenges/angularjs-form.html')

element = await page.get_element_by_prompt('zip code input', llm)

if element:
	await element.click()

这个档位往往是性价比最高的落点:流程的绝大部分步骤是确定的,只有个别定位或抽取环节需要语义理解。你既拿到了脚本的可调试性,又避开了”每一步都问模型”的成本和抖动。判断你的任务能不能落在这一档,关键是先把流程画出来,逐步标注哪一步真的需要语义理解。

AGENTS.md 里也给了一条同向的提示:自定义工具里如果要做确定式操作,用 browser_session 这个参数拿到会话。而且它专门警告过,这个参数名必须一字不差地写成 browser_session、类型标注为 BrowserSession,写成别的名字工具会静默失效——因为注入是按参数名匹配的。

五、各部分对应关系速查

下面这张表的仓库位置都是可以直接打开核对的:

组成部分它负责什么对应仓库位置你什么时候会碰到它
系统提示词约束模型输出什么动作、怎么思考browser_use/agent/system_prompts/(8 份 md,按 thinking / flash 等模式分开)想用 override_system_messageextend_system_message 改行为时
默认动作集注册模型可调用的动作browser_use/tools/service.py想用 exclude_actions 砍掉动作,或对照 search / navigate / extract / evaluate 的真实签名时
actor 底层确定式的页面/元素/鼠标操作browser_use/actor/page.pyelement.pymouse.py你决定自己写流程、只在个别步骤调模型时
watchdog监听浏览器事件、拦导航、盯崩溃与下载browser_use/browser/watchdogs/(14 个)排查”为什么这次导航被拦了”、下载没落盘、页面崩掉时
LLM 适配层对接各家模型服务browser_use/llm/(15 个 provider 目录)换模型、接自建端点、做多模型兜底时
历史与回放落盘执行轨迹、检测变量、带新值重放examples/features/rerun_history.py把探索出的路径固化成日常任务时
安全边界示例限制可访问域名、屏蔽敏感值examples/features/secure.pyexamples/features/restrict_urls.py上线前收紧权限时

examples/ 目录一共 124 个文件,遇到不确定的用法,先在这里翻,比凭印象写参数靠谱得多。项目采用 MIT 许可证。

六、边界与代价:它明确不管的那些事

这一节比前面几节重要,因为它决定你敢不敢把这套东西放到生产里。

它不承诺步数和结果的确定性。Agent 的参数表就知道它对自己的预期:max_steps 给整个任务设了步数上限,max_failures 规定连续失败多少次就放弃,step_timeout 管单步的墙上时间,llm_timeout 管单次模型调用的等待,final_response_after_failure 还专门约定了失败次数用尽后要不要再强行要一次收尾输出。这一组参数存在本身就说明了设计假设:会失败、会重试、会超时、可能没跑完就得交答案。你要按”这次可能跑不完”来设计上层逻辑,而不是按”它会成功”——具体表现就是任务的成功标志必须由你自己校验,不能因为循环退出了就认为事情办成了。

它不替你评估目标站点的使用条款。 用一个自动化浏览器去访问第三方站点,是否违反对方的服务协议、是否触碰 robots 约定、抓下来的数据能不能商用,这些都是你自己的判断和责任,代码层面没有任何东西会替你把关。这条不是免责套话——真出问题时,第一个被追的是运行脚本的那一方。

它不保证你能穿过反自动化机制。 仓库里有 browser_use/browser/watchdogs/captcha_watchdog.py,但看它的文档字符串就明白定位:它监听验证码求解的开始/结束事件,并提供一个 wait_if_captcha_solving() 让 agent 的步骤循环阻塞等待——也就是”等一等”,而不是”破解”。同一个文件还坦白了一个已知限制:同一时刻只跟踪一个验证码求解,多个验证码重叠时只跟踪最新的那一个,先前的等待可能提前返回。AGENTS.md 里另有一套要 BROWSER_USE_API_KEY 的托管浏览器方案,本文不展开:换基础设施不会把你面对目标站点的合规责任转移出去,该判断的还是你自己判断。本文也不提供任何规避风控、反爬或验证码的做法——真正的结论恰恰是反过来的:如果一个任务只有靠对抗对方的防护措施才跑得通,那它就不该由自动化来做,该做的是去申请正式接口、走对方给的导出通道,或者接受这件事得人工完成。

它会带着你的登录态操作真实账号。 这是最容易被低估的一条。AGENTS.md 的 Real Browser 章节演示的就是直接指向本机 Chrome 的 executable_pathuser_data_dir,好处是”保留认证”,代价是这个 Agent 拿到的就是你本人的登录状态——你的邮箱、你的后台、你的支付页面它都进得去。模型判断错一步,动作是真的会发生的。所以这类实验请用独立的浏览器 profile、独立的低权限账号,别拿主账号试。原则性的做法见最小权限的 Agent 设计

它有敏感数据外泄面。 页面内容要送进模型,截图默认也会送。examples/features/secure.py 的注释里写得很清楚:sensitive_data 会把敏感信息从送给模型的输入里过滤掉、只留占位符,但默认传给模型的截图里可能仍然包含你的信息,要靠 use_vision=False 关掉。同一个文件还配了 allowed_domains=['*google.com', 'browser-use.com'] 来收窄可访问范围。至于各家模型服务商怎么保留和使用这些数据,规则不同且会调整,以官方最新说明为准。

遥测默认开启。 文档里写明它用 PostHog 收集匿名使用数据,关掉的方式是设 ANONYMIZED_TELEMETRY=false。进企业环境前把这一条列进检查项。

它不管你的成本上限。 calculate_cost 默认是 False,也就是说默认状态下你连账都不记。要控成本,得自己在外面加闸。

七、上手与避坑清单

别用主浏览器 profile 起步。 为什么会踩:AGENTS.md 的 Real Browser 示例直接指向系统 Chrome 的用户数据目录,照抄最省事,于是 Agent 拿到了你的全部登录态。怎么避:新建一个专用 user_data_dir,或者干脆传 None 走无痕模式,账号也另开一个权限最小的。

上线前一定配 allowed_domains 为什么会踩:默认不限域,模型一旦被页面上的诱导性文字带偏,可能导航到任何地方。怎么避:照 restrict_urls.py 的写法把域名白名单收窄;security_watchdog.py 会在导航前拦下不在白名单里的 URL 并派发一个导航被拦的错误事件,日志里能看到明确的拦截记录。注意文档里说了通配符不允许出现在顶级域的位置。

自定义工具的参数名不能改。 为什么会踩:习惯性写成 browser: Browser 看着更顺眼,结果工具静默失效,你还以为是模型不肯调。怎么避:一字不差地写 browser_session: BrowserSession,文档里对这一点连着警告了两次。

先把确定的步骤搬出模型。 为什么会踩:一股脑塞进 task 描述里,模型要多花好几步去”找到入口”。怎么避:入口导航、标签页准备这类固定动作放进 initial_actions;再进一步就是整段用 actor 层写死。

别把 evaluate 当万能钥匙。 为什么会踩:默认动作里有 evaluate 可以执行任意 JavaScript,看起来什么都能干,于是被用来绕过所有正常交互。怎么避:把它当成处理 shadow DOM、复杂选择器这类特殊情况的逃生口;actor 层的 README 还专门交代了它的调用约定——page.evaluate()element.evaluate() 必须写成箭头函数形式,返回值一律是字符串,对象会被自动 JSON 序列化。写错格式是很常见的第一个报错。

不要把 actor 层当成 Playwright 用。 为什么会踩:方法名看着很像,手就顺着敲下去了。怎么避:actor README 有一整段”重要用法说明”顶着写:这是 browser-use 的 actor,不是 Playwright 也不是 Selenium,只用文档里列出的方法;它明确点出不存在 element.submit()element.dispatch_event()element.get_property() 这类方法,下拉框要用 select_option() 而不是 fill(),表单提交要点提交按钮或者 page.press("Enter")。另外 get_elements_by_css_selector() 是立即返回、不会等元素可见的。

回放任务必须配失败告警。 为什么会踩:load_and_rerun 跑通一次之后很容易被当成稳定管道,而页面改版是不打招呼的。怎么避:把回放结果的成功标志接进你的告警,失败时回落到人工或者重新走一遍探索阶段。

压速度之前先量清楚代价。 为什么会踩:看到 flash_mode 能跳过 thinking、BrowserProfileminimum_wait_page_load_timewait_between_actions 都能往下调,就一路压到底。怎么避:05_fast_agent.py 里这些确实是仓库示范的加速手段,它还配了一段要求模型”尽量少说话、尽量合并动作”的 extend_system_message;但把等待时间压到极限会让”页面还没渲染完就去点”的失败模式明显变多,省下的时间要拿失败重跑还回去。先在你自己的目标站点上测出成功率曲线,再决定压到哪一档。

收尾:三句话的自检

真正要落地时,按这个顺序问自己:

  1. 这件事有没有接口、导出或者公开数据?有就别开浏览器。
  2. 没接口的话,流程路径是不是每次都一样?一样就用 initial_actions 加历史回放,把模型钱付在探索阶段。
  3. 路径大体固定但个别步骤需要语义判断?落在 browser_use/actor/,自己写流程,只在那一步调 get_element_by_promptextract_content

三条都过不去,才轮到完整的 Agent 循环。到那时候,你要准备好的是失败重试、域名白名单、独立账号、成本闸门和告警——而不只是那五行启动代码。

接下来该读哪个文件:想改行为,去 browser_use/agent/system_prompts/ 看那 8 份提示词到底约束了什么;想知道模型手里究竟有哪些动作以及每个动作的真实参数,去 browser_use/tools/service.py;想自己写流程,browser_use/actor/README.mdbrowser_use/actor/playground/ 里的几个脚本够你起步。别凭方法名的印象写调用,参数名错一个就是一次静默失败。

本篇属于一个把开源浏览器操作 Agent 项目 browser-use逐层拆开讲的系列,整体地图见 browser-use 是什么;沿着这条线往下,还可以看 browser-use 两种并发browser-use 源码梳理

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。