--- title: "我们如何构建安全可扩展的智能体沙箱基础架构" source: wechat url: https://mp.weixin.qq.com/s/BN6BeP_sb8_MGqmvLwBswQ ingest_date: 2026-07-04 vxc: 64 stars: 4 sha256: 26adf9b5870889fc2439ef48a354a7d74825d3756433b359344847d1a156d6cf --- # 我们如何构建安全可扩展的智能体沙箱基础架构 **来源**: 高可用架构 **发布日期**: 2026-02-27 **原文链接**: https://mp.weixin.qq.com/s/BN6BeP_sb8_MGqmvLwBswQ --- 导读:给 AI 智能体执行代码的权限,等于把服务器底裤交出去?本文分享了 Browser Use  运行百万级智能体的架构演进。他们放弃传统的工具隔离,转而采用“基于控制面的微型虚拟机”来彻底隔离智能体。 ### 从 AWS Lambda 走向基于控制面架构的 Unikraft 微型虚拟机 ## 演进历程 在 Browser Use ,我们运行着数百万个网页智能体(Web Agents)。起初,我们使用 AWS Lambda 运行纯浏览器智能体,其优势在于每次调用都是隔离的、扩展是即时的,且无需担心密钥安全。 随后,我们增加了 代码执行 功能。智能体可以编写并运行 Python 代码、执行 Shell 命令、创建文件。我们将此构建为一个被智能体视为“工具”的隔离沙箱。安全性表现良好:代码在沙箱中运行,而非在后端运行。 但问题在于: 智能体循环(Agent Loop)仍然与我们的 REST API 运行在同一个后端。 重新部署 API?所有运行中的智能体都会崩溃。智能体占用大量内存?API 响应变慢。两种截然不同的工作负载在共享同一个进程。 ## 两种模式 当一个智能体可以执行任意代码时,它理论上可以访问机器上的任何内容:环境变量、API 密钥、数据库凭据、内部服务。因此,它必须与你的基础设施和秘密(Secrets)隔离。实现这一点有两种方式: - 模式 1:隔离工具(Isolate the tool)。 智能体运行在你的基础设施上。危险操作(代码执行、终端访问)在独立的沙箱中运行。智能体通过 HTTP 调用沙箱。代码在没有泄露风险的地方运行。 - 模式 2:隔离智能体(Isolate the agent)。 整个智能体都运行在一个没有任何密钥的沙箱中。它通过一个持有所有凭据的控制面(Control Plane)与外界通信。 智能体变成了“即用即弃”的。 没有可窃取的秘密,没有需要保留的状态,你可以随时杀死它、重启它、独立扩展它。控制面掌握着“真相”。 我们最初采用了模式 1,后来转向了模式 2。 ## 沙箱环境 相同的容器镜像在任何地方都能运行。在生产环境中,它作为 Unikraft 微型虚拟机 (micro-VM) 运行;在本地开发和评估(Evals)中,它作为 Docker 容器 运行。通过一个简单的配置开关 ( sandbox_mode: 'docker' | 'ukc' ) 即可控制环境。 ### 生产环境中的 Unikraft 每个智能体拥有独立的 Unikraft 微型虚拟机,启动时间 不到一秒 。我们通过 Unikraft Cloud 的 REST API 在 AWS 的专用裸金属服务器上进行配置。 沙箱仅从外界接收三个环境变量: SESSION_TOKEN 、 CONTROL_PLANE_URL 和 SESSION_ID 。没有 AWS 密钥,没有数据库凭据,也没有 API 令牌。 Unikraft 提供了开箱即用的 “缩容至零”(Scale-to-zero) 功能。当沙箱空闲时,VM 会挂起;当下一个请求进入时,它会立即恢复。在查询间隙,沙箱几乎不产生费用,但能瞬间唤醒执行后续任务。 我们跨多个 Unikraft 区域(Metros)分布沙箱,以防止单一区域成为瓶颈。 ### 开发与评估中的 Docker 在本地和评估流水线中,相同的镜像作为 Docker 容器运行。同样的镜像、同样的入口点、同样的控制面协议。我们可以在开发笔记本上运行完全相同的智能体,并发启动数百个进行评估,最后部署到生产环境的 Unikraft。 ## 安全加固 在运行任何智能体代码之前,沙箱会执行以下操作: - 仅字节码执行: 在 Docker 构建期间,我们将所有 Python 源码编译为 .pyc 字节码,然后删除所有 .py 文件。智能体框架代码以 root 身份加载到内存中,一旦加载完成,源码即消失。 - 权限剥夺: 入口点以 root 身份启动(读取 root 拥有的字节码所需),然后立即通过 setuid / setgid 降权为 sandbox 用户。此后,一切都在非特权下运行。 - 环境清理: 将 SESSION_TOKEN 、 CONTROL_PLANE_URL 和 SESSION_ID 读取到 Python 变量后,我们从 os.environ 中彻底删除它们。即使智能体检查环境变量,它们也不复存在。况且,在沙箱的网络环境之外,这些令牌毫无用处。VM 位于私有 VPC 中,除了与控制面通信外没有任何权限。 ## 控制面如何工作 可以将控制面看作一个 代理服务 。沙箱无法直接访问外部世界,每个请求都必须经过控制面。 - 需要调用 LLM?走控制面。 - 需要上传文件到 S3?走控制面。 这是智能体与其 VM 之外的世界交流的唯一途径。 它是一个无状态的 FastAPI 服务。来自沙箱的每个请求都携带 Bearer: {session_token} 请求头。控制面通过令牌查找会话,验证其有效性,并使用真实凭据执行操作。 ### LLM 代理 对于每次 LLM 调用,沙箱只发送新消息。 控制面在数据库中保存完整的对话历史 ,在每次调用时重构历史,并将完整的上下文转发给供应商。这保持了沙箱的无状态性:你可以杀掉它并启动一个新沙箱,对话依然能从中断处接续。 控制面还负责强制执行成本限制并处理计费,让沙箱只专注于任务本身。 ### 通过预签名 URL 进行文件同步 沙箱有一个 /workspace 目录用于读写文件。文件同步服务会监控更改并定期同步到 S3,但 沙箱永远看不到 AWS 凭据 。相反,它向控制面索取预签名 URL: - 沙箱检测到 /workspace 中的文件变动。 - 沙箱调用 POST /presigned-urls 并发送文件路径。 - 控制面生成受限的 S3 上传预签名 URL。 - 沙箱使用这些 URL 直接将文件上传到 S3。 下载过程则反向进行。沙箱获得了受限的 S3 访问权限,而无需持有任何 AWS 凭据。 ### 网关协议 (Gateway Protocol) 在沙箱内部,智能体通过“网关”协议与控制面通信: class AgentGateway(Protocol):     async def invoke_llm(self, new_messages, tools, tool_choice) -> LLMResponse: ...     async def persist_messages(self, messages) -> None: ... 在生产中, ControlPlaneGateway 发送 HTTP 请求;在开发中, DirectGateway 直接调用 LLM 并将历史保留在内存中。智能体代码感知不到差异。相同的接口,相同的行为,不同的后端。 ## 扩展性 控制面是无状态的:验证令牌、执行任务、返回结果。 - 需要更多智能体?启动更多沙箱。 - 需要更高吞吐量?在负载均衡器后增加控制面实例。 每一层都根据自身的瓶颈进行扩展。我们的后端运行在基于 ALB 的私有子网 ECS Fargate 上,控制面根据 CPU 利用率自动缩放。沙箱通过 Unikraft 独立扩展,每个会话拥有自己的 VM,并且由 Unikraft 负责跨区域(metros)的调度。 ## 总结 对可以执行代码的智能体进行沙箱化有两种方法:隔离工具(将代码执行放在沙箱中,智能体保留在后端)或隔离智能体(将整个智能体放入沙箱,通过控制面与外界通信)。 我们选择了 模式 2 。控制面持有所有凭据并充当一切事物的代理:LLM 调用、文件存储、计费。沙箱只接收三个环境变量,无权访问其他任何内容。它在生产中是 Unikraft 微型虚拟机,在开发中是 Docker 容器。无论哪里都运行相同的镜像。 权衡之处在于:每次操作多了一次网络跳转,且需要部署三个服务而非一个。但在实践中,这种延迟相对于 LLM 的响应时间几乎可以忽略不计,而运维复杂度也是团队完全可以胜任的。 核心启示:你的智能体里不应该有任何值得偷的东西,也不应该有任何值得保留的状态。 https://x.com/larsencc/status/2027225210412470668 ## 参考阅读 - LangChain创始人:Agent 连接沙箱的两种模式(附深度架构解析) - 关于软件开发未来的三点思考 - 代码不再是真相:AI Agent 时代从 Code 到 Traces 的范式转移 - Claude Code 作者:编程问题在大多数用例已解决 - 如何让 AI 替你干活?OpenClaw 98 个实战案例全公开