
1. 从“一天 300 万个沙箱”说起这个数字到底意味着什么第一次看到“一天 300 万个沙箱”这个说法我的反应是先去算一笔账。300 万除以 86400 秒大约是每秒 34.7 个沙箱的创建速率。如果按 8 小时有效训练窗口算那就是每秒 104 个。这个量级已经不是“跑个 Docker 容器”能糊弄过去的了它逼着整个训练底座必须重新设计。沙箱这个词在 Agent 训练语境里指的是一套隔离的执行环境给 Agent 一个任务它写代码、调工具、访问文件系统、执行命令这些动作全部发生在一个受控的、可随时销毁重建的容器或微虚拟机里。为什么必须隔离因为 Agent 在强化学习阶段会疯狂试错它会删文件、会死循环、会写出把内存吃满的脚本甚至可能尝试访问不该访问的路径。如果这些行为直接跑在训练集群的宿主机上一次意外就能让整个训练任务崩掉。所以“一天 300 万个沙箱”背后真正在说的是Agentic RL 的训练吞吐已经被沙箱的创建和销毁速度卡住了。这不是一个运维指标而是一个训练效率指标。你沙箱起得慢Agent 的 rollout 就慢采样效率就低单位时间能拿到的训练信号就少。DeepSeek 把这件事写进论文本质上是在说Agent 训练的瓶颈已经从模型侧转移到了环境侧。这篇文章我想拆几件事沙箱底座到底该怎么设计才能扛住这个量级、Agentic RL 和传统 RL 在工程上的核心差异在哪、以及论文里提到的那些“家丑”——也就是他们自己踩过的坑——对普通团队有什么参考价值。适合正在做 Agent 训练、准备搭 Agent 沙箱、或者单纯想搞清楚 Agentic RL 工程复杂度的人看。不管你是刚接触 Agent 开发还是已经在调 Agent 框架这里面的取舍逻辑都值得过一遍。2. 沙箱底座的整体设计思路拆解2.1 为什么传统容器方案在这个量级会崩很多人第一反应是用 Docker 起沙箱。我早期也这么干过单机跑几十个并发没问题但一旦上到每秒几十个创建速率问题就全冒出来了。Docker daemon 本身是个单点所有容器创建请求都要经过它镜像层解压、网络命名空间配置、cgroup 挂载这些操作在高频调用下会迅速成为瓶颈。实测下来单 daemon 的容器创建速率大概在每秒 10 到 20 个之间再往上延迟就指数级上升。更麻烦的是清理。Agent 跑完任务后沙箱要销毁Docker 的 stop 加 rm 在容器里有残留进程时经常卡住得等超时再强杀。一天 300 万个沙箱意味着同样量级的销毁操作清理不及时就会导致宿主机资源泄漏跑着跑着磁盘满了、PID 耗尽了。还有一个隐蔽问题Agent 任务往往需要网络访问来调 API、拉依赖。Docker 默认的 bridge 网络在大量容器并发时iptables 规则会膨胀NAT 表项冲突的概率上升。我遇到过跑了几千个容器后新容器网络不通的情况排查半天发现是 conntrack 表满了。所以在这个量级上容器方案必须往更轻的隔离层走。业界常见的两条路一是微虚拟机比如 Firecracker 这类启动快、隔离强但内存开销比容器大二是用户态内核或进程级隔离启动极快但隔离性弱一些。DeepSeek 论文里提到的方案从公开信息推断更偏向于在容器基础上做深度定制把创建路径上的所有同步操作异步化、把镜像分发做成按需加载。2.2 沙箱生命周期管理的核心矛盾沙箱这个东西有个根本矛盾隔离性和启动速度天然对立。隔离越彻底要初始化的东西越多启动越慢启动越快往往意味着共享了更多宿主机资源隔离就越弱。Agentic RL 的场景又给这个矛盾加了两个约束。第一沙箱是短命的一个 rollout 可能就几十秒到几分钟生命周期短意味着创建销毁的开销占比极高。第二沙箱是有状态的Agent 在里面写文件、装包、改配置这些状态在任务结束后要能干净地丢弃不能污染下一个任务。我的经验是解决这个矛盾的关键不在于选哪种隔离技术而在于把沙箱的“冷启动”变成“热启动”。具体做法是预创建一批沙箱池任务来了直接从池里取用完不销毁而是重置。重置比创建快得多因为文件系统可以用 overlay 的快照回滚进程全部杀掉网络规则复用。这样把创建开销摊薄到池的维护上池子大小根据并发峰值动态调整。但池化也有坑。Agent 任务对环境的依赖差异很大有的要 Python 3.11有的要 Node 20有的要特定版本的 CUDA。如果池子里只有一种镜像命中率就低。常见做法是按镜像维度分池每个池子维护自己的预热实例。这就引出了镜像管理的问题下一节细说。2.3 镜像分发被低估的瓶颈一天 300 万个沙箱如果每个都要拉一次镜像哪怕镜像只有 500MB那也是 1.5PB 的传输量。这个量级下镜像分发必须重新设计。传统做法是每个节点本地缓存镜像但 Agent 任务的镜像种类可能成百上千节点磁盘根本放不下。我见过一个团队的做法是搞一个中心镜像仓库加 P2P 分发节点之间互相传镜像层减少回源压力。这个思路对但 P2P 在容器创建高频时会有调度延迟节点等镜像层的时间可能比直接回源还长。更实用的方案是镜像分层加按需加载。把镜像拆成基础层和任务层基础层OS、运行时、常用库在所有节点预热任务层特定依赖按需拉取。任务层通常很小几十 MB 级别拉取快。再配合 lazy loading容器启动时只加载实际用到的文件不用等整个镜像就绪。这个技术在一些容器运行时里已经支持效果很明显启动延迟能从秒级降到百毫秒级。注意按需加载对文件系统的随机读性能要求高如果底层存储是网络盘反而可能比全量拉取更慢。选型前一定要在自己的存储上压测。3. Agentic RL 与传统 RL 的工程差异3.1 采样单元从“一步”变成“一整段交互”传统 RL 训练里一个采样单元往往是一步动作环境返回一个 reward。Agentic RL 完全不是这个逻辑。一个采样单元是一整段 Agent 与环境的交互轨迹Agent 可能先思考、再调工具、看结果、再思考、再调工具循环几十轮才完成一个任务。这段轨迹里每一步都可能产生 token但 reward 往往只在任务结束时给一个。这个差异对沙箱底座的影响是巨大的。传统 RL 的环境可以是无状态的、轻量的甚至就是个函数。Agentic RL 的环境必须是有状态的、能执行任意代码的、能维持长时间会话的。沙箱不再是“跑一下就算”而是要陪 Agent 走完整个任务流程。我踩过的一个坑是早期我们把沙箱超时设得很短比如 60 秒结果很多 Agent 任务跑到一半就被杀了轨迹不完整训练信号全是噪声。后来把超时放宽到 10 分钟但这样沙箱占用时间变长并发数上不去。最后的解法是分级超时简单任务 2 分钟复杂任务 15 分钟根据任务类型动态分配同时用抢占式调度保证高优先级任务能拿到资源。3.2 工具调用的可靠性直接决定训练质量Agent 在沙箱里调工具工具可能失败。网络超时、依赖缺失、权限不足、命令写错这些都会让工具调用返回错误。传统 RL 里环境返回错误就是错误Agent 学到的就是“这个动作不好”。但 Agentic RL 里工具失败的原因可能跟 Agent 的决策无关纯粹是环境问题。如果沙箱底座不稳定工具调用失败率忽高忽低Agent 学到的策略就会混乱。它可能学会“不要调这个工具”但实际上工具本身没问题只是沙箱偶尔抽风。这种噪声对训练的伤害比想象中大因为 Agent 会把环境的不确定性归因到自己的动作上。所以沙箱底座的一个核心指标是工具调用的成功率稳定性。我们内部的要求是同一类工具调用的失败率波动不能超过 1%。为了做到这点沙箱里要预装常用工具、预配网络白名单、预置依赖缓存。Agent 调 pip install 的时候如果每次都从公网拉失败率必然高。常见做法是搭一个内网镜像源pip、npm、apt 全部指向内网拉取速度快且稳定。3.3 轨迹数据的采集与回放Agentic RL 的训练依赖大量轨迹数据。每条轨迹包含 Agent 的每一步思考、每一次工具调用、每一个观察结果。这些数据要从沙箱里采集出来喂给训练框架。采集本身不难难的是回放。调试训练问题时经常需要把某条轨迹重新跑一遍看看 Agent 当时为什么做了那个决策。如果沙箱环境不能精确复现回放就没意义。这就要求沙箱底座支持环境快照在轨迹的每个关键节点保存文件系统状态和进程状态回放时从快照恢复。这个功能实现起来很重。文件系统快照可以用 overlay 的 lowerdir 做但进程状态快照基本做不到通用。我们的折中方案是只快照文件系统进程状态通过重放命令序列来恢复。对于大多数 Agent 任务够用因为 Agent 的状态主要存在文件里进程都是短命的。4. 沙箱底座的实操实现要点4.1 创建路径的极致优化沙箱创建慢慢在哪我拆过创建流程大致是调度到节点、准备文件系统、配置网络、启动进程、注入环境变量。每一步都有优化空间。调度环节如果每次创建都走中心调度器调度器会成为瓶颈。常见做法是节点本地维护一个沙箱池创建请求直接由节点本地处理中心调度器只负责池子的容量管理。这样创建路径缩短到节点内部延迟从几十毫秒降到几毫秒。文件系统准备用 overlayfs 做分层。基础层只读所有沙箱共享任务层每个沙箱一份写时复制。这样创建时不用拷贝整个文件系统只需要建一个空的 upperdir。实测这个优化能把文件系统准备时间从几百毫秒降到几毫秒。网络配置是最容易忽略的。每个沙箱都要有独立的网络命名空间配置 veth pair、分配 IP、设路由。这些操作涉及内核网络栈在高频创建时会有锁竞争。优化方向是预创建网络命名空间池创建沙箱时直接绑定一个空闲的命名空间只改必要的路由规则。进程启动用轻量级的 init 进程而不是完整的 systemd。Agent 任务通常只需要一个 shell 加几个工具进程不需要完整的服务管理。用一个几十 KB 的 init 程序启动时间能压到毫秒级。4.2 资源隔离与配额管理沙箱之间必须隔离资源否则一个 Agent 跑飞了会拖垮整个节点。CPU 用 cgroup 限制内存用 cgroup 加 OOM killer磁盘用 quota网络用 tc 限速。这里有个取舍限制太严Agent 正常任务跑不动限制太松一个沙箱能把节点吃满。我的经验是按任务类型给配额。代码生成类任务 CPU 需求高但内存需求低给 2 核 2G数据处理类任务内存需求高给 1 核 8G网络密集型任务给带宽配额。磁盘配额特别重要。Agent 写文件可能失控一个死循环写日志能把磁盘写满。我们用项目配额每个沙箱的写目录限制在 1GB超了直接报错。同时监控节点的磁盘使用率超过 80% 就停止接受新沙箱。提示cgroup v2 的 memory.high 比 memory.max 更适合沙箱场景。high 是软限制超了会触发回收但不杀进程max 是硬限制超了直接 OOM。Agent 任务对 OOM 很敏感用 high 更温和。4.3 沙箱重置与状态清理池化沙箱的核心是重置。重置要做几件事杀进程、清文件、复位网络、清环境变量。杀进程不能只杀主进程要杀整个进程组。Agent 可能 fork 出一堆子进程漏杀了会残留。用 cgroup 的 kill 接口最彻底直接杀掉 cgroup 下所有进程。清文件用 overlay 的 upperdir 重置。把 upperdir 删掉重建文件系统就回到基础层状态。这比遍历删除快得多而且不会有遗漏。但要注意如果有挂载点或者特殊文件重建 upperdir 可能失败需要先卸载。复位网络主要是清 conntrack 表项和重置 iptables 规则。如果沙箱用了独立网络命名空间直接删掉命名空间重建更干净。环境变量清理容易被忽略。Agent 可能设置了 PATH、LD_PRELOAD 之类的变量残留到下一个任务会导致诡异问题。重置时把环境变量恢复成基础镜像的默认值。4.4 监控与可观测性一天 300 万个沙箱出问题是必然的。关键是要能快速定位。监控要覆盖几个维度创建成功率、创建延迟分布、沙箱存活时长分布、资源使用峰值、工具调用失败率。创建失败的原因要分类统计调度失败、镜像拉取失败、网络配置失败、资源不足。每类失败的排查路径不同。我们内部搞了个看板实时显示各类失败率超过阈值就告警。沙箱存活时长分布能反映很多问题。如果大量沙箱存活时间极短可能是 Agent 一启动就崩了如果大量沙箱跑满超时可能是任务太难或者 Agent 卡住了。这个分布的变化往往比绝对值更有信息量。工具调用失败率要按工具类型细分。某个工具的失败率突然上升可能是那个工具依赖的外部服务出问题了。这种细粒度监控能帮你在 Agent 训练质量下降之前就发现问题。5. 论文里那些“家丑”的参考价值5.1 为什么公开踩坑记录比成功经验更有用论文里提到的“家丑”我理解是那些不体面的工程细节早期方案怎么崩的、哪些设计后来被推翻了、哪些指标一开始没考虑到。这些东西在正式的技术文档里通常不会写但对实际做工程的人价值极大。成功经验往往有幸存者偏差。论文说“我们用了方案 X”但没说“我们试了方案 Y 和 Z 都失败了”。如果你照着方案 X 去做遇到问题时不知道 Y 和 Z 的坑可能就会在同一个地方摔倒。公开踩坑记录相当于帮你排雷告诉你哪些路走不通。我特别关注论文里关于沙箱创建失败率的描述。如果他们在早期遇到过创建失败率高达百分之几的情况那说明这个量级下失败是常态必须设计容错机制。如果他们的失败率能压到千分之一以下那说明有系统性的优化手段。这些数字比任何架构图都有参考价值。5.2 从“家丑”反推设计约束论文里如果提到“我们最初用 X 方案但在 Y 场景下崩了”这个 Y 场景就是关键约束。比如如果提到“单节点沙箱数超过 500 后网络开始不稳定”那 500 就是一个经验阈值你在设计时要么控制在 500 以内要么就得解决网络栈的扩展性问题。另一个常见的“家丑”是性能数据不达预期。论文可能说“我们预期创建延迟 10ms实测 50ms”。这个差距背后的原因往往是最有价值的可能是内核某个锁竞争、可能是存储 IO 瓶颈、可能是调度器设计问题。搞清楚这个原因你就能在自己的系统里提前规避。我读这类论文的习惯是先看他们遇到的问题再看他们的解决方案最后看他们没解决的问题。没解决的问题往往是最难的也是你未来可能遇到的。如果论文坦诚地说“沙箱的冷启动问题我们还没完全解决”那你就知道这块是个硬骨头别指望有现成方案。5.3 对普通团队的启示不是每个团队都需要一天 300 万个沙箱。大多数团队可能一天几千到几万个。但论文里的设计思路是通用的把创建路径做短、把状态管理做轻、把失败处理做细。我的建议是先别追求极致性能先把稳定性做起来。沙箱创建失败率控制在千分之一以内工具调用成功率稳定在 99% 以上再考虑优化延迟。很多团队一上来就追求毫秒级创建结果稳定性一塌糊涂训练根本跑不起来。另外沙箱底座的可观测性要尽早做。不要等到出问题了才加监控。创建成功率、延迟分布、资源使用这些指标从第一天就要采集。数据积累起来后面优化才有依据。6. 常见问题与排查技巧实录6.1 沙箱创建失败排查速查表现象可能原因排查方法解决方向创建延迟突然升高节点资源不足查节点 CPU/内存/磁盘使用率扩容或清理僵尸沙箱创建直接失败镜像拉取失败查镜像仓库连通性和镜像是否存在修网络或补镜像创建成功但网络不通conntrack 表满查nf_conntrack_count调大表容量或缩短超时创建成功但进程起不来基础镜像损坏手动起一个沙箱复现重建基础镜像创建成功率波动调度器过载查调度器 QPS 和延迟改本地调度或加调度器实例6.2 沙箱内工具调用失败的典型场景Agent 在沙箱里调工具失败原因往往不在工具本身。我整理了几类高频问题。第一类是网络问题。Agent 调外部 API沙箱的网络策略没放行请求直接被拒。排查方法是进沙箱手动 curl 一下目标地址看是 DNS 问题还是防火墙问题。常见解法是把常用 API 域名加进白名单或者配一个内网代理。第二类是依赖问题。Agent 写的代码 import 了一个没装的库运行时报 ModuleNotFoundError。这个在训练早期特别常见因为 Agent 还不知道环境里有什么。解法是在基础镜像里预装常用库同时在沙箱启动时注入一个环境说明文件告诉 Agent 有哪些依赖可用。第三类是权限问题。Agent 尝试写一个只读目录或者执行一个没有执行权限的脚本。这类失败其实是有效的训练信号Agent 应该学会检查权限。但如果权限配置本身不合理比如工作目录设成了只读那就是底座的问题。第四类是资源问题。Agent 跑了一个内存密集的操作被 OOM killer 杀了。这种失败对 Agent 来说是困惑的因为它看不到 OOM 信号只看到进程突然消失。解法是在沙箱里加一个资源监控进程资源快耗尽时给 Agent 发一个明确的错误信号。6.3 沙箱泄漏的排查与预防沙箱泄漏是指沙箱已经没用了但没被销毁一直占着资源。一天 300 万个沙箱哪怕泄漏率只有万分之一一天也泄漏 300 个。积累几天节点就满了。泄漏的常见原因有几个。一是 Agent 进程卡死沙箱超时机制没生效。排查方法是查沙箱的存活时长超过阈值的列出来看。二是销毁流程失败比如 umount 卡住导致清理中断。这种要在销毁流程里加超时和重试。三是调度器状态不一致调度器以为沙箱已销毁但节点上还在。这种要靠定期对账来发现。预防泄漏的核心是加兜底清理。不管正常销毁流程走没走完节点上跑一个定时任务扫描超过最大存活时长的沙箱强制清理。这个兜底任务要足够健壮不能因为某个沙箱清理失败就卡住整个任务。注意强制清理沙箱时如果沙箱里有正在写的数据可能会损坏文件系统。所以强制清理前要先尝试优雅停止给一个短超时超了再强杀。6.4 训练质量下降的环境侧归因Agent 训练质量下降大家第一反应是模型或算法问题。但我的经验是先排查环境侧。环境不稳定导致的训练质量下降表现和算法问题很像但排查起来快得多。排查步骤先看工具调用成功率如果下降了基本可以确定是环境问题。再看沙箱创建失败率如果上升了说明底座不稳定。然后看沙箱存活时长分布如果异常说明任务执行出了问题。最后看资源使用如果某个资源接近上限说明配额需要调整。环境侧的问题解决后训练质量往往能恢复。如果环境指标都正常但质量还是下降那才需要往算法侧查。这个排查顺序能帮你省很多时间因为环境问题通常比算法问题好定位。7. 我对 Agent 沙箱底座的一些个人判断做了一段时间 Agent 训练底座我最大的体会是这个领域的工程复杂度被严重低估了。大家讨论 Agent 的时候焦点都在模型能力、prompt 设计、工具生态上但真正卡住训练效率的往往是沙箱底座这种“脏活累活”。沙箱底座的核心指标不是单点性能而是稳定性。创建快 10ms 但失败率 1%不如创建慢 50ms 但失败率 0.01%。因为训练是个长期过程失败率高的底座会让训练信号充满噪声模型学不到东西。我宁愿牺牲一点延迟换稳定性。另一个体会是沙箱底座的设计要跟着训练需求走不能闭门造车。训练侧需要什么粒度的轨迹、需要多长的超时、需要哪些工具预装这些都要和底座设计对齐。我见过底座团队和训练团队各做各的结果底座提供的功能和训练需要的对不上返工成本极高。最后别指望有银弹。沙箱底座的每个环节都有取舍容器 vs 微虚拟机、池化 vs 按需、中心调度 vs 本地调度没有哪个方案全面占优。关键是搞清楚自己的约束并发量多大、任务多长、隔离要求多高、团队运维能力如何。约束清楚了方案自然就出来了。如果让我给刚起步的团队一个建议我会说先把单节点的沙箱管理做扎实再考虑横向扩展。单节点上把创建、重置、监控、清理这套流程跑通失败率压到千分之一以内再去做多节点调度。很多团队一上来就搞分布式结果单节点的问题没解决分布式又引入新问题最后两头顾不上。