2026/10/24 2:53:35

Browser-Use源码解析:AI如何操控浏览器实现网页自动化

Browser-Use源码解析:AI如何操控浏览器实现网页自动化 1. “看懂网页”这件事到底难在哪1.1 浏览器看到的网页和 AI 看到的不是一回事很多人在接触 Browser-Use 之前第一反应是让 AI 读网页直接扔给它一段 HTML 不就行了实际做一遍就会发现完全不是这么回事。一个普通电商页面原始的 HTML 少说几十 KB多则几 MB里面塞满了 script、style、meta、svg 图标、埋点标签和无数的 div。把这种原始内容原封不动丢给大模型且不说 token 费用撑不撑得住模型自己就先迷失在标签堆里了。所以项目里最核心的一层设计不是“喂页面”而是“翻译页面”。它要做的是把浏览器渲染完之后的、人类肉眼能看懂的网页逻辑转换成大模型能理解的结构化文本。这中间要解决的问题有三个元素怎么表示、哪些元素值得看、上下文怎么不超限。1.2 DOM 不是拿来就用的三明治式的节点表示我看源码时第一感觉是作者对 DOM 的处理方式很像“做三明治”——底层是页面原始结构中间是过滤逻辑顶层才是真正交给模型的文本。简单说Browser-Use 在拿到一个网页后不会直接序列化整棵 DOM 树。它会先剔除明显无用的节点比如隐藏元素、纯装饰性的 SVG、脚本残留、样式节点。然后对剩下的每一个可交互或有关联的节点生成一个类似这样的索引标记[1234] button 加入购物车这个编号不是随机生成的而是树形 DFS 遍历时的顺序。AI 后续在输出动作时只需要引用这个编号就能定位到具体元素。之所以用这种带编号的扁平文本而不是把整个 DOM 树打印出来是因为大模型在处理长文本时对“顺序排列的列表”的命中率远高于“层层嵌套的树形结构”。你让模型读一份标了号的菜单它很容易找到第 7 号菜但你要让它在一棵深度 20 层的树里找某个叶子节点它大概率会给出一个看起来合理但实际错位的路径。1.3 可交互元素的过滤与候选列表构建另一个有意思的设计是“候选元素”的筛选。项目里会复用浏览器自身的 Accessibility Tree 或元素属性来判断一个节点是否可交互有 href 的链接、有 onclick 或 rolebutton 的元素、input 输入框、select 下拉框以及 textarea这些都是高优先级候选。相反一个单纯的容器 div哪怕它外面包了三层事件监听只要没有明确的交互语义通常会被降级或者过滤掉。这个过滤策略非常实用。因为模型的能力再强如果候选列表里混入了大量无关元素它的注意力就会被稀释导致点击错目标。源码里会让每个候选元素带上一段“精简属性描述”比如标签名、可见文本、name、id、aria-label 等但不会带上整个元素的 style 与大数据属性。这些描述字段的取舍顺序看得出是经过大量试错磨出来的。提示真正干过这个项目的人都会告诉你——宁可候选列表少而精准不要多而庞杂。模型不是搜索引擎它不会“检索”元素它只会“看见”你给它的东西。2. Agent 主循环AI 操控浏览器的“大脑”2.1 消息历史与系统提示词的设计把网页翻译成文本只是第一步。真正让 AI 操作浏览器的核心是那套 Agent 主循环。我第一次读源码时一度以为会有很复杂的强化学习或者专门的操控模型结果发现底层就是 LLM 的 function calling。但妙就妙在项目把聊天的“多轮对话”思路完整迁移到了浏览器操作场景里。系统提示词不是简单的一句“你是助手”而是一整套包含浏览器状态说明、页面元素格式说明、动作格式约束的指令集。在每一轮循环里模型收到的消息大致长这样system你的任务当前页面编号规则输出 JSON 的 schema 约束user当前页面的节点列表来自第一章的翻译结果assistant模型上一轮输出的动作比如点击元素 1234tool执行动作后的结果反馈比如页面跳转、弹窗出现、新元素列表。这套设计最巧妙的地方在于它把浏览器的“状态变化”当作消息回灌给了模型。模型并不需要记住页面长什么样只需要根据最新一轮 tool 返回的结果决定下一步动作。这种“感知—决策—行动—再感知”的循环和人用浏览器时的习惯几乎一样。2.2 从 LLM 输出到浏览器动作的解析源码里有一个专门的解析器负责把模型的输出从自然语言/JSON 半成品转换成结构化动作对象。这里有个很现实的工程问题LLM 的输出不可能 100% 符合格式。你要求它输出{action: click, index: 1234}它偶尔会来一句“好的我将点击加入购物车按钮”甚至会在 JSON 里加注释。项目对此的处理是多层兜底先尝试严格 JSON 解析失败后用正则从文本中提取动作关键词和参数再失败则把这轮输出标记为不可解析并回传一条错误消息让模型重新生成。这种“宽容模式”非常值得借鉴因为在实际使用中模型的格式遵守能力受温度、上下文长度和语言版本影响极大你不能赌它每次都听话。2.3 工具调用与动作注册机制动作类型在设计上被收敛得很好。整个项目的基础动作集只有十几个不是放任模型自由发挥点击元素、输入文本、清空输入框滚动页面向上/向下/到某位置跳转到 URL、后退、前进切换标签页、打开新标签页等待页面加载或元素出现从页面提取内容、截取屏幕结束任务让我觉得聪明的是项目把动作集定义成一组“可注册的工具”每个工具都有明确的参数 schema。这么做的好处有两层第一模型的输出空间被大幅约束幻觉概率自然下降第二开发者想扩展自定义动作时不需要改核心主循环只需要新注册一个工具并描述它的用途模型会在需要时自动调用它。这种插件化的思路让项目从“玩票”变成了一个可以被认真使用的框架。2.4 状态检查与执行反馈动作执行完后并不是立刻把结果丢给模型。源码里会包含一个“验证”环节点击了按钮之后页面是否真的发生了变化跳转之后URL 是否和预期一致表单提交之后是否出现了成功提示这一步在工程上很关键。因为很多页面是异步渲染的点击之后内容要等 1-2 秒才出现。如果反馈太快模型会误以为操作无效从而重复点击或直接放弃。项目的默认做法是每次动作执行后不只是返回“已完成”还会附带当前页面的摘要、URL、标题以及可见范围内的元素编号。模型看到这些信息自己就能判断“刚才那步是否有效”。3. 动作执行层怎么把 JSON 变成真实点击3.1 选择器策略XPath 与 CSS 选择器的平衡AI 引用的是元素编号浏览器驱动可认不得什么编号。执行层要做的是把编号映射回真实的 DOM 节点然后交给浏览器去操作。这里出现了一个实际工程里很常见的问题页面是动态渲染的同一个元素在两次访问中的 XPath 可能完全不同。我读源码时发现执行层并没有迷信某一种定位方式。它维护了一个三级定位策略首选带稳定属性的 CSS 选择器比如 id 或专属 name次选基于文本内容的 XPath比如//button[contains(text(),登录)]最后兜底才是按编号重建的树路径。这个顺序是有讲究的CSS 选择器定位快但经常因为 class 名变化而失效XPath 对文本稳定但慢树路径在以上两者都失败时还能凭位置找到元素。3.2 坐标点击与元素定位的优先级很多自动化工具的做法是算元素中心点直接派发鼠标事件。Browser-Use 在这一点上做得更细致它并不总用中心点而是会先判断元素是否在可视区域内如果不在会先滚动到可视位置再执行点击。因为真实浏览器里一个“看得见”才能“点得着”的元素和“在 DOM 里存在”的元素是两回事。如果一个按钮被底部固定导航遮住了程序模拟的点击事件会被拦截。项目里对这类问题有专门的检查逻辑点击之前先看元素可见性、是否被遮挡、是否处于 disabled 状态。这个方法很笨但真的能解决很多实际问题。3.3 等待机制与页面稳定判断动态页面最让人头疼的就是“时序”。源码里实现了一个比较稳健的等待机制每次动作执行完后不是 sleep 固定时间而是轮询检查页面状态。它会等待网络请求数降到 0、DOM 不再变化、或者指定元素出现。这三种条件只要有一个满足就认为页面基本稳定了。个人经验是固定等待时间在真实场景中几乎永远不合适。有的页面 200ms 就跳转完有的 SPA 页面要白屏 3 秒。轮询方案虽然少了一点“确定性”但整体效率高得多。项目里默认给了 5 秒上限超过就按异常处理并把这个信息如实反馈给模型去判断下一步。3.4 多标签页与 iframe 的处理这两处是做 Web 自动化绕不开的坑。Browser-Use 的处理方式是给每个标签页维护独立的上下文切换标签时同步更新模型所见的节点列表。iframe 的处理则更特殊——它会递归地读取 iframe 内部的 DOM并把内部元素编号映射到外层页面坐标系上。实际测试中跨域 iframe 往往读取受限源码会把这部分内容标记为“不可访问”并告诉模型这里操作不了。我发现这一点处理得比很多商业工具还诚实——有些工具遇到跨域 iframe 会直接报错中断而这里只是缩小上下文、把决策权交还给模型让它换一条路走。4. 上下文窗口与记忆管理AI 不“失忆”的关键4.1 token 预算与长任务处理用 LLM 操作浏览器最大的敌人不是模型能力而是上下文窗口。一个页面翻译成节点文本轻松几千 token再加上历史消息、工具反馈、系统提示词做 10 步操作可能就要消耗几万 token。这是一个非常现实的工程问题让模型“看完”整个页面和让它“准确点击”某个按钮对上下文质量的需求是完全不同的。项目里对单轮页面上下文做了截断保护如果节点列表超长它会优先保留靠近视口用户当前能看到的区域的元素放弃屏幕外的内容。因为模型大多数决策都基于当前可见信息屏幕外的东西可以等滚动之后再读。4.2 历史压缩与事件记忆任务一旦长了前面几步的操作记录不能无限累积。源码里有一层特殊的“记忆压缩”机制当历史消息超过预设阈值系统会把早期动作序列经过 LLM 或模板提炼成一段摘要。比如把 20 条工具消息压缩成一句“用户已完成登录流程当前停留在购物车页面”。这种做法直接避免了一个常见 bug任务做了一半模型因为上下文里填满旧信息忘了最初的目标开始漫无目的地乱点。压缩后的摘要保留了“我们在哪、目标是什么、已完成什么”三个核心要素给模型一个不占地方的“便签纸”。4.3 关键任务特定工具表单填写与下载表单填写是浏览器自动化的高频需求也是容易翻车的环节。源码里针对这一块做了专门优化——输入框不会简单地用输入文本动作而是会先判断表单的校验规则比如 required、pattern、type再把多参数回传给模型。遇到日期选择器、文件上传这类特殊控件也有对应的专用工具。我特别喜欢的细节是下载场景的处理。很多自动化脚本一碰到下载就崩因为浏览器会弹下载框或者卡在等待状态。项目会先截获下载事件拿到文件名和大小然后返回给模型确认“文件是否已保存”。这避免了模型在下载过程中干等也方便后续流程继续。5. 源码里的工程亮点与安全设计5.1 任务失败的自恢复机制没有哪个自动化框架能保证一次成功。Browser-Use 里有一套重试与自恢复逻辑如果动作执行后返回异常模型会被要求重新审视页面状态而不是盲目重试。比如点击后弹出了一个预期外的弹窗模型需要先决定“是否先关闭弹窗”这比直接重复点击原按钮聪明得多。源码里还使用了一种“动作纠偏”策略——如果上一轮动作没让页面状态发生变化系统会把“页面没有变化”这个信号直接写进历史模型便会尝试另一种路径比如改用键盘事件、滚动至底部再点击等。5.2 谨慎的网页操作边界开源项目面对的世界是五花八门的网页所以操作边界必须设计得非常谨慎。比如项目默认禁止对chrome://等内部页面执行脚本禁止在非目标标签页执行跳转甚至对一些站点会主动规避过度操作。记得有一次我让它自动登录某个站点结果页面弹出了“该站点检测到自动化操作”的提示。源码会把这个提示原样反馈给模型然后由模型决定是继续还是终止。这种“让模型自己决定是否继续”的机制比硬性阻断或盲目硬闯都更符合真实使用需求。至少它把你的选择权还给了你。5.3 调试与可视化回放源码里还有一个很适合本地开发的功能操作轨迹的逐步回放。调试过程中你能看到每一步模型接收了什么输入、输出了什么动作、执行后页面反馈是什么。我遇到过很多次模型第一步操作是对的但第二步错得离谱。如果只看最终结果根本定位不了问题。有了追踪面板你就能清晰地看到模型是在哪一步“走丢”的——通常都是页面状态的某个关键信息没给够。6. 常见问题与调优实录6.1 模型输出 JSON 解析失败怎么办这是遇到频率最高的问题尤其在用相对小尺寸的模型时。我自己的排查思路是三步走先确认系统提示词里的输出 schema 是否足够严格最好直接给出一个可复制的 JSON 示例再把温度调到 0 或接近 0让输出更稳定最后在解析器层面做好兜底不要因为一次解析失败就整段任务失败应该把错误信息回灌给模型并允许它重新生成。实践下来这三点改完后解析失败率能降低一半以上。项目源码本身的兜底逻辑已经不错但如果你用了不一样的模型还是需要针对性地调提示词。6.2 元素选择器失效的对策选择器失效通常有三个原因页面结构变化、元素在 iframe 里、或者是异步加载导致元素还没出现。前两种可以通过配置调整解决第三种则要靠等待机制。我的建议是不要依赖单一选择器。在自定义流程里尽量同时提供元素的文本特征和位置特征项目会优先根据文本特征去寻找这样即使 CSS 类名变了也能通过按钮文字定位到目标。6.3 长任务中途断裂的续跑思路长任务最怕的是上下文爆掉或者中间某个步骤迟迟没有反馈。现在的工程化做法是给任务分段把“登录 → 搜索 → 加购物车 → 提交订单”拆成多个独立的子任务。前一个子任务完成时保存页面状态摘要下一个子任务直接基于摘要启动。这样做的好处是任何一步坏了都不用从头开始。6.4 一套可复用的调参建议最后说点我实际跑下来的配置感受。模型优先选带 function calling 且指令跟随能力较强的上下文长度至少 8k否则页面节点文本都塞不下动作等待时间不要固定开启页面变化轮询给自己的业务流程保留一个“人工确认点”尤其是下单、支付、删除这类不可逆操作。如果遇到模型反复执行同一个动作先别急着调模型——极大概率是页面反馈没有传回去或者是等待机制没有生效。把这个问题排查清楚整个流程会顺畅很多。串起来的体会是Browser-Use 这类项目真正厉害的地方不是某一个模型的强弱而是把“网页翻译、循环决策、动作执行、记忆压缩、失败恢复”这一整套闭环工程化地打通了。你拿它来做几十步以内的交互流程是能实实在在跑通的。我个人建议第一次接触的人不要急着上很复杂的业务先拿一个内部系统里“登录—查询—导出”的流程跑一遍把模型、选择器、等待机制三者的配合手感找到了再逐步扩到更长的任务链路。这样踩坑最少上手的成就感也最高。