跳到正文

浏览器使用开源库的生产架构

我们在开源 browser-use 库之上运行的 SQS-to-Lambda 架构。

本文目录文章开头
部分图表可能显示不完整

我们开源了 browser-use,这样任何人都可以用几行 Python 在本地运行浏览器代理。

在生产环境中运行数百万个这样的代理,涉及重试、超时、截图、审计追踪和计费,需要一套基础设施,我们花了 4,000 多次提交才把它做好。本文介绍的就是我们生产环境所用的架构。如果你想在自己的基础设施上大规模运行 browser-use,这是一个很好的起点。

Architecture overview: client to REST API to SQS to Lambda Worker with browser-use, with S3 for state and Postgres for metadata

开源库为你提供了什么文章图表

Agent 类是工作单元。你向它传入一个任务、一个 LLM 和一个 BrowserSession。在每一步中,它会并行截取屏幕截图并提取 DOM,将两者都发送给 LLM 以决定下一个动作,执行该动作,并重复此过程直到任务完成。

from browser_use import Agent, BrowserSession
 
agent = Agent(
    task="Book a table for two at the closest sushi place",
    llm=ChatAnthropic(model="claude-sonnet-4-6"),
    browser_session=BrowserSession(),
)
result = await agent.run()

这就是我们在生产环境中进行的唯一一次方法调用。这就是整个开源库。本文中的其他所有内容——队列、工作进程、状态管理、重试逻辑——都是我们围绕它构建的基础设施。

架构文章图表

API 是运行在 ECS Fargate 上的 FastAPI 服务。它接收任务创建请求,进行验证,向数据库写入一行记录,向 SQS 投递一条消息,并返回 HTTP 202。

其背后是一个标准的 SQS 队列,每次代理运行对应一条消息,携带任务 ID 和执行配置。没有排序,没有去重,也没有为每种工作负载类型设置单独的队列,因为代理任务彼此独立。

工作进程是一个 AWS Lambda 函数,顶部是 from browser_use import Agent。它从 SQS 拉取消息,实例化代理,运行至完成,并将结果写入 S3。

复杂性在于每个部分如何处理故障。

Sequence diagram: client sends POST, API returns 202 immediately, SQS delivers to Lambda, Lambda runs agent and writes to S3

API 入口文章图表

处理程序验证负载,在数据库中创建会话和任务记录,提交,发送 SQS 消息,并在 50 毫秒内返回带有任务 ID 的 HTTP 202。工作尚未开始。

队列层文章图表

我们对所有代理运行使用单个标准 SQS 队列。早期我们尝试过按客户划分队列和基于优先级的路由,但这些都没有提高吞吐量,反而都增加了我们不想维护的运维开销。事实证明,一个带有独立消息的单个队列就足够了。

消息携带任务 ID、模型和运行时设置,以及一个从零开始的续跑计数器:

{
  "agent_task_id": "a1b2c3d4-...",
  "llm_model": "claude-sonnet-4-6",
  "max_agent_steps": 100,
  "use_vision": true,
  "continuation_count": 0
}

工作进程 Lambda文章图表

Lambda 处理程序通过 SQS 事件源映射连接到队列。对于每条消息,它解析负载,配置浏览器会话,构建 LLM 客户端,调用 Agent(...).run(),将逐步状态写入 S3,并将最终结果持久化到数据库。

S3 中的状态文章图表

我们在 S3 中存储四种状态。代理检查点在每一步之后以 JSON 形式序列化,这正是使恢复成为可能的原因。每一步都会截取屏幕截图,供代理和调试失败运行的人类使用。执行日志在任务完成时上传一次。下载或生成的工件等输出文件通过预签名 URL 提供。

上传是即发即忘的。如果 S3 变慢或某次上传返回 503,代理运行会继续,因为我们宁愿丢失一张截图,也不愿让任务失败。

当 Lambda 时间用尽时文章图表

AWS Lambda 有 15 分钟的硬性限制,但浏览器代理没有。一个需要 20 分钟的任务不应该因为运行时限制而失败。

Lambda continuation: checkpoint state to S3, new Lambda invocation restores state and continues

在 Lambda 截止时间前两分钟,代理会优雅地停止:

# Inside the agent loop — check before each step
if time.monotonic() >= deadline:
    _time_limit_stop = True
    return True  # signals the agent to stop after this step
 
# After agent stops, re-queue with incremented counter
next_count = event_data.continuation_count + 1
continuation_data = event_data.model_copy(
    update={'continuation_count': next_count}
)
sqs.send_message(
    QueueUrl=AGENT_TASKS_QUEUE_URL,
    MessageBody=continuation_data.model_dump_json(),
)

处理器将状态检查点保存到 S3,发送一条新的 SQS 消息(其中续传计数器已递增),并返回成功。

新的 Lambda 调用获取该消息,从 S3 获取状态,恢复 Agent,并从上一个完成的步骤继续。用户看到的是一个任务,而系统则通过 S3 将 N 次 Lambda 调用拼接在一起。

目前我们将计数器上限设为 12,这大约能提供三小时的挂钟时间。这不是一个硬性限制。我们可以提高它,但在实践中我们发现,Agent 在那么多步骤下并不可靠,不足以证明延长运行时间是合理的。

故障处理文章图表

Lambda 本身已经提供了重试机制,SQS 本身也已经提供了死信队列(DLQ),所以我们不需要自定义的重试框架。我们只需要知道何时使用哪一种。

如果处理器抛出异常,我们会通过 ReportBatchItemFailures 将消息报告为失败。当可见性超时到期时,SQS 会将其放回队列,由另一个 Lambda 调用获取它。经过三次失败尝试后,消息会进入死信队列。

我们的数据库中没有重试计数器。重试状态完全存在于 SQS 元数据中,因此如果我们需要知道某个任务是否处于第二次尝试,我们会检查消息属性,而不是我们自己的表。

死信队列就像火警。当消息落入其中时,通常意味着我们引入了回归问题,或者存在更深层次的问题需要调查。这种情况很少见,一旦发生,工程师会介入查看。我们不会自动重新驱动。

临时存储清理文章图表

我们学到的一件事是,Lambda 的临时存储(/tmp)在热 Lambda 上的调用之间不会自动清理。如果之前的 Agent 运行在 /tmp 中留下了文件,那么在同一 Lambda 实例上的下一次调用将会看到它们。这可能会导致会话之间的数据泄露,或在长时间运行的实例上造成磁盘空间问题。

我们在每次调用开始时清空工作区,并将所有写入限定在会话特定的目录中:

shutil.rmtree('/tmp/agent-workspace', ignore_errors=True)
os.makedirs(f'/tmp/agent-workspace/{session_id}', exist_ok=True)

为何有效文章图表

Lambda 在调用之间不保留任何内容。所有需要持久化的内容都存在于数据库或 S3 中,因此我们可以终止工作进程而不会丢失工作成果。

API 不会等待 Agent。它接受任务,将其放入队列,然后就不再干预。客户永远不会在 API 层等待 Agent 执行。

Lambda 的 15 分钟限制是早期最大的悬而未决的问题。通过“保存状态、重新入队、恢复状态”来解决它,而不是切换到不同的运行时,使我们能够继续使用 Lambda 的自动扩缩容和 SQS 的重试语义,而无需引入新的基础设施。

幸存下来的部分文章图表

该库将自然语言任务转化为浏览器操作。基础设施则让数千个这样的任务并发运行。

让两者可靠地协同工作花费了 4,000+ 次提交和许多错误。我们遇到了 SQS 和 Lambda 可能抛给你的每一种故障模式,从事件循环死锁到临时存储在调用之间泄露,再到 Lambda 续传静默丢失状态。本文中的架构就是从这一切中幸存下来的结果。

Browser Use Cloud 在此架构上运行开源库,以及我们在其之上构建的所有内容。

阅读显示设置

调整正文排版

当前浏览器记住选择,应用于文章正文。手机上正文宽度以屏幕可用空间为限。

图表完整性提示

普通文章遇到图表保存问题时显示提示。