browser-use:AI 驱动的浏览器自动化神器——MessageManager
browser-use 中 LLM 本身是无状态的,它无法记住之前的对话,为了让 LLM 能够理解上下文并能做出连贯的决策, agent 就需要将相关的对话历史和当前的状态信息一起发送给它。 MessageManager 就是负责管理这个 “记忆” 与 “沟通” 过程的关键组件。
一、MessageManager 作用
上下文管理: 追踪对话历史,包括系统指令、用户任务、智能体的思考、执行的动作、动作结果以及浏览器状态。
状态格式化: 将复杂的浏览器状态(DOM 树、截图)转换为 LLM 能够理解的文本和图像格式。
Token 限制: LLM 的输入有长度限制(Token Limit)。当对话历史变得过长时,需要有策略地进行删减,以确保最重要的信息得以保留。
敏感数据处理: 在将信息发送给 LLM 之前,自动过滤或替换掉配置中的敏感信息(如密码、API 密钥)。
二、_init_messages
MessageManager 在通过 _init_messages 初始化时,会构建一个基顾贩对话开端,通常包含以下内容:
系统消息 (SystemMessage): 来自 system_prompt.md,定义了智能体的角色、能力、规则和输出格式。
上下文消息 (HumanMessage): (可选) 任务的额外背景信息。
任务消息 (HumanMessage): 清晰地陈述最终目标。
敏感数据提示 (HumanMessage): (可选) 告知 LLM 存在敏感数据占位符以及如何使用它们(例如 <secret>password</secret>)。
示例输出 (HumanMessage, AIMessage, ToolMessage): 给 LLM 一个清晰的、符合格式要求的输出示例,帮助它理解期望的 JSON 结构和动作调用方式。
历史占位符 (HumanMessage): 标记任务历史记忆的开始位置。
可用文件路径 (HumanMessage): (可选) 告知 LLM 可以访问哪些本地文件。
三、add_state_message
当 agent 获取到新的浏览器状态(BrowserState)时,add_state_message 方法会将浏览器状态转化成一条 HumanMessage 。
具体代码实现如下:
# add_state_message 简化逻辑
def add_state_message(self, state: BrowserState, result: Optional[List[ActionResult]] = None, ...) -> None:
# 处理上一步的结果/错误,如果需要直接加入记忆
if result:
for r in result:
if r.include_in_memory:
# 将需要记忆的结果/错误直接添加为单独的 HumanMessage
if r.extracted_content:
self._add_message_with_tokens(HumanMessage(content='Action result: ' + str(r.extracted_content)))
if r.error:
self._add_message_with_tokens(HumanMessage(content='Action error: ' + r.error.split('\n')[-1]))
result = None # 标记已处理,避免重复添加
# 使用 AgentMessagePrompt 格式化当前状态
state_message_prompt = AgentMessagePrompt(state, result, ...)
# 获取包含文本描述和可选截图的 HumanMessage
state_message = state_message_prompt.get_user_message(use_vision)
# 将格式化后的状态消息添加到历史记录
self._add_message_with_tokens(state_message)
AgentMessagePrompt (在 prompts.py 中定义) 负责具体的格式化工作。它会提取下面的内容:
- 当前 URL
- 打开的标签页列表
- 视口内可交互元素的文本表示(例如 [1]<button>Login</button>, [2]<input type="text">Username</input>),并根据配置包含指定的 HTML 属性 (include_attributes)。
- 页面滚动信息(例如 ... 500 pixels above ..., ... 1200 pixels below ...)。
- 当前步骤信息和时间。
- 截图的 Base64 编码(如果 use_vision=True 且存在截图),嵌入到消息中。
四、Token 管理
LLM 的上下文窗口(能处理的最大 Token 数)是有限的。随着对话进行,消息历史会越来越长,最终可能超出限制。MessageManager 通过以下方式处理这个问题:
Token 计算 (_count_tokens, _add_message_with_tokens): 在添加每条消息时,估算其 Token 数量(文本按字符数估算,图片按固定值 image_tokens 计算),并累加到 MessageHistory 的 current_tokens 中。
历史裁剪 (cut_messages): 当 current_tokens 超过 max_input_tokens 时,触发裁剪逻辑:
优先移除图片: 如果最后一条消息包含图片,先将其移除,因为图片通常占用大量 Token(例如 800 个)。
截断最后一条消息: 如果移除图片后仍然超限,则按比例截断最后一条(通常是状态消息)的文本内容,直到 Token 总数低于限制。
五、_filter_sensitive_data
在将任何消息添加到历史记录之前,_add_message_with_tokens 会调用 _filter_sensitive_data。这个方法会遍历消息内容,查找 settings.sensitive_data 字典中定义的值(例如,用户密码明文),并将其替换为占位符(例如 <secret>user_password</secret>)。这样可以防止敏感信息被发送给 LLM 或记录在日志/历史文件中。
六、message_manager/views.py
ManagedMessage: 将 Langchain 的 BaseMessage(如 HumanMessage, AIMessage)与 MessageMetadata(目前只有 tokens)封装在一起。它还实现了自定义的 Pydantic 序列化/反序列化逻辑,利用 Langchain 的 dumpd 和 load 来正确处理各种消息类型。
MessageHistory: 存储 ManagedMessage 列表,并维护 current_tokens 总数。提供了添加、获取、移除消息的方法。
MessageManagerState: 保存 MessageManager 的整体状态,主要是 MessageHistory 和下一个 ToolMessage 需要使用的 tool_id。