browser.md 12 KB

浏览器自动化技术文档

agent 框架的浏览器操作模块:会话管理、工具体系、内容提取、Cookie 管理。


目录

  1. 整体架构
  2. 浏览器会话管理
  3. 工具体系
  4. 结果转换机制
  5. URL 清洗
  6. 文件存储
  7. Skill 集成

整体架构

浏览器操作的核心实现位于 agent/tools/builtin/browser/,采用适配器模式将第三方库 browser-use 的能力封装为 agent 框架的标准工具。

agent/tools/builtin/browser/
├── __init__.py          # 统一导出所有浏览器工具
├── baseClass.py         # 核心实现(~2200行):会话管理 + 27个工具函数
└── sync_mysql_help.py   # 同步 MySQL 辅助类(Cookie 查询)

agent/skill/skills/
└── browser.md           # 浏览器工具使用指南(Skill 注入到 LLM system prompt)

关键依赖关系:

agent 框架 (@tool 装饰器, ToolResult)
    ↓ 适配
browser-use (BrowserSession, Tools, ActionResult)
    ↓ 底层
Chrome DevTools Protocol (CDP)

不直接依赖 Playwright,完全基于 CDP 协议与浏览器通信。


浏览器会话管理

全局单例模式

使用模块级全局变量维护唯一的浏览器会话,避免重复创建/销毁浏览器实例:

# baseClass.py:98-101
_browser_session: Optional[BrowserSession] = None
_browser_tools: Optional[Tools] = None
_file_system: Optional[FileSystem] = None
_last_browser_type: str = "local"
_last_headless: bool = True
_live_url: Optional[str] = None

三种浏览器模式

通过 init_browser_session(browser_type=...) 初始化,支持三种运行模式:

模式 browser_type 底层实现 适用场景
本地浏览器 "local" 启动本地 Chrome,通过 user_data_dir 持久化 profile 开发调试,速度最快
云浏览器 "cloud" 连接 browser-use 云服务,通过 cdp_url 远程控制 生产环境,不占本地资源
容器浏览器 "container" 调用远程 API 创建 Docker 容器,内含 Chrome,通过 CDP 连接 隔离性好,支持预配置账户

初始化流程(baseClass.py:230-322):

init_browser_session()
  ├─ local:  检测 macOS Chrome 路径 → 创建 user_data_dir → BrowserSession(is_local=True)
  ├─ cloud:  BrowserSession(use_cloud=True) → 解析 cdp_url 生成 live_url
  └─ container: create_container() API → 等待浏览器启动 → BrowserSession(cdp_url=...)

容器创建流程

容器模式通过 HTTP API 与远程容器管理服务交互(baseClass.py:105-228):

步骤 1.1: POST /api/v1/container/create
  → 返回 container_id, vnc, cdp 地址
  → 等待 5 秒让容器内浏览器启动

步骤 1.2: POST /api/v1/browser/page/create
  → 传入 container_id, url, account_name
  → 返回 connection_id
  → 内置重试机制(最多 3 次)

会话健康检查与自动恢复

get_browser_session()baseClass.py:330-372)在每次工具调用前检查 CDP 连接是否存活:

# 通过 CDP 执行 Runtime.evaluate('1+1') 探测连接
cdp_session = await _browser_session.get_or_create_cdp_session()
await asyncio.wait_for(
    cdp_session.cdp_client.send.Runtime.evaluate(
        params={'expression': '1+1'},
        session_id=cdp_session.session_id
    ),
    timeout=3.0,
)

如果连接断开(WebSocket 超时等),自动 cleanup 并重新初始化。

会话生命周期

init_browser_session()     → 创建会话(幂等,已存在则直接返回)
get_browser_session()      → 获取会话(自动健康检查 + 重连)
cleanup_browser_session()  → 优雅停止(session.stop())
kill_browser_session()     → 强制终止(session.kill())

工具体系

所有工具通过 @tool() 装饰器注册到 agent 框架的 ToolRegistry,LLM 可直接调用。共 27 个工具,分为 8 类。

导航类(Navigation)

工具 功能 底层调用
browser_navigate_to_url(url, new_tab) 导航到 URL tools.navigate()
browser_search_web(query, engine) 搜索引擎搜索(支持 bing/google/duckduckgo) tools.search()
browser_go_back() 浏览器后退 tools.go_back()
browser_wait(seconds) 等待指定秒数 tools.wait()
browser_get_live_url() 获取云浏览器实时画面链接 读取全局 _live_url

元素交互类(Interaction)

工具 功能 特殊处理
browser_click_element(index) 点击元素 挂载日志监听器自动捕获下载链接
browser_input_text(index, text, clear) 输入文本 支持清除已有内容
browser_send_keys(keys) 发送键盘按键/快捷键 支持组合键如 Control+A
browser_upload_file(index, path) 上传文件 需要绝对路径

browser_click_element 的下载链接捕获机制(baseClass.py:711-846):

# 1. 挂载自定义 logging.Handler 到 browser_use 命名空间
capture_handler = DownloadLinkCaptureHandler()
logger = logging.getLogger("browser_use")
logger.addHandler(capture_handler)

# 2. 执行点击
result = await tools.click(index=index, browser_session=browser)

# 3. 检查是否捕获到下载链接(通过正则匹配日志中的 URL)
if capture_handler.captured_url:
    # 将链接注入到 ToolResult.output,LLM 可以看到并决定下一步

滚动与视图类(Scroll & View)

工具 功能 特殊处理
browser_scroll_page(down, pages, index) 滚动页面 通过 CDP 检测 scrollY 变化,判断是否到达边界;限制单次最大 10 页
browser_find_text(text) 查找文本并滚动到位 tools.find_text()
browser_screenshot() 截图 tools.screenshot()
browser_get_visual_selector_map() 获取带元素索引标注的截图 + 交互元素列表 使用 create_highlighted_screenshot_async 生成标注图
browser_get_selector_map() 获取交互元素索引映射(纯文本) 通过 BrowserStateRequestEvent 触发 DOM 更新

browser_get_visual_selector_mapbaseClass.py:1066-1161)是最核心的观察工具:

1. 触发 BrowserStateRequestEvent(include_dom=True, include_screenshot=True)
2. 等待浏览器返回完整状态(DOM 树 + 截图)
3. 从 DOM 状态提取 selector_map(所有可交互元素的索引)
4. 调用 create_highlighted_screenshot_async 在截图上标注元素索引号
5. 构建元素列表(tag, aria-label, href, type, role 等属性)
6. 返回 ToolResult(images 字段含标注截图,output 含元素列表)

标签页管理类(Tab)

工具 功能
browser_switch_tab(tab_id) 切换标签页(4 字符 ID)
browser_close_tab(tab_id) 关闭标签页

下拉框类(Dropdown)

工具 功能
browser_get_dropdown_options(index) 获取下拉框选项
browser_select_dropdown_option(index, text) 选择下拉框选项

内容提取类(Content Extraction)

工具 功能 底层
browser_extract_content(query, extract_links) LLM 驱动的结构化数据提取 tools.extract() + Qwen LLM
browser_read_long_content(goal, source) 智能长内容读取(自动检测 PDF) tools.read_long_content()
browser_get_page_html() 获取完整 HTML CDP Runtime.evaluate
browser_download_direct_url(url, save_name) HTTP 直链下载 httpx.AsyncClient 流式下载

内容提取的 LLM 适配

extraction_adapterbaseClass.py:1387-1409)将 browser-use 的 LangChain Runnable 接口适配为 Qwen LLM 调用:

async def extraction_adapter(input_data):
    response = await qwen_llm_call(messages=[{"role": "user", "content": prompt}])
    content = response["content"]
    # 自动清洗搜索引擎重定向 URL(Bing Base64 解码、Google url 参数提取)
    urls = re.findall(r'https?://[^\s<>"\']+', content)
    for original_url in urls:
        clean_url = scrub_search_redirect_url(original_url)
        if clean_url != original_url:
            content = content.replace(original_url, clean_url)
    return Namespace(completion=content)

PDF 自动检测与下载

_detect_and_download_pdf_via_cdpbaseClass.py:1458-1550):

1. 检查 URL 是否以 .pdf 结尾
2. 如果不明显,通过 CDP 检查 document.contentType
3. 确认是 PDF 后,通过浏览器内 fetch API 下载(自动携带 cookies/session)
4. 将 data URL 中的 base64 解码为 PDF 文件保存到本地
5. 将 source 参数改为本地文件路径,交给 pypdf 解析

Cookie 管理类

工具 功能
browser_export_cookies(name, account) 导出当前域名 Cookie 到 .cache/.cookies/
browser_load_cookies(url, name) 自动匹配 Cookie 文件并注入浏览器
browser_ensure_login_with_cookies(cookie_type, url) 检查登录状态,需要时从 MySQL 查询 Cookie 注入

Cookie 加载匹配策略

browser_load_cookiesbaseClass.py:2046-2177):

1. 精确匹配:{domain}.json(如 xiaohongshu.com.json)
2. 前缀匹配:{domain}*.json
3. 模糊匹配:{主域名}*.json(如 xiaohongshu*.json)
4. 未找到时:根据 auto_navigate 参数决定是否直接导航到目标页面

从 MySQL 加载 Cookie

browser_ensure_login_with_cookies 流程:

1. 导航到目标 URL
2. 执行 JS 检测登录状态(查找登录按钮/用户头像)
3. 如果需要登录:
   a. 从 agent_channel_cookies 表查询 Cookie
   b. 解析 Cookie(支持 JSON 数组、JSON 对象、分号分隔字符串)
   c. 通过 CDP _cdp_set_cookies 注入
   d. 刷新页面

控制流类

工具 功能
browser_evaluate(code) 在页面执行任意 JavaScript
browser_wait_for_user_action(message, timeout) 暂停等待用户手动操作(如验证码)
browser_done(text, success) 标记任务完成

结果转换机制

所有工具的返回值统一为 agent 框架的 ToolResult,通过 action_result_to_tool_result() 将 browser-use 的 ActionResult 转换:

# baseClass.py:407-431
def action_result_to_tool_result(result: ActionResult, title: str = None) -> ToolResult:
    if result.error:
        return ToolResult(title=..., output="", error=result.error,
                         long_term_memory=result.long_term_memory or result.error)
    return ToolResult(title=..., output=result.extracted_content or "",
                     long_term_memory=..., metadata=result.metadata or {})

ToolResult 支持双层记忆管理(agent/tools/models.py):

  • output:完整内容,可配置 include_output_only_once=True 只给 LLM 看一次
  • long_term_memory:简短摘要,永久保留在对话历史中
  • images:截图等图片数据(base64)

URL 清洗

scrub_search_redirect_url()baseClass.py:1347-1385)自动解析搜索引擎重定向链接:

引擎 处理方式
Bing 提取 u 参数,去掉 a1 前缀,Base64 解码
Google 提取 url 参数,URL 解码
通用 检查 target/dest/destination/link 参数

文件存储

所有浏览器产生的文件统一存储在工作目录下:

.cache/
├── .browser_use_files/    # 浏览器下载、截图、PDF 等临时文件
└── .cookies/              # Cookie 持久化文件({domain}_{account}.json)

Skill 集成

agent/skill/skills/browser.md 作为 Skill 注入到 LLM 的 system prompt,指导 LLM 正确使用浏览器工具。核心规则:

  1. 操作前必须先通过 browser_get_visual_selector_map 获取元素索引
  2. 任何触发页面变化的操作后都要 browser_wait
  3. 登录优先用 browser_load_cookies,首次登录需请求人类协助
  4. 优先使用高级提取工具(extract_content/read_long_content)而非手动解析 DOM