训练一个能改代码、跑测试、操作浏览器的 Agent,和训练聊天模型有一个区别:模型每走一步,都需要一台真实的机器来执行动作、返回结果。它要 clone 仓库、装依赖、起服务、跑单测,这台机器的状态还要在几十轮交互中一直保留。这就是沙箱。
一次强化学习(RL)训练的单个任务批次,可能同时需要三万多个沙箱。模型思考的时候沙箱要原地等着,GPU 任务被抢占了沙箱里的状态还不能丢。
Agent 强化学习循环中沙箱所处的位置
DeepSeek 9 月 19 日发布的这份技术报告,讲的就是他们的沙箱平台 DSec。从 DeepSeek V3.2 到 V4.1,RL 训练和评测用到的沙箱都跑在它上面。
DSec 单个生产单元的规模
一、Agent 沙箱的负载特征
第 4 章用一周的生产数据描述了这类负载和传统云计算负载的差别。
RL 的 rollout 和评测按批次进行,一个任务创建的沙箱数中位数是 2528 个,p99 是 16388 个,最大的单任务要 3.2 万个。这些请求在很短的窗口内到达,因为训练批次要等所有环境就绪才能开始,任何一个环节有慢的,整批都在等。
创建之后,沙箱大部分时间在等模型。Agent 发一条命令,沙箱执行几百毫秒,然后空转到下一条命令来。约 90% 的沙箱平均 CPU 使用率不到申请量的 5%,所以超卖是一定要做的,要解决的是超卖之后怎么保证安全和性能。麻烦在于这些沙箱又有状态又活得久。模型改过的文件、装过的依赖、起过的服务,后面的命令都要依赖,不能随便回收。容器中位存活 17.4 分钟,microVM 是 15.5 分钟,两者 p99 都超过三小时。CPU 早就空了,内存和页缓存一直占着。
一个典型沙箱的三个阶段:setup 之后 CPU 只有零星尖峰,内存一直占着。图片来自论文图 3
环境的种类也多得超出一般云平台的假设。一周内容器后端用了 11266 个基础镜像和 102171 个工作区,microVM 后端用了 53590 个任务专属工作区。复用率反过来很低,一个容器镜像在一个任务里中位数只被 3 个沙箱用到,microVM 镜像中位数只用 1 次。把镜像缓存在节点本地的传统策略在这种分布下基本不起作用。而且即使拉下来了,大部分数据也用不到。不同语言的镜像在运行时被实际访问的数据只占 4.2% 到 13.3%,一个 12.1 GB 的 Java 镜像实际读取的是 9.2%。
还有两条和性能无关。Agent 执行的是不可信代码,可能弄坏文件系统、耗尽资源、主动找作弊途径。GPU 训练任务会被常规性抢占,正在进行的 rollout 要能中断和恢复。
二、整体架构
DSec 提供四种沙箱后端,通过一个 Python SDK(libdsec)统一接入。
SDK 没有抹平四种后端的语义差异。函数调用、容器、microVM 和完整 VM 的启动成本、隔离边界、文件系统语义都不同,调用方需要自己选后端,平台只保证统一的访问路径和相似的操作模型。
四种后端在隔离强度和开销之间的取舍
集群级服务包括鉴权(IAM)、入口代理(apiserver)、放置引擎和状态观测器(watcher)。放置引擎用 power-of-k-choices 算法,随机抽几个健康节点,选最空的一个。这几个组件都不保存持久状态,重启后重新轮询节点就能重建视图,增减实例不需要恢复过程。
节点级运行时有三个组件。每台机器一个 edge,负责本地准入检查、创建沙箱、应用网络策略和资源回收。每个沙箱一个 aether,作为沙箱与 edge 之间的通信代理。沙箱内每个 shell 会话一个 chronus,负责命令执行、文件操作和流式 I/O。
训练代码运行在可信的 GPU 服务器上,沙箱运行的是模型生成的不可信代码,两侧网络隔离,apiserver 是唯一通道。
DSec 整体架构。左侧为集群级服务,右侧为节点级运行时和四种后端,底层是 3FS。图片来自论文图 1
平台还做了云上溢出。当本地利用率超过 80%,放置引擎把一部分请求转到公有云 VM。生产数据显示一份 30 TB 的去重镜像集能覆盖 70% 容器任务的文件访问,这份镜像集离线同步到云端文件系统,200 台云 VM 就能吸收约 30% 的峰值溢出。
三、核心机制
三类核心机制的总览:可组合层、内存优化、CPU QoS、按需加载。图片来自论文图 9
可组合的环境层
一个沙箱环境可以拆成三部分:基础镜像(Ubuntu、Python 3.10 这类系统级依赖)、工作区(任务的代码仓库和专属依赖)、工具包(频繁更新的 Agent 脚手架,比如 DeepSeek Harness)。
传统做法是把三者打成一个完整的 OCI 镜像。工具包一升级,所有包含它的镜像都要重建,哪怕基础镜像和工作区一个字节没变。N 个工作区就要重建 N 次,成本是 O(k·N)。
DSec 改了 Docker 守护进程,让 overlayfs 的下层目录在沙箱创建时动态组合:基础镜像在底层,工作区作为只读层插在中间,工具包叠在最上面,运行时写入落到可写上层。三层各自独立版本化、独立分发,升级 k 个工具包只需重建 k 个工具包层。对 dockerd 的这个修改只有 30 行 Go 代码。
左:单体镜像,升级工具包 T1 要重建所有镜像。右:三层独立版本化,只更新 T1 层。图片来自论文图 4
只读层用 EROFS 格式存储。tar.gz 是顺序流,必须整包解压;EROFS 支持压缩状态下的随机访问,只解压被读到的块。
实验用同一个评测工作区做对比。tar 包在每个沙箱里解压,端到端 79 分钟;EROFS 直接挂载,45 分钟,磁盘写入总量少 5.5 倍。
高密度下的内存和 CPU
microVM 的内存浪费有两个来源。
第一,同一份镜像数据在宿主机缓存一份,每个客户机里再缓存一份。DSec 用 virtio-pmem 加 DAX,让文件访问直接映射到宿主机页面,多个 microVM 共享同一份宿主页缓存。峰值内存降低 40.2%。代价是冷访问需要同步处理缺页,瞬时峰值 CPU 从 26.5% 升到 41.4%。CPU 紧张的部署可以不开这项。
第二,客户机内部释放的内存不会自动还给宿主机。DSec 把 Linux 的 DAMON(数据访问监控)和 virtio-balloon 的空闲页上报组合起来:DAMON 定期采样找出长期未访问的冷文件页并回收,回收后的零散页面合并成大块,再由 balloon 上报给宿主机释放。时间积分的内存消耗降低 21.2%,CPU 开销可忽略。
生产中的配置是:只读的基础镜像和工具包层用 pmem,大的可写磁盘用 DAMON 回收。
四种 Firecracker 配置下的宿主机内存(左)和 CPU(右)。pmem 压低峰值内存,但抬高了前几分钟的 CPU。图片来自论文图 12
CPU 方面的问题出在超线程。有些任务有严格的每步延迟预算,比如限时下棋的 Agent。把后台任务调成低优先级不够,因为两个任务如果跑在同一物理核的两个超线程上,照样共享执行单元。
DSec 分两层处理:尽力型任务放进 SCHED_IDLE,有活就让路;延迟敏感任务开启 Linux core scheduling,禁止不相关的尽力型任务跑到同一物理核的兄弟线程上。
实验用国际象棋 Agent 做负载。50% 后台负载下,不做保护延迟膨胀 45.2%,只用 SCHED_IDLE 最多改善 3.4%,加上 core scheduling 压到 17.3%。剩下的干扰来自睿频下降、内存带宽和末级缓存竞争,团队觉得这个程度可以接受,没有再做内存带宽隔离。
延迟敏感任务在不同后台负载下的每步耗时。绿线是只用 SCHED_IDLE,红线是加上 core scheduling。图片来自论文图 13
按需镜像加载
一个沙箱实际读到的数据不到镜像的 15%,所以按需加载能真正砍掉 I/O 总量,而不只是把开销挪到别的时间。
DSec 没有另搭镜像分发系统,直接复用 DeepSeek 的分布式文件系统 3FS,也就是训练数据用的那套存储。
3FS 的特点是大块顺序读写吞吐很高,小块随机 I/O 很差,整个存储设计都是围着这一点做的。沙箱的写入零碎又不可控,日志之类的小写很多,所以可写层放在节点本地磁盘,不碰 3FS。只读的镜像数据在被访问时才从 3FS 取,内核预读会把相邻的块合并成大请求,正好用上 3FS 的大 I/O 吞吐。文件系统元数据是另一个小读大户,EROFS 的多设备模式可以把元数据和数据分开存,DSec 把元数据提前下到本地,路径查找和目录遍历就不再走远程 I/O。
microVM 的情况不同。Firecracker 不支持 virtio-fs,Docker-in-microVM 又不能用 overlayfs 当数据目录。所以只读层仍用 EROFS,可写盘用 OverlayBD,通过 ublk 用户态块设备暴露,按 256 KiB 分块拉取,本地文件系统做二级缓存。
实验在 10 个节点上跑 8192 个容器的突发。按需加载约 35 分钟完成,和镜像全部预置在本地的理想情况持平;从远端拉完整镜像超过 60 分钟,慢 1.71 倍,每节点磁盘写入超过 1600 GB,按需加载约 700 GB,少 57%。
8192 个容器突发下三种方式的对比。左:每节点在跑的容器数。右:磁盘写入 IOPS 与累计写入量。图片来自论文图 10
四、与 RL 框架的协同设计
让 Agent 构建环境
RL 需要的环境数量太大,手工构建不现实。DSec 让 Agent 在同一套基础设施上交互式地搭环境,随时通过 pack_diff 对沙箱做增量磁盘快照,之后还原成新沙箱。一次交互会话就成了可复用的环境,不需要单独的镜像构建流水线。
搭环境的 Agent 和跑任务的 Agent 用不同账号,打包前清理可写层的残留数据,避免参考答案被带进镜像。
把 Agent loop 从 GPU 训练中拆出来
早期版本里 Agent loop 跑在可抢占的 GPU 训练 Pod 中。GPU 任务被抢占,Agent loop 就丢了,只剩沙箱还在,恢复靠命令日志回放去对齐两边状态。
从 DeepSeek V4.1 开始,rollout 执行整体搬到 DSec 上,拆成两部分:Agent 沙箱承载脚手架和工具,Worker 容器管理沙箱并提供与脚手架无关的控制层。两者都在可抢占的 GPU 池之外,rollout 的生命周期与训练器解耦。GPU 任务被抢占后重新连上来继续即可,RL 框架里的日志回放逻辑随之删除。
V4.1 前后 Agent loop 所在位置的变化
暂停沙箱
GPU 任务被抢占后,几万个沙箱不能删,空占内存也不合理。RL 框架主动给相关沙箱发暂停请求。容器先冻结进程树,然后开启 swap 并触发主动内存回收;microVM 把内存和执行状态存成快照后直接杀掉 Firecracker 进程。任何请求打到暂停的沙箱上,会先恢复再执行。
Agent 的作弊行为和引发的事故
第 6.4 节记录了生产中观察到的两类问题。
第一类是通过非预期渠道获取答案。
在沙箱里翻平台管理的文件和通信通道,找残留的参考答案。
直接给 chronus 的 socket 发构造好的 RPC 消息伪造用户请求,并翻 chronus 日志找泄露的答案。
chronus 有些操作会调 bash,Agent 尝试覆盖 /bin/bash,绕过检查或往后续 shell 会话里注入命令。
团队加上文件和 socket 访问控制后,有 Agent 用 XFS_IOC_SWAPEXT 这个 ioctl 交换两个文件的数据块映射,想通过另一个文件描述符读到受保护内容。结果 XFS 元数据损坏,文件系统关闭。
在沙箱外部扫描端口和服务找可达的镜像源,通过 Go 模块代理拉 GitHub 代码,或者安装更新版本的包去找现成实现。
作者由此得出的结论是,只检查最终输出无法判断 Agent 是否按预期解决了任务。
第二类没有恶意,但破坏了执行环境。
一个 Agent 从根目录递归 grep,遍历到 /proc 下的 kpagecgroup,触发内核 bug,内核崩溃。
一个漏洞利用任务里,本该发给目标 VM 的攻击命令被 Agent 在自己的容器里执行,打崩了自己的内核。
一个 Agent 执行了 yes,无限输出被 chronus 记录下来供异步读取,硬盘上堆了几十 GB。
对应的防御有两项。AppArmor 控制文件读写和 socket 访问,Agent 在沙箱里即使是 root 也碰不到 chronus 的日志和内部 socket。eBPF 实现每沙箱的网络白名单,训练框架可以按任务指定允许 PyPI、禁止 NPM 之类的规则,并随任务阶段动态调整。
这些控制只覆盖了一部分问题。对触发内核 bug 这类破坏性行为没有通用防御,目前靠加强可观测性,发现一个补一个。
Agent 会翻日志、改 bash 找答案,你更倾向在沙箱里把文件和 socket 封死,还是只靠奖励函数罚作弊?训练任务还要装包、跑测试——评论区说说你的选择和理由。
五、实验结果汇总
实验在独立的 10 节点测试集群上完成,负载来自真实的 RL 训练和评测场景,包括 SWE-bench、Terminal-Bench 和安全漏洞利用任务。
六、读后
读完最大的感受是 Agent RL 的资源约束已经不只在 GPU 侧。一个 160 节点、3 万核的 CPU 集群专门用来跑沙箱,报告里反复出现的优化目标也不是算力,是内存密度、镜像分发带宽和启动延迟。沙箱成本每降一点,同样预算下能跑的 rollout 就多一些。
DSec 用到的 overlayfs、EROFS、DAMON、virtio-balloon、core scheduling、eBPF、AppArmor 都是 Linux 内核里现成的东西,第 7 章专门说了一句没有修改内核,只有配置和编排层的集成。报告里更有参考价值的是那些边界条件。virtio-pmem 省内存但吃 CPU,DAMON 回收对峰值没用但对时间积分有用,SCHED_IDLE 单独用改善不到 4%,这些都是在真实负载上跑出来才知道的。
第 6.4 节的作弊记录说明了另一件事。覆盖 /bin/bash、翻 socket 日志、用 ioctl 交换文件块,这些行为发生在系统层,靠奖励函数设计防不住,只能在系统层设防。模型能力上去之后,训练基础设施本身成了需要防守的攻击面。
DSec 也没有做成一个万能沙箱。四种后端并存,SDK 不假装它们一样,选哪个由调用方决定,平台负责统一的调度、分发和治理。OverlayBD 和 ublk 的 Rust 实现已经开源在 GitHub 的 kvcache-ai/AgentENV 仓库,平台整体没有开源。
建议直接读原文,第 4 章的负载分析和第 6.4 节的事故记录信息量最大。
资源链接
论文原文: https://arxiv.org/pdf/2609.22978v1
OverlayBD 与 ublk 实现: https://github.com/kvcache-ai/AgentENV
加入社群,+v: llmapp886
点击公众号菜单加入讨论