揭秘AI自动化框架Browser-use(四):Browser-use记忆模块技术解析
一、从一次失败的景点采集说起
在 AI 自动化任务中,记忆模块是实现复杂任务处理的关键组件。Browser-use 项目通过引入记忆模块,解决了 LLM 在连续性任务中的无状态性问题,使代理能够维持上下文连贯性,执行复杂多步骤任务,并从错误中学习和恢复。
上一篇(公众号首发)-揭秘AI自动化框架Browser-use(三):Browser-use控制浏览器的核心机制
设想一个场景:你正在开发一个自动采集四川著名景点信息的程序,目标是收集九寨沟、峨眉山等 10 个景点的基本信息。初始方案可能是这样的:
async def collect_landscape_info():
# 访问九寨沟页面
await llm.invoke("在页面上查找九寨沟的门票价格")
# ✓ 找到了"220元"
# 继续查找开放时间
await llm.invoke("查找开放时间")
# 失败:LLM不知道在查找谁的开放时间
# 尝试获取交通信息
await llm.invoke("查找如何到达")
# 失败:LLM不知道要到达哪里
这个简单的尝试很快就失败了,原因在于 LLM 是"无状态"的,每次调用都是独立的,没有上下文的概念。
人类在完成类似任务时,会依赖记忆能力:
- 记住当前目标:"我正在查找九寨沟的信息"
- 追踪进度:"已经找到门票价格,还需要查找开放时间"
- 关联信息:"这个价格是旺季的,淡季应该更便宜"
- 错误恢复:"这个页面打不开,我换个网站试试"
LLM 的无状态性使其在连续性任务中表现不佳,每次调用都像是一个"失忆"的助手,需要重新解释任务。记忆模块的引入使代理能够维持上下文连贯性,执行复杂多步骤任务,并从错误中学习和恢复。
二、记忆模块的必要性
1维持上下文连贯性
LLM 的无状态性使其在连续性任务中表现不佳。记忆模块存储了整个交互历史,使模型能够理解当前状态与之前操作的关系,避免重复已执行的操作,并基于过去的结果调整策略。
处理复杂多步骤任务
浏览任务通常需要多个步骤才能完成,如搜索信息、导航到特定页面、填写表单等。没有记忆,代理将无法执行需要多个连续步骤的任务。
错误恢复和学习
记忆模块记录了错误和失败的尝试,使代理能够避免重复相同的错误,从失败中学习并尝试替代方法,在遇到问题时回溯到之前的状态。
令牌管理和优化
LLM 有输入令牌限制,记忆模块通过智能管理历史信息来裁剪不必要的历史,保持在令牌限制内,优先保留重要信息。
状态持久化和恢复
记忆模块允许保存代理状态到文件,在会话之间恢复状态,或在系统崩溃后恢复执行。
长期规划能力
通过记忆,代理能够制定多步骤计划,跟踪计划的执行进度,根据新信息调整计划。
多模态信息整合
Browser-use 需要整合多种信息类型,记忆模块将这些不同类型的信息组织成结构化的历史,使模型能够全面理解网页环境。
三、记忆模块的技术实现解析
1. 核心记忆组件
1.1 MessageManager
MessageManager
是记忆模块的核心组件,负责管理与 LLM 的对话历史。它维护与 LLM 的对话历史,添加浏览器状态信息到对话中,添加模型输出到对话中,并管理令牌限制,必要时裁剪历史。
#browser_use/agent/message_manager/service.py
class MessageManager:
def __init__(
self,
task: str,
system_message: str,
settings: MessageManagerSettings,
state: Optional[MessageManagerState] = None,
):
# 初始化消息历史
self.task = task
self.system_message = system_message
self.settings = settings
self.state = state or MessageManagerState()
self._messages: List[BaseMessage] = []
self._init_messages()
1.2 MessageManagerSettings
消息管理的核心配置由 MessageManagerSettings
定义,决定了最大输入令牌数、图片令牌预算、需要包含的 HTML 属性列表、敏感数据处理方式等。
#browser_use/agent/message_manager/service.py
class MessageManagerSettings(BaseModel):
max_input_tokens: int = 128000
estimated_characters_per_token: int = 3
image_tokens: int = 800
include_attributes: list[str] = []
message_context: Optional[str] = None
sensitive_data: Optional[Dict[str, str]] = None
available_file_paths: Optional[List[str]] = None
1.3 AgentState
AgentState
类存储代理的当前状态,包括代理 ID、步骤数、连续失败次数、历史记录和消息管理状态。
class AgentState:
agent_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
n_steps: int = 0
consecutive_failures: int = 0
history: AgentHistoryList = Field(default_factory=AgentHistoryList)
message_manager_state: MessageManagerState = Field(default_factory=MessageManagerState)
1.4 AgentHistoryList
AgentHistoryList
存储执行历史的列表,提供了多种辅助方法来分析执行过程,如获取 URL、截图、操作名称、提取的内容、错误、模型操作等。
class AgentHistoryList:
history: List[AgentHistory] = Field(default_factory=list)
# 提供多种辅助方法
def urls(self) -> List[str]
def screenshots(self) -> List[str]
def action_names(self) -> List[str]
def extracted_content(self) -> List[str]
def errors(self) -> List[str]
def model_actions(self) -> List[Dict[str, Any]]
def final_result(self) -> Optional[str]
def is_done(self) -> bool
def has_errors(self) -> bool
def model_thoughts(self) -> List[str]
def action_results(self) -> List[Dict[str, Any]]
1.5 BrowserStateHistory
保存浏览器状态的历史记录,包括 URL、标题、标签页、交互元素和截图。
@dataclass
class BrowserStateHistory:
url: str
title: str
tabs: list[TabInfo]
interacted_element: list[DOMHistoryElement | None] | list[None]
screenshot: Optional[str] = None
2. 记忆管理流程
2.1 初始化记忆
在 Agent
类的初始化方法中,创建 MessageManager
并初始化系统提示和任务。
self._message_manager = MessageManager(
task=task,
system_message=SystemPrompt(
action_description=self.available_actions,
max_actions_per_step=self.settings.max_actions_per_step,
override_system_message=override_system_message,
extend_system_message=extend_system_message,
).get_system_message(),
settings=MessageManagerSettings(
max_input_tokens=self.settings.max_input_tokens,
include_attributes=self.settings.include_attributes,
message_context=self.settings.message_context,
sensitive_data=sensitive_data,
available_file_paths=self.settings.available_file_paths,
),
state=self.state.message_manager_state,
)
2.2 记录浏览器状态
每个步骤中,通过 add_state_message
方法将当前浏览器状态添加到记忆中。
def add_state_message(
self,
state: BrowserState,
result: Optional[List[ActionResult]] = None,
step_info: Optional[AgentStepInfo] = None,
use_vision=True,
) -> None:
# 如果需要保留在记忆中,直接添加到历史
if result:
for r in result:
if r.include_in_memory:
if r.extracted_content:
msg = HumanMessage(content='Action result: ' + str(r.extracted_content))
self._add_message_with_tokens(msg)
if r.error:
# 获取错误的最后一行
last_line = r.error.split('\n')[-1]
msg = HumanMessage(content='Action error: ' + last_line)
self._add_message_with_tokens(msg)
result = None # 如果结果已添加到历史,不再重复添加
# 创建状态消息
state_message = AgentMessagePrompt(
state,
result,
include_attributes=self.settings.include_attributes,
step_info=step_info,
).get_user_message(use_vision)
self._add_message_with_tokens(state_message)
2.3 记录模型输出
模型的输出通过 add_model_output
方法添加到记忆中。
def add_model_output(self, model_output: AgentOutput) -> None:
tool_calls = [
{
'name': 'AgentOutput',
'args': model_output.model_dump(mode='json', exclude_unset=True),
'id': str(self.state.tool_id),
'type': 'tool_call',
}
]
msg = AIMessage(
content='',
tool_calls=tool_calls,
)
self._add_message_with_tokens(msg)
# 空工具响应
self.add_tool_message(content='')
2.4 创建历史记录项
在每个步骤结束时,通过 _make_history_item
方法创建历史记录。
def _make_history_item(
self,
model_output: AgentOutput | None,
state: BrowserState,
result: list[ActionResult],
metadata: Optional[StepMetadata] = None,
) -> None:
if model_output:
interacted_elements = AgentHistory.get_interacted_element(model_output, state.selector_map)
else:
interacted_elements = [None]
state_history = BrowserStateHistory(
url=state.url,
title=state.title,
tabs=state.tabs,
interacted_element=interacted_elements,
screenshot=state.screenshot,
)
history_item = AgentHistory(model_output=model_output, result=result, state=state_history, metadata=metadata)
self.state.history.history.append(history_item)
2.5 令牌管理
当达到令牌限制时,MessageManager
会裁剪历史,优先保留系统消息,移除最旧的非系统消息,必要时移除最后的状态消息。
if 'Max token limit reached' in error_msg:
# 从历史中裁剪令牌
self._message_manager.settings.max_input_tokens = self.settings.max_input_tokens - 500
logger.info(
f'Cutting tokens from history - new max input tokens: {self._message_manager.settings.max_input_tokens}'
)
self._message_manager.cut_messages()
3. 记忆持久化
3.1 保存对话历史
可以通过 save_conversation_path
参数指定保存对话历史的路径。
if self.settings.save_conversation_path:
logger.info(f'Saving conversation to {self.settings.save_conversation_path}')
3.2 生成 GIF 记录
可以通过 generate_gif
参数生成任务执行的 GIF 记录。
if self.settings.generate_gif:
output_path: str = 'agent_history.gif'
if isinstance(self.settings.generate_gif, str):
output_path = self.settings.generate_gif
create_history_gif(task=self.task, history=self.state.history, output_path=output_path)
4. 记忆的使用
4.1 规划器使用记忆
规划器使用完整的消息历史来分析状态并建议下一步操作。
async def _run_planner(self) -> Optional[str]:
# 创建规划器消息历史,使用完整的消息历史
planner_messages = [
PlannerPrompt(self.controller.registry.get_prompt_description()).get_system_message(),
*self._message_manager.get_messages()[1:], # 使用完整的消息历史,除了第一条
]
# 获取规划器输出
response = await self.settings.planner_llm.ainvoke(planner_messages)
plan = str(response.content)
4.2 重放历史步骤
可以通过 _execute_history_step
方法重放历史步骤。
async def _execute_history_step(self, history_item: AgentHistory, delay: float) -> list[ActionResult]:
state = await self.browser_context.get_state()
if not state or not history_item.model_output:
raise ValueError('Invalid state or model output')
updated_actions = []
for i, action in enumerate(history_item.model_output.action):
updated_action = await self._update_action_indices(
history_item.state.interacted_element[i],
action,
state,
)
updated_actions.append(updated_action)
if updated_action is None:
raise ValueError(f'Could not find matching element {i} in current page')
result = await self.multi_act(updated_actions)
await asyncio.sleep(delay)
return result
5. 错误处理与恢复机制
Agent 类中内置了完整的错误处理机制,关键配置包括最大失败次数、重试延迟、输出验证等。
#browser_use/agent/service.py
def __init__(
self,
task: str,
llm: BaseChatModel,
# Optional parameters
browser: Browser | None = None,
browser_context: BrowserContext | None = None,
controller: Controller[Context] = Controller(),
# Initial agent run parameters
sensitive_data: Optional[Dict[str, str]] = None,
initial_actions: Optional[List[Dict[str, Dict[str, Any]]]] = None,
# Cloud Callbacks
register_new_step_callback: Callable[['BrowserState', 'AgentOutput', int], Awaitable[None]] | None = None,
register_done_callback: Callable[['AgentHistoryList'], Awaitable[None]] | None = None,
register_external_agent_status_raise_error_callback: Callable[[], Awaitable[bool]] | None = None,
# Agent settings
use_vision: bool = True,
use_vision_for_planner: bool = False,
save_conversation_path: Optional[str] = None,
save_conversation_path_encoding: Optional[str] = 'utf-8',
max_failures: int = 3,
retry_delay: int = 10,
override_system_message: Optional[str] = None,
extend_system_message: Optional[str] = None,
max_input_tokens: int = 128000,
validate_output: bool = False,
message_context: Optional[str] = None,
总结
Browser-use 项目的记忆模块设计非常全面,主要特点包括分层记忆结构、智能令牌管理、丰富的历史分析方法、DOM 元素追踪、多种持久化选项等。这种设计使得代理能够有效地利用历史信息来指导未来的行动,同时提供了丰富的调试和分析功能。
想了解更多技术实现细节和源码解析,欢迎关注我的微信公众号【松哥ai自动化】。每周我都会带来一篇深度技术文章,从源码角度剖析各种实用工具的实现原理。
通过这些实践建议和最佳实践,开发者可以借鉴 Browser-use 的记忆模块,构建更可靠、更高效的自动化任务。
朋友们,有啥想要分析的,请在评论区发我,我会优先分析。下一篇我们将深入分析一个最近很火的ai自动化框架,你知道是谁吗?
揭秘AI自动化框架Browser-use(四):Browser-use记忆模块技术解析的更多相关文章
- selenium + python自动化测试unittest框架学习(四)python导入模块及包知识点
在写脚本的时候,发现导入某些模块,经常报错提示导入模块失败,这里来恶补下python导入模块的知识点. 1.模块导入时文件查找顺序 在脚本中,import xxx模块时的具体步骤: (1)新建一个mo ...
- ASP.NET MVC5总结(四)登陆中常用技术解析之验证码
在应用软件中,登陆系统我们往往会用到验证码技术,下面将介绍在MVC中用到的验证码技术. 1.前端代码段及前端效果图如下 <div class="row"> <in ...
- RobotFramework自动化测试框架-Selenium Web自动化(-)-Open Browser和Close Browser
Selenium出来已经有很多年了,从最初的Selenium1到后来的Selenium2,也变得越来越成熟,而且也已经被很多公司广泛使用.Selenium发展的过程中,分了很多模块,这里我们主要介绍W ...
- Python3+Selenium2完整的自动化测试实现之旅(七):完整的轻量级自动化框架实现
一.前言 前面系列Python3+Selenium2自动化系列博文,陆陆续续总结了自动化环境最基础环境的搭建.IE和Chrome浏览器驱动配置.selenium下的webdriver模块提供的元素定位 ...
- 基于Selenium的Web自动化框架增强篇
在写完上一篇“基于Selenium的Web自动化框架”(http://www.cnblogs.com/AlwinXu/p/5836709.html)之后一直没有时间重新审视该框架,正好趁着给同事分享的 ...
- 如何选择Android自动化框架的几点拙见
首先由于我自己也是个新手,也是在学习各种框架然后给公司项目选定相应自动化框架,研究移动自动化测试框架也就近段时间而已,所以我只能从我自己今天为止的认知角度给各个框架抒发我自己的拙见,你看是否能从中接纳 ...
- HBase框架基础(四)
* HBase框架基础(四) 上一节我们介绍了如何使用HBase搞一些MapReduce小程序,其主要作用呢是可以做一些数据清洗和分析或者导入数据的工作,这一节我们来介绍如何使用HBase与其他框架进 ...
- 基于Java+Selenium的WebUI自动化测试框架(十四)-----使用TestNG的Sample
到目前为止,我们所写的东西,都是集中在如何使用Selenium和Java来定位和读取元素.那么,到底如何具体开展测试,如何实现参数化,如何实现判定呢?下面,我们来看看Java应用程序的测试框架吧. 当 ...
- ui自动化笔记 selenium_webdriver,ui自动化框架(web)
Selenium学习笔记 selenium webdriver是业界公认ui自动化测试的标准,其封装的api可以对浏览器的任何地方进行操作 selenium2.0和selenium3.0的区别? 3. ...
- web自动化框架之三获取数据库值与界面值比较~~
数据库用到的是mysql,框架涉及数据库,主要包含两个方面,一个是每个案例执行完毕后,插入案例相关信息与数据:一个是web界面数据核对的时候,需要从sql中获取某行某列值与界面某个值做比较. 描述:w ...
随机推荐
- Centos7部署DVWA靶场
Centos7部署DVWA靶场 DVWA 款开源的渗透测试漏洞练习平台,其中内含xs SQL注入. 文件上传.文件包含. CSRF和暴力破解等各个难度的测试环境. 安装httpd及其相关的组件 y ...
- Flink内存解释
一.JobManager内存 JobManager 是 Flink 集群的控制单元. 它由三种不同的组件组成:ResourceManager.Dispatcher 和每个正在运行作业的 JobMast ...
- 用python做时间序列预测六:相关函数图、偏相关函数图、滞后图
经典的时间序列预测方法都是假设如果一个时间序列有显著的自相关性,那么历史值对预测当前值会很有帮助,但是究竟取多少阶的历史值,就需要通过分析相关函数图和偏相关函数图来得到.本文介绍如何什么是相关函数图和 ...
- 理解 SystemVerilog 中的循环与并发线程
1. 首先理解 scope 的概念 除了常见的module.interface.class.task以及function等等,另外,begin-end block 和 fork-join block ...
- ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this
mysql操作错误: mysql> use mysql;ERROR 1820 (HY000): You must reset your password using ALTER USER sta ...
- Flume - [03] HDFS Sink
一.概述 将事件写入 Hadoop 分布式文件系统(HDFS).目前支持创建文本和序列文件.支持两种文件类型的压缩.可以根据经过的时间.数据大小或事件数 周期性地滚动文件(关闭当前文件并创建文件) ...
- Shell - 脚本案例
题记部分 一.节点状态监控脚本(nodeStatusCheck.sh) [脚本名称]nodeStatusCheck.sh [监控规则]通过ping的方式监控集群节点状态,检查节点是否失联 [实现方式] ...
- Scala Map集合
package com.wyh.day01 object ScalaMap { def main(args: Array[String]): Unit = { /** * 不可变Map * */ // ...
- 【FAQ】HarmonyOS SDK 闭源开放能力 —Map Kit(5)
1.问题描述: 提供两套标准方案,可根据体验需求选择: 1.地图Picker(地点详情) 用户体验:①展示地图 ②标记地点 ③用户选择已安装地图应用 接入文档:https://developer.hu ...
- Unable to Connect: sPort: 0 C# ServiceStack.Redis 访问 redis
需求: 对数据库中的不断抓取的文章进行缓存,因此需要定时访问数据,写入缓存中 在捕获到的异常日志发现错误:Unable to Connect: sPort: 0 使用的访问方式是线程池的方式:Poo ...