Rohit Ghumare 写了一篇关于 Agent 沙盒基础设施的深度技术文章。核心论点:2026 年构建 Agent 基础设施的问题不再是"选哪个 microVM monitor",而是"哪个 monitor 能在 Agent 实际运行的所有地方工作"。
本地 Agent 的崛起改变了沙盒需求
所有重要的编程 Agent 现在都跑在开发者的笔记本电脑上:Claude Code、Codex、OpenCode、Cursor agent mode。它们安装一个二进制文件,接管一个目录,然后开始生成想要实际运行的代码。
Firecracker 的假设是:Agent 和沙盒都活在别人的数据中心。这个模型确实支撑了很多云端 Agent。但正在吞噬世界的 Agent 形态(本地 CLI Agent)活在开发者的 MacBook 上,而 Firecracker 根本启动不了——不是"还没支持",是架构上不可能。
Firecracker 依赖 /dev/kvm,KVM 是 Linux 内核模块。macOS 上没有 KVM 端口,也不会有,因为 Hypervisor.framework 已经占据了那个架构位置。
libkrun:唯一跨平台的答案
libkrun 最初是 Red Hat 为在 macOS 上运行 Podman 而开发的项目。它是一个动态库(Linux 上是 .so,macOS 上是 .dylib),把 VMM 暴露为可以链接的函数,而不是需要 exec 的进程。
关键特性:
- 跨平台:Linux 用 KVM,macOS Apple Silicon 直接用 Hypervisor.framework
- 无镜像:通过 virtio-fs 把主机目录直通挂载到 guest,不需要 ext4 镜像
- 快速启动:warm 状态下几百毫秒完成从 create 到 shell ready
- 统一 API:一个设备模型,两种宿主操作系统
iii-sandbox 的设计哲学
Ghumare 团队在 iii(一个开源引擎)中实现了 iii-sandbox worker。核心设计选择:
1. fork + exec 隔离 —— libkrun 是 C 库,带有大量 unsafe Rust 绑定代码。如果 guest kernel panic 或 libkrun segfault,父 daemon 不应该跟着死。每次启动 fork 一次,代价可接受。
2. 无 virtio-net + TAP —— 用 smoltcp(纯 Rust TCP/IP 实现,无堆分配,小到可以审计)作为用户态网络栈。macOS 上不需要内核扩展,不需要 root 权限装 tuntap。
3. virtio-console 替代 vsock —— macOS 的 vsock 支持半实现且未公开。用命名 virtio-console 端口(iii.control, iii.exec)+ socketpair(AF_UNIX) 替代。guest 只能通过 host 显式附加的端口与 host 通信,安全模型相同,传输稍笨重。
4. 极简设备模型 —— 无 PCI、无 virtio-block、无 ACPI。一条 MMIO 总线、一个 PMU、builder 附加的 virtio 设备,仅此而已。
真正的工程难点:init 二进制
Ghumare 的最重要的观点:microVM monitor 是商品,内核是商品,你花时间的地方是那个 init 二进制。
iii-init(PID 1)做了五件事,每件都对应一个真实踩过的坑:
-
pivot_to_tmpfs_root —— libkrun 的 virtio-fs 根目录有 readdir bug,在 <1GB RAM 的 guest 上 ls / 会 OOM。 workaround:把 / pivot 到 tmpfs,通过 bind mount 重新暴露根目录条目。
-
override_proc_meminfo —— bun 的分配器直接读 MemTotal 而忽略 cgroup v2 的 memory.max。sandbox 配了 512MB RAM,bun 却按主机内存分配。fix:bind mount 一个伪造的 /proc/meminfo。
-
raise_nofile —— node 的 fs.promises 和 Python 的 asyncio 并行测试时会打开大量 fd,默认 1024 不够。host 端 vm_boot.rs 通过 KRUN_RLIMITS 提前设置,init 里再设一次,双保险。
-
configure_network —— 从环境变量读 IP/GW/CIDR/DNS 配置 eth0 和 /etc/resolv.conf。没设环境变量就什么都不做,network: false 的 sandbox 真正无网络:无接口、无 DNS、无外循环回。
-
exec_worker —— 监管循环。打开 virtio-console 控制端口,服务 Restart/Shutdown/Ping/Status RPC,exec 用户命令。交互式 shell 通过 multiplexed dispatcher 让并发 exec 请求共享同一个 VM。
iii-init 总共 6.4k 行 Rust 代码。这才是负载-bearing 的工程。
沙盒即 worker,而非独立产品
iii 的设计哲学:沙盒不是单独的产品,而是众多 worker 中的一种。HTTP 路由、队列、cron、agent、sandbox 都是同一种东西:通过 WebSocket 连接到引擎、注册函数和触发器的 worker。
这意味着:
- 同一个 trace ID 从 agent 的 researcher 函数穿过 sandbox::create 和 sandbox::exec,出现在 agent worker 和 sandbox worker 的日志中
- 同一个镜像 allowlist 控制 agent 能启动什么
- 同一个生命周期管理处理 sandbox 死亡时的清理
传统沙盒即服务的痛点:单独的 SDK、单独的身份面、单独的观测性、单独的 rate limit。当沙盒是 worker 时,这些"现在要把这个跟那个关联起来"的失败模式根本没有机会存在。
选型建议
云端托管 Agent 沙盒 → Firecracker 仍然是正确答案。AWS 会持续维护,你的客户的 Agent 反正跑在你的数据中心。
本地优先 Agent(CLI、IDE 插件、任何会被 npm install 或 brew install 的东西)→ libkrun 是唯一开源路径。代价是稍笨重的设备模型和更有主见的网络栈;收益是用户可以在他们实际拥有的机器上运行你的沙盒。
任何构建者 → 负载-bearing 工程在 init 二进制,不在 microVM monitor。monitor 是商品,内核是商品。启动速度来自你删除了什么(PCI、BIOS、vsock)以及你的 PID 1 不做什么。
组合形态(sandbox-as-worker 而非 sandbox-as-separate-product)是最被低估的部分。agent、沙盒、队列、状态存储、HTTP 前门应该是同一种东西,在同一个系统中,带着同一个 trace ID。