乐于分享
好东西不私藏

工具沙箱(SandBox):让 AI Agent 安全地动手

工具沙箱(SandBox):让 AI Agent 安全地动手
🏖️ AgentOS 系列 —— SandBox 篇agent 之所以有用,是因为它能动手;之所以危险,也是因为它能动手。这篇聊聊:它写的代码、跑的命令,到底该在哪落地,才不会把你和你的系统一起带走。
你让一个 AI agent 帮你整理一份报表。它二话不说,写了一段 Python,调了几个系统命令,还顺手装了个 pip 包。
方便吗?方便。这恰恰是 agent 和聊天机器人最本质的区别——聊天机器人只会说,agent 会做。
但你有没有想过一件事:这段代码,到底在哪跑?
它写的如果不是"整理报表",而是"把数据库删了"呢?它装的那个 pip 包,万一是个恶意包呢?它跑的命令,如果想往外发请求、顺手拖走你的凭证呢?
agent 的执行能力,恰恰是它最大的风险面。
那问题就来了:怎么让 agent 既能动手,又不会把你的系统一起带走?
答案就是这篇的主角——沙箱(SandBox)
一、沙箱不是"跑代码的地方",是圈住"爆炸半径"的地方
很多人对沙箱的理解,停留在"一个跑代码的容器"。这是误解。
沙箱的本质,不是"在哪跑",而是"跑炸了,能圈多大"。
从一道选择题:爆竹在哪点
打个比方。你手里有一颗不确定威力的爆竹。两个选择:

在客厅里点——方便,但万一炸了,整个客厅完蛋。

放进一个防爆箱里再点——稍微费点事,但炸了也只炸在箱子里。

沙箱就是那个防爆箱。
"爆炸半径"到底是什么
把刚才的比喻翻译成技术语言:沙箱是一个隔离的执行环境,它圈住的是"爆炸半径"——不可信代码跑炸了,破坏也只能停在沙箱里,波及不到宿主机,波及不到别的用户。
"爆炸半径"这个词很关键。它不是说你信不信得过这个 agent,而是说——一旦它失控,损失能被限制在多大范围。一个没有沙箱的 agent 平台,爆炸半径是"整台宿主机";一个有沙箱的,爆炸半径是"一个用完就扔的盒子"。这是质的差别。
为什么 agent 时代,它从加分项变底线
沙箱不是新概念。浏览器早就用沙箱跑你访问的每个网页——你打开一个带毒的网站,它炸的是浏览器沙箱,不是你的电脑。代码评测平台用沙箱跑你提交的代码——一个死循环不会拖垮整台服务器。
但在 agent 时代,它从"可选的安全加分项",变成了"agent 平台的物理底线"。
为什么?因为以前的沙箱,跑的是"别人给你的代码"——你心里有数它是不可信的。而 agent 时代不一样:跑的是 agent 现场生成的代码,而且 agent 还被授权去跑它。代码不可信、又有执行权限,这两件事凑一起,沙箱就从"锦上添花"变成了"没它不行"。
二、基座负责想,sidecar 负责拦,沙箱负责动手
要理解沙箱的位置,先用一句话给 AgentOS 定个位:AgentOS 是一个 agent 平台的底座,它自己不直接做事,是给各种 agent 提供运行和治理能力的那一层。
三个角色,各管一摊
在这个底座里,"让 agent 跑起来"这件事,被拆成了三个角色:

基座(比如 openclaw、opencode 这类 agent 框架):负责"想"——推理、决定下一步干啥、生成要跑的代码。

sidecar:负责"拦和路由"——agent 的每个动作都从它手里过,注入身份、判断权限、记录审计、决定流量去哪。

沙箱:负责"动手"——凡是真要执行代码、跑命令、读写文件的,只能在这里发生

换句话说,基座产生意图,sidecar 把关意图,沙箱执行意图。沙箱只接"动手"的活,不接别的。
一次"跑代码"的动作,怎么走完
光说分工有点抽象,走一遍。假设 agent 决定跑一段 Python:

1. 基座生成"我要跑这段代码"的意图。

2. 这个意图先到 sidecar。sidecar 干三件事:注入"这是哪个用户、哪个 agent、代表谁"的身份;判断"这个动作敏感不敏感、要不要人审批";把流量路由到该去的地方。

3. 代码进沙箱。沙箱在隔离环境里执行它,跑炸了也只炸在自己里面。

4. 执行结果原路返回,sidecar 顺手记一笔审计。

你看,沙箱不是孤立的"代码执行器",它是这条流水线的最后一环——前面有人把关,它负责安全地落地。
一条不能越的边界
沙箱有一条特别重要的边界,必须说清:
纯计算,留在沙箱里;要碰外部世界的,沙箱默认不让。
什么意思?agent 在沙箱里跑个脚本、处理个文件,没问题,随便跑——反正炸了也在沙箱里。但如果它想联网、想调你公司的系统,沙箱默认是无网的。要出门,得经过一层"外部接入"的治理,由它来决定能不能放行、用什么凭证放行。
一句话总结:沙箱管爆炸半径以内的;半径以外,不归它管,归治理。
三、隔离没有银弹:越强越慢,是铁律
现在到了这篇最该讲透的部分:沙箱到底靠什么隔离?
这是理解所有沙箱产品的钥匙,也是你以后看任何沙箱方案、能自己判断好坏的尺子。
隔离技术不是一个东西,是一条谱。从弱到强,主流就三种。换个你可能更熟的比方——把宿主机的内核,想象成一栋大楼的承重结构和管线。
runc:锁门办公室
这是默认那个。它在大楼里给你分了一间锁了门的办公室——你用自己的门禁,看不到别的房间。
但你跟整栋楼共用承重墙和管线(共享宿主内核)。门锁得住视线,却锁不住楼的塌。一旦内核有漏洞,代码能顺着漏洞破门出去。
它最快、最轻,也最弱。
什么场景用它?低风险的常规操作:跑个数据处理脚本、读个文件、算个数。这些动作你心里有数,不会要命。
gVisor:门口的前台
它不让你进楼。在门口摆了个前台(一个"用户态内核"),你要什么系统调用都得跟前台说,前台替你去办,只转发安全的部分。你不直接碰楼的结构。
它的隔离更硬(宿主内核的攻击面被收得很小),但每个系统调用都过前台,有点慢,少数程序还会因为兼容性跑不了。
什么场景用它?跑来源不太放心的代码、跑要碰用户数据的脚本——风险中等,runc 不够放心,又没必要上最重的。
Kata / Firecracker:单独盖一栋小楼
它不让你进这栋楼——直接给你单独盖一栋小楼(一个独立虚拟机,带自己独立的内核)。你那栋楼的墙塌了,跟主楼没关系,这是硬件级隔离,跟云上虚拟机同一个安全级别。
最强,也最重——起一个虚拟机就是要花更多时间和内存。
什么场景用它?跑真正不可信、高风险的代码:从外部抓来的脚本、用户提交的任意代码、碰高危数据的操作。这种时候,"慢一点"换"炸不出来",值。
越强越慢,是物理规律
一张表收一下三种技术:
技术
机制
跟宿主内核
强度
开销/启动
适合
runc
锁门办公室(namespace)
共享
最小 / \~1s
低风险常规操作
gVisor
门口前台拦调用
不直接碰
中 / \~1-4s
中风险、不太放心的代码
Kata/FC
独立小楼(microVM)
完全独立内核
大 / \~5-15s
不可信、高风险代码
看出规律了吗?
隔离越强,开销越大、启动越慢。这不是某个产品的缺陷,是物理规律。
同样一段代码,runc 一秒就跑起来;换 Kata,可能要等十几秒——因为它真的给你现搭了一栋"楼"。这就是为什么沙箱设计里最忌讳一句话:"安全第一,全都上最强的"。全都上 microVM,你的 agent 每跑一步都等十几秒,根本没法用。
成熟的做法从来不是一刀切,而是按风险分档:绝大多数低危的操作,用轻的;少数真正高危的,才上重的。这一条,记住,后面要考。
给你一把判断尺子
以后判断任何一个 agent 平台的安全底子,不用看它宣传什么,就问一句——它让 agent 跑代码的地方,爆炸半径圈住了没有?圈住了、用什么圈的、分了几档?这一问,比看十页宣传册都管用。
四、不要自建沙箱内核,借力才是版本答案
搞清楚隔离的原理,下一个问题是:这套沙箱,自己从零造吗?
大路货不自造
我的判断很明确:隔离内核、生命周期管理、池化这些"大路货",不要自造。借力。
为什么?因为这些事,业界已经做成标准件了,自己造既慢、又容易出安全漏洞。隔离这种东西,自己造出一个没被人审过的实现,反而是最大的安全风险。
你真正该把精力花在哪?花在别人没做的、你真正有差异化的地方——agent 的身份、委托、审计、敏感度判断这一圈"治理外壳"。这才是平台的价值,也是别人抄不走的东西。
借力 OpenSandbox
具体借什么?我们选的是阿里开源的 OpenSandbox——一个专门为 AI agent 设计的沙箱平台。它自带了你需要的几乎一切:多种隔离技术、完整的生命周期管理(建、暂停、恢复、销毁)、warm 池(预热待命)、出网控制,还有跟 agent 框架对接的 SDK。
换句话说,别人已经把"防爆箱"造成了标准件,你买来用就行,不必自己焊。
沙箱长什么样:不是 server 里的容器
这里有一个特别容易误会的点,值得展开:沙箱长什么样?
很多人以为,沙箱是装在某个 server 里的容器。不是的。
实际拓扑是这样的:OpenSandbox 自己有一个控制面(你可以理解成一个调度服务),你跟它说"给我起个沙箱",它会让底层集群去另起一个独立的 pod,专门跑你这个沙箱。沙箱和沙箱之间、沙箱和 server 之间,都是各自独立的。server 是管理者,不是容器。
这个区分重要在哪?它意味着每个沙箱都是独立可调度的资源——可以单独分配、单独回收、单独隔离,不会互相牵连。这正是它能做 warm 池、能 pause/resume 的前提。
一个当大脑,一个当手
还有一个分工要说清:谁决定"什么时候建、什么时候销毁",谁只管"怎么执行"。
前者是 agent 平台自己的 sidecar——它懂业务(这是什么 agent、敏感不敏感、要不要保留状态),由它拍板。后者是 OpenSandbox——它懂执行,给指令就干。一个当大脑,一个当手。
为什么不反过来?因为 OpenSandbox 不懂你的业务——它不知道这个 agent 是谁、在干什么、该用什么隔离强度。业务判断必须留在自己平台,执行能力可以借。
一句话总结:借力不借脑。沙箱的"手"用开源的,"大脑"留在自己平台。
五、搭沙箱绕不开三个问题
不管你最终用什么方案落地,搭一个 agent 沙箱,有三个设计问题绕不开。下面讲的是一种思路,不是唯一解。
问题一:一个沙箱,给谁用(粒度)
这是最核心的,因为后面的生命周期、性能,全挂在它下面。
你可能会想:每个 agent 配一个沙箱,不就行了?没那么简单。不同的 agent,需要的"复用粒度"根本不同。一张表感受一下:
形态
它要什么
对应粒度
开发型 agent
今天装的包、改的代码,明天接着用
长期占一个沙箱
业务 agent
一轮轮聊,上一轮装的东西这轮还在、聊完就清
一个会话复用一个
一次性任务 agent
跑不可信脚本,跑完拉倒
全新一个、跑完销毁
复用,是为了省启动、保状态;全新,是为了保隔离。这两者是矛盾的,所以不能一刀切。我们的思路是按 agent 的形态分三档:per-session(一会话复用一个)、per-task(一任务全新、跑完销毁)、per-agent(一个 agent 长期占一个)。
顺带说一句多用户:沙箱永远不跨用户共享,A 和 B 永远进不了同一个沙箱,这是分配时就钉死的边界。
问题二:状态怎么留、资源怎么省(生命周期)
沙箱用完不马上销毁,里面装的包、跑的环境、写的临时文件,你都希望留着——不然 agent 下一步又得重装一遍,体验极差。
但你也不能让一堆闲置沙箱一直占着内存。
解法是两件事配合:warm 池 + pause/resume
warm 池,就是预先建好一批沙箱待命,agent 一调用,命中的就是热的,毫秒级起步,不用等冷启动。冷启动只在池子被掏空时才付。
pause/resume,是沙箱闲下来时不直接销毁,而是把它的整个文件系统存成快照、释放掉资源;等再要用,从快照恢复,装的包、写的文件都还在。
走一个具体场景:你让开发型 agent 装了个数据分析的环境,然后合上笔记本去吃午饭。一小时回来——

如果是 kill:环境没了,回来重新装一遍,等它装几分钟。

如果是 pause:环境存成快照、资源释放了;回来 resume,环境原样还在,几秒就能接着干。

既要状态连续,又不白白占资源。这就是 pause/resume 的价值。
问题三:沙箱怎么联网(外部访问)
前面说了,沙箱默认无网。但 agent 总有要出门的时候——装个包、调个 API、连个公司系统。这事得分两类看:
类别
是什么
怎么放行
A 类
公网、公开 API、装包(pip install)
开一张出网白名单(允许的域名),白名单内让它自己跑
B 类
公司内部系统(要真鉴权)
凭证不进沙箱,经外部连接器代理执行
B 类那条特别强调一下:绝不能把真凭证塞进沙箱——万一沙箱里的代码是恶意的呢?让凭证留在外部的"连接器"里,沙箱要调公司系统,经连接器代理,连接器替它带着凭证去办。
一句话总结这三个问题:粒度跟形态走,状态用快照省,出门走登记。
六、没有完美方案,只有权衡
前面讲的都是"怎么想",这一节讲几个"没那么显然、踩过坑才想得通"的取舍。注意,是思考方向,不是定论——下面是我们目前的倾向和理由,不保证最后一定这么落地。
取舍一:隔离分几档
我们一开始的想法很自然:agent 的每个动作,按敏感度自动选隔离强度——低危用轻的,高危用重的。听起来很美,对吧?
结果一查 OpenSandbox 的源码,撞墙了:它的隔离强度是"server 级别"的,一个 server 只能跑一种隔离。你没法在同一个 server 上,让这个沙箱用 runc、那个用 Kata。
理想 vs 现实,对比一下:

理想:每个沙箱按需切档,runc 和 Kata 混着跑,灵活。

现实:一个 server 只能一种,想混就得开多个 server。

退路是什么?多起几个 server,每个 server 一种强度。一个配 runc(常规档,快),一个配 Kata(高危档,强)。sidecar 根据动作的敏感度,把请求路由到对应的 server。
我们目前的倾向就是这种"双档":常规动作走轻档求快,真正高危的才上重档求稳。没搞"每动作切档"那种多档,因为收益不抵运维的复杂度。这是一个从"理想"被"现实"修正回来的典型例子。
取舍二:pause 还是 kill
沙箱闲下来(比如你合上了笔记本),有两个选择:pause(存快照、放资源)或 kill(直接销毁)。

kill:简单粗暴、彻底释放,但状态全丢,回来得冷启动重建。

pause:保住了状态,但快照本身也要存储、恢复也要一点时间。

我们倾向 pause,理由是:对开发型 agent,状态太贵了(装的包、配的环境,丢了重建很痛),而 pause 的代价(一点存储和恢复时间)能接受。但要承认,pause/resume 这类机制本身还在演进,成熟度要观察。
取舍三:B 类凭证给不给沙箱
前面说 B 类走代理、凭证不进沙箱。其实还有另一条路:把凭证塞进沙箱边缘(有个叫 Credential Vault 的机制,能自动往请求里塞凭证),让 agent 代码像"自带凭证"一样原生跑。

原生跑(Credential Vault):agent 代码写起来更顺,但凭证碰到了沙箱边缘,而且那套机制目前还不够成熟。

代理优先:凭证全程不进沙箱、更安全,但 agent 代码要走连接器,稍微不"原生"。

我们目前的倾向是代理优先:先用更成熟、凭证不落地的方式兜底,等那套机制稳定了、且确实有非原生不可的场景,再上。
这三个取舍的共同点是:都不是"哪个显然更好",而是"在约束下选一个代价更小的"。沙箱设计,本质上就是一连串这种权衡。
结尾:三问判断一个沙箱行不行
回到开头那个问题:agent 写的代码,到底该在哪跑?
现在你有答案了:在沙箱(SandBox)里跑。
用一句话给你建立心智模型:沙箱是 agent 的隔离执行区——门锁死、爆炸半径圈住、要联网走登记。
最后,给你一个判断沙箱行不行的清单。下次你看到一个 agent 平台,问自己三个问题:

1. 它让 agent 跑代码的地方,爆炸半径圈住了没有?

2. 圈住了,是用什么圈的?分了几档?还是一刀切?

3. 要联网的时候,凭证进没进沙箱?

这三个问题答得漂亮,底子就稳;答不上来,那宣传再响也要打个问号。
agent 时代,agent 的"动手能力"只会越来越强。它能跑的代码越来越复杂、能碰的系统越来越深。这意味着,那个圈住它爆炸半径的东西,只会越来越重要。
沙箱不是 agent 平台里最显眼的部分,但它很可能是最不能省的那一个。因为别的模块出问题,往往是"不好用";沙箱出问题,是"出事"。
窗口已经打开了。剩下的,是怎么把它做扎实。