揭秘AI自动化框架Browser-use(三):Browser-use控制浏览器的核心机制

255 阅读11分钟

1. 概述

在Browser-use框架中,核心任务是使大模型能够像人类一样操作浏览器。本文深入探讨大模型如何实际控制浏览器,重点解析从模型输出到浏览器动作执行的完整流程。

上一篇(公众号首发)-Browser-use AI自动化框架深度解析(二):提示词构造机制

2. 系统架构与数据流

Browser-use采用标准的Agent-Environment交互范式,以闭环反馈机制实现大模型与浏览器的交互:

┌────────────┐    状态提示词    ┌───────────┐
│            │ ───────────────> │           │
│  浏览器环境  │                 │   大模型   │
│            │ <───────────────  │           │
└────────────┘    结构化动作    └───────────┘

2.1 核心交互流程

  1. 状态获取:框架捕获当前浏览器状态,包括DOM结构、可交互元素和页面元数据
  2. 状态表示:将状态转换为结构化提示词注入大模型上下文
  3. 决策生成:大模型分析状态并生成结构化的动作计划
  4. 动作执行:框架解析动作计划并在浏览器中执行相应操作
  5. 结果反馈:执行结果和新状态反馈回大模型,进入下一轮迭代

这种基于状态-动作-反馈的迭代模式支持大模型进行多步骤任务规划和执行。

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 输出结构解析

  1. current_state:任务状态信息

    • evaluation_previous_goal:上一步骤完成状态的评估
    • memory:工作记忆,存储任务关键信息
    • next_goal:规划的下一步目标
  2. 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 调用方式分类

  1. 原始模式 (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.')
    
  2. 函数调用 (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']
    
  3. 结构化输出 (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)

动作注册时需指定三个关键要素:

  1. 动作名称:用于在大模型输出中识别动作
  2. 参数模型:定义参数类型和验证规则
  3. 实现函数:执行具体浏览器操作的异步函数 这些模型用于验证模型输出并生成函数调用的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

执行流程中的关键步骤:

  1. 解析动作类型和参数
  2. 处理敏感数据(如密码等)
  3. 从注册表中查找对应的动作处理函数
  4. 执行动作并获取结果
  5. 记录执行结果,处理可能出现的异常

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

元素跟踪的核心逻辑:

  1. 保存历史元素的关键属性(XPath、属性、文本等)
  2. 在新状态中查找匹配度最高的元素
  3. 自动更新动作索引以适应元素位置变化
  4. 处理元素消失的情况并提供错误反馈

这一机制使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)

自定义动作需要三个关键组件:

  1. 参数模型:定义输入参数及验证规则
  2. 动作名称:注册动作时指定的识别标识
  3. 执行函数:实现具体操作逻辑的异步函数

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是如何进行数据处理的!