📚 本系列目录 :《Harness Engineering》
当前第 1/10 篇 · 下一篇:Harness Engineering:驯服 AI Agent 的系统工程学
📚 系列导航
《Harness Engineering 》共 10 篇,本篇是第 1 篇。
下一篇:Harness Engineering:驯服 AI Agent 的系统工程学 →
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 成本 OpenAI GPT-4o / GPT-4.1-mini 复杂推理与导航 Anthropic Claude Sonnet 4 高质量推理 Google Gemini 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() 启动了一个迭代循环,每一轮循环中:
系统提取当前页面的 DOM 状态和截图
将状态信息与任务描述一起发送给 LLM
LLM 返回一个或多个动作指令(如 click、type、navigate)
Playwright 执行这些动作
系统观察执行结果,更新状态,进入下一轮
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 ,
paint_order_filtering=True ,
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" ,
vision_detail_level="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:快速模式,跳过评估和推理步骤,仅使用记忆,适合简单任务
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()
history.action_names()
history.extracted_content()
history.errors()
history.final_result()
history.is_done()
history.is_successful()
history.model_thoughts()
history.number_of_steps()
history.total_duration_seconds()
history.structured_output
这套历史记录系统为调试、审计和优化提供了完整的可观测性基础。
第二部分: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: 降级模型]
|-- 成功 --> 验证 --> 返回
|-- 失败 --> [升级到人工处理]
重试策略的层次:
简单重试——相同 Prompt,相同模型(处理瞬时 API 错误)
重组重试——根据错误修改 Prompt
模型降级——切换到备用模型
分解重试——将失败的任务拆分为更小的子任务
人工升级——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
primary_llm = ChatBrowserUse()
from browser_use import ChatOpenAI
fallback_llm = ChatOpenAI(model="gpt-4.1-mini" )
extraction_llm = ChatOpenAI(model="gpt-4.1-mini" )
class ResearchResult (BaseModel ):
title: str
url: str
summary: str
confidence: float
tools = Tools(exclude_actions=['evaluate' ])
@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 ,
)
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():.1 f} s" )
return result
else :
logging.warning(f"Low confidence result ({result.confidence} ), retrying..." )
continue
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,
page_extraction_llm=ChatOpenAI(model="gpt-4.1-mini" ),
max_history_items=20 ,
browser=Browser(paint_order_filtering=True ),
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" ,
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 的框架下,记忆系统的设计原则是:
选择性记忆——不是所有信息都值得记住,过多的记忆会稀释关键信息
结构化存储——使用明确的类型(事实、偏好、约束)而非原始文本
衰减机制——旧的、不再相关的记忆应该被淡化或移除
检索优先——记忆的价值不在于存储,而在于能否在正确的时刻被检索出来
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' ,
'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()
researcher = Agent(
task="Search for the latest AI research papers on arxiv about browser agents" ,
llm=llm,
browser=browser,
)
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()
agents = [
Agent(task=f"Search {source} for: {topic} " , llm=llm, browser=browser)
for source in ["Google Scholar" , "arXiv" , "GitHub" ]
]
results = []
for agent in agents:
history = await agent.run()
results.append(history.extracted_content())
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
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' ),
},
)
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
browser = Browser(
cdp_url="ws://localhost:9222/devtools/browser/..."
)
browser = Browser(
user_data_dir='./chrome_profile' ,
keep_alive=True ,
)
browser = Browser(use_cloud=True )
4.4 性能优化策略
在生产环境中,Browser Use Agent 的性能优化需要从多个维度入手:
Token 成本优化
agent = Agent(
task="..." ,
llm=llm,
page_extraction_llm=ChatOpenAI(model="gpt-4.1-mini" ),
vision_detail_level="low" ,
max_history_items=10 ,
flash_mode=True ,
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,
save_conversation_path='./debug/conversation.json' ,
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():.1 f} 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()),
}
self .runs.append(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 ,
)
挑战三:幻觉与错误累积
LLM 可能"幻觉"出不存在的页面元素,或者误解页面内容。在多步任务中,一个早期的错误可能像滚雪球一样累积,导致后续所有步骤都偏离正确路径。
Harness Engineering 的应对策略:
检查点验证:在关键步骤后插入验证逻辑
状态断言:定期检查 Agent 是否仍在正确的页面/状态
回滚机制:检测到偏差时回退到上一个已知正确的状态
多路径探索:对关键决策点尝试多种方案
@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 Use Playwright MCP Stagehand LaVague 开源 ✅ MIT ✅ Apache 2.0 ✅ MIT ✅ Apache 2.0 语言 Python TypeScript TypeScript Python LLM 支持 多模型 依赖 MCP 客户端 OpenAI/Anthropic 多模型 视觉理解 ✅ 混合模式 ❌ 纯 DOM ✅ 视觉优先 ✅ 自定义工具 ✅ 装饰器 API ❌ 有限 ✅ 云端浏览器 ✅ 官方云 ❌ ❌ ❌ GitHub Stars 60k+ N/A 15k+ 5k+ 生产就绪度 高 中 中 中
Browser Use 的核心优势在于:
最大的社区和生态(60k+ Stars)
DOM + Vision 混合分析的成熟实现
灵活的工具扩展系统
官方云端浏览器服务(解决反爬问题)
完善的可观测性工具链
5.3 前沿探索:Agent 的自我进化
Harness Engineering 的终极愿景不仅是让 Agent 可靠地执行任务,还包括让 Agent 能够自我改进。OpenAI Codex 团队的实践揭示了一个有趣的模式:
Agent 可以为自己编写工具、测试和文档。
在 Browser Use 的语境下,这意味着:
@tools.action(description='Create a new reusable tool from learned pattern' )
async def create_tool (tool_name: str , tool_code: str , description: str ) -> ActionResult:
pass
这与 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:.1 f} % "
f"(was {last_price} {currency} , now {price} {currency} )" )
logger.warning(f"Price alert for {product_name} : {alert_msg} " )
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} "
)
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():.1 f} s"
)
return CompetitorReport(
competitor=competitor_name,
products=saved_products,
extraction_success=True ,
)
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)
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 的七大组件:
工具选择——精选 save_price 和 report_failure,禁用 evaluate 和 upload_file
记忆——long_term_memory 防止重复保存,save_conversation_path 持久化对话
护栏——Pydantic Schema 验证、价格非负约束、工具黑名单
重试——3 次重试 + 指数退避 + 错误日志
上下文管理——use_vision="auto" 按需使用视觉,use_thinking=True 启用推理
可观测性——完整日志、GIF 录制、对话保存、汇总报告
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" ]
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——精心设计的工具系统、多层护栏、智能记忆、弹性重试、全面可观测性和严谨的编排逻辑。
对于今天的开发者而言,最务实的建议是:
从简单任务开始,用 Browser Use 的默认配置快速验证可行性
逐步添加 Harness 组件——先加护栏和重试,再加可观测性和记忆
将 Prompt 和工具定义视为代码,纳入版本控制和 Code Review
为 Agent 设计环境,而非为环境编写 Agent——投资于清晰的架构约束和自动化验证,而非越来越复杂的 Prompt
保持人类在回路中——至少在关键决策点,保留人工审批的能力
浏览器 Agent 的时代才刚刚开始。我们正站在一个从"编写自动化脚本"到"设计智能体运行环境"的转折点上。掌握 Harness Engineering 的团队,将在这场转型中占据先机。
参考资料: