跳转到内容

03 能力清单

本章导读|预计 12 分钟

把这章当成需求清单。选型或自建时逐条打勾;做设计评审时,用「优先级表」砍掉不必要的能力。 每一项能力都配了最小可用配置和常见坑,方便直接落到实现。

能力 说明 最小可用配置
文件系统 读 / 写 / 上传 / 下载,支持挂载数据卷 只读根 + 单一可写工作区 /work
Shell / PTY 交互式终端(很多 Agent 依赖 bash) sh -lc <cmd>,带超时
多语言运行时 Python、Node、Go…… 按业务只装 1–2 种,避免镜像膨胀
包管理 pip install / npm i(同时是风险源) 走内部镜像 / 白名单源
网络策略 默认拒绝 or 白名单出网(最关键的安全开关) --network none,需要时接代理
快照 / 分叉 保存状态、复制多份并行探索 文件系统快照 + 会话隔离
生命周期管理 超时、保活、暂停 / 恢复、持久卷 硬超时 + 自动回收
可观测 / 审计 日志、命令记录、资源用量 记录每条命令 + 退出码 + 耗时
接口层 SDK / HTTP API / MCP server 先做 create / exec / read_file / write_file / destroy 五个接口

接口层的最小集合(建议照抄):

create() -> sandbox_id
exec(id, cmd) -> {stdout, stderr, exit_code, duration}
write_file(id, path, content)
read_file(id, path) -> content
destroy(id)

这五个接口就能支撑绝大多数 Agent 场景;upload / download / snapshot / fork 属于第二批。

优先级 能力 为什么是这个级别
P0 文件系统、Shell、网络策略、超时 没这些「跑不起来」或「不安全」,谈不上产品
P1 多语言、包管理、可观测 决定好不好用、出问题能不能排查
P2 快照 / 分叉、持久卷、GPU 决定能不能做高级场景(RL、长任务、大模型)

判断技巧:P0 缺任何一项,系统就有洞或不能用;P1 缺了能上线但体验差;P2 缺了只影响特定场景。

能力 常见坑 正确做法
文件系统 挂载了宿主敏感目录;没有配额导致塞满磁盘 只挂工作区;给工作区设大小上限
Shell / PTY 命令没超时,一条 sleep infinity 卡死 每条命令强制超时,超时即 kill
多语言 镜像装了十几种语言,冷启动变慢 按需的镜像分层 / 按语言预热池
包管理 默认从公网装包,引入供应链风险 内部镜像 / 白名单源 + 定期扫描
网络 图省事给了 bridge,等于全开 默认 none,出网只走白名单代理
快照 快照含敏感数据,恢复时越权访问 快照加密 + 归属校验 + 定期清理
生命周期 忘了回收,僵尸环境堆积、账单暴涨 TTL + 空闲检测 + 定时清扫
可观测 只存 stdout,不记命令与来源 记命令、会话、发起者、资源用量
接口层 接口过多、语义不一致 统一 exec 语义,文件操作独立成对

① 一次性 vs 持久

  • 一次性:每个任务新建、用完销毁。安全、干净,但每次要重装依赖。
  • 持久:会话级保留工作区。体验好,但有残留与成本风险。
  • 务实做法:默认一次性,允许显式申请「会话级持久」,并给持久环境加 TTL。

② 强隔离 vs 兼容性

隔离越强,越可能牺牲兼容性——但这种代价主要出现在 gVisor 这类用户态内核实现上:它要重新实现系统调用,遇到未实现或行为不一致的调用就会「跑不起来」。microVM 内是真实 Linux 内核,兼容性接近原生,但仍需验证镜像与内核版本差异。建议保留一个降级通道(隔离等级可调),而不是全站一刀切最强。

③ 自建镜像 vs 运行时安装

  • 预装镜像:启动快、可控,但体积大、更新慢。
  • 运行时 pip install:灵活,但慢且引入供应链风险。
  • 务实做法:高频依赖预装,长尾依赖走白名单源按需安装。

选型或评审时,逐条问:

  • 能创建 / 执行 / 销毁环境吗?(接口层)
  • 有唯一可写工作区,其余只读吗?(文件系统)
  • 命令有硬超时吗?(Shell / 生命周期)
  • 默认断网吗?需要时能加白名单吗?(网络)
  • 能装依赖而不引入任意公网访问吗?(包管理)
  • 出问题能看到命令与输出吗?(可观测)
  • 用完会回收吗?有 TTL 吗?(生命周期)
  • 需要时能快照 / 分叉吗?(高级场景)
  1. 拿一个真实产品(E2B / Daytona / Modal 任选),对照清单打勾,看它缺哪几项。
  2. 为你自己的场景,写出「最小接口集合」的具体函数签名。