1. 概述
在Browser-use框架中,核心任务是使大模型能够像人类一样操作浏览器。本文深入探讨大模型如何实际控制浏览器,重点解析从模型输出到浏览器动作执行的完整流程。
上一篇(公众号首发)-Browser-use AI自动化框架深度解析(二):提示词构造机制
2. 系统架构与数据流
Browser-use采用标准的Agent-Environment交互范式,以闭环反馈机制实现大模型与浏览器的交互:
┌────────────┐ 状态提示词 ┌───────────┐
│ │ ───────────────> │ │
│ 浏览器环境 │ │ 大模型 │
│ │ <─────────────── │ │
└────────────┘ 结构化动作 └───────────┘
2.1 核心交互流程
- 状态获取:框架捕获当前浏览器状态,包括DOM结构、可交互元素和页面元数据
- 状态表示:将状态转换为结构化提示词注入大模型上下文
- 决策生成:大模型分析状态并生成结构化的动作计划
- 动作执行:框架解析动作计划并在浏览器中执行相应操作
- 结果反馈:执行结果和新状态反馈回大模型,进入下一轮迭代
这种基于状态-动作-反馈的迭代模式支持大模型进行多步骤任务规划和执行。
3. 大模型输出设计
大模型需要生成特定格式的JSON结构以实现对浏览器的控制,主要通过工具调用(Tool Calling)机制实现。
3.1 结构化输出示例
{
"current_state": {
"evaluation_previous_goal": "成功打开了百度首页",
"memory": "我正在执行搜索四川十大景点的任务。已完成第1步:打开百度首页。",
"next_goal": "在搜索框中输入'四川十大景点'"
},
"action": [
{
"input_text": {
"index": 5,
"text": "四川十大景点"
}
},
{
"click_element": {
"index": 6
}
}
]
}
3.2 输出结构解析
-
current_state
:任务状态信息evaluation_previous_goal
:上一步骤完成状态的评估memory
:工作记忆,存储任务关键信息next_goal
:规划的下一步目标
-
action
:动作序列- 支持单步多动作执行
- 每个动作以键值对形式表示,键为动作名称,值为参数对象
这种设计不仅规范了输出格式,还通过 memory 字段为大模型提供了上下文记忆能力,从而支持复杂任务的分解与执行。
3.3 Pydantic模型定义
Browser-use使用Pydantic模型定义输出结构,确保类型安全和参数验证:
def _setup_action_models(self):
class CurrentState(BaseModel):
evaluation_previous_goal: str = Field(
...,
description="评估上一步目标的完成情况"
)
memory: str = Field(
...,
description="记录重要信息,用于跟踪进度"
)
next_goal: str = Field(
...,
description="下一步要完成的具体目标"
)
class AgentOutput(BaseModel):
current_state: CurrentState = Field(
...,
description="当前状态评估和下一步计划"
)
action: List[Dict[str, Dict[str, Any]]] = Field(
...,
description="要执行的动作列表"
)
self.CurrentState = CurrentState
self.AgentOutput = AgentOutput
这些模型不仅用于验证输出合规性,还作为函数调用的JSON Schema,指导大模型生成规范化输出。
4. 工具调用机制
4.1 多模型适配策略
Browser-use支持多种工具调用方式,以适配不同的模型能力边界:
def _set_tool_calling_method(self) -> Optional[ToolCallingMethod]:
"""Set tool calling method based on model and provided setting"""
if self.settings.tool_calling_method == 'auto':
if 'gpt-' in self.model_name or 'claude-3' in self.model_name:
return 'function_calling'
elif 'Qwen' in self.model_name or 'deepseek' in self.model_name:
return 'raw'
else:
return None
return self.settings.tool_calling_method
4.2 调用方式分类
-
原始模式 (
raw
)- 适用于不支持原生工具调用的模型
- 从文本输出中解析JSON结构
- 实现方式:
output = self.llm.invoke(input_messages) output.content = self._remove_think_tags(str(output.content)) try: parsed_json = extract_json_from_model_output(output.content) parsed = self.AgentOutput(**parsed_json) except (ValueError, ValidationError) as e: raise ValueError('Could not parse response.')
-
函数调用 (
function_calling
)- 适用于支持工具调用的现代模型(OpenAI/Anthropic)
- 利用模型原生工具调用接口
- 实现方式:
structured_llm = self.llm.with_structured_output( self.AgentOutput, include_raw=True, method=self.tool_calling_method ) response: dict[str, Any] = await structured_llm.ainvoke(input_messages) parsed: AgentOutput | None = response['parsed']
-
结构化输出 (
None
)- 使用LangChain的结构化输出机制
- 提供类型安全和验证
- 兼容各种模型
5. 动作注册与执行系统
动作系统是Browser-use的核心组件,负责将大模型的意图转换为具体的浏览器操作。
5.1 动作注册机制
Browser-use使用装饰器模式实现动作注册:
class Controller(Generic[Context]):
def __init__(self):
self.registry = Registry()
@self.registry.action('Go to URL', param_model=GoToUrlAction)
async def go_to_url(params: GoToUrlAction, browser: BrowserContext):
url = params.url
await browser.goto(url)
msg = f'🌐 Navigated to {url}'
logger.info(msg)
return ActionResult(extracted_content=msg, include_in_memory=True)
@self.registry.action('Click on element', param_model=ClickElementAction)
async def click_element(params: ClickElementAction, browser: BrowserContext):
state = await browser.get_state()
element_node = state.element_tree.find_by_index(params.index)
if not element_node:
msg = f'❌ Could not find element with index {params.index}'
logger.warning(msg)
return ActionResult(error=msg, include_in_memory=True)
await browser.click(element_node.xpath)
msg = f'🖱️ Clicked on element [{params.index}] {element_node.text}'
logger.info(msg)
return ActionResult(extracted_content=msg, include_in_memory=True)
动作注册时需指定三个关键要素:
- 动作名称:用于在大模型输出中识别动作
- 参数模型:定义参数类型和验证规则
- 实现函数:执行具体浏览器操作的异步函数
这些模型用于验证模型输出并生成函数调用的JSON模式,帮助大模型理解预期输出格式。
5.2 内置动作类型
Browser-use预定义了多种浏览器动作类型:
类别 | 动作 | 功能 |
---|---|---|
导航类 | go_to_url | 导航到指定URL |
back | 返回上一页 | |
forward | 前进到下一页 | |
refresh | 刷新当前页面 | |
交互类 | click_element | 点击页面元素 |
input_text | 输入文本 | |
select_option | 选择下拉选项 | |
scroll | 滚动页面 | |
标签页管理 | open_tab | 打开新标签页 |
switch_tab | 切换标签页 | |
close_tab | 关闭当前标签页 | |
内容提取 | extract_content | 提取页面内容 |
任务控制 | done | 标记任务完成 |
5.3 动作执行流程
动作执行由execute_actions
方法实现,该方法处理参数解析、动作调度和错误处理:
async def execute_actions(
self,
actions: List[Dict[str, Dict[str, Any]]],
browser_context: BrowserContext,
agent_output: Optional[Any] = None,
llm: Optional[BaseChatModel] = None,
sensitive_data: Optional[Dict[str, str]] = None
) -> List[ActionResult]:
"""Execute a list of actions on the browser"""
results = []
for action in actions:
# 1. 解析动作类型和参数
action_type = list(action.keys())[0]
action_params = list(action.values())[0]
# 2. 处理敏感数据
action_params = self._process_sensitive_data(action_params, sensitive_data)
# 3. 查找动作处理函数
try:
action_handler = self.registry.get_action(action_type)
# 4. 执行动作
result = await action_handler(
browser_context=browser_context,
**action_params
)
# 5. 记录结果
results.append(result)
except Exception as e:
# 6. 处理错误
error_traceback = traceback.format_exc()
results.append(ActionResult(error=error_traceback, include_in_memory=True))
return results
执行流程中的关键步骤:
- 解析动作类型和参数
- 处理敏感数据(如密码等)
- 从注册表中查找对应的动作处理函数
- 执行动作并获取结果
- 记录执行结果,处理可能出现的异常
6. 迭代执行与状态管理
Browser-use通过_run_iteration
方法实现Agent的单步迭代执行,包含完整的状态捕获、动作生成和执行环节:
async def _run_iteration(self) -> Tuple[bool, bool]:
"""Run one iteration of the agent loop"""
# 1. 获取当前浏览器状态
state = await self.browser_context.get_state()
# 2. 创建步骤信息
step_info = AgentStepInfo(
step_number=self.state.n_steps,
consecutive_failures=self.state.consecutive_failures,
)
# 3. 将状态添加到消息管理器
self._message_manager.add_state_message(
state=state,
result=self.state.last_result,
step_info=step_info,
use_vision=self.settings.use_vision,
)
# 4. 运行规划器(如果启用)
if self.settings.planner_llm and self.state.n_steps % self.settings.planner_interval == 0:
plan = await self._run_planner()
self._message_manager.add_plan(plan, position=-1)
# 5. 获取消息历史
input_messages = self._message_manager.get_messages()
# 6. 获取下一个动作
action_output = await self.get_next_action(input_messages)
# 7. 保存状态和输出
self.state.last_state = state
self._message_manager.add_model_output(action_output)
# 8. 检测是否完成任务
done = any(list(a.keys())[0] == 'done' for a in action_output.action)
if done:
return True, False
# 9. 执行动作
results = await self.controller.execute_actions(
actions=action_output.action,
browser_context=self.browser_context,
agent_output=action_output,
llm=self.settings.page_extraction_llm or self.llm,
sensitive_data=self.sensitive_data
)
# 10. 处理结果
self.state.last_result = results
# 11. 检查结果是否包含错误
any_error = any(r.error for r in results)
return False, any_error
这种迭代执行模式实现了完整的感知-决策-执行闭环,使大模型能够基于当前状态调整策略,处理动态变化的网页环境。
7. 元素选择与跟踪机制
7.1 基于索引的元素选择
Browser-use通过索引机制简化元素选择,大模型根据提示词中的元素索引选择交互对象:
Interactive elements from top layer of the current page inside the viewport:
[0]<input type="text" placeholder="搜索关键词">搜索</input>
[1]<button>搜索</button>
[2]<a href="https://example.com">链接文本</a>
大模型通过指定索引实现元素选择:
{
"click_element": {
"index": 1
}
}
7.2 智能元素跟踪
为应对动态网页中元素位置的变化,Browser-use实现了元素跟踪机制:
async def _update_action_indices(
self,
historical_element: Optional[DOMHistoryElement],
action: ActionModel,
current_state: BrowserState,
) -> Optional[ActionModel]:
"""
更新动作索引以匹配当前页面状态。
如果找不到元素,返回None。
"""
if not historical_element or not current_state.element_tree:
return action
# 在当前元素树中查找历史元素
current_element = HistoryTreeProcessor.find_history_element_in_tree(
historical_element,
current_state.element_tree
)
if not current_element or current_element.highlight_index is None:
return None
# 如果索引变化了,更新动作索引
old_index = action.get_index()
if old_index != current_element.highlight_index:
action.set_index(current_element.highlight_index)
logger.info(f'元素在DOM中移动,索引从{old_index}更新为{current_element.highlight_index}')
return action
元素跟踪的核心逻辑:
- 保存历史元素的关键属性(XPath、属性、文本等)
- 在新状态中查找匹配度最高的元素
- 自动更新动作索引以适应元素位置变化
- 处理元素消失的情况并提供错误反馈
这一机制使Browser-use具备适应动态网页的能力,提高任务执行的稳定性。
8. 可扩展性与自定义动作
8.1 自定义动作注册
Browser-use提供了简洁的API用于扩展自定义动作:
from browser_use import Agent, Browser, ActionRegistry
from pydantic import BaseModel, Field
from browser_use.agent.views import ActionResult
# 定义动作参数模型
class TakeScreenshotAction(BaseModel):
filename: str = Field(..., description="保存截图的文件名")
# 创建Agent实例
browser = Browser()
agent = Agent(task="测试截图功能", llm=llm, browser=browser)
# 注册自定义动作
@agent.controller.registry.action("Take screenshot", param_model=TakeScreenshotAction)
async def take_screenshot(params: TakeScreenshotAction, browser_context):
# 获取页面对象
page = await browser_context.get_current_page()
# 执行截图
await page.screenshot(path=params.filename)
# 返回结果
msg = f"📸 Screenshot saved to {params.filename}"
return ActionResult(extracted_content=msg, include_in_memory=True)
自定义动作需要三个关键组件:
- 参数模型:定义输入参数及验证规则
- 动作名称:注册动作时指定的识别标识
- 执行函数:实现具体操作逻辑的异步函数
8.2 后端适配层
Browser-use设计了模块化的后端适配层,支持多种浏览器自动化技术:
- Playwright:默认后端,支持多种浏览器
- Selenium:传统的浏览器自动化工具
- Chrome DevTools Protocol (CDP):直接与Chrome通信
- 自定义后端:通过实现接口扩展其他自动化技术
选择后端的示例:
from browser_use import Browser, BrowserConfig, PlaywrightBackend
config = BrowserConfig(browser_type="firefox")
browser = Browser(config=config, backend=PlaywrightBackend())
agent = Agent(task="搜索四川的10大景点", llm=llm, browser=browser)
这种可扩展的后端架构使Browser-use能够适配不同的浏览器环境和自动化需求。
技术实现案例
搜索引擎交互
完整的搜索任务执行流程展示:我需要搜索四川的10大景点
初始状态
Current url: about:blank
Available tabs:
- Tab 1 (active): about:blank
Interactive elements from top layer of the current page inside the viewport:
(no elements found)
我需要搜索四川的10大景点
初始动作(导航到搜索引擎)
{
"current_state": {
"evaluation_previous_goal": "我刚开始任务,还没有执行任何目标",
"memory": "我需要搜索四川的10大景点",
"next_goal": "导航到百度搜索引擎"
},
"action": [
{
"go_to_url": {
"url": "https://www.baidu.com"
}
}
]
}
执行结果
Action result: 🌐 Navigated to https://www.baidu.com
更新状态
Current url: https://www.baidu.com
Available tabs:
- Tab 1 (active): https://www.baidu.com
Interactive elements from top layer of the current page inside the viewport:
[0]<input type="text" placeholder="百度一下">百度一下</input>
[1]<button>百度一下</button>
...
搜索动作
{
"current_state": {
"evaluation_previous_goal": "成功导航到百度搜索引擎",
"memory": "我正在搜索四川的10大景点,已成功打开百度首页",
"next_goal": "在搜索框中输入'四川十大景点'"
},
"action": [
{
"input_text": {
"index": 0,
"text": "四川十大景点"
}
},
{
"click_element": {
"index": 1
}
}
]
}
执行结果
Action result: ⌨️ Input text "四川十大景点" into index 0
Action result: 🖱️ Clicked on element [1] 百度一下
这个循环持续进行,直到满足完成条件或达到最大步骤限制。
总结
Browser-use通过结构化输出和工具调用机制,使大模型能够灵活控制浏览器。其核心组件包括工具调用机制、动作注册系统、执行引擎和状态反馈机制。这种设计使AI能够执行从简单信息收集到复杂多步骤交互的Web任务。理解这些控制机制,开发者可以更有效地利用Browser-use构建强大的浏览器自动化应用。
想了解更多技术实现细节和源码解析,欢迎关注我的微信公众号**【松哥ai自动化】**。每周我都会带来一篇深度技术文章,从源码角度剖析各种实用工具的实现原理。
下一篇我们将深入分析Browser-use是如何进行数据处理的!