设计哲学:在混沌中建立秩序
网页的世界本质上是混沌的、多变的。对于一个AI代理而言,直接面对这份复杂性无异于在迷雾中航行。browser-use
的核心设计哲学,正是在这种混沌的Web环境中,为AI代理建立一套结构化、可预测且健壮的推理与行动框架。
它并非试图让AI“完全自由”地理解和操作网页,而是通过以下四大支柱,在人类的意图与机器的执行之间,架起一座坚实的桥梁:
-
抽象与简化 (Abstraction & Simplification): 将原始、复杂的DOM转化为对模型友好的、带有明确交互标记( bid
)的结构化信息,屏蔽无关细节。 -
工具化行动 (Tool-based Actions): 限制代理的行动空间,将其约束在一套定义明确、功能稳定的“工具集”内(如 click
,type
),避免了不可控的代码生成。 -
结构化推理 (Structured Reasoning): 强制代理在行动前必须进行“思考”( thought
),并以标准化的格式(JSON)输出决策,使其行为不仅可执行,更可解释。 -
健壮性设计 (Robust Design): 承认网页操作的脆弱性,通过多层回退(Fallback)机制确保每一个行动都尽最大可能被成功执行。
本文将围绕这一核心哲学,深入剖析 browser-use
代理的感知、思考与行动的全过程。
观察:从网页到结构化感知的转化
代理的第一步是“观察”,即将混沌的网页转化为自身能够理解的结构化数据。这一过程完美体现了“抽象与简化”的设计哲学。
1. 前端注入:DOM的“翻译官” (dom/buildDomTree.js
)
browser-use
并非直接处理原始HTML,而是通过Playwright的 page.evaluate_handle()
方法,向当前页面注入一段核心的JavaScript脚本。这个脚本扮演了“翻译官”的角色:
-
智能识别与标记:它递归地扫描整个DOM树,依据标签名(如 <a>
,<button>
)和ARIA角色,精准识别出所有可交互的元素。最关键的是,它会为这些元素动态添加一个唯一的data-browser-use-id
属性,也就是我们熟知的bid
。这个bid
成为了后续AI与具体元素进行沟通的唯一标识。 -
结构化信息提取:脚本不仅进行标记,还提取了元素的 aria-label
、文本内容、placeholder
等关键属性,最终将这些信息整合成一个结构化的JSON对象返回给Python后端。
2. 后端缓存:连接AI决策与浏览器元素的“地址簿”
当Python后端从前端接收到包含bid
标记的JSON数据后,Session
对象会立即执行一个至关重要的动作:构建或更新元素缓存 (element_cache
)。
我们可以把element_cache
理解为一个动态更新的“地址簿”。它的作用是:
-
键(Key): AI在思考时使用的抽象ID,即 bid
(例如 "12")。 -
值(Value): 浏览器执行时需要的具体“地址”,即一个Playwright Locator
对象。Locator
是Playwright库中用于定位和操作元素的强大工具。
构建过程:在每一次“观察”循环中,这个“地址簿”都会被清空并重建。dom/service.py
会遍历从前端获取的所有带bid
的元素信息,并为每一个bid
创建一个对应的Locator
,然后存入element_cache
字典中。
# 简化示例:构建 bid 到 Locator 的映射# self.element_cache 是一个字典# "12" 是从JS收到的 bid# self.page.locator(...) 是 Playwright 的元素定位器self.element_cache["12"] = self.page.locator('[]')
这个缓存机制是连接AI抽象决策(“我要点击bid
为12的元素”)与浏览器具体执行(“找到data-browser-use-id
为12的元素并点击它”)的核心枢纽。正因为有了这个实时更新的“地址簿”,代理才能在动态变化的网页上准确地执行LLM的指令。
思考:基于规则与上下文的决策
代理的“思考”阶段是其智能的核心,它体现了“工具化行动”与“结构化推理”的哲学。代理的思考并非天马行空,而是被一套严谨的规则和丰富的上下文所引导。
代理的“宪法”:系统提示词 (system_prompt.md
)
每次与LLM交互,browser-use
都会发送一段不变的系统提示词,这构成了代理行为的“宪法”:
-
工具箱定义:明确列出所有可用的工具,如 click(bid, ...)
、type(bid, ...)
。这为LLM提供了一个封闭且确定的行动集合,从根本上保证了行为的可预测性和安全性。 -
结构化输出指令:强制要求LLM的回答必须是一个包含 thought
(思考过程)和action
(行动指令)的JSON对象。thought
字段让我们可以洞察其决策逻辑,而action
字段则保证了机器可以稳定解析并执行。
任务的“上下文”:动态提示词
在“宪法”的框架下,动态信息被填充进来,为LLM提供决策所需的一切情境:
-
用户总目标 (Objective):确保代理在多步操作后依然“不忘初心”。 -
行动历史 (Memory):将过往的“思考-行动-结果”序列传入,形成短期记忆,使代理能从过去的成功与失败中学习。 -
当前页面状态:将“观察”阶段获取的简化DOM和截图作为输入,让代理基于最新的环境信息进行决策。
行动:追求确定性的健壮执行
“行动”阶段充分展示了“健壮性设计”的哲学。由于网页的脆弱性,任何操作都可能失败。browser-use
并非天真地假设每次点击都会成功,而是在 browser_use/browser/session.py
中构建了一套强大的、追求确定性的执行逻辑。
以 Session.click
为例,其执行流程堪称典范:
-
标准尝试:首先,通过 element_cache
找到元素的Locator
,并执行Playwright标准的locator.click()
。此方法会自动等待元素变得可见且可操作,应对了大部分场景。 -
强制回退:如果标准点击因元素被遮挡等原因失败,系统会尝试 locator.click(force=True)
。此方法会忽略部分可操作性检查,增加了成功的概率。 -
JavaScript回退:如果强制点击依然失败,系统将采用最后的“杀手锏”—— locator.dispatch_event('click')
。这会直接在浏览器中执行JavaScript来触发点击事件,能够绕过许多复杂的前端框架限制。
这种“标准尝试 -> 超时重试 -> 多种回退策略”的层层保障,使得代理的每一步行动都极为可靠,极大地提升了自动化任务的整体成功率。
工具箱(Toolbox)深度解析:从代码到实例
以下是 browser-use
代理的完整工具集。需要强调的是,下文中每个“典型使用场景”里的JSON示例,都是AI代理在接收到一个完整的、包含系统提示词、用户目标、当前页面状态和历史记录的提示词后,所生成的最终决策输出。其中的 thought
字段,正是其内部决策逻辑的体现。
1. click(bid, description)
-
用途: 点击由 bid
标识的元素。这是最核心的交互动作。 -
代码实现细节 ( Session.click
): -
通过 element_cache
将bid
转换为 PlaywrightLocator
。 -
调用 locator.click(timeout=...)
,并辅以force=True
和dispatch_event('click')
作为回退机制。 -
典型使用场景: { "thought": "我已经填写完所有信息,现在需要点击'提交'按钮 (bid=45) 来完成表单提交。", "action": { "name": "click", "args": {"bid": "45", "description": "点击提交按钮"} }}
2. type(bid, text, description)
-
用途: 在由 bid
标识的输入框中输入文本。 -
代码实现细节 ( Session.type
): -
同样通过 element_cache
找到Locator
。 -
主要调用 locator.fill(text)
,此方法会先清空输入框再填入新内容,高效且稳定。在某些需要模拟真实输入的场景,也可能使用locator.type(text)
。 -
典型使用场景: { "thought": "页面上有一个标签为'搜索'的输入框 (bid=12),我需要在这里输入用户的查询内容'AI最新的发展'。", "action": { "name": "type", "args": {"bid": "12", "text": "AI最新的发展", "description": "在搜索框中输入查询"} }}
3. scroll(direction, description)
-
用途: 滚动页面以浏览更多内容。 -
代码实现细节 ( Session.scroll
): -
这是一个页面级操作,不针对特定 bid
。 -
通过调用 self.page.mouse.wheel(delta_x, delta_y)
模拟鼠标滚轮滚动。delta_y
的正负值决定了向上还是向下滚动。 -
对于滚动到底部/顶部,可能会直接执行JS: self.page.evaluate("window.scrollTo(...)")
。 -
典型使用场景: { "thought": "当前屏幕上没有看到'下一步'按钮,我需要向下滚动页面 (direction='down') 来寻找更多的内容。", "action": { "name": "scroll", "args": {"direction": "down", "description": "向下滚动页面查看更多选项"} }}
4. select_option(bid, value, description)
-
用途: 在 <select>
下拉菜单中选择一个指定的选项。 -
代码实现细节 ( Session.select_option
): -
找到 bid
对应的<select>
元素的Locator
。 -
调用Playwright的 locator.select_option(value=...)
。value
通常是选项的value
属性值或可见文本。 -
典型使用场景: { "thought": "我需要选择配送国家。我看到了一个国家选择的下拉框 (bid=33),我将选择值为'USA'的选项。", "action": { "name": "select_option", "args": {"bid": "33", "value": "USA", "description": "选择美国作为配送国家"} }}
5. navigate(url, description)
-
用途: 导航到新的网址。 -
代码实现细节 ( Session.navigate
): -
核心是调用 self.page.goto(url)
。此方法会自动等待页面加载完成(load
事件触发)后才返回。 -
典型使用场景: { "thought": "任务的第一步是访问Hacker News网站。我将直接导航至'https://news.ycombinator.com'。", "action": { "name": "navigate", "args": {"url": "https://news.ycombinator.com", "description": "访问Hacker News首页"} }}
6. 标签页管理 (open_new_tab
, switch_tab
, close_tab
)
-
用途: 管理多个浏览器标签页。 -
代码实现细节 ( Browser
类): -
open_new_tab
: 调用self.browser_context.new_page()
创建新页面,并为其包裹一个新的Session
对象。 -
switch_tab
: 调用page.bring_to_front()
将指定索引的标签页激活。 -
close_tab
: 调用page.close()
关闭当前标签页。 -
典型使用场景: { "thought": "我需要在一个新的标签页中打开这个链接(bid=50),以便不离开当前页面。", "action": { "name": "open_new_tab", "args": {"url": "https://example.com/details", "description": "在新标签页中打开详情"} }}
7. ask_user(question)
-
用途: 当代理遇到无法解决的困难时(如复杂的验证码),向用户请求帮助。 -
代码实现细节: -
这并非一个浏览器操作,而是一个中断信号。它会暂停代理的执行,并将 question
的内容输出给用户。 -
典型使用场景: { "thought": "我遇到了一个复杂的图形验证码,我无法识别。我需要向用户询问验证码的内容。", "action": { "name": "ask_user", "args": {"question": "请帮我输入图片中的验证码字符。"} }}
8. done(result)
-
用途: 标志着任务的成功完成,并返回最终结果。 -
代码实现细节: -
这是一个终止信号,它会使 agent.run()
的主循环退出,并将result
作为最终返回值。 -
典型使用场景: { "thought": "我已经成功将商品加入购物车并跳转到了确认页面。任务已经完成,我将返回'成功加入购物车'作为结果。", "action": { "name": "done", "args": {"result": "成功加入购物车"} }}
完整的提示词示例:一个具体的场景
为了更直观地理解,让我们构建一个完整的、发送给大语言模型的提示词。
场景: 代理已经完成了在GitHub上搜索“browser-use”的操作,现在正处于搜索结果页面。它的下一步任务是点击第一个搜索结果。
发送给模型的完整提示词将由以下部分组成:
SYSTEM MESSAGE (系统提示词)
你是一个AI网页浏览助手,你的任务是帮助用户完成网页操作。你会收到一个简化版的网页DOM,其中所有可交互的元素都有一个唯一的 "bid" 属性。请根据用户的目标,分析当前的页面状态和历史记录,然后从以下可用工具中选择一个来执行。# 可用工具:- `click(bid, description)`: 点击指定bid的元素。- `type(bid, text, description)`: 在指定bid的元素里输入文本。- `scroll(direction, description)`: 向下或向上滚动页面。- `done(result)`: 当你认为任务已经完成时调用,并附上最终结果。- ... (其他工具)# 规则:1. 仔细思考,一步一步来。2. 在你的 `thought` 字段中解释你为什么选择这个行动。3. 你的输出必须是严格的JSON格式,包含 `thought` 和 `action` 两个键。# 输出格式:{ "thought": "你的思考过程...", "action": { "name": "你的工具名称...", "args": { "参数1": "值1", "参数2": "值2" } }}
USER MESSAGE (用户消息)
的构建分析
用户消息并非一个单一的文本块,而是由多个结构化部分组合而成,为LLM提供决策所需的所有上下文。
第一部分: 最终目标 (Objective)
这部分始终保持不变,作为代理所有行动的“指北针”。
# 最终目标在GitHub上搜索 "browser-use" 仓库,并点击进入第一个搜索结果。
第二部分: 行动历史 (Memory)
这部分记录了代理从任务开始到当前的所有“思考-行动-结果”循环,是代理的短期记忆。
# 历史记录- **思考**: 我需要先访问 GitHub,然后在搜索框里输入 "browser-use" 并执行搜索。- **行动**: `navigate(url="https://github.com")`- **结果**: 成功导航到 GitHub。- **思考**: 我现在在 GitHub 首页,我需要找到搜索框并输入 "browser-use"。- **行动**: `type(bid="5", text="browser-use")`- **结果**: 成功在搜索框中输入文本。- **思考**: 我已经输入了搜索词,现在需要点击搜索按钮或者按回车键来执行搜索。- **行动**: `click(bid="6")`- **结果**: 点击成功,页面已跳转到搜索结果。
第三部分: 当前页面状态 (Current State)
这是代理的“眼睛”,包含最新的简化DOM和页面截图。
# 当前页面状态 (简化DOM)这是GitHub搜索 "browser-use" 的结果页面。<div role="group"> <a bid="25" href="/browser-use/browser-use" text="browser-use/browser-use"> <span>Make websites accessible for AI agents.</span> </a> <p>Stars: 8.5k</p></div><div role="group"> <a bid="26" href="/other/repo" text="other/repo"> <span>Some other repository.</span> </a> <p>Stars: 1.2k</p></div>... (其他搜索结果)# 截图[这里会附上一张当前浏览器页面的截图,以供多模态模型参考]
模型的预期决策:
在接收到由以上三部分组合而成的完整用户消息后,LLM会分析所有信息:
-
它知道最终目标是点击第一个结果。 -
它看到历史记录表明搜索已经完成。 -
它分析当前页面DOM,发现第一个结果是一个 <a>
标签,文本是browser-use/browser-use
,并且它的bid
是 "25"。
因此,它会生成如下的决策JSON:
{ "thought": "我已经成功搜索了'browser-use'。根据页面内容,第一个搜索结果是'browser-use/browser-use',它的bid是25。我的目标是点击第一个结果,所以我现在应该点击这个链接。", "action": { "name": "click", "args": { "bid": "25", "description": "点击第一个搜索结果 'browser-use/browser-use'" } }}
这个例子完整地展示了代理如何利用其“宪法”和丰富的“上下文”来进行一次精准的、有理有据的决策。
结论
browser-use
的成功,并非源于让AI拥有无尽的自由,恰恰相反,它源于一套在混沌中建立秩序的深刻设计哲学。通过抽象简化环境、工具化行动、结构化推理和健壮化执行,它为AI代理戴上了一副既能精准感知世界、又能稳健采取行动的“镣铐”。正是这副“镣铐”,让代理的舞蹈步步为营,最终在复杂的网络舞台上完成了精准而优美的演出。
对于所有致力于构建AI原生应用的开发者来说,browser-use
所体现的设计思想,无疑提供了宝贵的参考与启示。
原文始发于微信公众号(先进攻防):带着脚镣跳舞 - Browser Use智能体行为模式分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论