browser-use 跑通第一个任务:三个官方入门示例里的最小运行骨架

2026-07-30

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

examples/getting_started/ 下的三个入门示例并排读完,你会得到一个略显扫兴的结论:它们的代码骨架一模一样,真正被改动的只有那个任务字符串。 也就是说,第一次跑不通的原因大概率不在代码里——代码只有四行,改不坏——而在你把任务写成了什么样、环境里的 key 有没有被读到、以及你有没有意识到这东西在动的是一个真实浏览器。

这篇就沿着这三个示例往下走:先看那份共用骨架长什么样,再看骨架背后仓库里对应的零件,然后谈任务描述的颗粒度,最后是第一次跑的卡点清单和这套设计的代价。项目采用 MIT 许可证,examples/ 目录下有 124 个文件,本文只咬住入门那三个。

一、三个示例,一份骨架

01_basic_search.py02_form_filling.py03_data_extraction.py 三个文件,去掉注释后的结构完全重合:导入 asyncioload_dotenv() 读环境变量,从 browser_use 导入 AgentChatBrowserUse,构造模型对象,写一个 task 字符串,把两者塞给 Agentawait agent.run()。第一个示例是这样:

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()

后两个示例只在两处不同。一是 task 从单行字符串变成三引号多行文本:02 把要填的表单字段逐条列出(客户名、电话、邮箱、尺寸、配料、送达时间、备注),03 除了列出要抽的三类信息(前 5 条引言、作者、标签),还给了逐行的输出格式样例(Quote 1: "…" - Author: … - Tags: … 这样一行一条)。二是它们的目标站点是公开的测试页面——https://httpbin.org/forms/posthttps://quotes.toscrape.com/——这个选择本身就是入门示例该有的样子。

三个文件顶部的 docstring 都写了同一段 setup:去拿 API key,然后设置环境变量 BROWSER_USE_API_KEY。README 里给的等价做法是把它写进 .env,同一段还列了 GOOGLE_API_KEYANTHROPIC_API_KEY 作为自带 provider key 的选项。

还有一行你抄代码时应该删掉:三个示例都有 sys.path.append(...) 把仓库上级目录塞进 sys.path,注释写明了用途是”so we can import browser_use”。那是为了在仓库里直接跑示例,你自己的项目里装好包之后不需要这行。

二、这四行背后有哪些零件

四行代码能跑起来,是因为默认值已经把大部分决定替你做了。Agent.__init__ 的签名里 llm 允许为 None,此时会去看 CONFIG.DEFAULT_LLM,没有配置就退回 ChatBrowserUse()run()max_steps 默认 500,max_failures 默认 5,max_actions_per_step 默认 5,step_timeout 默认 180,use_vision 默认 True。还有一条不太显眼的联动:当 llm.provider'browser-use'flash_mode 会被自动置为 True,而 flash_mode 会把 enable_planning 关掉——因为它会从输出 schema 里剥掉计划字段,规划在结构上就不成立了。你换模型时行为跟着变,根源在这里。

组成部分它负责什么对应仓库位置你什么时候会碰到它
任务字符串全程可见的最终目标,模型每步都拿它对照examples/getting_started/01_basic_search.pytask 变量写第一行代码就碰到,也是你改得最多的地方
模型适配层把一套统一的聊天接口映射到各家服务商browser_use/llm/ 下 15 个 provider 子目录换模型、换 key、排查模型侧报错
Agent 主循环每步组装输入、让模型给出动作、执行、记账browser_use/agent/service.pysteptake_stepmulti_act调步数上限、读日志、加 hook
系统提示词规定输入分区、动作纪律、收尾前的自检流程browser_use/agent/system_prompts/ 下 8 份提示词想改默认行为,用 extend_system_messageoverride_system_message
动作集searchnavigateclickinputextractscrolldonebrowser_use/tools/service.py自定义工具、排查某个动作为什么失败
浏览器会话旁路崩溃、弹窗、下载、权限、安全域、截图这些横切职责browser_use/browser/watchdogs/ 下 14 个 watchdog页面异常、要限制可访问域名
运行历史把整轮运行的结果、错误、访问过的 URL 结构化留下browser_use/agent/views.pyAgentHistoryList要把结果接回你自己的程序里

模型每一步看到的输入是分区的,系统提示词里写得很清楚:<user_request> 是最终目标,<agent_history> 是历史事件流,<agent_state> 汇总文件系统和 <todo_contents><browser_state> 给当前 URL、标签页和带索引的可交互元素,<browser_vision> 给带边框标注的截图,<read_state> 只在上一步是 extractread_file 时出现且只显示一次。可交互元素的格式是 [index]<tagname attribute=value />,新出现的元素前面带星号标记。动作只能作用在有数字索引的元素上:ClickElementActionindex 约束是 ge=1InputTextActionindexge=0

终止只有一条路:done 动作。它的参数是 text(给用户的最终答复)、success(默认 True)和 files_to_display。提示词里对它的约束相当严:只能单独调用,不能和别的动作一起发;任何部分缺失或不确定就把 success 设成 False;调 done 之前要回头逐条核对请求里的每个具体要求,包括条目数量、筛选条件、输出格式,还要确认表单是否真的提交成功;所有 URL、价格、名称都必须在工具输出或 browser_state 里逐字出现过,页面上没找到就明说没找到。这套自检是写在提示词里的,不是运行时校验——它是软约束,你的任务描述越含糊,这套自检就越无从下手。

三、任务描述该写到什么颗粒度

系统提示词里给了一条分水岭:请求非常具体时,要仔细跟着每一步走、不许跳过也不许臆造步骤;任务开放时,模型自己规划怎么完成。你写任务的时候实际上是在选走哪一边,而不是在”把话说清楚”这个单一维度上加码。

有一个具体到可以当规则用的行为:directly_open_url 默认为 True,构造 Agent 时会从任务文本里抽起始 URL,抽到就把 navigate 作为首个初始动作直接塞进去。但代码里明确处理了歧义——如果任务里出现多个不同 URL,就跳过这个优化。所以起始点写一个明确的 URL,能省掉开头的搜索与试探;一段话里堆三四个链接,反而把这个捷径关掉了。

0203 那两个多行任务示范的是另一件事:把交付物拆成可核对的条目。字段逐条列,输出格式给一行样例。这跟 done 前的自检是配套的——“前 5 条”能被数出来,“清晰结构化”数不出来。长任务还有一层:提示词要求文件系统初始化时就带一个 todo.md,多步任务要把分步计划写进去,完成一项就用 replace_file 把标记勾掉。你把任务写成可勾选的条目,天然对齐了它的记账方式。

要避开的是反方向的过度具体:把颗粒度下探到 UI 操作级别(“点右上角第二个按钮”)。元素索引是每步根据当前页面重新生成的,你写死的位置描述跟真实页面对不上,模型只能在你的描述和它看到的东西之间二选一。

这三件事——目标写多细、拆到什么层级、跑偏了怎么发现——本站另有三篇专门谈:Agent 任务分解的颗粒度 讲的是跨框架通用的拆分原则,Agent 目标漂移 讲长任务里目标怎么被一步步带偏,另一个开源 Agent 项目的安装上手 则可以当环境准备环节的对照读物。本篇不重复这些通用结论,只回答 browser-use 这个仓库里的具体机制是怎么落的。

四、第一次跑最容易卡在哪

Python 版本不够。 pyproject.tomlrequires-python>=3.11,<4.0,README 的安装段也标了 Python >= 3.11。会踩是因为大多数人的默认解释器不是为这个项目准备的,安装报错信息又常常指向依赖而不是版本。装之前先确认你要用的那个解释器的版本,别只看系统里最新的那个。

key 没有真的进到进程里。 示例靠 load_dotenv().env,而 .env 是相对当前工作目录找的。会踩的两种典型情形:在一个 shell 里 export 完,实际用另一个 shell 或 IDE 跑;.env 放在项目根目录但你从子目录启动脚本。先确认工作目录和 .env 的相对关系,再确认变量名和 docstring 里的 BROWSER_USE_API_KEY 一致。

照抄了 sys.path.append 那一行。 会踩是因为它就在示例正文里,看起来像标准写法。它的作用只是让仓库内的文件能 import 到同仓库的 browser_use,你 pip/uv 装过包之后留着它没有意义,还可能在多环境时导入到意料之外的副本。

忘了整条链路是异步的。 agent.run() 是协程,示例用 asyncio.run(main()) 起。会踩是因为很多人习惯把这类调用直接写在模块顶层或同步函数里,得到一个没有 await 的协程对象却看不到任何浏览器动作。

丢掉了返回值。 三个入门示例都是裸 await agent.run(),README 的快速开始里则写成把返回接住。会踩是因为示例里的结果是打印出来给人看的,而你要的是数据。AgentHistoryList 上有一批现成访问器:final_result()is_done()is_successful()has_errors()errors()urls()action_names()extracted_content()number_of_steps(),还有 save_to_file()。要把结果接进程序,从这里取,别去解析日志。

第一次就拿真实账号试。 README 的认证方案是复用你已有的 Chrome 配置文件,也就是带着已登录状态操作。会踩是因为这条路最省事,看起来也最像”真实场景”。但真实浏览器上的操作不可回滚,提交、发送、下单一旦发生就发生了;带登录态的自动化行为也可能让账号被平台判为异常。第一次跑就用示例里的公开测试页面,同时用 BrowserProfileallowed_domains 把可访问范围钉死——security_watchdog.py 里的判定逻辑是:没配置就全放开,配置了才拦。

无头模式下什么都看不见。 05_fast_agent.py 里显式写了 headless=False,这不是随手写的。会踩是因为默认配置和你的期待可能不一致,而模型的每一步判断都依赖它看到的页面,你看不到页面就无法判断它是不是看错了。第一次带界面跑,看它点哪儿。

把预算问题误判成模型能力问题。 卡住时容易直接换模型。实际先看几个数:步数是不是逼近 max_steps,连续失败是不是撞上 max_failures 的 5 次,单步是不是被 step_timeout 的 180 秒截断。提示词里还有一条策略:用到 75% 步数预算时要重新评估能否完成,不行就转向保住高价值部分。日志里的步序和动作名比换模型更能告诉你发生了什么。

五、边界与代价

这套设计把稳定性换成了通用性,代价要摆明。

它放弃了确定性。 元素靠每步重新生成的索引加截图定位,同一个任务两次运行的动作序列可以不同。这不是稳定选择器脚本的替代品——要做可重复的回归验证,用常规自动化脚本,或者参考 Agent 的回归测试怎么做 里那套把不确定输出转成可判定断言的思路。

它不解决反自动化对抗,也不该被用来对抗。 README 对验证码的回答是需要更好的浏览器指纹和代理,并指向其云端产品;开源库这一侧,browser_use/browser/watchdogs/captcha_watchdog.py 做的事是监听浏览器侧的验证码求解开始/结束事件,提供一个阻塞等待的方法,注释里还写明同一时刻只跟踪一个求解过程。也就是说它是”等”,不是”破”。目标站点的使用条款和技术限制优先于你的自动化需求,这件事没有技术捷径。

敏感数据面比你想的宽。 Agentsensitive_data 参数,历史记录的 model_dump 也支持按敏感数据做过滤。但页面内容和截图是要进模型上下文的,storage_stateuser_data_dir 这类配置意味着 cookie 与登录态被复用。哪些数据在哪一层被谁看到,需要你自己划线,参考 Agent 的最小权限设计

成本和延迟跟步数线性挂钩。 每一步都是一次模型调用。page_extraction_llmjudge_llm 不传时会被直接赋成主模型,一旦分开配置就是额外的调用与额外的账;fallback_llm 不同,它默认没有值,只在主模型抛出限流或服务商错误时才被切进来顶班,没配置时日志里只留一条提醒。步数直接换成 token 和墙上时间。各家服务商的计费与限制规则不同且会调整,以官方最新说明为准。

它明确不管的事:不保证操作幂等,同一任务重跑可能产生重复副作用;不判断某个操作在目标站点是否被允许;不做账号合规审查;本地 Chrome 的内存占用和并发规模,README 在生产部署那一问里也是把它列为需要额外处理的问题。

收尾

第一个任务跑之前,四条自检:解释器版本对得上 >=3.11.env 和你的启动目录对得上;任务里只有一个起始 URL 且交付物是可数的条目;目标站点是你有权自动操作的、最好先用公开测试页,allowed_domains 已经设好。

跑通之后按这个顺序往下读:examples/getting_started/04_multi_step_task.py 看多步任务的任务文本怎么组织,05_fast_agent.pyflash_modeBrowserProfile 的等待参数和 extend_system_message 三个旋钮怎么配合,然后是 browser_use/tools/service.py 看清动作集的全貌,最后读 browser_use/agent/system_prompts/system_prompt.md。读完那份提示词,你对”它为什么这么干”的疑问会少掉一大半——很多你以为是代码逻辑的行为,其实写在那里面。

本篇属于一个把开源浏览器操作 Agent 项目 browser-use逐层拆开讲的系列,整体地图见 browser-use 是什么;沿着这条线往下,还可以看 browser-use 跑不动时的排查顺序browser-use 的 LLM 适配层怎么接国产模型与本地模型

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