谁来守住一次 Agent 任务的边界
上一篇写 Agent 集群,收在五个词上:调度、契约、仲裁、幂等、验证。那是站在集群这一层往下看。这段时间我把视线又往下挪了一层,去看承载这些集群的基础设施——具体一点,是两个最基本的问题:Agent 在哪里跑,它的状态放在哪里。
把 Agent Infra 按产品拆开——沙箱运行时、文件系统、状态后端——是常见的画法,每一类里都能排出一串公司。分类很清晰,但顺着各家的文档读下来,会遇到一个类别图解释不了的现象:每家都在长出别的类别的功能。 沙箱厂商在加快照、加持久卷、加 fork;文件系统厂商在加计算、加分支;Kubernetes 社区在给沙箱定生命周期标准。如果类别的边界是真的,不该有这么整齐的越界。
要解释这个现象,得换一张图。
两个直觉答案,两个都不对
给 Agent 安排“住处”,直觉上有两个方案。
一个是给它一台长期运行的 VM。状态天然连续,但代价立刻出现:不可信代码常驻在一台不回收的机器上,隔离、弹性、成本三头受压;机器坏了,任务跟着陪葬。
另一个是无状态沙箱,用完就扔。安全、便宜、随时替换——但 Agent 干到一半的上下文、装了一半的依赖、等着人审批的中间产物,全部随沙箱蒸发。长任务变成每次从零开始。
两个方案各错一半,因为它们都想用一个生命周期去装两组需求:安全和弹性要求计算实例短暂、可替换;长周期任务要求状态长期、可恢复、可治理。 这对矛盾不是谁的设计失误,两边都是硬需求。它就是这条赛道的主要矛盾——各家的产品动作,几乎都可以读作从自己的据点出发,去够矛盾的另一端。
先把“状态”拆开
“让 Agent 有状态”这个说法的最大问题是,状态不是一个东西。一次 Agent 任务的状态至少散落在六个地方,生命周期各不相同:
- 内存里的:进程、终端会话、没写盘的变量。沙箱一销毁就没了。
- 磁盘上的:代码、文档、中间产物。这是工作区,需要比任何一个沙箱都活得久。
- 账本里的:任务进行到哪一步、重试了几次、在等谁审批。丢了它,就算恢复出内存也不知道下一步该干嘛。
- 钥匙串里的:身份、凭证、权限租约。恢复任务时最不该做的事,就是盲目复用旧钥匙。
- 日志里的:为什么选这个工具、基于哪一版上下文、谁批准的。审计和评估都指着它。
- 外部系统里的:已经发出去的邮件、已提交的数据库事务、已完成的支付。
“Stateful sandbox” 这个词通常只覆盖前两个。即使把内存和文件系统恢复得一字不差,任务卡在哪一步、权限还有没有效、外部系统已经发生过什么——都不会自动跟着回来。
双向靠拢
带着这张图再看产品,越界就不奇怪了:
| 阵营 | 代表 | 正在长出什么 |
|---|---|---|
| 沙箱:从执行出发 | E2B、Daytona、Modal、Morph | pause/resume、内存+文件快照、fork、外部 volume |
| 文件系统:从状态出发 | Archil、Mesa | 数据旁计算、checkpoint/branch、版本化变更历史 |
| 编排与标准:从接口出发 | K8s Agent Sandbox、OpenSandbox、OpenKruise | 生命周期 CRD 与 API 规范、兼容接口 |
沙箱阵营向状态走。E2B 的 pause/resume 保存文件系统和内存,snapshot 支持从同一状态派生多个沙箱;Daytona 把持久化拆得最细——stop/start、归档、冷热快照、fork,外加独立于沙箱生存的 S3 volume;Modal 的文件系统快照已经可用,内存快照还在 Alpha;Morph 干脆把 “Git for compute” 当卖点,宣称 250 毫秒内 fork 整台 VM,连进程状态一起带走。
文件系统阵营向计算走。Archil 把持久盘当核心资源,计算变成数据旁边按需拉起的临时函数——disk.exec() 跑完即走,状态留在盘上;Mesa 走得更远,把文件系统做成版本化的变更图:每次修改进入可分支、可审查、可回滚的历史,文件接口只是兼容层。
第三个阵营在收编接口。Kubernetes 社区的 Agent Sandbox 用 CRD 把沙箱的创建、认领、暂停、到期做成标准对象;阿里开源的 OpenSandbox 直接定 API 规范;OpenKruise Agents 的运行时 sidecar 干脆兼容 E2B 的命令与文件 API。最后这条值得停一秒:“E2B-compatible” 开始像当年的 “S3-compatible” 了。当别人开始兼容你的接口,说明这层能力正在商品化——也说明钱要去更上层赚了。新入场者的定位也印证这一点:a16z 刚投的 Runta,卖点不是更快的沙箱,而是权限、预算上限和审计日志。
三个还没人解决的问题
Fork 容易,merge 难。 快照、克隆、写时复制,都能低成本造出一百条平行时间线。可任务最终要收敛:哪条是对的?怎么合回去?文本代码还有 Git 和三方合并托底;二进制、表格、数据库没有通用方案。上一篇里 Cursor 那七万个合并冲突,是在应用层自己扛的;现在有人想把这件事下沉为基础设施——Mesa 参照 Jujutsu 把冲突做成可保留、可传播的一等状态——但“保留冲突”和“解决冲突”之间,还隔着全部的业务语义。
快照可以回滚,副作用不能。 文件系统退回昨天的版本,退不回已经发出去的邮件、已提交的数据库事务、已完成的支付。它们发生在你的系统之外,不归任何快照管,需要的是另一套东西:幂等键、副作用账本、先记意图再执行、恢复时先对账再重试。上一篇说的“幂等”,落到基础设施层就落在这里——它是协议,不是存储功能。
指标没法横向比。 Modal 的百万并发沙箱和 0.5 秒中位启动来自厂商自己的压测,Morph 的 250 毫秒同理;E2B 和 Daytona 的 snapshot 一个保内存、一个分冷热,语义本身就不同。连 suspend 这样的基础词都有两种含义:有的实现是删掉执行实例、只保留磁盘和身份,有的才是保存内存现场、原地复活。不是说这些数字是假的,是口径不一:快照保存到哪一层、“ready” 从哪一刻起算,各家各说各话。在术语统一之前,这条赛道上的一切排行榜都应该先打个问号。
一点思考
回到开头的现象:为什么每家都在越界?
因为类别是按产品划分的,而问题是沿着状态生长的。沙箱、文件系统、状态后端,最终瞄准的是同一个位置——一次 Agent 任务的边界:从哪一版状态开始、在哪个隔离环境执行、以什么身份访问什么、产生了哪些可审查的变化、外部系统发生了什么、失败后从哪里继续、成功后哪一版成为可信状态。今天没有任何单一产品守住这条链的全程,所以谁也吃不掉谁;短期的答案是组合,长期的悬念是谁先把这个边界变成一份说得清、验得了的契约。