ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

DeepSeek日建300万沙箱:Agent训练沙箱架构与优化实战

DeepSeek日建300万沙箱:Agent训练沙箱架构与优化实战 1. 一天300万个沙箱这个数字到底意味着什么第一次看到“DeepSeek 一天创建 300 万个沙箱”这个说法我的反应不是“哇好厉害”而是下意识开始算账。300万除以86400秒大约是每秒34.7个沙箱。也就是说在你读完上面这句话的时间里已经有六七十个沙箱被创建出来了。这不是一个靠人工点按钮能完成的量级它背后必然是一套高度自动化的编排系统在支撑。先把概念说清楚。这里的“沙箱”不是支付宝沙箱支付那种给开发者做支付联调的模拟环境也不是某些安全软件里用来隔离可疑程序的轻量容器。在Agent训练的语境下沙箱是一个隔离的、可执行代码的、可被随时销毁的运行时环境。Agent在训练或推理过程中需要调用工具、执行代码、访问文件系统、发起网络请求这些动作如果直接在训练集群的主机上跑风险极高——一段死循环代码可能拖垮整个节点一个误删操作可能污染共享存储。所以业界通行的做法是给每一次代码执行分配一个独立的沙箱执行完就销毁。那为什么是300万这个量级因为Agent训练的核心范式是rollout。你可以把rollout理解成“让Agent在真实或模拟环境里跑一遍任务收集轨迹数据”。一个训练批次里可能有几千条任务每条任务Agent要执行几十到上百步操作每一步如果都涉及代码执行就需要一个沙箱。再乘以并行训练的多个批次、多个模型版本、多轮迭代300万这个数字就变得合理了。它不是某一天突然冒出来的峰值而是大规模Agent训练常态化之后的日均消耗。这里就引出了标题里那个关键问题怎么撑住。撑住的意思不是“能创建出来”而是要在成本、延迟、隔离性、稳定性四个维度上同时达标。成本要低到可以忽略不计否则训练预算会被沙箱吃掉延迟要低到不拖慢rollout吞吐否则GPU利用率上不去隔离性要强到恶意代码或bug代码无法逃逸稳定性要高到单个沙箱崩溃不影响整个训练任务。这四个要求彼此拉扯任何一个单独优化都容易同时满足才是真正的工程挑战。我见过不少团队在Agent训练早期用Docker起沙箱单机跑几十个还行一旦上到分布式训练就各种问题镜像拉取慢、容器启动抖动、资源回收不及时导致节点被占满。所以当看到DeepSeek这个量级的数字时我第一反应是他们的沙箱方案一定不是简单的Docker套壳而是有专门的运行时设计和调度层。后面会结合DSec、harness这些热词把整套思路拆开讲。这篇文章适合谁看如果你正在做Agent开发、正在搭建Agent训练管线、或者单纯好奇“大厂到底怎么把沙箱做到这个量级”那接下来的内容应该对你有用。我会尽量把原理讲透把可复现的部分给到具体参数和步骤同时把那些只有踩过坑才知道的细节分享出来。2. 沙箱在Agent训练里的真实角色拆解2.1 为什么Agent训练离不开沙箱要理解沙箱为什么是刚需得先理解Agent训练和传统模型训练的本质区别。传统LLM训练是“输入文本、输出文本”模型不跟外部世界交互训练数据是静态的。Agent训练不一样模型要采取行动调用API、写代码、执行代码、读文件、根据执行结果决定下一步。这就意味着训练过程中会产生大量“副作用”而这些副作用必须被限制在可控范围内。举个具体场景。你在训练一个代码Agent任务是“修复这个仓库里的bug”。Agent会先生成一段补丁代码然后需要实际运行测试来验证补丁是否有效。这个“运行测试”的动作就必须在沙箱里完成因为第一测试代码可能包含死循环或资源耗尽操作第二Agent生成的补丁可能破坏环境第三你需要一个干净的环境来保证每次测试的初始状态一致。如果没有沙箱你只能让Agent“想象”测试结果那训练出来的模型就是纸上谈兵。再往深一层说沙箱还承担着奖励信号生成的职责。Agent执行完一个动作后沙箱返回的执行结果成功/失败、输出内容、退出码、耗时会转化为强化学习里的reward。沙箱的反馈越真实、越细粒度训练信号的质量就越高。所以沙箱不是训练管线的附属品它是训练信号的生产车间。车间停摆整条产线就断了。2.2 rollout对沙箱的四个硬性要求Rollout这个词在Agent训练里出现的频率极高它指的是“让当前策略模型在环境中执行任务并收集轨迹”的过程。一次完整的rollout包含环境初始化、Agent多轮决策、工具调用、代码执行、结果收集、环境销毁。沙箱贯穿始终所以rollout的吞吐直接受沙箱性能制约。从工程角度看rollout对沙箱提出了四个硬性要求。第一是启动速度。如果每个沙箱启动要2秒那每秒34个沙箱就意味着需要68个并发启动槽位而且这2秒里GPU是闲置的。实际训练中沙箱启动延迟通常要压到100毫秒以内最好在50毫秒级别。第二是资源密度。一台物理机要能承载几百甚至上千个沙箱否则300万日创建量需要的机器数量会失控。第三是隔离强度。Agent生成的代码不可信必须假设它可能尝试逃逸、可能fork炸弹、可能写满磁盘。第四是生命周期管理。沙箱创建、使用、销毁、资源回收必须全自动不能有泄漏否则跑几个小时节点就满了。这四个要求里启动速度和资源密度往往互相矛盾——启动越快通常意味着预分配资源越多密度就上不去。隔离强度和资源密度也矛盾——隔离越强通常开销越大。所以沙箱方案的本质是在这四者之间找平衡点而不同的平衡点对应不同的技术选型。2.3 DSec和harness在管线中的位置热词里出现了DSec和harness这两个词值得单独说一下。DSec大概率是DeepSeek内部的安全执行层或沙箱运行时的代号它负责的应该就是上面说的隔离、资源限制、生命周期管理这些事。而harness在Agent语境里通常指“测试框架”或“执行框架”它负责把Agent的输出转换成可执行的指令送进沙箱再把结果收集回来。用个类比如果把Agent训练比作一条汽车生产线harness是机械臂负责抓取零件、执行动作DSec是安全围栏和工位保证机械臂在限定范围内工作且不会伤到人。两者配合才能让rollout高效且安全地跑起来。热词里还有“harness和agent区别”的搜索简单说agent是“决策者”决定做什么harness是“执行者”负责把决策落地并返回结果。一个Agent可以搭配不同的harness就像同一个人可以开不同的车。理解了这个分工后面讲具体实现时就不会混淆——哪些优化是harness层的哪些是沙箱运行时的哪些是调度层的各自解决什么问题。3. 撑住300万日创建量的核心技术方案3.1 从容器到微虚拟机隔离方案的选型逻辑早期Agent训练最常用的沙箱方案是Docker容器。Docker的优势是生态成熟、镜像管理方便、启动速度在秒级。但放到300万日创建量的场景下Docker的几个短板就暴露了。首先是隔离性容器共享宿主机内核Agent代码如果触发内核漏洞就可能逃逸虽然概率低但在大规模训练里概率会被放大。其次是启动开销Docker daemon的调用链较长冷启动通常在500毫秒到2秒之间热容器池可以优化但管理复杂。最后是密度每个容器都有独立的命名空间和cgroup开销单机跑到几百个之后调度延迟明显上升。所以大规模Agent训练场景下更常见的选型是微虚拟机或用户态内核方案。微虚拟机比如基于KVM的轻量虚拟机每个沙箱有独立内核隔离强度接近传统虚拟机但启动速度通过精简内核和内存快照技术可以压到100毫秒以内。用户态内核方案比如gVisor这类则是在用户态实现系统调用拦截隔离性介于容器和虚拟机之间启动速度接近容器。实际选型时我的经验是看两个指标单沙箱内存开销和系统调用拦截开销。微虚拟机每个实例通常需要几十MB内存打底用户态内核方案可以做到几MB。但用户态内核对某些系统调用的拦截会带来性能损耗如果Agent任务涉及大量IO或网络操作这个损耗会累积。DeepSeek这个量级我推测他们用的是自研的轻量运行时可能在微虚拟机基础上做了大量裁剪把不需要的内核模块全部去掉只保留代码执行必需的最小集。注意不要盲目追求最强隔离。隔离强度和性能是 trade-off如果你的Agent任务本身不涉及不可信代码比如只是调用内部API容器方案完全够用。选型前先明确威胁模型。3.2 沙箱池化与预热把启动延迟压到50毫秒300万日创建量意味着沙箱的创建和销毁是高频操作。如果每次都从零启动即使单次只要100毫秒累计的延迟也会拖垮rollout吞吐。解决办法是池化预热。具体做法是维护一个“热沙箱池”。系统预先启动一批沙箱让它们处于“已初始化但未分配”的状态。当rollout需要沙箱时直接从池里取一个把Agent代码注入进去执行执行完不销毁而是重置回初始状态放回池里。这样创建延迟就从“启动一个沙箱”变成了“从池里取一个”可以压到毫秒级。池化方案的关键参数是池大小和预热策略。池太小高峰期取不到沙箱rollout会阻塞池太大闲置沙箱占用内存浪费资源。我的经验是池大小设置为“峰值并发需求 × 1.2”留20%余量应对突发。预热策略则要根据流量曲线调整训练任务通常有周期性可以在批次开始前提前扩容。重置环节容易被忽视。沙箱执行完Agent代码后文件系统、进程、网络状态都可能被改变必须彻底重置才能复用。重置比创建更容易出bug——如果重置不干净上一个任务的残留状态会污染下一个任务导致训练数据出现难以排查的异常。所以重置逻辑要覆盖文件系统回滚、进程清理、网络规则重置、环境变量恢复。我见过团队因为重置时漏了清理临时文件导致后续任务读到脏数据排查了整整两天。3.3 资源限制与逃逸防护的实操配置Agent生成的代码不可信所以每个沙箱必须有硬性资源限制。CPU、内存、磁盘、进程数、文件描述符、网络连接数每一项都要设上限。这些限制不是可选项是必须项。一个没有内存限制的沙箱Agent写个无限申请内存的循环就能把宿主机打爆。具体配置上CPU限制用cgroup的cpu.max或类似机制内存用memory.max进程数用pids.max。磁盘配额要特别注意很多方案只限制了写入速度没限制总量Agent可以慢慢写满磁盘。网络方面默认应该禁止所有出站连接只允许白名单内的地址。Agent任务如果需要访问外部API走代理层统一管控不要在沙箱里直接放行。逃逸防护是另一个重点。除了运行时本身的隔离还要在系统调用层面做过滤。比如禁止mount、禁止ptrace、禁止加载内核模块、禁止访问宿主机设备文件。这些规则要写在沙箱的启动配置里不能依赖Agent自觉。我建议用seccomp或类似机制做系统调用白名单只放行代码执行必需的那几十个调用其余全部拒绝。提示资源限制的值不要拍脑袋定。先跑一批典型任务统计CPU时间、内存峰值、磁盘写入量的分布取P99值再上浮50%作为限制。限制太紧会导致正常任务被误杀太松则失去保护意义。3.4 调度层如何应对每秒34个的创建洪峰每秒34个沙箱创建听起来不算特别高但要注意这是日均摊薄的数字。实际训练中流量是波动的批次开始时可能瞬间涌入几百个创建请求批次结束时又集中销毁。调度层要能扛住这种脉冲式负载。调度层的核心职责是接收创建请求、选择合适节点、分配沙箱、返回句柄、回收资源。这里有几个设计要点。第一是去中心化。如果所有请求都经过一个中心调度器这个调度器很容易成为瓶颈。更好的做法是每个节点有本地调度代理中心层只做粗粒度分配。第二是异步化。创建请求不应该阻塞rollout主流程可以用future或回调机制沙箱就绪后再通知rollout继续。第三是背压机制。当池子耗尽且无法快速扩容时要能优雅地拒绝或排队而不是让请求堆积导致雪崩。节点选择策略也有讲究。最简单的轮询会导致热点因为不同节点的负载不一样。更好的做法是基于实时负载打分优先选择沙箱密度低、资源余量大的节点。同时要考虑亲和性如果Agent任务需要读取某个数据集把沙箱调度到数据所在的节点可以减少网络传输。销毁环节同样重要。沙箱用完必须及时销毁并释放资源否则节点会被僵尸沙箱占满。销毁要幂等重复销毁不能报错。销毁后要确认资源真正释放不能只看句柄是否关闭。我踩过的坑是某个沙箱进程卡在D状态无法kill导致cgroup无法回收节点内存缓慢泄漏跑了半天才发现。4. 从创建到销毁一个沙箱的完整生命周期实录4.1 创建阶段的参数计算与配置示例假设我们要为一个代码Agent任务创建沙箱任务内容是“运行Python测试套件”。先做参数估算。测试套件通常需要CPU 1核、内存512MB、磁盘200MB、进程数上限64、执行超时300秒。这些值不是随便定的而是根据历史任务统计得出。创建请求发到调度层后调度层从热池取一个已预热的沙箱注入以下配置sandbox: image: python:3.11-slim cpu_limit: 1.0 memory_limit: 512Mi disk_limit: 200Mi pids_limit: 64 timeout: 300s network: none mounts: - source: /data/repo target: /workspace readonly: false env: PYTHONDONTWRITEBYTECODE: 1 PYTHONUNBUFFERED: 1这里有几个细节值得说。network: none表示完全断网如果测试需要装依赖应该在镜像构建阶段就装好而不是运行时pip install。PYTHONDONTWRITEBYTECODE避免生成pyc文件污染工作区PYTHONUNBUFFERED保证输出实时可见。挂载的repo目录是可写的因为测试可能生成临时文件但沙箱销毁后这些改动会被丢弃不会影响宿主机。创建完成后调度层返回一个句柄rollout层通过这个句柄向沙箱发送执行指令。整个创建过程如果走热池耗时在20到50毫秒之间如果热池为空需要冷启动耗时在100到300毫秒之间。所以监控热池命中率是关键指标命中率低于90%就说明池大小需要调整。4.2 执行阶段的输入输出与超时处理执行阶段是沙箱真正干活的时候。rollout层把Agent生成的代码通过标准输入或文件写入的方式送进沙箱然后等待执行结果。结果包含退出码、标准输出、标准错误、执行耗时、资源使用峰值。超时处理是执行阶段最容易出问题的地方。Agent代码可能死循环也可能卡在某个IO操作上。超时机制必须可靠到点强制终止不能依赖代码自己退出。终止时要先发SIGTERM给进程组等待几秒如果还没退出再发SIGKILL。注意要杀整个进程组因为Agent代码可能fork了子进程只杀主进程会留下孤儿进程。输出收集也有坑。如果Agent代码输出量极大比如打印一个超大数组标准输出缓冲区可能撑爆内存。所以要对输出做截断比如只保留前1MB和后1MB中间用省略号代替。同时要限制输出速率防止Agent用疯狂打印的方式做资源耗尽攻击。资源使用峰值要实时采集不能等执行完再看。如果内存使用超过限制要立即终止并返回OOM错误而不是等系统OOM killer介入。后者会杀掉不确定的进程可能影响宿主机上其他沙箱。4.3 销毁与重置别让残留状态污染下一个任务执行完成后沙箱进入销毁或重置流程。如果走池化复用就是重置如果不复用就是销毁。重置流程比销毁复杂因为要保证状态完全干净。重置步骤我通常按这个顺序做先杀掉所有残留进程然后清理临时文件/tmp、/var/tmp、工作区里的生成文件再重置环境变量最后恢复网络规则。文件系统如果用的是overlay可以直接丢弃upper层这是最快的方式。如果用的是普通目录挂载就要逐个清理容易漏。重置完成后要做一次健康检查确认沙箱可以接受新任务。检查项包括关键进程是否在运行、磁盘空间是否恢复、网络是否按预期隔离。健康检查不通过就销毁重建不要勉强复用。销毁环节要确保资源真正释放。cgroup删除后要确认目录已消失网络命名空间要确认已回收挂载点要确认已卸载。我建议加一个后台巡检任务定期扫描是否有泄漏的沙箱资源发现后自动清理。这个巡检能救命尤其是在代码有bug导致偶发泄漏时。注意重置和销毁的日志要保留足够长时间。当训练出现异常数据时可能需要回溯某个沙箱的执行记录。日志里至少要有沙箱ID、创建时间、销毁时间、执行的任务ID、资源使用峰值。5. 常见问题与排查技巧实录5.1 沙箱创建失败的高频原因速查大规模运行下沙箱创建失败是家常便饭。下面这张表是我根据经验整理的常见原因和排查方向。现象可能原因排查方法解决方向创建超时热池耗尽且冷启动慢看热池命中率、节点负载扩大池、优化镜像拉取创建被拒节点资源不足看节点内存/磁盘余量扩容、清理泄漏资源创建后立即退出镜像损坏或配置错误看沙箱启动日志修复镜像、校验配置创建成功但无法执行挂载失败或权限问题进沙箱检查挂载点修复挂载配置创建抖动大节点间负载不均看各节点创建延迟分布优化调度策略排查时我习惯先看三个指标热池命中率、节点资源余量、创建延迟P99。这三个指标能覆盖大部分问题。如果热池命中率正常但创建还是慢那问题可能在镜像层或存储层。如果节点资源余量充足但创建被拒那可能是配额配置有问题。5.2 执行卡死与资源泄漏的排查路径执行卡死比创建失败更难排查因为现象不明显——rollout只是变慢了没有报错。我遇到过几次执行卡死最后定位到不同原因这里分享排查路径。第一步看沙箱内的进程状态。如果进程处于D状态不可中断睡眠通常是IO问题可能是挂载的存储出故障了。如果进程处于R状态但CPU占用为0可能是死锁。如果进程已经不存在但沙箱还没返回可能是harness层的通信出了问题。第二步看资源使用曲线。内存缓慢上升不下降通常是泄漏CPU持续100%可能是死循环磁盘写入持续不断可能是日志疯狂输出。这些曲线在监控系统里应该能直接看到。第三步看超时机制是否生效。如果超时时间到了但沙箱没被终止说明超时监控本身有问题。我见过因为时钟漂移导致超时判断失效的案例最后改用单调时钟才解决。资源泄漏的排查更依赖巡检。我建议每天跑一次全量扫描对比“已分配沙箱数”和“实际存活沙箱数”差值就是泄漏量。泄漏量持续上升就要查创建和销毁的配对逻辑通常是某个异常分支没有走到销毁代码。5.3 训练侧反馈的沙箱问题定位有时候问题不是沙箱本身报错而是训练侧发现异常。比如模型loss突然飙升、某个任务的成功率骤降。这时候要能把训练异常和沙箱行为关联起来。我的做法是在rollout轨迹里记录沙箱ID这样从异常样本可以反查到具体沙箱。然后看这个沙箱的执行日志是不是超时了、是不是OOM了、是不是返回了异常输出。如果多个异常样本指向同一批沙箱那可能是这批沙箱所在的节点有问题。还有一种情况是沙箱返回的结果“看起来正常但实际错误”。比如Agent执行了一个命令退出码是0但输出内容是错的。这通常是环境问题——沙箱里的依赖版本和预期不一致。解决办法是固定镜像版本并且在沙箱启动时做一次环境自检确认关键依赖的版本符合预期。提示训练侧和沙箱侧的日志要用同一个trace ID串起来。否则出问题时两边对不上排查效率极低。这个投入在早期就要做后期补很痛苦。6. 我踩过的坑和几条实用建议先说一个最容易被低估的坑沙箱的时间同步。沙箱里的时钟如果和宿主机不一致会导致超时判断、日志时间戳、任务调度全部出问题。我遇到过沙箱时钟漂移几秒的情况导致超时机制提前触发正常任务被误杀。解决办法是在沙箱启动时强制同步时钟并且定期校验。第二个坑是文件描述符泄漏。Agent代码如果打开了文件或socket没关闭沙箱销毁时这些fd可能没被回收。单个沙箱泄漏几个fd不明显但沙箱复用多次后fd耗尽新任务就无法执行了。所以重置流程里要强制关闭所有非必要的fd或者干脆每次都用新沙箱不复用。第三个坑是镜像层缓存失效。为了加速创建镜像通常有缓存。但如果缓存策略没设计好某个节点缓存失效后会去远程拉取拉取期间该节点上的创建全部阻塞。解决办法是镜像预热到所有节点并且监控缓存命中率低于阈值就主动刷新。几条实用建议。第一沙箱方案要可观测。创建延迟、执行延迟、销毁延迟、资源使用、失败率这些指标一个都不能少。没有可观测性出了问题就是盲人摸象。第二压测要模拟真实流量。不要用均匀流量压测要用脉冲流量因为真实训练就是脉冲式的。第三灰度上线。新的沙箱方案先跑小批量任务确认稳定后再全量。沙箱出问题影响的是整条训练管线代价很高。第四保留降级路径。如果沙箱系统整体不可用要有办法让训练降级到“不执行代码只做文本推理”的模式虽然训练效果打折但至少不停摆。最后分享一个我觉得很有用的技巧给每个沙箱打标签记录它执行过的任务类型、耗时、资源峰值。积累一段时间后你会得到一张“任务类型-资源需求”的映射表。下次创建沙箱时可以根据任务类型直接套用最优配置而不是用统一配置。这个优化能把资源利用率提升不少因为很多任务其实用不到那么大的内存和那么长的超时。
返回列表