第 12 章:沙箱——本地与远程执行环境
第 12 章:沙箱——本地与远程执行环境
评测的可信度取决于执行环境:agent 必须在隔离的沙箱里解题,结果才可复现、宿主机才安全。Harbor 预集成了 4 种本地运行时与 27 种远程沙箱,本章介绍它们的安装与选配方式、能力差异,并逐方法拆解
BaseEnvironment接口——读完你就能接入自己的沙箱 provider。
沙箱在评测中的角色
Harbor 的每次 trial 都运行在一个隔离环境中:agent 只能看到任务环境,宿主机、模型凭据、其他 trial 的状态都被挡在外面。这带来两个直接收益:隔离——agent 的任意 shell 操作(包括失败与破坏性操作)都不伤及宿主、互不干扰;可复现——任务的 environment 定义(如 environment/Dockerfile)描述了完整环境,换机器、换 provider 重放结果一致。
本地运行时(docker 等)启动快、零云成本,适合开发调试;远程沙箱通过 provider API 运行,释放本地资源、支持更高并发,适合大规模跑分。
命名上有个官方自嘲:Harbor 在 CLI 与配置里把沙箱叫 "environments"——--env/-e 旗标与 environment.type 配置键指的都是沙箱。"名字不好,我们知道。但现在改已经太晚了。"
安装与运行
沙箱依赖默认不安装,按需安装(extra 名即沙箱名,以 Daytona 为例):
uv tool install "harbor[daytona]" # 单装某个沙箱,名字可替换
uv tool install "harbor[cloud]" # 一次装全部预集成沙箱本地运行时(如 docker)不需要额外的 Harbor Python 依赖,但其运行时或 CLI 本身仍需安装。从源码运行 Harbor 时,用 --extra 安装可选依赖:
export DAYTONA_API_KEY="..."
uv run --no-dev --extra daytona harbor run \
-t hello-world/hello-world \
-a codex -m openai/gpt-5.6-sol \
-e daytona云端沙箱的典型用法(-e 选沙箱,-n 控并发):
export DAYTONA_API_KEY="..."
harbor run \
-d terminal-bench@2.0 \
-a codex -m openai/gpt-5.6-sol \
-e daytona \
-n 32等价的 config.json:
{
"n_concurrent_trials": 32,
"environment": {
"type": "daytona"
},
"agents": [
{ "name": "codex", "model_name": "openai/gpt-5.6-sol" }
],
"datasets": [
{ "name": "terminal-bench", "version": "2.0" }
]
}provider 支持时会执行preflight 预检,否则在 provider 启动时报缺失配置。provider 凭据(如 DAYTONA_API_KEY)通过环境变量传入,哪些变量能进入沙箱请参阅 Environment variables 文档——凭据应留在 Harbor 进程,不要流进 agent 可见的范围。
预集成沙箱清单
本地运行时(4 种):docker(默认)、podman、apple-container、singularity。
远程沙箱(27 种):ack(使用 kubeconfig)、beam、blaxel、cua-cloud(extra 名为 cua)、cwsandbox、daytona、e2b、ec2、gke、hf-sandbox、hyperbrowser、islo、langsmith、modal、novita、opensandbox、openshift(使用 oc CLI)、runloop、skypilot(early access)、tensorlake、use-computer、vercel、runta、prime、mosaic、smol、sail。
本地/远程只是文档上的分组,在 Harbor API 中并非不同类型——切换 provider 时,任务的 environment 定义保持不变(受能力差异约束,见下节)。
常用 CLI 选项
| Flag | 用途 | 默认值 |
|---|---|---|
-e、--env |
选择内置沙箱或自定义导入路径 | docker |
--ek、--environment-kwarg |
传递 provider 特有的构造参数,可重复 | None |
-n、--n-concurrent |
限制并发 trial 数 | 4 |
--force-build / --no-force-build |
重建或复用任务环境 | --no-force-build |
--delete / --no-delete |
trial 结束后删除或保留沙箱 | --delete |
--cpus、--memory |
选择 auto、limit、request、guarantee 或 ignore |
auto |
--override-cpus 等 |
本次运行覆盖任务的 CPU/内存/存储/GPU/TPU 配置 | 任务配置 |
--env-file |
加载宿主凭据等环境变量 | None |
注意 provider 的生命周期规则可能覆盖 --no-delete。完整选项以 harbor run --help 为准。
能力矩阵
不同沙箱支持的能力不同,且可能依赖任务模式或 provider 设置。以下矩阵按三类汇总(节选常见项,完整清单见官方页面)。
任务与硬件能力:
| 能力 | 支持的环境 |
|---|---|
| Docker Compose | docker、podman、daytona、modal、ec2、gke、islo、langsmith、novita、blaxel、beam、hyperbrowser、vercel、runta、prime、sail |
| GPUs | daytona、modal、gke、beam、opensandbox、prime、docker |
| TPUs | gke |
| Windows | docker、daytona、cua-cloud、use-computer |
| Host-mounted logs | docker、podman、apple-container、singularity |
| Stream over SSH | daytona、docker、modal、tensorlake |
网络管控能力(评测防 reward hacking 的关键):
| 能力 | 支持的环境 |
|---|---|
| 禁用外网 | docker、podman、daytona、e2b、modal、runloop、langsmith、ec2、gke、novita、islo、tensorlake、cwsandbox、blaxel、opensandbox、beam、skypilot、hyperbrowser、vercel、runta、prime、mosaic、smol、sail |
| 精确域名放行 | docker、podman、daytona、e2b、modal、runloop、langsmith、novita、islo、tensorlake、blaxel、beam、hyperbrowser、vercel、runta、prime、mosaic、smol、sail |
| IPv4 CIDR 放行 | docker、podman、daytona、modal、novita、tensorlake、beam、hyperbrowser、prime、mosaic、smol、sail |
| 运行期策略变更 | docker、podman、daytona、e2b、modal、novita、islo、beam、hyperbrowser、vercel、runta、prime、sail |
CPU 与内存能力:
| 能力 | 支持的环境 |
|---|---|
| CPU / 内存 limit | docker、podman、apple-container、modal、gke、openshift、skypilot、cwsandbox、opensandbox、ec2、runta、prime、smol |
| CPU / 内存 request | daytona、e2b、modal、runloop、gke、openshift、novita、islo、tensorlake、cwsandbox、beam、skypilot、hyperbrowser、vercel、runta、mosaic、sail 等 |
三个资源策略语义要分清:limit 是硬上限,request 是预留/选择容量,guarantee 要求两者兼备。一旦 provider 声明了资源能力,Harbor 会在 trial 开始之前拒绝不支持的策略,而不是跑到一半失败。
自定义沙箱:BaseEnvironment 接口逐方法详解
预集成清单之外的 provider,通过实现 BaseEnvironment 接入。与自定义 agent 一样不注册名字,把 模块路径:类名 传给 --env(-e)即可,provider SDK 需与 Harbor 装在同一 Python 环境:
harbor run ... -e my_sandbox:MySandbox --ek region=us-west-2配置文件与 Python API 中对应的键是 environment.import_path,构造参数走 environment.kwargs。下面是完整的接口实现模板:
from pathlib import Path
from harbor.environments.base import BaseEnvironment, ExecResult
from harbor.environments.capabilities import EnvironmentCapabilities
class MySandbox(BaseEnvironment):
@staticmethod
def type() -> str:
return "my-sandbox"
@property
def capabilities(self) -> EnvironmentCapabilities:
return EnvironmentCapabilities()
def _validate_definition(self) -> None:
# 校验任务的 environment/ 目录。
...
async def start(self, force_build: bool) -> None:
# 构建或创建沙箱,然后准备 prebuilt-image 任务。
...
await self._upload_environment_dir_after_start()
async def stop(self, delete: bool) -> None:
...
async def exec(
self,
command: str,
cwd: str | None = None,
env: dict[str, str] | None = None,
timeout_sec: int | None = None,
user: str | int | None = None,
) -> ExecResult:
env = self._merge_env(env)
user = self._resolve_user(user)
# 调用 provider 执行命令,返回 stdout、stderr 与返回码。
...
async def upload_file(self, source_path: Path | str, target_path: str) -> None:
...
async def upload_dir(self, source_dir: Path | str, target_dir: str) -> None:
...
async def download_file(self, source_path: str, target_path: Path | str) -> None:
...
async def download_dir(self, source_dir: str, target_dir: Path | str) -> None:
...各方法的职责与实现要点:
| 方法 | 职责 |
|---|---|
type() |
返回沙箱类型名,即 CLI -e 与 environment.type 使用的标识 |
capabilities |
声明 provider 真正强制执行的能力(如是否支持 Docker Compose);只声明真实支持的,多报会导致 Harbor 放行实际不可行的任务策略 |
_validate_definition() |
校验任务的 environment/ 目录,缺必需文件(如 environment/Dockerfile)时拒绝;无文件要求可为空实现 |
start(force_build) |
构建或创建沙箱;对 prebuilt-image 任务做相应准备;收尾调用 _upload_environment_dir_after_start() 上传环境目录 |
stop(delete) |
按 delete 决定销毁还是保留沙箱 |
exec(...) |
核心执行入口:先 self._merge_env(env) 再 self._resolve_user(user),然后调用 provider,返回含 stdout、stderr、return code 的 ExecResult |
upload_file / upload_dir / download_file / download_dir |
文件与目录双向传输,agent 与 verifier 的产物都靠它们进出沙箱 |
_merge_env(env) |
合并沙箱级、agent 阶段、verifier 阶段三组环境变量——每个 exec() 实现都必须先调用它 |
_resolve_user(user) |
应用 Harbor 的默认执行用户——同样每个 exec() 实现都必须调用 |
两个补充要求:若 capabilities 声明了 docker_compose,还需实现多容器(Compose)任务所需的按服务执行与文件传输方法;建议实现 preflight(),在 trial 排队前检查 provider 凭据,把"钥匙没配"拦截在最早时刻。凭据始终保存在 Harbor 进程内。
本章小结
- 每次 trial 运行在隔离沙箱中,隔离与可复现是评测可信度的前提;CLI 与配置中沙箱叫 "environment"(
-e、environment.type)。 - 沙箱依赖默认不装:
uv tool install "harbor[xxx]"按需安装,"harbor[cloud]"全量安装,本地运行时只需装好 docker 等 CLI。 - 预集成清单:4 种本地运行时(
docker默认、podman、apple-container、singularity)加 27 种远程沙箱。 - 常用旗标:
-e选沙箱、--ek传 provider 参数、-n控并发、--cpus/--memory选资源策略(auto/limit/request/guarantee/ignore)。 - 能力矩阵分任务硬件、网络管控、CPU 内存三类;limit 是硬上限、request 是预留、guarantee 二者兼备;不支持的策略会在 trial 开始前被拒绝。
- 自定义沙箱实现
BaseEnvironment,以模块路径:类名传给-e使用。 exec()实现必须先调用_merge_env()与_resolve_user();capabilities只声明真实强制执行的能力。start()收尾记得_upload_environment_dir_after_start();声明docker_compose需补齐按服务的方法;preflight()用于提前校验凭据。
延伸阅读
- Pre-integrated sandboxes——本章对应的官方页面(含完整能力矩阵)
- Custom sandboxes——自定义沙箱官方页面
- BaseEnvironment 源码——接口定义
- Tasks: environment——任务环境定义(
environment/Dockerfile等) - Environment variables——provider 凭据与变量进沙箱的控制