@LegitSeanSmith: https://x.com/LegitSeanSmith/status/2073100286009483302

X AI KOLs Following 新闻

摘要

Adapt将每个用户与AI智能体的对话运行在独立的VM沙箱中,模型拥有完全root权限以执行任意代码,并使用gVisor和Firecracker确保安全性和可扩展性。

https://t.co/GnJbXVG4JC
查看原文
查看缓存全文

缓存时间: 2026/07/06 10:06

我们如何为智能体大规模运行沙箱

我们早期就押注让 LLM 能够运行任意代码。这篇文章将解释我们为何做出这一选择,以及需要怎样的基础设施,才能同时运行数千个这样的沙箱,在用户与智能体开始和结束对话时迅速启动和关闭。

用户与 Adapt 智能体的每一次对话,背后都有一台独立的计算机支撑。这并非共享服务器上的某个受限容器,而是一个隔离的虚拟机,模型可以在其中为所欲为:安装软件、编写并运行程序、浏览网页、调用 API。我们称这些为沙箱,它们是 Adapt 的核心基础之一。

完全控制

LLM 是编码天才,我的工作很大程度上就是为它们构建完美的开发者环境。

将 AI 连接到外部世界的常规方式是手动构建集成:为 GitHub 写一个专属连接器,为 HubSpot 再写一个,为 Stripe 再写一个,或者等待每个服务推出 MCP 服务器。这种方式根本无法扩展,而且我也不喜欢日复一日地编写集成代码。

因此,我们没去自己做这些工作,而是让模型来完成。任何暴露 API 的服务都可以从 Adapt 访问,因为我们给了 LLM 所需的一切,让它能编写与该 API 通信的脚本或程序。这就是我们将 Adapt 称为“横向智能”的重要原因:它不绑定在固定的工具列表上,而是能随时构建所需工具。

这一切的基础是给予 LLM 对沙箱的完全访问权。我们不是为了给模型提供一组静态语言和 CLI 工具,并限制文件系统访问,而是赋予它对一切资源的完全控制。它是以 root 身份运行的。虽然我们的沙箱预装了 Node 和 Python 等常见运行时,但如果某个服务的 API 最佳 SDK 是用 Go 写的呢?模型可以直接安装并运行它。

LLM 是否需要编写 Go 程序?直接安装 Go 并运行即可。

LLM 是否需要编写 Go 程序?直接安装 Go 并运行即可。

那么,如果我们允许模型随意安装任何软件并执行未经人工验证的代码,又该如何确保安全呢?幸好,我们并非第一个需要运行不可信代码的人。为此存在两个非常流行的安全运行时:gVisor 和 Firecracker。到目前为止,我们的历程让我们对两者都非常熟悉。

从 gVisor 到 Firecracker

我们最初尝试为 LLM 构建安全沙箱时采用了“简单”方式:使用 GKE Sandbox 在 GKE(Google Kubernetes Engine)上通过 gVisor 运行每个沙箱。我们已经在 GKE 上运行所有其他服务,因此这对我们来说是自然而然的一步。

gVisor 位于容器和宿主机内核之间。它不让程序直接向真正的 Linux 内核发起系统调用——你绝对不希望不可信代码触碰内核——而是在自己的用户空间内核中拦截这些调用并自行处理。这样,你获得了普通容器的大部分便利性,同时攻击面大大减小。GKE Sandbox 将这些全部打包:你部署 Pod(容器),它们透明地在 gVisor 下运行,我们几乎不需要进行任何基础设施配置。

一开始效果非常好。我们将“基础”沙箱定义为一个 Docker 镜像,让 GKE 根据我们需要沙箱数量随时扩展规模。沙箱附带的软件更新只需更新 Dockerfile 并在清单中提升版本号即可。

数百个沙箱 Pod 在 GKE Sandbox 下运行。

数百个沙箱 Pod 在 GKE Sandbox 下运行。

但是,正是让 gVisor 易于使用的抽象层,成了我们不断与之斗争的根源。由于 gVisor 在用户空间重新实现了 Linux 系统调用接口,并非所有行为都像在真实内核上一样,而我们的模型构想出的工作负载几乎无法预测。为你带来安全性的拦截,也导致了系统调用和 I/O 密集型工作上的性能损失。而且,将整个生命周期依赖 GKE,意味着我们最想控制的那些部分——启动时间、打包密度、网络以及回收机器的激进程度——恰恰是我们控制力最弱的。上面那个 OutOfcpu Pod 就是当你把别人的调度器推向极限时开始出现的情况。

这正是促使我们转向 Firecracker 的原因。

Firecracker 微型虚拟机是真正的虚拟机,每个都有自己的客户内核,利用硬件虚拟化运行,但经过精简后,只需几百毫秒即可启动,开销仅有几 MB。AWS 正是使用同样的技术在共享硬件上打包了大量的 Lambda 和 Fargate 工作负载。它提供了比共享内核更强的隔离边界,启动速度快到几乎感觉不到,并且足够小,可以在单台物理机上打包大量微型 VM。

代价是 Firecracker 只提供 VM,其他几乎什么都没有。没有像 GKE 那样的层来处理调度、网络和生命周期编排。因此,我们自己构建了一个,称之为 orc。

根文件系统只是一个镜像

在从容器迁移的过程中,我们不想放弃的一件事就是将沙箱定义为普通的 Dockerfile。容器让这变得很简单;而传统 VM 通常不支持,因为微型 VM 启动的是根文件系统,而非 OCI 镜像。

因此 orc 在两者之间架起桥梁。当收到创建 VM 的请求时,它接收一个普通的 Docker/OCI 镜像,并实时从该镜像生成 VM 的根文件系统,同时缓存结果,以便后续同一镜像的启动更加快速。我们的基础沙箱仍然只是一个 Dockerfile,orc 在请求时将其转换为可启动的 rootfs。

这使得我们的工作流与 GKE 时代完全一致:编辑 Dockerfile,部署新沙箱,而底层运行的是真正的 VM。同时,这也打开了一扇我们才刚刚开始探索的大门。因为任何 OCI 镜像都可以变成微型 VM,所以我们可以启动非默认镜像的沙箱。想要一台已经内置了 Postgres 和 pgvector 的 VM?让 orc 指向那个镜像,你就能得到一个独立的机器。沙箱不再是一个固定的环境,而是“任务所需的任何镜像,作为独立的 VM 启动”。

大规模执行

真正让这个问题变得棘手的是:每个聊天都拥有自己的沙箱。一个对话对应一台机器。在任何时刻,我们都有数千个沙箱处于活跃状态,而且这个数字从未静止。每当有人打开聊天,就必需有一个沙箱出现;每当聊天进入静默,就必需有一个沙箱消失,这样我们才不会为它付费。我们不断地在创建和销毁沙箱。

有两个数字决定了所有事情:多快能准备好一个沙箱,以及一台主机上能容纳多少个沙箱。

启动延迟。 Firecracker 微型虚拟机在几百毫秒内启动。这个速度快到我们完全不需要预热池,这是切换的一个比较低调的好处。在 GKE 下,我们需要维持空闲容量来隐藏启动时间。而有了 orc,一个全新的沙箱在你注意到之前就已经就绪了,所以我们只需在聊天开始时按需创建一个,当聊天结束时将其销毁。再也不用照看或为空闲池付费。

密度。 由于每个微型 VM 都非常小,我们可以在一台物理主机上打包很多个。我们根据沙箱实际需要的 CPU 和内存来分配资源,而不是过度配置,这正是我们能够经济地运行数千个沙箱的原因。

orc 本身刻意保持小巧。它是一个控制平面,通过简单的 API 进行交互:用给定镜像创建具有 N 个 vCPU 和 M MB 内存的 VM,向其中流式传输命令,读写内部文件,用标签标记以便后续查找,以及在完成后删除。每个客户机运行一个微小的 init 进程作为 PID 1,并拥有自己独立的网络。大致就是这样。神奇之处不在于某个巧妙技巧,而在于这些原语足够简单且足够快,足以支撑整个机群。

所有这些管道工作的回报,就是我们在文章开头所提到的:一个模型可以安装任何软件、编写程序、调用 API,并将答案交给你——所有这些都在一台真实的计算机上完成。

相似文章

@paulmillr: https://x.com/paulmillr/status/2075335421920239651

X AI KOLs Following

这篇推文描述了一种安全且可靠的代理AI开发环境设置,使用通过SSH访问的专用服务器,配合tmux实现会话持久化,带有原生tmux集成的终端模拟器,以及用于安全访问的VPN。作者主张采用这种方式而非本地代理执行,原因是安全和可靠性方面的考虑。

@sydneyrunkle: https://x.com/sydneyrunkle/status/2056419909941522687

X AI KOLs Following

Deep Agents v0.6 引入了代码解释器、用于按模型调优的测试配置文件、流式支持、用于检查点存储的 DeltaChannel 以及用于版本化代理记忆的 ContextHubBackend,实现了模型无关的编程式工具调用和递归工作流。