@larsencc: https://x.com/larsencc/status/2053862900289470765
摘要
本文详解了开源 browser-use 库的生产架构,阐述了如何利用 AWS Lambda、SQS 和 S3 扩展浏览器代理,实现状态管理与重试机制。
查看缓存全文
缓存时间: 2026/05/11 16:40
Browser Use 开源库的生产环境架构
我们开源了 browser-use,让任何人都能用几行 Python 代码在本地运行浏览器智能体(agent)。
要在生产环境中运行数百万个此类智能体,并支持重试、超时、截图、审计日志和计费,需要一套完善的基础设施。为了打磨这套架构,我们提交了 4,000 多次代码。本文介绍了我们在生产环境中使用的架构。如果你希望在自己的基础设施中大规模运行 browser-use,这是一个很好的起点。
开源库提供了什么
Agent 类是工作的基本单元。你向它传入一个任务、一个大语言模型(LLM)和一个 BrowserSession。在每一步中,它会并行截取屏幕截图并提取 DOM,将两者发送给 LLM 以决定下一步操作,执行该操作,然后循环往复,直到任务完成。
这是我们在生产环境中唯一调用的方法。这就是整个开源库的全部核心。本文其余部分提到的队列、Worker、状态管理和重试逻辑,都是我们围绕它构建的基础设施。
架构设计
API 是部署在 ECS Fargate 上的 FastAPI 服务。它接收任务创建请求,进行验证,向数据库写入一条记录,向 SQS 投递一条消息,然后返回 HTTP 202。
其后是一个标准的 SQS 队列,每次智能体运行对应一条消息,携带任务 ID 和执行配置。由于智能体任务彼此独立,因此不需要排序、去重,也不按工作负载类型划分独立队列。
Worker 是一个 AWS Lambda 函数,代码开头写着 from browser_use import Agent。它从 SQS 拉取消息,实例化智能体,运行至完成,并将结果写入 S3。
复杂性在于各个组件如何处理故障。
API 入口
处理器验证请求载荷,在数据库中创建会话和任务记录,提交事务,发送 SQS 消息,并在 50 毫秒内返回带有任务 ID 的 HTTP 202。此时实际工作尚未开始。
队列层
我们为所有智能体运行使用单一的标准 SQS 队列。早期我们尝试过按客户划分队列和基于优先级的路由,但这些都没有提升吞吐量,反而增加了我们不想维护的运维开销。事实证明,一个包含独立消息的单一队列就足够了。
消息携带任务 ID、模型和运行时设置,以及一个从零开始的续传计数器:
Worker Lambda
Lambda 处理器通过 SQS 事件源映射与队列连接。对于每条消息,它会解析载荷、配置浏览器会话、构建 LLM 客户端、调用 Agent(...).run(),将逐步状态写入 S3,并将最终结果持久化到数据库。
S3 中的状态
我们在 S3 中存储四种状态。智能体检查点在每一步之后序列化为 JSON,这是实现任务恢复的关键。每一步都会截取屏幕截图,供智能体使用,也供人工调试失败的运行记录。执行日志在任务完成时统一上传。下载文件或生成的产物等输出文件通过预签名 URL 返回。
上传操作采用“发后即忘”(fire-and-forget)模式。如果 S3 响应缓慢或在某次上传时返回 503,智能体运行仍会继续,因为我们宁愿丢失一张截图,也不愿让整个任务失败。
当 Lambda 超时
AWS Lambda 有 15 分钟的硬性限制,但浏览器智能体没有。一个需要 20 分钟的任务不应因运行时限制而失败。
在 Lambda 截止时间前两分钟,智能体会优雅停止:
处理器将状态检查点保存至 S3,发送一条续传计数器递增的新 SQS 消息,并返回成功。
新的 Lambda 调用会接收该消息,从 S3 获取状态,恢复 Agent,并从上一个完成的步骤继续执行。用户看到的是一个完整的任务,而系统则通过 S3 将 N 次 Lambda 调用无缝拼接在一起。
目前我们将计数器上限设为 12,这大约提供三小时的挂钟时间。这不是硬性限制。我们可以调高它,但在实践中我们发现,智能体在这么多步骤后的可靠性不足以支撑更长时间的运行。
故障处理
Lambda 本身提供重试机制,SQS 也提供死信队列(DLQ),因此我们不需要自定义重试框架。我们只需要知道何时使用哪种机制。
如果处理器抛出异常,我们会通过 ReportBatchItemFailures 将消息标记为失败。当可见性超时过期后,SQS 会将其重新放回队列,由另一个 Lambda 调用接管。三次尝试失败后,消息将进入 DLQ。
我们的数据库中没有重试计数器。重试状态完全保存在 SQS 元数据中,因此如果需要知道任务是否处于第二次尝试,我们检查的是消息属性,而不是自己的数据表。
DLQ 就像火灾警报。当消息进入那里时,通常意味着我们引入了回归缺陷,或者存在需要深入调查的底层问题。这种情况很少见,一旦发生会有工程师介入排查。我们不会自动重新驱动(auto-redrive)这些消息。
临时存储清理
我们学到的一个经验是:在热启动的 Lambda 上,临时存储(/tmp)不会在调用之间自动清理。如果前一次智能体运行在 /tmp 中留下了文件,同一 Lambda 实例上的下一次调用将会看到它们。这可能导致会话间数据泄露,或在长时间运行的实例上引发磁盘空间问题。
我们在每次调用开始时清空工作区,并将所有写入操作限制在会话专属目录中:
为什么这套架构有效
Lambda 在调用之间不保留任何状态。所有需要持久化的数据都存放在数据库或 S3 中,因此我们可以随时终止 Worker 而不会丢失工作进度。
API 不会等待智能体执行。它接收任务,将其投入队列,然后立即返回。客户在 API 层永远不会因智能体执行而阻塞等待。
Lambda 的 15 分钟限制在早期是最大的未知数。通过“保存状态、重新入队、恢复状态”的方式解决它,而不是切换到其他运行时,使我们能够继续利用 Lambda 的自动扩缩容和 SQS 的重试语义,而无需引入新的基础设施。
最终沉淀下来的架构
该库将自然语言任务转化为浏览器操作。基础设施则确保成千上万个此类任务能够并发运行。
让两者可靠地协同工作经历了 4,000 多次提交和无数踩坑。我们遇到了 SQS 和 Lambda 能抛出的所有故障模式:从事件循环死锁、调用间临时存储泄露,到 Lambda 续传静默丢失状态。本文中的架构正是历经这一切后沉淀下来的方案。
Browser Use Cloud 正是基于此架构运行该开源库,并叠加了我们在其上构建的所有功能。
相似文章
@jhleath: https://x.com/jhleath/status/2065408690992148698
作者解释了如何构建一个能够在恒定时间内每秒启动数百万个沙箱的计算平台,重点介绍了使用Cassandra和S3进行解耦调度和能力聚合。
“浏览器代理成本高昂且仍在成熟”这种表述可能忽略了架构方面的问题
讨论了当前使用无头Chrome加AI层的浏览器代理的架构问题,并介绍了Opera Neon的命令行界面作为替代方案,将AI集成到浏览器中,从而降低令牌开销并提高理解能力。
@browser_use: https://x.com/browser_use/status/2066911791360422071
Browser Use Cloud 使用 Firecracker 重建基础设施,将浏览器会话成本从每小时 0.06 美元降至 0.02 美元,同时实现亚秒级启动时间,并保持隔离性和可扩展性。
我们如何构建安全、可扩展的代理沙箱基础设施(8分钟阅读)
Browser Use 描述了隔离执行代码的 AI 代理的两种模式:隔离工具与隔离代理。他们使用 AWS 上的 Unikraft 微虚拟机实现了代理隔离模式,获得了安全、可扩展且一次性的沙箱。
“代理需要浏览器”问题——我开源了自己的解决方案
Otto (MIT) 是一个开源浏览器扩展,它通过 CLI 或代理将真实标签页转化为可控节点,解决了“代理需要浏览器”的问题,无需无头农场或昂贵的云服务。