尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

为什么大模型需要重造沙箱?DeepSeek代码执行隔离的工程实践

发布时间:2026/9/28 17:23:15

资讯中心
01
ARTICLE

为什么大模型需要重造沙箱?DeepSeek代码执行隔离的工程实践

为什么大模型需要重造沙箱?DeepSeek代码执行隔离的工程实践
1. 沙箱不是新东西但给大模型用的沙箱是另一回事沙箱这个概念在软件工程里确实不算新鲜。从最早的 Java Applet 沙箱、浏览器同源策略到后来的 Docker 容器隔离、gVisor、Firecracker microVM再到支付宝沙箱支付这种业务层面的模拟环境沙箱两个字在开发者语境里已经存在了二十多年。所以当 DeepSeek 传出要自研沙箱的消息时很多人的第一反应是这东西不是早就成熟了吗直接拿现成的容器方案套一层不就行了我一开始也是这么想的。直到我自己动手把几个主流方案拼起来跑了一遍代码执行场景才发现问题根本不在隔离这一层。传统沙箱解决的核心问题是安全隔离——别让一段不可信的代码把宿主机搞崩、把别人的数据读走。但大模型驱动的代码执行场景它要解决的问题清单完全不一样它要的是低延迟的冷启动、可预测的资源计量、对模型输出的容错、多轮对话中的状态保持以及执行结果能被模型读懂的结构化回传。这几件事传统沙箱方案要么不关心要么做得很别扭。这篇文章我想聊的就是这个错位。不是复述 DeepSeek 官方说了什么而是从一个实际搭过、踩过坑的从业者角度把为什么现成沙箱不够用这件事拆开讲清楚。如果你正在做 AI Agent 的代码执行、正在给企业微信或内部系统接一个能跑代码的助手、或者单纯好奇 deepseek harness 这类工具背后的执行层是怎么设计的这篇应该能给你一些可以直接参考的判断依据。核心关键词就三个代码沙箱、DeepSeek、执行隔离但我会把重点放在为什么重造而不是怎么调用 API上。先说结论免得你看到一半觉得我在绕传统沙箱优化的是隔离强度 vs 性能这条曲线而大模型沙箱优化的是启动速度 vs 状态可管理性 vs 结果可解释性这条完全不同的曲线。目标函数变了最优解自然就变了。下面我分几层把这个判断展开。2. 传统沙箱的成熟成熟在哪个维度上要理解为什么还要重造得先诚实地承认传统沙箱到底成熟在哪。不然容易陷入什么都得自研的民科思维。我梳理了一下现成方案的成熟度主要集中在三个地方而这三个地方恰好都不是大模型场景的痛点。2.1 隔离强度从 namespace 到 microVM 的完整光谱Linux 的 namespace cgroup 提供了进程级的隔离Docker 把它包装成了易用的镜像和网络模型。再往上gVisor 用用户态内核拦截系统调用Firecracker 用轻量级虚拟机做到接近硬件级的隔离。这条光谱非常完整你可以根据威胁模型选不同强度的方案。这是十几年工程积累的结果自研很难在短时间内达到同等成熟度。但问题在于大模型执行代码的威胁模型和运行陌生人的代码不完全一样。模型生成的代码通常是短小的、功能明确的片段——排序、字符串处理、调个 API、画个图。它很少是恶意的更多是有 bug 的。真正的风险不是这段代码要攻击我而是这段代码死循环了这段代码把内存吃光了这段代码 import 了一个不存在的包然后抛异常。这些是可用性问题不是安全问题。传统沙箱在安全隔离上做到 99 分但在优雅地处理一个笨拙的、会犯错的执行者这件事上可能只有 60 分。2.2 资源计量cgroup 给的是用了多少不是该收多少cgroup 能告诉你一个容器用了多少 CPU 时间、多少内存峰值。这在传统场景够用了——你是按容器收费的或者你只是想知道谁在偷跑。但在大模型场景里资源计量要服务于一个更细的需求按次执行的配额控制和成本归因。一次代码执行可能只跑 200 毫秒你要能精确地知道这一次花了多少、属于哪个会话、哪个用户、哪个 Agent 的哪一步。cgroup 的统计粒度是容器级的、持续累积的你要把它拆成每次调用的账单中间得自己做一层映射而且这层映射在容器复用时很容易串味。我实测过一个方案每个会话起一个容器会话结束销毁。结果是冷启动开销把整个体验拖垮了用户等三秒才看到结果。改成容器池复用之后资源计量就开始互相污染——上一个会话的残留内存算到了下一个会话头上。这不是 cgroup 的错是它本来就不是为这种高频、短命、需要精确归因的场景设计的。2.3 镜像与依赖一次构建处处运行的代价Docker 镜像的哲学是一次构建处处运行依赖在构建期就固化好。这在部署服务时是巨大优势。但大模型执行代码时依赖是动态的、不可预知的。模型可能今天想用 pandas明天想用 requests后天想画个 matplotlib 图。你不可能预装所有库——镜像会大到离谱而且大部分用不上。你也不可能每次执行都 pip install——那延迟没法看。现成方案里有的用预装常用库 禁止安装来妥协有的用允许安装但每次重建环境来妥协。前者限制了模型的能力边界后者牺牲了响应速度。这个矛盾在传统沙箱的框架里没有优雅解因为传统沙箱假设依赖是构建期确定的。大模型场景把这个假设直接推翻了。这里有个容易被忽略的点模型生成的代码经常带import语句但它不一定知道你的环境里装了什么。如果环境里没有这个包代码直接抛ModuleNotFoundError。好的执行层应该能捕获这个错误并且把缺哪个包这个信息结构化地回传给模型让模型有机会自己修正。传统沙箱的报错是给运维看的大模型沙箱的报错是给模型看的这两者的信息设计目标完全不同。3. 大模型执行场景真正卡脖子的四个点上一节说了传统沙箱成熟但不匹配。这一节我把不匹配的地方具体化成四个卡点。这四个点是我在实际搭 deepseek harness 类执行链路时反复撞墙撞出来的不是纸上谈兵。3.1 冷启动从秒级可接受到百毫秒级才及格传统容器冷启动从docker run到进程真正跑起来视镜像大小和存储驱动通常在 300 毫秒到 2 秒之间。对部署服务来说这个开销摊薄到长期运行上可以忽略。但对用户发一句话模型生成代码执行返回结果这种交互每一次执行都是一次冷启动的话用户感知到的就是明显的卡顿。我做过一个对比测试同一个简单的 Python 计算任务方案冷启动耗时执行耗时用户总等待每次新建 Docker 容器约 800ms约 50ms约 850ms容器池预热复用约 20ms约 50ms约 70ms进程级隔离 预热解释器约 5ms约 50ms约 55ms差距是数量级的。用户对 850ms 和 55ms 的感知完全不同前者是卡了一下后者是秒回。这就是为什么大模型沙箱必须在预热和复用上做文章而传统沙箱的默认模型是用完即弃。但预热复用带来新问题状态污染。上一个执行留下的全局变量、文件、环境变量会不会影响下一个执行传统沙箱靠销毁重建天然规避了这个问题大模型沙箱靠复用就必须自己解决。常见的做法是在复用前做一次状态快照回滚或者把执行限制在一个干净的命名空间里只复用底层的解释器进程。这两种做法各有取舍后面会细说。3.2 状态保持多轮对话里代码执行不是孤立的这是最容易被低估的一点。传统沙箱假设每次执行是独立的、无状态的。但大模型的多轮对话天然是有状态的。用户第一轮说帮我读一下这个 CSV第二轮说把上一轮读的数据按第二列排序。如果每次执行都是全新的环境第二轮就找不到第一轮读进来的数据了。我见过两种处理方式。一种是把状态显式地序列化——第一轮执行完把变量 dump 成文件第二轮执行前再 load 回来。这种方式可控但需要模型配合而且序列化本身有开销和兼容性问题。另一种是保持一个长活的执行会话——同一个对话对应同一个解释器进程变量自然保留。这种方式体验好但资源占用高而且会话超时、进程崩溃的处理很麻烦。DeepSeek 这类方案倾向于后者因为对话式交互的体验优先级最高。但这就意味着沙箱不能是一次性的它得是一个有生命周期的会话对象能创建、能挂起、能恢复、能销毁。传统沙箱的 API 里没有挂起这个概念你得自己在上面包一层。3.3 结果回传模型要的不是 stdout是结构化的观察传统沙箱执行完返回的是 stdout、stderr、exit code。这对人来说够用了。但模型消费这个结果时纯文本的 stdout 是很低效的——它得自己解析、自己判断哪部分是结果、哪部分是日志、哪部分是警告。好的大模型沙箱会把执行结果结构化。比如{ status: success, stdout: ..., stderr: , exit_code: 0, artifacts: [ {type: image, path: /tmp/plot.png, mime: image/png}, {type: dataframe, preview: ..., shape: [100, 5]} ], duration_ms: 52, memory_peak_mb: 38 }模型拿到这个结构能直接知道哦生成了图片我该把它展示给用户而不是从一堆 print 里猜。这个结构化层是传统沙箱完全没有的因为它假设消费者是人。当消费者变成模型时输出的信息设计目标就变了。3.4 容错与自愈模型会写错代码沙箱得接得住模型生成的代码出错是常态不是异常。语法错误、运行时错误、超时、内存溢出这些在传统沙箱里都是失败需要人工介入。但在大模型场景里这些失败应该被自动转化为模型的下一轮输入让模型自己修。这就要求沙箱的错误信息是可操作的。比如IndexError: list index out of range这种报错模型看到之后大概率能自己改。但如果沙箱返回的是Container exited with code 1模型就懵了。所以大模型沙箱在错误捕获和格式化上要下功夫把底层错误翻译成模型能理解的、带上下文的提示。我踩过的一个坑早期我用 subprocess 跑模型生成的代码超时直接 kill 进程返回一个笼统的 timeout。模型收到之后不知道是哪里超时经常原样再生成一遍同样的死循环代码。后来改成在超时前捕获执行栈返回卡在哪一行、哪个函数模型修正的成功率立刻上去了。这个细节传统沙箱文档里不会写因为它的目标用户是运维不是模型。4. 重造一遍具体重造了哪些东西前面讲了为什么现成的不够用这一节讲那到底重造了什么。我不掌握 DeepSeek 内部的实现细节所以这里讲的是基于公开信息和常见工程实践一个合格的大模型沙箱应该具备的架构特征。你可以拿这个清单去对照任何号称支持代码执行的方案看它是不是真的为大模型场景设计的。4.1 执行单元从容器下沉到进程 轻量隔离为了把冷启动压到百毫秒以内很多方案会把执行单元从容器下沉到进程。具体做法是预先启动一批 Python 解释器进程每个进程加载好常用库处于待命状态。执行请求来了直接往空闲进程里投喂代码跑完清理状态进程回到待命池。隔离强度靠什么保证靠语言层面的限制 系统调用过滤 资源限制的组合。比如限制可用的模块白名单、拦截危险的系统调用、用 rlimit 限制内存和 CPU 时间。这种隔离强度不如 microVM但对于模型生成的、非恶意的、功能性的代码来说够用了。这是一个典型的威胁模型驱动的取舍你不需要防住国家级攻击者你只需要防住模型的手滑。注意这个取舍有前提。如果你的场景是执行用户上传的任意代码那进程级隔离是不够的必须上容器或 microVM。DeepSeek 的场景是执行模型生成的代码威胁模型不同所以可以下沉。别把这个结论无脑套到你的场景里。4.2 会话生命周期管理创建、挂起、恢复、销毁前面提到多轮对话需要状态保持。实现上一个会话通常对应一个执行上下文这个上下文包含解释器进程、工作目录、环境变量、已导入的模块、全局变量。会话可以处于几种状态活跃正在执行或刚执行完进程驻留内存挂起一段时间没活动进程被冻结或序列化到磁盘释放内存恢复收到新请求从挂起状态唤醒销毁会话结束或超时资源回收这套生命周期管理是传统沙箱没有的。Docker 容器的生命周期是创建-运行-停止-删除没有挂起-恢复这个中间态docker pause有但语义不同它冻结的是进程而不是释放资源。大模型沙箱需要的是一个能低成本挂起、快速恢复的会话模型这更接近 Jupyter kernel 的管理方式而不是容器编排。4.3 依赖解析按需加载而不是全量预装依赖问题前面提过。实际方案里常见的做法是分层第一层预装高频库numpy、pandas、requests 等保证大部分执行零等待第二层维护一个本地包缓存模型请求的包如果在缓存里秒级安装第三层缓存没有的走网络安装但加超时和降级策略这个分层的关键是第一层的选品。选哪些库预装直接决定了命中率。选多了镜像大、启动慢选少了命中率低、经常走安装。这是一个需要根据实际流量数据持续调优的事情没有一劳永逸的答案。我见过有团队用最近 30 天执行日志里 import 频率 Top 50来动态调整预装列表效果比拍脑袋选好得多。4.4 结果结构化与产物管理执行产生的图片、文件、数据表需要被收集、存储、生成可访问的引用然后结构化地回传给模型。这涉及几个子问题产物识别怎么知道执行产生了哪些文件常见做法是给每次执行分配一个独立的工作目录执行前后 diff 目录内容。产物存储图片存对象存储返回一个 URL 或引用 ID。产物预览数据表给个前几行的预览图片给个缩略图方便模型判断内容。产物清理会话结束后清理避免存储泄漏。这套东西传统沙箱基本不管因为它假设执行结果是文本。大模型场景里产物管理是核心功能之一因为模型经常需要画个图给用户看。5. 自己搭一套时我踩过的那些坑这一节是纯经验分享。如果你打算自己搭一套类似的执行层或者基于 deepseek harness 这类工具做二次开发下面这些坑大概率会撞上。我按踩坑的先后顺序讲。5.1 进程池的僵尸状态问题用进程池复用解释器最开始的实现是进程跑完代码清理全局变量回到池子。但 Python 的全局状态清理没那么干净——已导入的模块还在sys.modules里C 扩展的全局状态可能没重置matplotlib 的 figure 可能还挂在内存里。跑了几百次之后进程内存涨到几个 G而且行为开始诡异。解决办法是定期回收每个进程跑 N 次之后强制销毁重建N 根据内存增长曲线定。我实测下来 N50 到 100 之间比较平衡既摊薄了冷启动又避免了状态累积。另外清理时用gc.collect()强制垃圾回收对 matplotlib 这类库还要显式plt.close(all)。5.2 超时处理的杀不干净模型生成的死循环代码你用signal.alarm或线程超时去中断经常中断不干净——因为 Python 的信号处理只在主线程的字节码边界生效如果代码卡在 C 扩展里比如 numpy 的大矩阵运算信号根本进不去。最后只能上SIGKILL强杀进程但强杀之后进程池就少了一个得补。更麻烦的是强杀之后工作目录里可能留下半截文件下次复用这个目录会读到脏数据。所以每次执行前要清空工作目录而不是假设它是干净的。这个细节不注意会出现上一次执行的残留文件被这一次读到的诡异 bug排查起来很费时间。5.3 内存限制的软硬之分用resource.setrlimit限制内存有软限制和硬限制。软限制触发时进程收到信号可以自己处理硬限制触发时直接 OOM kill。我一开始只设了硬限制结果模型生成的代码一超内存就被杀返回一个笼统的 killed模型完全不知道发生了什么。后来改成软限制略低于硬限制软限制触发时捕获MemoryError返回结构化的错误信息内存超限当前限制 256MB建议优化数据结构或分批处理。模型收到这个提示修正率明显提高。这个技巧在传统沙箱文档里基本不会提因为传统场景下 OOM 就是 OOM运维去看日志就行。但大模型场景下错误信息是给模型看的得写得让它能行动。5.4 网络访问的开还是关模型生成的代码可能想访问网络——调个 API、下载个数据。开网络吧安全风险大而且可能被用来做坏事关网络吧很多有用的场景做不了。我的做法是默认关闭按需开启且只允许白名单域名。具体实现是在网络层做拦截而不是在代码层做限制代码层的socket禁用很容易被绕过。白名单根据实际业务配置比如只允许访问公司内部的 API 网关。这个策略的代价是模型有时候会因为网络被拒而失败但相比安全风险这个代价可以接受。这里有个判断标准如果你的执行场景是内部工具、模型生成的是数据处理代码网络可以关。如果是模型需要调外部 API 完成任务那网络得开但必须配白名单和审计日志。没有通用答案看你的威胁模型。5.5 文件系统的共享还是隔离每次执行的工作目录是共享一个还是各自独立共享的话会话之间会互相看到文件有隐私问题独立的话多轮对话里上一轮写的文件下一轮读不到。我的方案是会话级隔离会话内共享。每个会话一个独立的工作目录会话内的多次执行共享这个目录会话之间互不可见。这样既保证了多轮对话的状态连续性又避免了跨会话污染。实现上工作目录的路径里带上会话 ID执行前 chdir 进去执行后不清理留给下一轮会话销毁时统一清理。6. 这套东西对你的项目意味着什么聊了这么多为什么重造和怎么重造最后落到实际如果你正在做 AI Agent、正在接 deepseek 这类模型的代码执行能力这些经验对你有什么用6.1 别自己从零造但也别随便套如果你只是想要一个能跑代码的能力直接用现成的执行服务是最省事的。但如果你对延迟、状态保持、结果结构化有要求现成方案大概率需要你包一层。包这一层的成本取决于你对上面那些坑的认知程度。认知到了可能几天就能搭出可用的认知不到可能几周都在填坑。6.2 威胁模型决定架构别抄错作业最重要的一点先想清楚你的威胁模型。是执行模型生成的代码还是执行用户上传的代码前者可以用进程级隔离 白名单后者必须上容器或 microVM。抄别人的架构之前先确认你们的威胁模型一样。我见过有团队照搬了进程池方案结果他们的场景是执行用户上传的脚本出了安全事故。这个教训很贵。6.3 把给模型看的错误信息当成一等公民这是最容易被忽略、但投入产出比最高的一点。花时间设计错误信息的格式让模型能读懂、能行动。具体来说错误类型要明确、出错位置要具体、修复建议要给到。这件事不需要多高深的技术但需要你站在模型的角度想问题。做好了你的 Agent 自愈能力会明显上一个台阶。6.4 资源计量要能拆到每次调用如果你要做成本控制或者配额管理资源计量必须能精确到每次执行。这意味着你不能只依赖容器级的 cgroup 统计得在执行层自己做埋点。每次执行记录开始时间、结束时间、CPU 时间、内存峰值、网络流量。这些数据积累起来既能做账单也能做容量规划还能发现异常执行比如某个会话突然吃很多资源。我在实际项目里的体会是这套执行层看起来是基础设施但它直接决定了上层 Agent 的能力边界和用户体验。沙箱成熟不成熟取决于你拿它干什么。给传统服务用的沙箱确实成熟了但给大模型用的沙箱还在快速演化的阶段。DeepSeek 要重造一遍不是重复造轮子是因为轮子的规格变了。你要是也在做类似的事希望上面这些踩坑记录能帮你少走点弯路。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。