从开源到生产
Browser Use 团队开源了库,让任何人用几行 Python 就能本地运行 browser agent。但把数百万个这样的 agent 跑在生产环境——带重试、超时、截图、审计追踪和计费——需要 4,000+ commit 才能搞对的基础设施。
这是他们的生产架构。如果你想在自己的基础设施里规模化运行 browser-use,这是个好起点。
核心抽象:Agent 类
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()
这是生产环境中唯一的方法调用。这就是整个开源库。帖子里的其他一切——队列、worker、状态管理、重试逻辑——是他们围绕它构建的基础设施。
架构三层
API 层:FastAPI on ECS Fargate
接受任务创建请求,验证,写数据库一行,丢消息到 SQS,返回 HTTP 202。
Handler 验证 payload,创建 session 和 task 数据库行,提交,发送 SQS 消息,50 毫秒内返回 HTTP 202 和 task ID。工作还没开始。
队列层:标准 SQS
一个标准 SQS 队列,每条消息一个 agent run,携带 task ID 和执行配置。没有顺序要求,没有去重,没有按工作负载类型分队列——因为 agent 任务互相独立。
早期试过按客户分队列和基于优先级的路由,但都没提高吞吐量,反而增加了不想维护的运营开销。单队列加独立消息就够了。
消息格式:
{
"agent_task_id": "a1b2c3d4-...",
"llm_model": "claude-sonnet-4-6",
"max_agent_steps": 100,
"use_vision": true,
"continuation_count": 0
}
Worker 层:AWS Lambda
Lambda handler 通过 SQS event source mapping 连接到队列。对每条消息:解析 payload,provision browser session,构造 LLM client,调用 Agent(...).run(),把逐步状态写到 S3,把最终结果持久化到数据库。
状态管理
四种状态存在 S3:
- Agent checkpoints:每步后序列化为 JSON,使续接成为可能
- Screenshots:每步捕获,供 agent 和人类调试失败运行
- Execution logs:任务完成时一次性上传
- Output files:下载或生成的 artifact,通过 presigned URL 返回
上传是 fire-and-forget。如果 S3 慢或返回 503,agent run 继续——宁可丢失截图也不让任务失败。
突破 Lambda 15 分钟限制
AWS Lambda 有 15 分钟硬限制,但 browser agent 没有。一个跑 20 分钟的任务不应该因为运行时约束而失败。
解法:状态续接
Lambda 截止时间前两分钟,agent 优雅停止:
# Agent 循环内部——每步前检查
if time.monotonic() >= deadline:
_time_limit_stop = True
return True # 信号 agent 在这步后停止
# Agent 停止后,用递增计数器重入队
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(),
)
Handler 把状态 checkpoint 到 S3,发送 continuation counter 递增的新 SQS 消息,返回成功。
新的 Lambda 调用捡起那条消息,从 S3 获取状态,恢复 Agent,从最后完成的步骤续接。用户看到一个任务,系统通过 S3 把 N 个 Lambda 调用缝在一起。
当前 counter 上限 12,约三小时 wall-clock 时间。不是硬约束,可以提高,但实践中发现 agent 在那么多步骤后不够可靠,不值得跑更久。
失败处理
Lambda 已经提供重试,SQS 已经提供 DLQ,所以不需要自定义重试框架。只需要知道何时用哪个。
如果 handler 抛异常,通过 ReportBatchItemFailures 报告消息失败。SQS 在可见性超时到期后把它放回队列,不同的 Lambda 调用捡起它。三次失败后消息进 DLQ。
数据库里没有重试计数器。重试状态完全活在 SQS 元数据里——如果需要知道任务是否在第二次尝试,查消息属性,不查自己的表。
DLQ 是火警。消息落进去通常意味着引入了回归或有更深的问题要调查。很少发生,发生时工程师会看。不自动重驱。
数据隔离教训
Lambda 的临时存储(/tmp)在热 Lambda 上不会在调用间自动清理。如果前一次 agent run 在 /tmp 留了文件,下一次调用在同一个 Lambda 实例上会看到它们。这可能在会话间泄漏数据,或在长运行实例上导致磁盘空间问题。
每次调用开始时 wipe workspace,所有写入限定在 session-specific 目录:
shutil.rmtree('/tmp/agent-workspace', ignore_errors=True)
os.makedirs(f'/tmp/agent-workspace/{session_id}', exist_ok=True)
Lambda 在调用间不持有任何东西。需要存活的一切都在数据库或 S3 里,所以可以杀掉 worker 而不丢失工作。
设计哲学
API 不等待 agent。它接受任务,丢到队列,然后让开。客户从不在 API 层等待 agent 执行。
Lambda 的 15 分钟限制曾是最初最大的开放问题。用"保存状态、重入队、恢复状态"解决它,而不是切换到不同运行时,让他们保留 Lambda 的自动扩缩容和 SQS 的重试语义,不引入新基础设施。
开源库把自然语言任务变成浏览器动作。基础设施让数千个这样的任务并发可靠运行。
两者一起可靠工作花了 4,000+ commits。他们踩过了 SQS 和 Lambda 能扔给你的每个失败模式——从事件循环死锁到临时存储在调用间泄漏到 Lambda 续接静默丢失状态。这篇帖子里的架构是 survive 了所有那些的东西。
Browserbase 在这个架构上运行开源库,加上他们构建在其上的一切。