
我最早把 AI Agent 落地到业务里时第一件事不是选模型而是先解决一个看着很小的问题Agent 生成的代码在哪里跑。当时我用 FastAPI LangGraph 搭了一条工具调用链路图省事直接 subprocess.Popen 去执行模型生成的 Python 脚本来做文件解析。结果模型在一段清洗 CSV 的代码里写了个 while True整个实例的 CPU 被打满我那台服务器上其他接口全部超时。那次事故之后AI Agent 沙箱就成了我架构里的默认项而不是可选项。这两年我先后试过 E2B 这种基于 MicroVM 的执行沙箱、Modal 这种偏平台化的云端沙箱也长期维护过基于 All-in-One 容器思路的自建沙箱。这篇文章不打算做纯概念科普我按自己的理解讲讲这几条路线各自解决什么问题、适合什么规模的业务以及真正决定选型的那几个指标。1. 为什么 Agent 需要一个真正的执行沙箱从一次被打爆的实例说起1.1 Agent 不是只有调用 API这一个动作传统业务代码里的风险是可控的调用链路上的每个函数都是我们自己写的输入输出边界清晰。但 Agent 不一样它会把任务拆成多个工具调用而这些工具里经常包含生成一段代码并执行它这类动作。典型场景就是 Python 代码解释器。模型为了完成数据清洗、格式转换、文件处理会生成暂时存在的脚本你没法在运行前逐行审查。更麻烦的是Agent 还会调用外部命令安装依赖包、跑 shell 脚本、连接外部服务。这些动作如果直接在业务进程里执行一旦出现死循环、内存暴涨、恶意读取系统文件整个服务都会被拖下水。我那次被打爆的事故本质上就是缺少一层执行边界。subprocess 确实能启动一个新进程但这个进程仍然共享宿主的内核、文件系统和网络栈。进程的唯一隔离手段是操作系统账号和权限对资源消耗基本没有硬性限制。1.2 隔离边界有四个层级你要知道自己在哪一层我在对比方案之前先把自己脑子里那套隔离坐标系理清楚了。从宽松到严格大致分四层子进程隔离启动方便共享内核资源不可控适合完全可信代码。容器隔离借助 Linux namespace 和 cgroup 做资源限制密度高、启动快但共享宿主内核理论上存在内核逃逸风险。用户态内核/轻量虚拟机像 gVisor 这种给容器加一层用户态内核拦截系统调用安全性和性能做了折中。MicroVM像 Firecracker、Cloud Hypervisor 这种基于 KVM 的轻量虚拟化每个执行环境有独立内核隔离性最接近传统虚拟机但启动延迟和内存开销都比容器高。这四层不是谁替代谁的关系。很多做 Agent 执行底座的人会同时保留容器和 MicroVM 两条路按任务的信任级别拆分。你首先要搞清楚自己业务里执行代码的信任边界是内部同事提交的脚本还是外部用户随意输入的内容这两者的威胁模型完全不同。1.3 你要隔离的不只是代码还有网络和状态选沙箱方案时我见过很多新手只盯着能不能跑代码忽略了另外两个同样重要的隔离面。第一是网络隔离。Agent 执行的代码可能需要访问公网 API但它绝对不应该能访问你的内网数据库和管理端口。第二是状态隔离。沙箱里的工作目录、临时文件、环境变量必须和宿主隔离否则上一次任务残留的脏数据会污染下一次任务甚至通过磁盘文件把数据传出去。所以我在评估一个沙箱方案时会同时问三个问题执行资源能不能限制死网络进出口能不能控制文件系统和持久化会不会泄漏这三个问题的答案基本决定了你最后该选 MicroVM、云端沙箱还是 All-in-One 容器。2. E2B 的 MicroVM 路线用一颗迷你虚拟机换回安全边界2.1 E2B 的运行模型为一个执行任务开一台微型机器E2B 是我最早接触的 Agent 专用沙箱方案它在技术上踩中的路线就是 Firecracker MicroVM。简单理解每次调用 SDK 创建一个 Sandbox实际上是在云端起了一台只有几十到几百 MB 内存的微型虚拟机里面有完整的 Linux 用户态、shell、Python/Node 运行时但和宿主之间隔着一层完整的虚拟化边界。我当时用 Python SDK 写的调用大概长这样from e2b import Sandbox with Sandbox(timeout60, memory_mb256) as sbx: proc sbx.run(python3 -c print(42)) print(proc.stdout)这段代码背后的逻辑值得展开说Sandbox 对象代表一个独立的执行环境timeout 控制整个沙箱的存活时间超过之后会自动销毁防止任务卡死烧钱memory_mb 限制内存上限防止脚本一个列表推导式把内存撑爆。创建完成后可以往沙箱里传文件、执行 bash 命令、读取 stdout/stderr甚至可以交互式地起一个 Jupyter Kernel。这套 API 设计很明显是为 Agent tool-use 准备的模型让沙箱去执行命令沙箱把输出返回给模型一次工具调用对应一个短暂的沙箱生命周期。2.2 MicroVM 的强隔离到底强在哪里MicroVM 和容器最本质的差别是内核边界。容器里的进程虽然被 namespace 隔开但用的还是宿主内核一旦某个系统调用存在漏洞恶意代码拿到的就是宿主内核权限。而 E2B 这种基于 Firecracker 的沙箱里面是一个独立的 guest 内核代码再怎么提权最多也就是把这台微型虚拟机的内核搞崩宿主毫发无损。这个特性决定了它最适合跑什么代码不可信的、来自外部用户的、或者模型生成的临时脚本。如果你要做的是一个开放给用户提交代码的 AI Code Interpreter或是一个允许第三方插件运行的 Agent 平台MicroVM 几乎是唯一能让人安心的选择。另一个值得说透的点是启动速度。很多人一听虚拟机就觉得要等几十秒但 Firecracker 这类轻量虚拟化把启动过程优化得很极致加上 E2B 用镜像模板和快照技术做预热冷启动能压到秒级甚至更低。这个秒级和容器预热后的百毫秒级依然有差距但在安全收益面前对大多数 Agent 调用场景完全可接受。2.3 什么时候不该选 MicroVM性能和密度的真实成本MicroVM 不是没有代价。每个沙箱都有 guest 内核和基础用户态的内存开销同样一台物理机上能容纳的沙箱数量比容器少一个量级。对信任边界较高的内部脚本执行任务比如跑一些固定的数据处理流程上 MicroVM 属于杀鸡用牛刀基础设施成本和工作量都会涨上去。E2B 还涉及一个数据边界问题托管的沙箱默认运行在它的云上你的业务文件和 Agent 脚本要传到对方环境里执行。如果团队对数据出境和存储位置有强约束就需要考虑自托管部署自己管理 Firecracker 的虚拟机模板、镜像仓库和沙箱调度运维复杂度会明显上升。3. Modal 的云端沙箱一个平台级的远程执行体系3.1 从一次真实使用看 Modal Sandbox 的工作方式Modal 和 E2B 不是一个物种。Modal 的核心是云端函数平台你可以把 Python 代码定义成远程函数不需要管服务器运维平台自动伸缩、按量计费。Sandbox 只是它能力矩阵里的一项更准确地说它提供了一个可以任意执行命令的远程环境。我当时用 Modal 做沙箱的代码大概长这样import modal app modal.App(sbox_demo) sbx modal.Sandbox.create( python:3.11-slim, cmd[python, -c, print(hello modal)], volumes{/workspace: modal.Volume.from_name(my-vol)}, timeout60, ) print(sbx.get_logs())这里有两个关键点。第一Modal 允许你在函数或沙箱里挂载 Volume这个 Volume 是平台上的持久化存储多个沙箱可以共享数据卷。第二Modal 天然有 scale-to-zero 能力没有任务时平台会自动把资源缩掉来任务时再拉起来对 AI Agent 这种低频但单次必需跑的负载非常友好。3.2 为什么我说 Modal 是工作负载平台而不是单纯沙箱把 Modal 和 E2B 放在一起对比很容易产生混淆因为两个产品都能执行远程代码。但使用体验完全不同E2B 的本体是沙箱SDK 里围绕 Sandbox 做文章模板、快照、命令执行都是服务于给不可信代码一个封闭运行环境这个目标Modal 的本体是工作负载调度沙箱只是它支持的运行形态之一同样的底层能力还能跑批处理任务、跑 GPU 推理、跑定时调度、跑并行数据管道。这个差异决定了一件事如果你的需求只是让 Agent 安全地执行一段不可信代码直接用 E2B 更顺如果你的需求更宽比如还要统一管理 GPU、批量任务、定时任务和数据卷那 Modal 这类平台能把基础设施层一次性包圆。说白了E2B 是给你一把锁Modal 是给你一套带锁的房子房子里的项目如果你根本用不上那把锁就不算优势。3.3 按量付费和云端依赖省了运维也交出了控制权Modal 的计费模型是按实际使用资源计费缩容到零时没有任何闲置成本。对个人开发者和中小团队来说这比自托管一台沙箱服务器划算很多。但另一方面你的任务执行变成了对云端服务的强依赖网络延迟、平台版本变更、配额限制都会直接影响你的业务。我自己实际体验下来Modal 的冷启动表现已经很优秀但每次执行环境的下载和容器拉起还是要比本地 Docker 预热池慢一个身位。在数据敏感性不高的场景里Modal 这类平台是很好的选择。我见过不少团队把 Agent 工具执行的负载放在 Modal 上跑代码逻辑和结果数据都通过 Volume 管理既省去了运维物理机的成本又获得了一个相当不错的并发伸缩底座。4. All-in-One 容器路线AIO Sandbox 的够用主义4.1 一体化的含义一个容器把运行时、依赖和工具链全部打进去现在我重点聊聊自建方案里最常见的 All-in-One 容器路线标题里的 AIO Sandbox 指的就是这种一体化容器沙箱思路。它不是一个特定产品而是一种工程化范式把 Agent 执行环境需要的所有东西Python 运行时、Node 运行时、常用命令行工具、项目依赖、监控 agent、网络代理配置全部放进一个可复现的容器镜像里。容器就是你的沙箱单元镜像就是沙箱的模板。一个最小化但完整的 Dockerfile 大概长这样FROM python:3.11-slim RUN pip install --no-cache-dir pandas numpy requests \ apt-get update apt-get install -y --no-install-recommends \ ca-certificates curl jq \ rm -rf /var/lib/apt/lists/* WORKDIR /sandbox COPY entrypoint.sh /entrypoint.sh ENTRYPOINT [/entrypoint.sh]这里的关键不是把依赖装完而是把沙箱的执行契约定清楚固定工作目录、固定入口脚本、固定资源限制。启动时再用 docker run 把限制参数套上docker run --rm \ --cpus0.5 \ --memory512m \ --pids-limit64 \ --networknone \ --read-only \ -v /tmp/workspaces/sbx-001:/workspace:rw \ my-aio-sandbox-image python main.py这一行命令里没有炫技但每一条 flag 都有明确目的cpus 和 memory 限制 CPU 与内存pids-limit 防止 fork 炸弹networknone 直接断掉外部网络read-only 让根文件系统不能再被写入volume 只暴露一个临时工作目录。4.2 容器沙箱的隔离强度够用和绝对安全之间有一条线容器沙箱最被人诟病的一点是共享宿主内核这确实是真实的安全短板。但大多数内部 Agent 场景里的威胁模型根本不需要对抗国家级攻击者。你要防的是模型写出的死循环、资源炸掉、误删文件、访问不该访问的内网端口这些用容器限制参数就能挡掉百分之九十九。如果还想往上补安全等级不需要直接跳到 MicroVM可以在容器运行时层面做文章。比如使用 gVisor 的 runsc 作为 runtime给容器加一层用户态内核拦截大部分危险系统调用再比如叠加 seccomp 去限制可用的 syscall 集合。这些做的复杂度比维护一套 MicroVM 模板低得多安全收益却非常接近。当然我必须把话说直白如果你的沙箱要承接的是外部用户随意提交的不可信任意代码而且开放网络访问那容器方案无论怎么加固都建议你再想想。这种场景直接上 MicroVM 路线更稳妥。4.3 All-in-One 在 Agent 主流架构里为什么这么流行现在很多 Agent 项目都是标准的三件套FastAPI 做 API 层、LangGraph 或 LangChain 做编排、外部沙箱做执行层。AIO 容器路线在这个架构里非常丝滑因为它不需要引入额外的云端平台也不需要学习一套新的 SDK所有沙箱就是容器所有调度就是已有的 Docker/Kubernetes 能力。我在社区里看到越来越多团队包括做 Java 生态 Spring AI Agent 的人都在走这条路用一个通用沙箱容器镜像让 Agent 的工具调用 handler 去启动它任务结束就销毁。这种模式成本低、可解释性强、把你对基础设施的控制权留在自己手里对那些Agent 里跑的都是自己团队写的、相对可信的脚本的场景是最务实的解法。5. 从冷启动到并发真实选型指标与决策流程5.1 别先看技术先回答三个问题很多热搜里都在问AI Agent 怎么扛并发但并发方案的起点不是技术而是任务模型。我一般会先把三个问题拍死任务时长多长一个 bash 命令几秒跑完和一个数据处理任务要跑十几分钟对沙箱生命周期管理的要求完全不同。调用方信任级别多高内部自动化任务和外部用户触发的任务隔离要求差一个量级。并发曲线是什么形状是大量短任务同时进来还是一批长任务交错执行这三个问题的答案组合直接决定了你该用哪种方案。短任务、低信任、高并发这是 E2B 这类 MicroVM 需要做预热池才能抗下来的长任务、内部信任、中流量All-in-One 容器反而更经济变动剧烈、想省运维、低频高爆发Modal 的按量付费优势最大。5.2 沙箱池化真正帮 AI Agent 扛住并发的机制并发能力的核心不在沙箱本身而在沙箱池化。每个 Agent 任务如果都从零开始 create 沙箱光环境加载就会把你的延迟和资源消耗推到高位。正确做法是把沙箱做成一个预热池提前预启动 N 个沙箱任务到达后从池子里取一个跑完再放回去而不是销毁重建。我的一次实践是这样的在 All-in-One 容器方案里我用消息队列把任务分发给一组常驻的沙箱 worker每个 worker 内部维护一个干净的临时目录任务执行完清理现场回到池子里等待下一个任务。只有遇到不可恢复的脏状态比如进程被杀、文件系统损坏才销毁重建。这样一个 200MB 的镜像基础上单台 4 核 8GB 的容器实例可以稳稳扛住几十个并发沙箱任务成本比每任务一个 MicroVM 低得多。5.3 三套方案的关键对比我整理了一张对比表用来在团队里快速对齐选型思路维度E2B / MicroVM 类Modal 云端沙箱All-in-One 容器隔离级别每沙箱独立内核最强云平台托管环境中等偏强默认容器隔离可叠加 gVisor启动效率秒级需预热池秒级平台自动伸缩百毫秒级镜像预热后内存开销每沙箱有 guest OS 开销平台托管按量计费极小纯进程组开销运维复杂度较高要管模板与网络低平台接管低依赖容器生态最佳场景外部不可信代码多变负载、不想管运维内部脚本、数据管道表格里没有绝对的好坏最后还是要回归到你的信任边界和成本模型。聊到这里你会发现AI Agent 怎么扛并发这个问题的答案其实很朴素并发不靠模型侧优化靠执行层的池化和伸缩。任务的弹性和无状态化是前提沙箱复用是手段超时和资源限制是底线。6. 我在沙箱落地过程中踩过的坑和现在的组合方案6.1 镜像膨胀、状态污染和死循环三个最容易烧钱的坑第一坑是镜像膨胀。All-in-One 容器刚起步时我图省事把训练推理用的依赖一股脑装进沙箱镜像结果镜像从 300MB 一路涨到 1.5GB每次冷启动都要多花十几秒。后来我把沙箱镜像拆成 base 层和任务层base 只装运行时和基础 CLI按需加载的依赖放到独立的层甚至直接用 pip download 缓存来解决启动时间明显降下来。第二坑是状态污染。沙箱复用时上一次工作目录里留下的缓存文件、临时数据会影响这一次任务的产出。我出现过 A 任务写入的文件被 B 任务误读取的乌龙。解决方案是固定契约每任务用独立子目录跑完强制清理或者干脆用临时挂载卷做一次性 workdir。第三坑是死循环。模型生成的代码里出现 while True 概率并不低尤其是让 Agent 去爬数据、重试接口时。我第一次只设了命令级超时忘了沙箱级 timeout结果一个死循环任务在后台跑了半小时按量计费的账单非常酸爽。现在的规则很简单任何沙箱创建都必须携带 timeout超时就是 kill没有例外。6.2 网络权限、持久化卷和日志隔离边界的黑洞网络是我后来才重视的坑。一开始我让沙箱开了全部网络Agent 确实能访问外部 API 了但在一次安全演练里沙箱里的脚本居然顺利连上了同一个内网段里的 Redis直接读到了生产缓存数据。自那以后我所有自建沙箱都走显式代理白名单默认只放行 Agent 运行必需的 API host其他一律拒绝内网 CIDR 在 iptables 层面直接丢弃。持久化卷也有坑。Modal 的 Volume 和自建的共享卷在多沙箱并发写同一个文件时会出现竞争。Agent 任务一旦都有往同路径写日志的习惯你就等着看脏输出。规范路径是唯一的解法每个沙箱拥有自己的命名空间前缀写结果前先确认路径所有权。日志则是另一个暗坑。Agent 执行命令产生的 stdout/stderr 如果全量收集几轮工具调用下来就能把模型的上下文窗口撑爆。我给沙箱输出加了长度限制默认只保留最后几千字符截断时附一个标志模型看完结果也够用成本却能压下一大截。6.3 我现在的默认组合双轨制不搞一家通吃回到标题里那个从 MicroVM 到 All-in-One 容器的认知我现在的落地做法是双轨制。外部用户触发的、代码不可信的 Agent 执行任务走 MicroVM 路线隔离第一成本第二内部自动化、数据管道、定时批处理这类信任度较高的任务走 All-in-One 容器路线密度第一运维第二Modal 这类平台则留给需要 GPU 或超强伸缩的专项任务按量用、用完缩。从 E2B 到 Modal 再到自建容器切换过程中最大的收获不是学会了哪个平台而是想明白了一个道理沙箱选型本质上是在回答你愿意为安全付多少成本、为密度牺牲多少隔离。没有全能的沙箱只有对得上业务模型的沙箱。先想清楚你的 Agent 代码从哪来、要到哪去再回头选工具每一步都会好走很多。