🏖️ AgentOS 系列 —— SandBox 篇agent 之所以有用,是因为它能动手;之所以危险,也是因为它能动手。这篇聊聊:它写的代码、跑的命令,到底该在哪落地,才不会把你和你的系统一起带走。你让一个 AI agent 帮你整理一份报表。它二话不说,写了一段 Python,调了几个系统命令,还顺手装了个 pip 包。方便吗?方便。这恰恰是 agent 和聊天机器人最本质的区别——聊天机器人只会说,agent 会做。它写的如果不是"整理报表",而是"把数据库删了"呢?它装的那个 pip 包,万一是个恶意包呢?它跑的命令,如果想往外发请求、顺手拖走你的凭证呢?那问题就来了:怎么让 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 不够放心,又没必要上最重的。Kata / Firecracker:单独盖一栋小楼它不让你进这栋楼——直接给你单独盖一栋小楼(一个独立虚拟机,带自己独立的内核)。你那栋楼的墙塌了,跟主楼没关系,这是硬件级隔离,跟云上虚拟机同一个安全级别。最强,也最重——起一个虚拟机就是要花更多时间和内存。什么场景用它?跑真正不可信、高风险的代码:从外部抓来的脚本、用户提交的任意代码、碰高危数据的操作。这种时候,"慢一点"换"炸不出来",值。隔离越强,开销越大、启动越慢。这不是某个产品的缺陷,是物理规律。同样一段代码,runc 一秒就跑起来;换 Kata,可能要等十几秒——因为它真的给你现搭了一栋"楼"。这就是为什么沙箱设计里最忌讳一句话:"安全第一,全都上最强的"。全都上 microVM,你的 agent 每跑一步都等十几秒,根本没法用。成熟的做法从来不是一刀切,而是按风险分档:绝大多数低危的操作,用轻的;少数真正高危的,才上重的。这一条,记住,后面要考。以后判断任何一个 agent 平台的安全底子,不用看它宣传什么,就问一句——它让 agent 跑代码的地方,爆炸半径圈住了没有?圈住了、用什么圈的、分了几档?这一问,比看十页宣传册都管用。搞清楚隔离的原理,下一个问题是:这套沙箱,自己从零造吗?我的判断很明确:隔离内核、生命周期管理、池化这些"大路货",不要自造。借力。为什么?因为这些事,业界已经做成标准件了,自己造既慢、又容易出安全漏洞。隔离这种东西,自己造出一个没被人审过的实现,反而是最大的安全风险。你真正该把精力花在哪?花在别人没做的、你真正有差异化的地方——agent 的身份、委托、审计、敏感度判断这一圈"治理外壳"。这才是平台的价值,也是别人抄不走的东西。具体借什么?我们选的是阿里开源的 OpenSandbox——一个专门为 AI agent 设计的沙箱平台。它自带了你需要的几乎一切:多种隔离技术、完整的生命周期管理(建、暂停、恢复、销毁)、warm 池(预热待命)、出网控制,还有跟 agent 框架对接的 SDK。换句话说,别人已经把"防爆箱"造成了标准件,你买来用就行,不必自己焊。这里有一个特别容易误会的点,值得展开:沙箱长什么样?很多人以为,沙箱是装在某个 server 里的容器。不是的。实际拓扑是这样的:OpenSandbox 自己有一个控制面(你可以理解成一个调度服务),你跟它说"给我起个沙箱",它会让底层集群去另起一个独立的 pod,专门跑你这个沙箱。沙箱和沙箱之间、沙箱和 server 之间,都是各自独立的。server 是管理者,不是容器。这个区分重要在哪?它意味着每个沙箱都是独立可调度的资源——可以单独分配、单独回收、单独隔离,不会互相牵连。这正是它能做 warm 池、能 pause/resume 的前提。还有一个分工要说清:谁决定"什么时候建、什么时候销毁",谁只管"怎么执行"。前者是 agent 平台自己的 sidecar——它懂业务(这是什么 agent、敏感不敏感、要不要保留状态),由它拍板。后者是 OpenSandbox——它懂执行,给指令就干。一个当大脑,一个当手。为什么不反过来?因为 OpenSandbox 不懂你的业务——它不知道这个 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、连个公司系统。这事得分两类看: | | |
| 公网、公开 API、装包(pip install) | 开一张出网白名单(允许的域名),白名单内让它自己跑 |
| | |
B 类那条特别强调一下:绝不能把真凭证塞进沙箱——万一沙箱里的代码是恶意的呢?让凭证留在外部的"连接器"里,沙箱要调公司系统,经连接器代理,连接器替它带着凭证去办。一句话总结这三个问题:粒度跟形态走,状态用快照省,出门走登记。前面讲的都是"怎么想",这一节讲几个"没那么显然、踩过坑才想得通"的取舍。注意,是思考方向,不是定论——下面是我们目前的倾向和理由,不保证最后一定这么落地。我们一开始的想法很自然:agent 的每个动作,按敏感度自动选隔离强度——低危用轻的,高危用重的。听起来很美,对吧?结果一查 OpenSandbox 的源码,撞墙了:它的隔离强度是"server 级别"的,一个 server 只能跑一种隔离。你没法在同一个 server 上,让这个沙箱用 runc、那个用 Kata。•理想:每个沙箱按需切档,runc 和 Kata 混着跑,灵活。
•现实:一个 server 只能一种,想混就得开多个 server。
退路是什么?多起几个 server,每个 server 一种强度。一个配 runc(常规档,快),一个配 Kata(高危档,强)。sidecar 根据动作的敏感度,把请求路由到对应的 server。我们目前的倾向就是这种"双档":常规动作走轻档求快,真正高危的才上重档求稳。没搞"每动作切档"那种多档,因为收益不抵运维的复杂度。这是一个从"理想"被"现实"修正回来的典型例子。沙箱闲下来(比如你合上了笔记本),有两个选择:pause(存快照、放资源)或 kill(直接销毁)。•kill:简单粗暴、彻底释放,但状态全丢,回来得冷启动重建。
•pause:保住了状态,但快照本身也要存储、恢复也要一点时间。
我们倾向 pause,理由是:对开发型 agent,状态太贵了(装的包、配的环境,丢了重建很痛),而 pause 的代价(一点存储和恢复时间)能接受。但要承认,pause/resume 这类机制本身还在演进,成熟度要观察。前面说 B 类走代理、凭证不进沙箱。其实还有另一条路:把凭证塞进沙箱边缘(有个叫 Credential Vault 的机制,能自动往请求里塞凭证),让 agent 代码像"自带凭证"一样原生跑。•原生跑(Credential Vault):agent 代码写起来更顺,但凭证碰到了沙箱边缘,而且那套机制目前还不够成熟。
•代理优先:凭证全程不进沙箱、更安全,但 agent 代码要走连接器,稍微不"原生"。
我们目前的倾向是代理优先:先用更成熟、凭证不落地的方式兜底,等那套机制稳定了、且确实有非原生不可的场景,再上。这三个取舍的共同点是:都不是"哪个显然更好",而是"在约束下选一个代价更小的"。沙箱设计,本质上就是一连串这种权衡。回到开头那个问题:agent 写的代码,到底该在哪跑?用一句话给你建立心智模型:沙箱是 agent 的隔离执行区——门锁死、爆炸半径圈住、要联网走登记。最后,给你一个判断沙箱行不行的清单。下次你看到一个 agent 平台,问自己三个问题:1. 它让 agent 跑代码的地方,爆炸半径圈住了没有?
2. 圈住了,是用什么圈的?分了几档?还是一刀切?
3. 要联网的时候,凭证进没进沙箱?
这三个问题答得漂亮,底子就稳;答不上来,那宣传再响也要打个问号。agent 时代,agent 的"动手能力"只会越来越强。它能跑的代码越来越复杂、能碰的系统越来越深。这意味着,那个圈住它爆炸半径的东西,只会越来越重要。沙箱不是 agent 平台里最显眼的部分,但它很可能是最不能省的那一个。因为别的模块出问题,往往是"不好用";沙箱出问题,是"出事"。