博客
首页归档关于友链搜索

鄂ICP备19019526号

© 2026 博客 · 保留所有权利

  1. 首页
  2. /文章
  3. /Browser Use Agent 深度解析:从技术原理到 Harness Engineering 的范式革命

目录

  • Browser Use Agent 深度解析:从技术原理到 Harness Engineering 的范式革命
  • 引言:从脚本自动化到智能体驱动
  • 第一部分:Browser Use Agent 技术架构全解
  • 1.1 整体架构概览
  • 1.2 LLM 集成层:Agent 的大脑
  • 1.3 浏览器控制引擎:Playwright 与 CDP
  • 1.4 视觉理解系统:DOM + Vision 的混合方案
  • 1.5 Agent Loop:核心控制循环
  • 1.6 工具系统(Tools)
  • 1.7 Agent 输出与历史记录
  • 基本信息
  • 分析方法
  • 结构化输出
  • 第二部分:Harness Engineering——Agent 时代的工程范式
  • 2.1 什么是 Harness Engineering?
  • 2.2 Harness 的三层架构
  • 2.3 Harness Engineering 的七大核心组件
  • 组件一:工具选择与集成
  • 组件二:记忆系统
  • 组件三:护栏与验证
  • 组件四:重试与错误恢复
  • 组件五:上下文管理
  • 组件六:可观测性与追踪
  • 组件七:Prompt 模板与版本控制
  • 2.4 OpenAI 的实践经验:给 Agent 一张地图,而非一本千页说明书
  • 2.5 通过不变量强制执行质量,而非微观管理
  • 第三部分:将 Harness Engineering 应用于 Browser Use Agent
  • 3.1 Browser Use 中的 Harness 体现
  • 3.2 构建生产级 Browser Use Agent 的 Harness 架构
  • ============================================
  • Layer 1: 模型接口 - 配置与降级策略
  • ============================================
  • 主模型
  • 降级模型(当主模型失败时)
  • 页面提取专用小模型(降低成本)
  • ============================================
  • Layer 2: 运行时环境 - 工具、护栏、验证
  • ============================================
  • 结构化输出 Schema(输出护栏)
  • 自定义工具
  • 浏览器配置(执行护栏)
  • ============================================
  • Layer 3: 编排层 - 重试、降级、反馈回路
  • ============================================
  • ============================================
  • 入口
  • ============================================
  • 3.3 上下文管理:给 Agent 一张地图
  • 3.4 记忆系统的深度设计
  • 短期记忆:步骤历史
  • 中期记忆:长期记忆注入
  • 跨会话记忆:对话持久化
  • 3.5 护栏的多层防御
  • 执行护栏:浏览器级别
  • 工具护栏:禁用危险操作
  • 输出护栏:结构化验证
  • 人工审批护栏
  • 第四部分:高级模式与最佳实践
  • 4.1 多 Agent 协作
  • Agent 1:负责搜索和收集信息
  • Agent 2:负责分析和总结
  • 顺序执行
  • 4.2 敏感操作处理
  • 方式一:通过 sensitive_data 参数注入(不会出现在日志中)
  • 方式二:通过自定义工具注入 2FA 代码
  • 4.3 连接已有浏览器会话
  • 方式一:连接到已运行的 Chrome 实例(通过 CDP)
  • 先启动 Chrome:chrome --remote-debugging-port=9222
  • 方式二:使用持久化的用户数据目录(保留 Cookie 和登录状态)
  • 方式三:使用 Browser Use Cloud(自动处理验证码和反爬)
  • 4.4 性能优化策略
  • Token 成本优化
  • 执行速度优化
  • 成本与质量的权衡矩阵
  • 4.5 调试与可观测性
  • 启用详细日志
  • 执行后分析
  • 自定义指标收集
  • 第五部分:Browser Use Agent 的技术挑战与前沿探索
  • 5.1 当前面临的核心挑战
  • 挑战一:反爬与验证码
  • 挑战二:动态内容与 SPA
  • 或者在 Agent 层面使用 wait 动作
  • Agent 可以主动调用 wait 工具等待特定条件
  • 挑战三:幻觉与错误累积
  • 挑战四:成本控制
  • 5.2 Browser Use 与竞品对比
  • 5.3 前沿探索:Agent 的自我进化
  • 未来可能的自进化模式
  • 1. Agent 从失败中学习,自动生成新的工具
  • 2. Agent 自动优化 Prompt
  • 基于历史成功/失败记录,调整系统 Prompt 中的指导信息
  • 3. Agent 自动生成测试用例
  • 对关键业务流程生成回归测试,确保网站变化时及时发现
  • 第六部分:实战案例——构建一个生产级的竞品监控系统
  • 6.1 需求描述
  • 6.2 完整实现
  • ============================================
  • 数据模型(输出护栏)
  • ============================================
  • ============================================
  • 工具定义
  • ============================================
  • 模拟数据库
  • ============================================
  • Harness 编排层
  • ============================================
  • ============================================
  • 入口
  • ============================================
  • 6.3 架构复盘
  • 第七部分:展望——Browser Agent 的未来
  • 7.1 从工具到基础设施
  • 未来可能的声明式任务定义
  • 7.2 Agent-Native 软件架构
  • 7.3 Harness Engineer 的角色定义
  • 7.4 开放问题
  • 结语

Browser Use Agent 深度解析:从技术原理到 Harness Engineering 的范式革命

2026年3月26日约 50 分钟阅读2 次阅读

Let我先搜索一下最新的相关资料。

让我继续搜索更多关于 Harness Engineering 和 browser-use 的深度技术资料。

让我再搜索一些补充资料,然后开始撰写文章。

好的,资料已经收集充分,下面开始撰写完整的技术文章。


Browser Use Agent 深度解析:从技术原理到 Harness Engineering 的范式革命

引言:从脚本自动化到智能体驱动

多年来,开发者使用 Selenium、Puppeteer、Playwright 等工具自动化浏览器操作。这些工具功能强大,但本质上是"脆性"的——我们编写依赖特定 CSS 选择器、XPath 或元素 ID 的脚本,一旦网站 UI 发生变化,脚本就会崩溃,维护成本极高。

2024 年末至 2025 年初,一种全新的范式开始崛起:让大语言模型(LLM)直接"看懂"并"操控"浏览器。Browser Use 正是这一浪潮中最具代表性的开源项目之一。它由 Magnus Müller 和 Gregor Žunić 创建,是一个基于 Python 的开源库,通过将 LLM 的推理能力与 Playwright 的浏览器控制能力相结合,让 AI Agent 能够像人类一样浏览网页、点击按钮、填写表单、提取数据。

与此同时,2025 年下半年,OpenAI 在其内部 Codex 项目中提出了一个影响深远的工程理念——Harness Engineering(驾驭工程)。这一理念的核心观点是:在 AI Agent 时代,工程师的核心工作不再是编写代码,而是设计环境、明确意图、构建反馈回路,使 Agent 能够可靠地工作。

本文将深入剖析 Browser Use Agent 的技术架构、核心原理和实现细节,并结合 Harness Engineering 的最新理念,探讨如何在生产环境中构建可靠的浏览器自动化智能体系统。


第一部分:Browser Use Agent 技术架构全解

1.1 整体架构概览

Browser Use 的架构可以用一条清晰的数据流管线来描述:

用户输入任务
    → LLM 处理与推理
    → DOM 提取 + 视觉分析
    → 动作规划
    → Playwright 执行
    → 状态更新
    → 反馈回路(循环直到任务完成)

这条管线的每一个环节都经过精心设计,形成了一个闭环的 Agent Loop(智能体循环)。整个系统由三大核心组件构成:

  • LLM 集成层(大脑)
  • 浏览器控制引擎(手脚)
  • 视觉理解系统(眼睛)

1.2 LLM 集成层:Agent 的大脑

LLM 集成层是 Browser Use 的决策中枢。它负责:

  • 接收用户的自然语言任务描述
  • 理解当前网页的状态(DOM 结构 + 截图)
  • 推理下一步应该执行什么动作
  • 判断任务是否已经完成

Browser Use 支持多种 LLM 后端,包括:

提供商模型示例适用场景
Browser Use 官方ChatBrowserUse最高精度 + 最快速度 + 最低 Token 成本
OpenAIGPT-4o / GPT-4.1-mini复杂推理与导航
AnthropicClaude Sonnet 4高质量推理
GoogleGemini 2.0 Flash视觉密集型任务
本地部署Ollama (Llama 3.1)隐私敏感场景,零 API 成本

一个最简单的 Agent 创建方式如下:

from browser_use import Agent, ChatBrowserUse
from dotenv import load_dotenv
import asyncio

load_dotenv()

async def main():
    llm = ChatBrowserUse()
    agent = Agent(
        task="Find the number 1 post on Show HN",
        llm=llm
    )
    await agent.run()

if __name__ == "__main__":
    asyncio.run(main())

这段代码背后发生的事情远比表面复杂。agent.run() 启动了一个迭代循环,每一轮循环中:

  1. 系统提取当前页面的 DOM 状态和截图
  2. 将状态信息与任务描述一起发送给 LLM
  3. LLM 返回一个或多个动作指令(如 click、type、navigate)
  4. Playwright 执行这些动作
  5. 系统观察执行结果,更新状态,进入下一轮

1.3 浏览器控制引擎:Playwright 与 CDP

Browser Use 底层使用 Playwright 作为浏览器自动化框架,通过 Chrome DevTools Protocol (CDP) 与 Chromium 浏览器通信。相比传统的 HTTP 驱动方式,CDP 基于 WebSocket 的双向通信具有更低的延迟和更丰富的控制能力。

Browser 对象的配置非常灵活:

from browser_use import Browser

browser = Browser(
    headless=False,                    # 是否无头模式
    window_size={'width': 1920, 'height': 1080},
    highlight_elements=True,           # 高亮交互元素(辅助 AI 视觉识别)
    paint_order_filtering=True,        # 过滤被遮挡的元素,优化 DOM 树
    minimum_wait_page_load_time=0.25,  # 页面加载最小等待时间
    wait_for_network_idle_page_load_time=0.5,
    wait_between_actions=0.5,          # 动作间等待时间
)

关键的浏览器能力包括:

  • 多标签页管理:Agent 可以打开、切换、关闭多个标签页
  • 域名限制:通过 allowed_domains 和 prohibited_domains 控制 Agent 的访问范围
  • 代理支持:内置 ProxySettings,支持认证代理
  • 云端浏览器:通过 use_cloud=True 一键接入 Browser Use Cloud,自动绕过验证码和反爬检测
  • 视频录制:支持录制 Agent 操作过程,便于调试和审计

1.4 视觉理解系统:DOM + Vision 的混合方案

这是 Browser Use 最具创新性的设计之一。传统自动化工具只依赖 DOM 结构来定位元素,但 DOM 信息往往不完整——动态渲染的内容、Canvas 元素、复杂的 CSS 布局都可能导致 DOM 分析失效。

Browser Use 采用了 DOM + Vision 的混合分析策略:

  • DOM 提取:解析页面的 HTML 结构,识别可交互元素(按钮、链接、输入框等),为每个元素分配索引编号
  • 视觉分析:对页面进行截图,利用 LLM 的多模态能力(如 GPT-4o 的视觉理解)分析截图内容
  • 元素高亮:在截图上高亮标注可交互元素,帮助 LLM 更准确地理解页面布局

Agent 的视觉模式有三种配置:

agent = Agent(
    task="...",
    llm=llm,
    use_vision="auto",        # "auto" | True | False
    vision_detail_level="auto" # "low" | "high" | "auto"
)
  • "auto"(默认):包含截图工具,但仅在 LLM 主动请求时使用视觉
  • True:每一步都包含截图
  • False:完全禁用视觉,仅依赖 DOM

这种混合方案的优势在于:即使一个按钮没有明确的 id 或 class,Agent 也能通过视觉识别找到它——就像人类用户一样。

1.5 Agent Loop:核心控制循环

Agent 的核心是一个迭代控制循环,其伪代码如下:

while not done and steps < max_steps:
    # 1. 获取当前页面状态
    state = extract_dom() + take_screenshot()
  
    # 2. 组装上下文,调用 LLM
    response = llm.call(system_prompt + state + task + history)
  
    # 3. 解析 LLM 返回的动作
    actions = parse_actions(response)
  
    # 4. 执行动作(最多 max_actions_per_step 个)
    for action in actions:
        result = playwright.execute(action)
        if page_changed:
            break  # 页面变化后停止执行剩余动作
  
    # 5. 更新历史记录
    history.append(state, actions, result)
  
    # 6. 检查是否完成
    if response.is_done:
        done = True

几个关键的设计决策:

  • max_actions_per_step(默认 3):每一步 LLM 最多可以输出 3 个动作。例如填写表单时,Agent 可以一次性输出"填写用户名 → 填写密码 → 点击登录"三个动作,减少 LLM 调用次数
  • max_failures(默认 3):连续失败 3 次后停止,避免无限循环
  • use_thinking(默认 True):允许 Agent 在输出动作前进行显式推理(类似 Chain-of-Thought)
  • flash_mode:快速模式,跳过评估和推理步骤,仅使用记忆,适合简单任务

1.6 工具系统(Tools)

Browser Use 提供了一套丰富的内置工具,同时支持开发者自定义扩展:

内置工具分类:

  • 导航控制:search、navigate、go_back、wait
  • 页面交互:click、input、scroll、find_text、send_keys、upload_file
  • 标签管理:switch、close
  • 内容提取:extract(使用 LLM 从页面提取结构化数据)
  • 视觉分析:screenshot
  • 表单控制:dropdown_options、select_dropdown
  • 文件操作:write_file、read_file、replace_file
  • JavaScript 执行:evaluate(执行自定义 JS 代码)
  • 任务完成:done

自定义工具的注册非常简洁:

from browser_use import Tools, Agent, ActionResult, BrowserSession

tools = Tools()

@tools.action(description='Save extracted data to database')
async def save_to_db(data: dict, browser_session: BrowserSession) -> ActionResult:
    # 你的数据库写入逻辑
    db.insert(data)
    return ActionResult(extracted_content=f"Saved {len(data)} records")

@tools.action(description='Get 2FA code from authenticator')
async def get_2fa_code() -> ActionResult:
    code = authenticator.get_current_code()
    return ActionResult(extracted_content=f"2FA code: {code}")

agent = Agent(
    task="Login to the dashboard and export monthly report",
    llm=llm,
    tools=tools
)

注意 browser_session: BrowserSession 这个参数名必须精确匹配——Agent 通过参数名注入依赖,使用错误的名称会导致工具静默失败。

1.7 Agent 输出与历史记录

agent.run() 返回一个 AgentHistoryList 对象,包含完整的执行历史:

history = await agent.run()

# 基本信息
history.urls()                    # 访问过的 URL 列表
history.action_names()            # 执行过的动作名称
history.extracted_content()       # 提取的内容列表
history.errors()                  # 错误列表

# 分析方法
history.final_result()            # 最终结果
history.is_done()                 # 是否完成
history.is_successful()           # 是否成功
history.model_thoughts()          # Agent 的推理过程
history.number_of_steps()         # 总步数
history.total_duration_seconds()  # 总耗时

# 结构化输出
history.structured_output         # 当使用 output_model_schema 时

这套历史记录系统为调试、审计和优化提供了完整的可观测性基础。


第二部分:Harness Engineering——Agent 时代的工程范式

2.1 什么是 Harness Engineering?

2025 年 8 月,OpenAI 的 Codex 团队启动了一项激进的实验:构建一款软件产品,其中没有一行代码是人工编写的。五个月后,这个代码仓库达到了约一百万行代码,由仅三名工程师(后扩展到七名)通过指导 Codex Agent 完成。约 1,500 个 Pull Request 被打开与合并,平均每位工程师每天处理 3.5 个 PR。

在这个过程中,他们提出了 Harness Engineering 的概念:

当软件工程团队的主要工作不再是编写代码,而是设计环境、明确意图和构建反馈回路,从而使 AI Agent 能够可靠地工作时——这就是 Harness Engineering。

用一个比喻来理解:LLM 是引擎,Harness(驾驭系统)是整辆车——底盘、变速箱、方向盘、刹车、燃油系统和电子设备,将原始动力转化为可控、可靠的运动。没有人会买一台裸露的引擎。

2.2 Harness 的三层架构

+--------------------------------------------------+
|  Layer 3: 编排层 (Orchestration)                   |
|  工作流逻辑、Agent 协调、路由                        |
+--------------------------------------------------+
|  Layer 2: 运行时环境 (Runtime Environment)          |
|  工具、记忆、护栏、I/O 处理                          |
+--------------------------------------------------+
|  Layer 1: 模型接口 (Model Interface)               |
|  API 调用、Prompt 组装、响应解析                     |
+--------------------------------------------------+
  • Layer 1(模型接口):如何调用模型——Prompt 模板、参数配置、响应解析、API 错误处理
  • Layer 2(运行时环境):模型周围的一切——工具定义、记忆存储、输入验证、输出护栏、上下文窗口管理
  • Layer 3(编排层):多次调用如何协调——Agent 循环、任务分解、条件分支、人工审批门、并行执行

2025 年大多数团队只构建了 Layer 1。而在 2026 年能够交付可靠 AI 产品的团队,三层都经过了精心工程化。

2.3 Harness Engineering 的七大核心组件

组件一:工具选择与集成

工具赋予模型超越文本生成的能力:网页搜索、代码执行、数据库查询、API 调用、文件操作。

关键设计决策:

  • 暴露哪些工具(多不一定好——工具泛滥会让模型困惑)
  • 如何描述工具(Schema 设计直接影响工具调用准确率)
  • 沙箱与权限(模型实际能做什么 vs 它认为自己能做什么)
  • 超时与降级行为(工具调用失败时怎么办)

最佳实践:从 3-5 个定义清晰的工具开始,每个工具有明确的、不重叠的用途。仅在有证据表明模型需要时才添加新工具。

组件二:记忆系统

LLM 是无状态的,每次调用都从零开始。记忆系统创造了连续性的效果。

记忆类型范围实现方式用途
对话记忆单次会话消息历史缓冲聊天应用
工作记忆单个任务草稿本/KV 存储多步推理
情景记忆跨会话向量数据库 + 摘要用户偏好、历史交互
语义记忆全局知识库/RAG领域知识、文档
程序记忆全局工具定义 + 示例已学习的工作流

关键问题不是"要不要加记忆",而是"什么应该被记住、记多久、如何检索"。

组件三:护栏与验证

护栏是 LLM 输出与生产后果之间的安全网,在三个阶段运作:

输入护栏:

  • 内容过滤(阻止 Prompt 注入、PII 泄露)
  • Schema 验证
  • 速率限制和成本控制

输出护栏:

  • 格式验证(JSON Schema、类型检查)
  • 事实性验证(与源文档交叉引用)
  • 安全分类器(毒性、偏见、幻觉检测)
  • 业务逻辑检查

执行护栏:

  • 工具调用审批(破坏性操作需人工确认)
  • 资源限制(最大迭代次数、最大 Token 数、单次请求最大成本)
  • 死锁检测(Agent 陷入循环)

组件四:重试与错误恢复

请求
  |
  v
[尝试 1: 主模型]
  |-- 成功 --> 验证 --> 返回
  |-- 失败 --> [尝试 2: 重新组织 Prompt]
                  |-- 成功 --> 验证 --> 返回
                  |-- 失败 --> [尝试 3: 降级模型]
                                  |-- 成功 --> 验证 --> 返回
                                  |-- 失败 --> [升级到人工处理]

重试策略的层次:

  1. 简单重试——相同 Prompt,相同模型(处理瞬时 API 错误)
  2. 重组重试——根据错误修改 Prompt
  3. 模型降级——切换到备用模型
  4. 分解重试——将失败的任务拆分为更小的子任务
  5. 人工升级——N 次失败后路由到人工操作员

组件五:上下文管理

随着上下文窗口扩展到 100 万+ Token,挑战不再是容量,而是策展。将所有内容塞入上下文会降低性能,策略性的上下文组装才能提升效果。

  • 相关性排序:使用 Embedding 为每个查询浮现最相关的文档
  • 时效性加权:优先使用最新信息
  • 压缩:摘要旧的上下文以保留含义同时减少 Token
  • 分块:将大文档拆分为语义有意义的段落
  • 优先区域:将最关键的信息放在上下文的开头和结尾(模型对这些位置的注意力更高)

组件六:可观测性与追踪

你无法改进你无法衡量的东西。需要追踪的指标:

  • 每个组件的延迟(模型调用、工具执行、检索、验证)
  • 每次请求的 Token 使用量和成本
  • 按任务类型的成功/失败率
  • 护栏触发频率
  • 用户满意度信号
  • 漂移检测(输出质量随时间退化)

组件七:Prompt 模板与版本控制

在 Harness Engineering 中,Prompt 作为代码管理,而非临时字符串:

  • 存储在版本控制的模板文件中
  • 使用变量进行动态上下文注入
  • 用生产流量 A/B 测试 Prompt 变体
  • 追踪 Prompt 性能指标
  • 将 Prompt 逻辑与应用逻辑分离

2.4 OpenAI 的实践经验:给 Agent 一张地图,而非一本千页说明书

OpenAI Codex 团队在实践中学到的最重要教训之一:

上下文是稀缺资源。一个巨大的指令文件会挤掉任务、代码和相关文档——Agent 要么会错过关键约束条件,要么开始针对错误的约束条件进行优化。

他们最初尝试将所有指导信息放入一个巨大的 AGENTS.md 文件,结果失败了。文件变得陈旧,Agent 无法判断哪些信息仍然有效。

最终方案是将 AGENTS.md 视为目录(约 100 行),指向更深层次的结构化知识源:

AGENTS.md (约 100 行,作为地图)
  ├── docs/design/        # 设计文档(含验证状态)
  ├── docs/architecture/  # 架构文档(域和包分层的顶层地图)
  ├── docs/quality/       # 质量文档(每个领域和层的评分)
  ├── docs/plans/         # 执行计划(进度和决策日志)
  └── docs/principles/    # 核心理念(Agent 优先的操作原则)

这实现了渐进式披露:Agent 从一个小而稳定的切入点开始,被指导下一步该去哪里查看,而不是一开始就被淹没。

2.5 通过不变量强制执行质量,而非微观管理

OpenAI 团队围绕一个严格的架构模型构建应用,每个业务域划分为固定的层:

Types → Config → Repo → Service → Runtime → UI

依赖方向经过严格验证,仅允许有限的边。这些约束通过自定义 Linter(由 Codex 自己生成)和结构测试机械地强制执行。

关键洞察:在以人为本的工作流中,这些规则可能让人感到迂腐。但有了 Agent,它们就成了倍增器——一旦编码,就能立即应用于所有地方。

他们还建立了"垃圾回收"机制:定期运行后台 Codex 任务,扫描偏差、更新质量等级、发起有针对性的重构 PR。技术债务就像高息贷款——持续小额偿还,远好过让债务累积后痛苦地一次性解决。


第三部分:将 Harness Engineering 应用于 Browser Use Agent

3.1 Browser Use 中的 Harness 体现

回过头来看 Browser Use 的架构,它实际上已经内置了 Harness Engineering 的许多核心理念:

Harness 组件Browser Use 中的实现
工具系统内置 20+ 工具 + 自定义 @tools.action 扩展
护栏allowed_domains、prohibited_domains、max_steps、max_failures
记忆AgentHistoryList、max_history_items 控制记忆窗口
重试max_failures + final_response_after_failure
可观测性完整的执行历史、截图记录、视频录制、HAR 追踪
上下文管理page_extraction_llm(可用小模型提取页面内容)、DOM 过滤
编排Agent Loop + 多动作步骤 + thinking 模式

3.2 构建生产级 Browser Use Agent 的 Harness 架构

以下是一个将 Harness Engineering 理念完整应用于 Browser Use Agent 的生产架构设计:

import asyncio
import logging
from browser_use import Agent, Browser, Tools, ActionResult, ChatBrowserUse
from pydantic import BaseModel

# ============================================
# Layer 1: 模型接口 - 配置与降级策略
# ============================================

# 主模型
primary_llm = ChatBrowserUse()

# 降级模型(当主模型失败时)
from browser_use import ChatOpenAI
fallback_llm = ChatOpenAI(model="gpt-4.1-mini")

# 页面提取专用小模型(降低成本)
extraction_llm = ChatOpenAI(model="gpt-4.1-mini")

# ============================================
# Layer 2: 运行时环境 - 工具、护栏、验证
# ============================================

# 结构化输出 Schema(输出护栏)
class ResearchResult(BaseModel):
    title: str
    url: str
    summary: str
    confidence: float

# 自定义工具
tools = Tools(exclude_actions=['evaluate'])  # 移除 JS 执行(安全护栏)

@tools.action(
    description='Save research results to database',
    allowed_domains=['*.example.com']  # 域名限制护栏
)
async def save_results(results: list[dict]) -> ActionResult:
    # 输出验证
    validated = [ResearchResult(**r) for r in results]
    # 持久化逻辑
    return ActionResult(
        extracted_content=f"Saved {len(validated)} results",
        long_term_memory="Results have been saved, no need to save again"
    )

@tools.action(description='Ask human for confirmation before proceeding')
async def human_confirmation(question: str) -> ActionResult:
    answer = input(f"[Agent asks]: {question} (y/n) > ")
    return ActionResult(extracted_content=f"Human responded: {answer}")

# 浏览器配置(执行护栏)
browser = Browser(
    headless=True,
    window_size={'width': 1920, 'height': 1080},
    allowed_domains=['*.google.com', '*.github.com'],  # 域名白名单
    prohibited_domains=['*.gambling-site.net'],          # 域名黑名单
    highlight_elements=True,
    paint_order_filtering=True,
    record_video_dir='./recordings',  # 可观测性:录制操作视频
    minimum_wait_page_load_time=0.5,
    wait_for_network_idle_page_load_time=1.0,
)

# ============================================
# Layer 3: 编排层 - 重试、降级、反馈回路
# ============================================

async def run_with_harness(task: str, max_retries: int = 3):
    """带完整 Harness 的 Agent 执行函数"""
  
    for attempt in range(max_retries):
        try:
            # 选择模型(首次用主模型,重试用降级模型)
            llm = primary_llm if attempt == 0 else fallback_llm
          
            agent = Agent(
                task=task,
                llm=llm,
                browser=browser,
                tools=tools,
                page_extraction_llm=extraction_llm,
                use_vision="auto",
                max_actions_per_step=3,
                max_failures=3,
                use_thinking=True,
                output_model_schema=ResearchResult,
                save_conversation_path=f'./logs/conversation_{attempt}.json',
                generate_gif=True,
            )


            history = await agent.run(max_steps=50)
          
            # ========== 执行后验证(输出护栏) ==========
            if history.is_done() and history.is_successful():
                result = history.structured_output
              
                # 业务逻辑验证
                if result and result.confidence >= 0.7:
                    logging.info(f"Task completed successfully on attempt {attempt + 1}")
                    logging.info(f"Steps: {history.number_of_steps()}, "
                                 f"Duration: {history.total_duration_seconds():.1f}s")
                    return result
                else:
                    logging.warning(f"Low confidence result ({result.confidence}), retrying...")
                    continue
          
            # 任务未完成但未报错——可能是 max_steps 耗尽
            if not history.is_done():
                logging.warning(f"Agent did not finish within max_steps on attempt {attempt + 1}")
                # 分析失败原因,调整下一轮策略
                thoughts = history.model_thoughts()
                logging.debug(f"Last thoughts: {thoughts[-3:] if thoughts else 'None'}")
                continue
              
        except Exception as e:
            logging.error(f"Attempt {attempt + 1} failed with error: {e}")
            if attempt == max_retries - 1:
                # 最终降级:人工升级
                logging.critical("All retries exhausted. Escalating to human operator.")
                raise
            await asyncio.sleep(2 ** attempt)  # 指数退避
  
    return None


# ============================================
# 入口
# ============================================
async def main():
    result = await run_with_harness(
        task="Go to GitHub trending repositories, find the top 3 Python projects "
             "from this week, extract their name, URL, and description."
    )
    if result:
        print(f"Result: {result}")

if __name__ == "__main__":
    asyncio.run(main())

这段代码展示了一个完整的三层 Harness 架构:

  • Layer 1 配置了主模型与降级模型,以及专用的页面提取小模型以降低成本
  • Layer 2 定义了工具(含安全限制)、结构化输出 Schema、浏览器安全配置
  • Layer 3 实现了带指数退避的重试循环、模型降级策略、输出质量验证和人工升级机制

3.3 上下文管理:给 Agent 一张地图

Browser Use 中的上下文管理是一个容易被忽视但极其关键的环节。每一步 Agent 循环都需要将以下信息塞入 LLM 的上下文窗口:

系统 Prompt(角色定义 + 工具描述)
  + 任务描述
  + 当前页面 DOM(可能非常大)
  + 截图(如果启用视觉)
  + 历史记录(之前的动作和结果)
  + 自定义指令

当页面 DOM 很大、历史步骤很多时,上下文窗口会迅速膨胀。Browser Use 提供了几个关键的上下文管理机制:

agent = Agent(
    task="...",
    llm=llm,
  
    # 上下文压缩:用小模型提取页面关键内容,而非传递完整 DOM
    page_extraction_llm=ChatOpenAI(model="gpt-4.1-mini"),
  
    # 历史窗口:仅保留最近 N 步的完整历史
    # 更早的历史会被压缩为摘要
    max_history_items=20,
  
    # DOM 过滤:paint_order_filtering 移除被遮挡的元素
    # 减少 DOM 噪声
    browser=Browser(paint_order_filtering=True),
  
    # 视觉细节级别:low 模式使用更少的 Token
    vision_detail_level="low",
)

这与 OpenAI Codex 团队的"地图而非说明书"理念完全一致——不要把所有信息一股脑塞给 Agent,而是提供一个精简的、分层的信息结构,让 Agent 按需深入。

3.4 记忆系统的深度设计

Browser Use 的记忆系统分为三个层次:

短期记忆:步骤历史

每一步的动作和结果都记录在 AgentHistoryList 中。这是 Agent 的"工作记忆",让它知道自己做过什么、看到过什么。

中期记忆:长期记忆注入

通过自定义工具的 ActionResult,可以向 Agent 注入持久化的记忆:

@tools.action(description='Login to the system')
async def login(username: str, password: str) -> ActionResult:
    # ... 登录逻辑 ...
    return ActionResult(
        extracted_content="Login successful",
        # 这条信息会被持久化到 Agent 的记忆中
        # 在后续所有步骤中都可见
        long_term_memory="Already logged in as admin. No need to login again."
    )

跨会话记忆:对话持久化

通过 save_conversation_path 保存完整的对话历史,可以在后续会话中加载:

agent = Agent(
    task="Continue the research from yesterday",
    llm=llm,
    save_conversation_path='./sessions/research_session.json',
)

在 Harness Engineering 的框架下,记忆系统的设计原则是:

  1. 选择性记忆——不是所有信息都值得记住,过多的记忆会稀释关键信息
  2. 结构化存储——使用明确的类型(事实、偏好、约束)而非原始文本
  3. 衰减机制——旧的、不再相关的记忆应该被淡化或移除
  4. 检索优先——记忆的价值不在于存储,而在于能否在正确的时刻被检索出来

3.5 护栏的多层防御

在生产环境中,Browser Use Agent 的护栏需要从多个维度构建:

┌─────────────────────────────────────────────────┐
│  第一层:输入护栏                                  │
│  - 任务描述的内容过滤(防止 Prompt 注入)           │
│  - 敏感信息脱敏(PII 检测与替换)                   │
│  - 任务复杂度评估(过于复杂的任务拒绝或拆分)        │
├─────────────────────────────────────────────────┤
│  第二层:执行护栏                                  │
│  - 域名白名单/黑名单                               │
│  - 工具权限控制(禁用危险工具如 evaluate)           │
│  - 最大步数限制(max_steps)                       │
│  - 最大失败次数(max_failures)                    │
│  - 单步最大动作数(max_actions_per_step)           │
│  - 页面加载超时                                    │
│  - 成本预算(Token 消耗上限)                       │
├─────────────────────────────────────────────────┤
│  第三层:输出护栏                                  │
│  - 结构化输出验证(output_model_schema)            │
│  - 业务逻辑检查(如置信度阈值)                     │
│  - 敏感数据过滤(防止 Agent 泄露密码等)             │
│  - 人工审批门(关键操作前请求确认)                  │
└─────────────────────────────────────────────────┘

一个实际的多层护栏实现:

from browser_use import Agent, Browser, Tools, ActionResult

# 执行护栏:浏览器级别
browser = Browser(
    allowed_domains=['*.target-site.com'],
    prohibited_domains=['*.social-media.com', '*.payment.com'],
    headless=True,
)

# 工具护栏:禁用危险操作
tools = Tools(
    exclude_actions=[
        'evaluate',      # 禁止执行任意 JavaScript
        'upload_file',   # 禁止上传文件
        'write_file',    # 禁止写入本地文件
    ]
)

# 输出护栏:结构化验证
from pydantic import BaseModel, validator

class SafeOutput(BaseModel):
    data: str
    source_url: str
  
    @validator('data')
    def no_sensitive_info(cls, v):
        import re
        # 检测并拒绝包含信用卡号的输出
        if re.search(r'\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b', v):
            raise ValueError("Output contains potential credit card number")
        return v

# 人工审批护栏
@tools.action(description='Submit form with important data')
async def submit_form(data: dict) -> ActionResult:
    print(f"\n⚠️  Agent wants to submit: {data}")
    approval = input("Approve? (yes/no) > ")
    if approval.lower() != 'yes':
        return ActionResult(extracted_content="Human rejected the submission. Try a different approach.")
    # ... 执行提交 ...
    return ActionResult(extracted_content="Form submitted successfully")

第四部分:高级模式与最佳实践

4.1 多 Agent 协作

Browser Use 支持多个 Agent 共享同一个浏览器实例,实现协作式任务处理:

from browser_use import Agent, Browser

browser = Browser()

# Agent 1:负责搜索和收集信息
researcher = Agent(
    task="Search for the latest AI research papers on arxiv about browser agents",
    llm=llm,
    browser=browser,
)

# Agent 2:负责分析和总结
analyst = Agent(
    task="Analyze the papers found in the browser tabs and create a summary",
    llm=llm,
    browser=browser,  # 共享同一个浏览器
)

# 顺序执行
research_history = await researcher.run()
analysis_history = await analyst.run()

在 Harness Engineering 的编排层中,多 Agent 协作可以进一步扩展为:

async def orchestrated_research(topic: str):
    browser = Browser()
  
    # 阶段 1:并行搜索(多个 Agent 搜索不同来源)
    agents = [
        Agent(task=f"Search {source} for: {topic}", llm=llm, browser=browser)
        for source in ["Google Scholar", "arXiv", "GitHub"]
    ]
  
    # 注意:Browser Use 的浏览器实例是单线程的
    # 并行需要多个浏览器实例
    results = []
    for agent in agents:
        history = await agent.run()
        results.append(history.extracted_content())
  
    # 阶段 2:综合分析
    synthesizer = Agent(
        task=f"Based on these findings, create a comprehensive summary:\n{results}",
        llm=llm,
        browser=browser,
    )
  
    return await synthesizer.run()

4.2 敏感操作处理

处理登录、支付等敏感操作时,Browser Use 提供了安全的凭证注入机制:

import os

# 方式一:通过 sensitive_data 参数注入(不会出现在日志中)
agent = Agent(
    task="Login to the admin dashboard at https://admin.example.com",
    llm=llm,
    sensitive_data={
        'ADMIN_USERNAME': os.getenv('ADMIN_USERNAME'),
        'ADMIN_PASSWORD': os.getenv('ADMIN_PASSWORD'),
        'TOTP_SECRET': os.getenv('TOTP_SECRET'),
    },
)

# 方式二:通过自定义工具注入 2FA 代码
import pyotp

@tools.action(description='Get current 2FA code')
async def get_2fa() -> ActionResult:
    totp = pyotp.TOTP(os.getenv('TOTP_SECRET'))
    return ActionResult(extracted_content=f"Current 2FA code: {totp.now()}")

sensitive_data 中的值会被替换为占位符(如 x])出现在 Agent 的上下文中,Agent 知道在需要时使用这些占位符,但实际值不会被发送到 LLM——这是一个关键的安全设计。

4.3 连接已有浏览器会话

在某些场景下,你可能需要 Agent 接管一个已经登录的浏览器会话,而不是从零开始:

from browser_use import Browser

# 方式一:连接到已运行的 Chrome 实例(通过 CDP)
# 先启动 Chrome:chrome --remote-debugging-port=9222
browser = Browser(
    cdp_url="ws://localhost:9222/devtools/browser/..."
)

# 方式二:使用持久化的用户数据目录(保留 Cookie 和登录状态)
browser = Browser(
    user_data_dir='./chrome_profile',
    keep_alive=True,  # 任务结束后不关闭浏览器
)

# 方式三:使用 Browser Use Cloud(自动处理验证码和反爬)
browser = Browser(use_cloud=True)

4.4 性能优化策略

在生产环境中,Browser Use Agent 的性能优化需要从多个维度入手:

Token 成本优化

agent = Agent(
    task="...",
    llm=llm,
  
    # 1. 使用小模型提取页面内容(大幅降低主模型的输入 Token)
    page_extraction_llm=ChatOpenAI(model="gpt-4.1-mini"),
  
    # 2. 降低视觉细节级别
    vision_detail_level="low",
  
    # 3. 限制历史记忆窗口
    max_history_items=10,
  
    # 4. 启用 flash 模式(跳过评估和推理)
    flash_mode=True,  # 适合简单、重复性任务
  
    # 5. 增加单步动作数(减少 LLM 调用次数)
    max_actions_per_step=5,
)

执行速度优化

browser = Browser(
    headless=True,                          # 无头模式更快
    minimum_wait_page_load_time=0.1,        # 减少等待时间
    wait_for_network_idle_page_load_time=0.3,
    wait_between_actions=0.2,               # 减少动作间隔
)

成本与质量的权衡矩阵

场景推荐配置预估成本/任务
简单数据提取flash_mode + gpt-4.1-mini + no vision$0.01-0.05
复杂表单填写thinking + gpt-4o + auto vision$0.10-0.50
多页面研究thinking + ChatBrowserUse + auto vision$0.20-1.00
关键业务流程thinking + ChatBrowserUse + high vision + human approval$0.50-2.00

4.5 调试与可观测性

Browser Use 提供了丰富的调试工具:

import logging

# 启用详细日志
logging.basicConfig(level=logging.DEBUG)

agent = Agent(
    task="...",
    llm=llm,
  
    # 保存完整对话历史(含 LLM 输入输出)
    save_conversation_path='./debug/conversation.json',
  
    # 生成操作 GIF 动画
    generate_gif=True,
  
    # 录制操作视频
    browser=Browser(record_video_dir='./debug/videos'),
)

history = await agent.run()

# 执行后分析
print(f"总步数: {history.number_of_steps()}")
print(f"总耗时: {history.total_duration_seconds():.1f}s")
print(f"访问的 URL: {history.urls()}")
print(f"执行的动作: {history.action_names()}")
print(f"错误: {history.errors()}")
print(f"Agent 思考过程: {history.model_thoughts()}")

在 Harness Engineering 的可观测性框架下,还应该追踪:

# 自定义指标收集
class AgentMetrics:
    def __init__(self):
        self.runs = []
  
    async def track_run(self, task: str, agent: Agent):
        import time
        start = time.time()
        history = await agent.run()
        duration = time.time() - start
      
        metrics = {
            'task': task,
            'success': history.is_successful(),
            'steps': history.number_of_steps(),
            'duration_seconds': duration,
            'errors': len(history.errors()),
            'urls_visited': len(history.urls()),
            'actions_executed': len(history.action_names()),
            # Token 使用量需要从 LLM 回调中获取
        }
      
        self.runs.append(metrics)
      
        # 发送到监控系统
        # datadog.send_metrics(metrics)
        # prometheus.observe(metrics)
      
        return history

第五部分:Browser Use Agent 的技术挑战与前沿探索

5.1 当前面临的核心挑战

挑战一:反爬与验证码

现代网站越来越多地部署反自动化措施——CAPTCHA、设备指纹、行为分析、IP 信誉评分。Browser Use 通过以下方式应对:

  • Browser Use Cloud:官方云端浏览器服务,内置验证码绕过和反检测能力
  • 代理支持:通过 ProxySettings 配置代理 IP 轮换
  • 持久化会话:使用 user_data_dir 保持真实的浏览器指纹
  • 人工介入:当遇到无法自动处理的验证码时,通过自定义工具请求人工协助
@tools.action(description='Solve CAPTCHA that the agent cannot handle')
async def solve_captcha() -> ActionResult:
    input("Please solve the CAPTCHA in the browser window, then press Enter...")
    return ActionResult(extracted_content="CAPTCHA solved by human")

挑战二:动态内容与 SPA

单页应用(SPA)的动态渲染给 DOM 提取带来了挑战。页面内容可能在初始加载后通过 JavaScript 异步更新,Agent 在 DOM 提取时可能看到不完整的页面。

Browser Use 的应对策略:

browser = Browser(
    # 等待网络空闲,确保异步请求完成
    wait_for_network_idle_page_load_time=2.0,
    # 额外的页面加载等待
    minimum_wait_page_load_time=1.0,
)

# 或者在 Agent 层面使用 wait 动作
# Agent 可以主动调用 wait 工具等待特定条件

挑战三:幻觉与错误累积

LLM 可能"幻觉"出不存在的页面元素,或者误解页面内容。在多步任务中,一个早期的错误可能像滚雪球一样累积,导致后续所有步骤都偏离正确路径。

Harness Engineering 的应对策略:

  1. 检查点验证:在关键步骤后插入验证逻辑
  2. 状态断言:定期检查 Agent 是否仍在正确的页面/状态
  3. 回滚机制:检测到偏差时回退到上一个已知正确的状态
  4. 多路径探索:对关键决策点尝试多种方案
@tools.action(description='Verify current page matches expected state')
async def verify_state(expected_url_pattern: str, expected_content: str, 
                       browser_session) -> ActionResult:
    page = browser_session.current_page
    current_url = page.url
  
    if expected_url_pattern not in current_url:
        return ActionResult(
            extracted_content=f"WARNING: Expected URL containing '{expected_url_pattern}' "
                            f"but got '{current_url}'. Navigation may have gone wrong.",
            long_term_memory=f"State verification failed. Current URL: {current_url}"
        )
  
    content = await page.content()
    if expected_content.lower() not in content.lower():
        return ActionResult(
            extracted_content=f"WARNING: Expected content '{expected_content}' not found on page."
        )
  
    return ActionResult(extracted_content="State verification passed.")

挑战四:成本控制

每一步 Agent 循环都需要调用 LLM,加上截图的 Token 开销,复杂任务的成本可能快速攀升。

成本控制的分层策略:

预算控制层
├── 任务级预算:每个任务设置最大 Token/成本上限
├── 步骤级优化:flash_mode、减少视觉使用、压缩历史
├── 模型级策略:简单步骤用小模型,关键决策用大模型
└── 架构级设计:缓存常见页面的 DOM 结构,避免重复提取

5.2 Browser Use 与竞品对比

2025 年的浏览器 Agent 赛道已经相当拥挤,以下是主要竞品的对比:

特性Browser UsePlaywright MCPStagehandLaVague
开源✅ MIT✅ Apache 2.0✅ MIT✅ Apache 2.0
语言PythonTypeScriptTypeScriptPython
LLM 支持多模型依赖 MCP 客户端OpenAI/Anthropic多模型
视觉理解✅ 混合模式❌ 纯 DOM✅ 视觉优先✅
自定义工具✅ 装饰器 API❌有限✅
云端浏览器✅ 官方云❌❌❌
GitHub Stars60k+N/A15k+5k+
生产就绪度高中中中

Browser Use 的核心优势在于:

  1. 最大的社区和生态(60k+ Stars)
  2. DOM + Vision 混合分析的成熟实现
  3. 灵活的工具扩展系统
  4. 官方云端浏览器服务(解决反爬问题)
  5. 完善的可观测性工具链

5.3 前沿探索:Agent 的自我进化

Harness Engineering 的终极愿景不仅是让 Agent 可靠地执行任务,还包括让 Agent 能够自我改进。OpenAI Codex 团队的实践揭示了一个有趣的模式:

Agent 可以为自己编写工具、测试和文档。

在 Browser Use 的语境下,这意味着:

# 未来可能的自进化模式

# 1. Agent 从失败中学习,自动生成新的工具
@tools.action(description='Create a new reusable tool from learned pattern')
async def create_tool(tool_name: str, tool_code: str, description: str) -> ActionResult:
    # Agent 识别到重复的操作模式后
    # 自动将其封装为可复用的工具
    # 保存到工具库中供后续任务使用
    pass

# 2. Agent 自动优化 Prompt
# 基于历史成功/失败记录,调整系统 Prompt 中的指导信息

# 3. Agent 自动生成测试用例
# 对关键业务流程生成回归测试,确保网站变化时及时发现

这与 Harness Engineering 中"垃圾回收"的理念一脉相承——Agent 不仅执行任务,还持续维护和优化自身的运行环境。


第六部分:实战案例——构建一个生产级的竞品监控系统

让我们将前面所有的理论知识整合到一个完整的实战案例中:构建一个自动化的竞品价格监控系统。

6.1 需求描述

  • 每天自动访问 3 个竞品网站
  • 提取指定产品的价格、库存状态和促销信息
  • 将数据存储到数据库
  • 价格变动超过 5% 时发送告警
  • 需要处理登录、反爬、动态加载等复杂场景

6.2 完整实现

import asyncio
import json
import logging
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field
from browser_use import Agent, Browser, Tools, ActionResult, ChatBrowserUse

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# ============================================
# 数据模型(输出护栏)
# ============================================

class ProductPrice(BaseModel):
    product_name: str
    price: float = Field(ge=0)
    currency: str = Field(default="USD")
    in_stock: bool
    promotion: Optional[str] = None
    source_url: str
    extracted_at: str = Field(default_factory=lambda: datetime.now().isoformat())

class CompetitorReport(BaseModel):
    competitor: str
    products: list[ProductPrice]
    extraction_success: bool
    error_message: Optional[str] = None

# ============================================
# 工具定义
# ============================================

tools = Tools(
    exclude_actions=['evaluate', 'upload_file']  # 安全护栏
)

# 模拟数据库
price_history_db: list[dict] = []

@tools.action(description='Save product price to database and check for significant changes')
async def save_price(product_name: str, price: float, currency: str, 
                     in_stock: bool, source_url: str, 
                     promotion: str = None) -> ActionResult:
    record = ProductPrice(
        product_name=product_name,
        price=price,
        currency=currency,
        in_stock=in_stock,
        promotion=promotion,
        source_url=source_url,
    )
  
    # 检查价格变动
    previous = [r for r in price_history_db 
                if r['product_name'] == product_name and r['source_url'] == source_url]
  
    alert_msg = ""
    if previous:
        last_price = previous[-1]['price']
        change_pct = abs(price - last_price) / last_price * 100
        if change_pct >= 5:
            alert_msg = (f" ALERT: Price changed by {change_pct:.1f}% "
                        f"(was {last_price} {currency}, now {price} {currency})")
            logger.warning(f"Price alert for {product_name}: {alert_msg}")
            # 在生产环境中,这里会发送 Slack/Email 通知
  
    price_history_db.append(record.model_dump())
  
    return ActionResult(
        extracted_content=f"Saved: {product_name} @ {price} {currency}.{alert_msg}",
        long_term_memory=f"Price for {product_name} has been saved. Do not save it again."
    )

@tools.action(description='Report extraction failure for a competitor')
async def report_failure(competitor: str, error: str) -> ActionResult:
    logger.error(f"Extraction failed for {competitor}: {error}")
    return ActionResult(
        extracted_content=f"Failure reported for {competitor}: {error}"
    )

# ============================================
# Harness 编排层
# ============================================

class CompetitorMonitor:
    def __init__(self):
        self.llm = ChatBrowserUse()
        self.browser = Browser(
            headless=True,
            window_size={'width': 1920, 'height': 1080},
            highlight_elements=True,
            paint_order_filtering=True,
            minimum_wait_page_load_time=1.0,
            wait_for_network_idle_page_load_time=2.0,
        )

    async def monitor_competitor(self, competitor_name: str, url: str,
                                  products: list[str]) -> CompetitorReport:
        """监控单个竞品网站,带完整的重试和降级策略"""
      
        max_retries = 3
      
        for attempt in range(max_retries):
            try:
                product_list = ", ".join(products)
                task = (
                    f"Go to {url} and find the following products: {product_list}. "
                    f"For each product, extract the current price, currency, "
                    f"whether it's in stock, and any active promotions. "
                    f"Use the save_price tool to save each product's data. "
                    f"If a product cannot be found, skip it and continue with the next one."
                )
              
                agent = Agent(
                    task=task,
                    llm=self.llm,
                    browser=self.browser,
                    tools=tools,
                    use_vision="auto",
                    use_thinking=True,
                    max_actions_per_step=3,
                    max_failures=3,
                    save_conversation_path=(
                        f'./logs/{competitor_name}_{datetime.now().strftime("%Y%m%d_%H%M")}'
                        f'_attempt{attempt}.json'
                    ),
                    generate_gif=True,
                )
              
                history = await agent.run(max_steps=40)
              
                if history.is_done():
                    # 从数据库中提取本次运行保存的记录
                    saved_products = [
                        ProductPrice(**r) for r in price_history_db
                        if r['source_url'].startswith(url.split('/')[2])  # 匹配域名
                    ]
                  
                    logger.info(
                        f"[{competitor_name}] Completed: {len(saved_products)} products, "
                        f"{history.number_of_steps()} steps, "
                        f"{history.total_duration_seconds():.1f}s"
                    )
                  
                    return CompetitorReport(
                        competitor=competitor_name,
                        products=saved_products,
                        extraction_success=True,
                    )
              
                # Agent 未完成——可能是步数耗尽
                logger.warning(
                    f"[{competitor_name}] Agent did not finish on attempt {attempt + 1}. "
                    f"Last thoughts: {history.model_thoughts()[-1] if history.model_thoughts() else 'N/A'}"
                )
              
            except Exception as e:
                logger.error(f"[{competitor_name}] Attempt {attempt + 1} failed: {e}")
                if attempt < max_retries - 1:
                    await asyncio.sleep(2 ** attempt)  # 指数退避:1s, 2s, 4s
      
        # 所有重试耗尽
        return CompetitorReport(
            competitor=competitor_name,
            products=[],
            extraction_success=False,
            error_message=f"All {max_retries} attempts failed",
        )
  
    async def run_daily_monitoring(self, competitors: list[dict]) -> list[CompetitorReport]:
        """每日监控入口:顺序执行所有竞品监控"""
      
        reports = []
      
        for comp in competitors:
            logger.info(f"Starting monitoring for: {comp['name']}")
            report = await self.monitor_competitor(
                competitor_name=comp['name'],
                url=comp['url'],
                products=comp['products'],
            )
            reports.append(report)
          
            # 竞品之间间隔,避免触发反爬
            await asyncio.sleep(5)
      
        # 生成汇总报告
        success_count = sum(1 for r in reports if r.extraction_success)
        total_products = sum(len(r.products) for r in reports)
      
        logger.info(
            f"\n{'='*50}\n"
            f"Daily Monitoring Summary\n"
            f"Competitors monitored: {len(reports)}\n"
            f"Successful: {success_count}/{len(reports)}\n"
            f"Total products tracked: {total_products}\n"
            f"{'='*50}"
        )
      
        return reports


# ============================================
# 入口
# ============================================

async def main():
    competitors = [
        {
            "name": "Competitor A",
            "url": "https://www.competitor-a.com/products",
            "products": ["Widget Pro", "Widget Lite", "Widget Enterprise"],
        },
        {
            "name": "Competitor B",
            "url": "https://www.competitor-b.com/pricing",
            "products": ["Basic Plan", "Pro Plan", "Enterprise Plan"],
        },
        {
            "name": "Competitor C",
            "url": "https://www.competitor-c.com/store",
            "products": ["Starter Kit", "Professional Kit"],
        },
    ]
  
    monitor = CompetitorMonitor()
    reports = await monitor.run_daily_monitoring(competitors)
  
    # 持久化报告
    with open(f'./reports/daily_{datetime.now().strftime("%Y%m%d")}.json', 'w') as f:
        json.dump([r.model_dump() for r in reports], f, indent=2, default=str)


if __name__ == "__main__":
    asyncio.run(main())

6.3 架构复盘

这个竞品监控系统完整体现了 Harness Engineering 的三层架构:

层级实现作用
Layer 1 模型接口ChatBrowserUse() + 结构化 task prompt驱动 Agent 的推理与决策
Layer 2 运行时环境Tools(含 save_price、report_failure)、ProductPrice Schema 验证、exclude_actions 安全限制约束 Agent 的行为边界
Layer 3 编排层CompetitorMonitor 类、重试循环、指数退避、汇总报告协调多任务执行与异常恢复

同时覆盖了 Harness 的七大组件:

  1. 工具选择——精选 save_price 和 report_failure,禁用 evaluate 和 upload_file
  2. 记忆——long_term_memory 防止重复保存,save_conversation_path 持久化对话
  3. 护栏——Pydantic Schema 验证、价格非负约束、工具黑名单
  4. 重试——3 次重试 + 指数退避 + 错误日志
  5. 上下文管理——use_vision="auto" 按需使用视觉,use_thinking=True 启用推理
  6. 可观测性——完整日志、GIF 录制、对话保存、汇总报告
  7. Prompt 管理——结构化的 task 描述,明确指定提取字段和工具使用方式

第七部分:展望——Browser Agent 的未来

7.1 从工具到基础设施

2025 年的 Browser Use 仍然主要被当作一个"工具"来使用——开发者编写脚本,调用 Agent,获取结果。但随着 Harness Engineering 理念的普及,Browser Agent 正在向"基础设施"演进。

未来的 Browser Agent 平台可能具备以下特征:

  • 声明式任务定义:开发者只需描述"我要什么",而非"怎么做"。类似于 Kubernetes 的声明式 API,你定义期望状态,系统自动规划执行路径。
# 未来可能的声明式任务定义
task:
  name: "daily-price-check"
  schedule: "0 9 * * *"
  targets:
    - url: "https://competitor.com/pricing"
      extract:
        - field: "price"
          selector_hint: "main pricing table"
          type: float
        - field: "plan_name"
          type: string
  output:
    format: "json"
    destination: "s3://my-bucket/prices/"
  guardrails:
    max_cost_per_run: "$0.50"
    max_duration: "5m"
    allowed_domains: ["competitor.com"]
  • 自愈能力:当网站 UI 变化导致提取失败时,Agent 自动适应新的页面结构,无需人工更新脚本。这是传统 Selenium 脚本永远无法实现的能力。

  • 联邦式执行:任务自动分发到全球各地的浏览器节点执行,绕过地理限制,提高可靠性。

7.2 Agent-Native 软件架构

OpenAI Codex 团队的实践揭示了一个更深层的趋势:软件架构本身正在为 Agent 而重新设计。

传统架构为人类开发者优化——代码组织方式、命名约定、文档风格都是为了让人类更容易理解和修改。Agent-Native 架构则增加了一个新的维度:让 Agent 也能高效地理解和操作代码。

这意味着:

  • 更严格的类型系统——类型不仅帮助人类,更帮助 Agent 理解代码的意图和约束
  • 更细粒度的模块化——小而独立的模块比大而复杂的模块更容易被 Agent 理解和修改
  • 机器可读的架构文档——不仅有人类可读的 README,还有结构化的架构描述文件
  • 自动化的不变量检查——通过 Linter、类型检查器和结构测试,机械地强制执行架构约束

7.3 Harness Engineer 的角色定义

随着 Agent 承担越来越多的执行工作,工程师的角色正在发生根本性转变。OpenAI 团队将这个新角色称为 Harness Engineer,其核心职责包括:

  • 环境设计师:设计 Agent 的运行环境——工具、权限、约束、反馈机制
  • 意图翻译者:将模糊的业务需求翻译为 Agent 可以理解和执行的精确规范
  • 质量守护者:设计验证机制,确保 Agent 的输出满足质量标准
  • 系统架构师:设计多 Agent 协作的编排逻辑,处理故障恢复和降级策略
  • 反馈回路工程师:构建从执行结果到 Agent 改进的闭环反馈系统

这不是"提示工程"的升级版。Harness Engineering 涵盖了从系统架构到运维监控的完整工程实践,它要求工程师同时具备软件工程的严谨性和对 LLM 行为特性的深刻理解。

7.4 开放问题

尽管 Browser Use Agent 和 Harness Engineering 代表了当前的最佳实践,仍有许多开放问题等待解决:

  • 可靠性的天花板在哪里? 当前最好的 Browser Agent 在复杂任务上的成功率大约在 70-90%。对于关键业务流程,这个数字是否足够?如何在不牺牲灵活性的前提下进一步提高可靠性?

  • 成本曲线何时交叉? 目前 Browser Agent 的单次任务成本(0.01−0.01-0.01−2.00)在某些场景下高于传统脚本。随着模型成本持续下降,何时会达到全面替代的经济拐点?

  • 安全边界如何定义? 让 AI Agent 自主浏览互联网并执行操作,安全风险如何量化和管控?特别是在涉及支付、个人数据和法律合规的场景中。

  • Agent 的可解释性? 当 Agent 做出意外决策时,我们能否追溯其推理过程并理解"为什么"?当前的 model_thoughts() 提供了部分可见性,但远未达到完全可解释的程度。

  • 多 Agent 协作的涌现行为? 当多个 Agent 在同一个浏览器或同一个网站上协作时,是否会出现意料之外的涌现行为?如何预防和检测?


结语

Browser Use Agent 代表了浏览器自动化从"脚本驱动"到"智能体驱动"的范式转换。它不再要求开发者精确指定每一个 CSS 选择器和操作步骤,而是让 AI 像人类一样理解页面、规划路径、执行操作。

但正如 OpenAI Codex 团队的实践所揭示的,原始的 LLM 能力只是引擎。要将这台引擎变成一辆可靠的汽车,需要 Harness Engineering——精心设计的工具系统、多层护栏、智能记忆、弹性重试、全面可观测性和严谨的编排逻辑。

对于今天的开发者而言,最务实的建议是:

  1. 从简单任务开始,用 Browser Use 的默认配置快速验证可行性
  2. 逐步添加 Harness 组件——先加护栏和重试,再加可观测性和记忆
  3. 将 Prompt 和工具定义视为代码,纳入版本控制和 Code Review
  4. 为 Agent 设计环境,而非为环境编写 Agent——投资于清晰的架构约束和自动化验证,而非越来越复杂的 Prompt
  5. 保持人类在回路中——至少在关键决策点,保留人工审批的能力

浏览器 Agent 的时代才刚刚开始。我们正站在一个从"编写自动化脚本"到"设计智能体运行环境"的转折点上。掌握 Harness Engineering 的团队,将在这场转型中占据先机。


参考资料:

  • Browser Use 官方文档:https://docs.browser-use.com
  • Browser Use GitHub 仓库:https://github.com/browser-use/browser-use
  • OpenAI Codex: Building Software with No Human-Written Code (2025)
  • Harness Engineering: The Emerging Discipline of Making AI Agents Work Reliably
  • Anthropic Computer Use, Google Project Mariner 等相关项目文档

评论

加载中…

发表评论

评论需审核后显示,邮箱不会公开。

← 返回文章列表