先说清楚:回退要回退什么

很多团队以为「回退」就是撤销代码改动。但 agent 跑长任务时改变的不只是文件:它可能装了依赖、起了后台进程、改了系统配置、把临时文件写到别处。用 git 只能回滚代码,回滚不了一个被污染的运行环境。所以要回退的是「整台机器的状态」,这也决定了你该用沙盒而不是 git。

反过来也成立:如果回退靠 git,那 agent 就只能在代码目录里活动——它一旦跨出那个目录,你的回退承诺就失效了。

前提:一台 Linux 机器,而且开了 /dev/kvm

Arrakis 用 cloud-hypervisor 做 MicroVM,要求宿主机开启虚拟化。Linux 上先确认 /dev/kvm 存在;Windows 与 macOS 上跑不了,这是硬前提,别在笔记本上折腾——一台便宜的 Linux 云主机就够用来试。

stat /dev/kvm
# 用预编译产物快速装好
curl -sSL https://raw.githubusercontent.com/abshkbh/arrakis/main/setup/setup.sh | bash
ls arrakis-prebuilt

第一步:把服务跑起来,开一个沙盒

Arrakis 的用法是「先起一个常驻的 restserver,再让客户端创建、管理沙盒」。沙盒里跑的是 Ubuntu,自带代码执行服务与 VNC 服务,端口转发由服务端自动处理,所以可以直接在浏览器里访问它的图形界面(包括 Chrome)——它同时覆盖「执行代码」和「操作电脑」两类任务是这么来的。

cd arrakis-prebuilt
sudo ./arrakis-restserver

# 另开一个终端,起一个沙盒
./arrakis-client start -n agent-sandbox

第二步:把任务关进沙盒,而不是留在宿主机上

把仓库和依赖装进沙盒里,agent 通过 REST API、Python SDK(py-arrakis)或 MCP server 上传文件、执行命令。这一步最容易偷懒的地方是「文件用挂载、命令在宿主机上跑」——那样沙盒只剩一层隔离外壳,回退依然不成立。

  • 上传代码走 API,不要挂载宿主机目录:挂载会让改动直接落到宿主机上,回退就无从谈起
  • 依赖装进沙盒内,避免 agent 改坏宿主机的全局环境
  • agent 的工具调用统一走沙盒的执行接口,别留「偶尔直接在宿主机跑一条命令」的后门

第三步:在不可逆动作之前打快照

Arrakis 自带 snapshot-and-restore,做的是「保存整台沙盒的状态、之后回到那一刻」——被拉起的进程、被改过的文件都在里面,这也是它相对「只备份文件」的关键差别。使用上只有一条约定:在一个不可逆动作之前打快照,而不是等出事了再想办法。什么算不可逆?删除、覆盖、安装、执行脚本、提交到外部服务。

# 在关键节点打快照(具体子命令以仓库 README 为准)
./arrakis-client snapshot -n agent-sandbox --tag step-3
快照的粒度决定回退的精度。按「动作类型」打快照(每次写文件、每次装依赖)比按时间打更好用——你回退时想说的是「回到刚才那个能跑的版本」,不是「回到三分十七秒前」。

第四步:回退之后必须验证

回退完不验证,等于把「不确定」换了个位置。至少验三件事:文件是否回到快照时的哈希、快照之后启动的进程是否已经消失、原本能通过的测试是否还能通过。第三条最关键——它证明你回到的是一个「可工作」的状态,而不只是一个「旧」的状态。

  1. 比对关键文件的哈希与快照前记录的值
  2. 确认快照之后启动的进程已经不在
  3. 把上一步的回归断言重跑一遍

第五步:把回退接进 agent 循环

手动回退只能救你一次,接进循环才有规模。做法是给 agent 一个明确的「我失败了」信号(测试失败、命令超时、输出与断言不符),失败之后要么回到上一个快照换一条路,要么停下来问人。不要在失败之后让它「在原状态上再试一次」——那正是把环境越改越乱的原因。

  • 定义清楚什么算失败:能自动判定的信号优先,模糊判断交给人
  • 回退次数设上限,超过就停下并报告,避免在死循环里烧预算
  • 每次回退都记一笔(步号、原因、耗时),用来判断哪一类动作最容易失败

回退不了什么:沙盒外面的世界

沙盒能回退的是机器内部的状态。凡是已经产生外部副作用的动作,快照都救不了:发出去的邮件、提交到对方系统的订单、调用的支付接口、推送到远端的 git 提交。这类动作的正确做法不是「准备好回退」,而是「放到沙盒外、并强制人工确认」——把不可逆的事留在沙盒边界之外。

一句话总结:沙盒解决的是「把环境试验的成本降到接近零」,它替代不了对不可逆动作的授权控制。两件事要分开做,别指望其中一件顺手把另一件也解决。