Agent 评测环境:Terminal-Bench、Harbor 与 Sandbox
一个 coding / terminal agent 写完了,怎么知道它到底行不行?你需要一套能把 agent 真的塞进一个真实终端里跑起来、跑完再自动判分 的环境。这篇把这套东西拆成三层讲清楚:benchmark(任务从哪来)、harness(谁来跑、怎么接 agent)、sandbox(在哪跑、怎么隔离),并落到 Terminal-Bench、Harbor 和主流 sandbox 的具体用法。
0. 先建立三层心智模型
Agent 评测和传统 benchmark(跑一个 forward、比对 label)最大的区别是:任务是开放式、需要真实执行的——agent 要在 shell 里敲命令、改文件、编译、训练,最后由测试脚本检查容器的最终状态(outcome-driven),而不是比对某条命令轨迹。
| 层 | 负责什么 | 代表 | 类比 |
|---|---|---|---|
| Benchmark | 定义任务:指令、初始环境、判分测试、参考解 | Terminal-Bench、SWE-bench | 「考卷 + 标准答案」 |
| Harness | 起环境、把 agent 接进去、跑、收集轨迹、判分 | Harbor、tb CLI |
「监考 + 阅卷流水线」 |
| Sandbox | 真正执行 agent 命令的隔离环境 | Docker、E2B、Modal、Daytona | 「考场」 |
下面从中间的 benchmark 说起,再到 harness,最后是 sandbox。
1. Terminal-Bench:任务集
是什么。 Terminal-Bench(简称 T-Bench / TB)是一个 “benchmark for testing AI agents in real terminal environments”——从编译代码、训练模型到配服务器,评测 agent 端到端自主完成真实命令行任务的能力。它由 Stanford × Laude Institute 合作出品(Laude Institute 是 Andy Konwinski 联合创立的非营利),论文见 arXiv:2601.11868(Merrill、Shaw 等,作者上百人,含 Carlini、Muennighoff、Schmidt 等)。
它由两部分组成:一个任务数据集 + 一个把 LLM 接到终端 sandbox 的 execution harness。
任务长什么样。 都是「给你一个真实 shell,做完产生一个可验证的最终状态」的任务,比如:
- 系统 / 构建:
build-linux-kernel-qemu、compile-compcert、configure-git-webserver - 安全:
crack-7z-hash(用 John the Ripper 破解带密码的 7z)、intrusion-detection、fix-code-vulnerability - ML / 数据:
train-fasttext、hf-model-inference、cartpole-rl-training - 调试 / 工程:
fix-ocaml-gc(修 OCaml 垃圾回收器,据估计初级工程师要花近 10 天)、db-wal-recovery
难度跨度很大:论文里约 48.6% 的任务专家能在 1 小时内做完,约 47.3% 要 1–24 小时,最难的接近 10 天。每个 2.0 任务都经过数小时的人工 + LM 辅助三轮 review,确保「可解、真实、描述清楚」。
版本与任务数(引用时务必说清是哪个版本):
| 版本 | 任务数 | 说明 |
|---|---|---|
| 1.0 / core | 启动时 80(terminal-bench-core v0.1.0),legacy repo 现已累积 241 个社区任务 |
老 leaderboard 用 terminal-bench-core v0.1.1 |
| 2.0 | 89 | 覆盖软件工程 / ML / 安全 / 数据科学,用 Harbor 任务格式 |
| 2.1 | 进行中 | 「受 Z.ai 工作启发的改进版」 |
| 3.0 / Science | 进行中 | 面向科学计算 |
任务的解剖结构(2.0 / Harbor 格式)
一个任务就是一个目录,长这样:
crack-7z-hash/
├── task.toml # 配置 + 元数据(分节)
├── instruction.md # 给 agent 的自然语言指令(单独成文件)
├── environment/
│ ├── Dockerfile # 或 docker_image 引用 / docker-compose.yaml
│ └── task-deps/… # 任务资产
├── solution/
│ └── solve.sh # oracle 参考解(可多文件依赖)
└── tests/
├── test.sh # 把 reward 写进 /logs/verifier/reward.txt
└── test_outputs.py # pytest 断言,检查容器最终状态
task.toml 真实样子(节选):
schema_version = "1.1"
[task]
name = "terminal-bench/crack-7z-hash"
description = "Evaluates the ability to crack a password-protected 7z archive..."
[metadata]
difficulty = "medium"
category = "security"
expert_time_estimate_min = 5.0
[verifier]
timeout_sec = 900.0
[agent]
timeout_sec = 1800.0
[environment]
docker_image = "alexgshaw/crack-7z-hash:20251031"
cpus = 1
memory_mb = 4096
gpus = 0
allow_internet = true
判分不再靠一个中心 parser,而是任务自己产出 reward:tests/test.sh 跑 pytest,成功就 echo 1 > /logs/verifier/reward.txt。这一步是关键的解耦——任何第三方 benchmark 只要会往那个文件写 0/1,就能挂进来。
顺带说,1.0 的老格式用的是
task.yaml+run-tests.sh+solution.sh,2.0 迁移成了task.toml+instruction.md+solution/solve.sh+tests/test.sh。两种格式并存,别混。
端到端执行流程
- 准备环境:从
environment.docker_image拉镜像,或用Dockerfile现 build(本地,或在 Daytona / Modal 这类云 sandbox 上并发 build)。 - 起容器:按
task.toml的 cpus / memory / storage / gpus 和allow_internet施加资源与网络限制。 - 装 agent、跑任务:把 agent 装进容器,喂
instruction.md,它通过 bash / 文件工具迭代探索,受[agent].timeout_sec约束。 - 跑 verifier:agent 结束后,
tests/test.sh针对最终容器状态跑断言(outcome-driven,不看命令轨迹)。 - 判分:reward 写进
/logs/verifier/reward.txt,harness 汇总通过率并落轨迹 / 日志。特殊的oracleagent 会直接跑solve.sh,用来 sanity-check「这个任务确实可解」。
Leaderboard(mid-2026 快照,发表前请复核)
论文发布时(TB 2.0)「frontier models and agents resolve less than 65% of tasks」:
| 组合 | 分数 |
|---|---|
| GPT-5.2 + Codex CLI | 62.9% |
| Claude Opus 4.5 + Terminus 2 | 57.8% |
| Gemini 3 Pro + Terminus 2 | 56.9% |
而 tbench.ai 上的 live board 随着新模型 / 新 harness 提交已经爬到 ~84%(GPT-5.5 系居多,Opus 4.7、Gemini 3.1 Pro 紧随)。这些是 mid-2026 的快照数字,会持续变化,引用前请到 live 页面复核。
2. Harbor:跑任务的框架
是什么。 Harbor 是 “a framework from the creators of Terminal-Bench for evaluating and optimizing agents and language models”——同一个 Stanford × Laude 团队把原来的 TB harness 从头重写了一遍,为的是可靠性、可观测性、可扩展性。它就是 Terminal-Bench 2.0 的官方 harness。 repo 在 harbor-framework org(和 laude-institute 互为镜像),站点 harborframework.com。
它的核心思想是把三样东西彻底解耦:task(指令 + sandbox 镜像 + verifier)、agent(工具面 + loop)、sandbox(docker / e2b / daytona / gke…),从而可以自由组合——同一套任务用任意 agent 在任意 sandbox 上跑。
装 + 跑
uv tool install harbor # 或 pip install harbor
# 先用 oracle(参考解)跑一遍,验证 Docker 环境没问题
uv run harbor run --dataset [email protected] --agent oracle --n-concurrent 4
# 用 Claude Code 跑 TB 2.0
export ANTHROPIC_API_KEY=<YOUR-KEY>
harbor run --dataset [email protected] \
--agent claude-code \
--model anthropic/claude-opus-4-1 \
--n-concurrent 4
想上规模,加 --env 换到云 sandbox provider,并发直接拉到几十上百:
export DAYTONA_API_KEY=<YOUR-KEY>
harbor run -d [email protected] -a claude-code \
-m anthropic/claude-opus-4-1 --n-concurrent 100 --env daytona
其它常用命令:
harbor init --task "<org>/<name>" # 脚手架一个新任务
harbor run -d [email protected] -a claude-code --include-task-name "<task>" # 只跑单个任务
harbor run -d [email protected] --agent-import-path "path.to.agent:MyAgent" # 挂自定义 agent
harbor datasets list # 列出第三方 benchmark(SWE-Bench、Aider Polyglot…)
harbor traces export <job-path> # 导出轨迹 / rollout
Agent / adapter 接口
开箱支持一堆真 agent:oracle、nop、terminus-2(TB 自带的参考 agent),加上一大排 installed agent——claude-code、codex、gemini_cli、aider、openhands、mini_swe_agent、goose、cursor_cli、opencode 等。
接自己的 agent 就实现两个基类之一:
BaseAgent:定义name()、import_path({module}:{ClassName})、model_name(provider/model解析),核心是async def run(...)驱动 agent 在环境里干活。BaseInstalledAgent(BaseAgent):适合以 CLI 形式装进容器的 agent。实现async def install(self, environment)(用exec_as_root装系统包、exec_as_agent装用户级),框架setup()会调用它;再用声明式的CLI_FLAGS(CliFlag+EnvVar)描述命令行参数。
官方给的例子就是 Claude Code adapter——ClaudeCode 类设了 SUPPORTS_ATIF = True、一个安装检查命令,以及 --max-turns / --permission-mode / --allowedTools 等 flag。
别混:Terminal-Bench vs Harbor
- Terminal-Bench = 任务数据集 + leaderboard。
- Harbor = 跑它的通用容器化 harness(还能挂 SWE-Bench、AppWorld 等一堆 benchmark,生成 RL rollout)。
- 老的
tb runCLI 仍在,但只服务 TB 1.0;现在的路子是harbor run。
顺带一提 Harbor 还是一等公民地支持 RL 训练:官方接了 HuggingFace TRL(trl.experimental.harbor),用 HarborSpec 把任务集接进 GRPOTrainer,--env e2b 时只有 environment.exec 跨进云 sandbox,从而大批并发 rollout。
3. Sandbox:隔离层
为什么一定要 sandbox
agent 评测跑的是模型自己生成的、不可信的 shell 命令,所以有四个硬需求:
- 隔离:agent 输出天然不可信,一个内核漏洞 / 配置错误就可能容器逃逸拿到宿主。sandbox 把一次 rollout 关起来,坏 agent 碰不到 harness 或别的任务。
- 可复现:镜像不 pin 死,分数就没法跨时间 / 跨机器比较。SWE-bench、Terminal-Bench 都为此把依赖冻进 Docker 镜像。
- 安全:agent 拿着真凭证、真网络,出网就是数据外泄风险,最佳实践是默认 deny 出网 + egress 过滤。容器是这套网络策略的落点。
- 并发:benchmark 动辄上千实例(SWE-bench full ≈ 2,300),要靠
--n-concurrent/--max_workers大规模 fan-out。容器共享内核和镜像层,一台机器上并存几百个很便宜。
Docker:默认基线
绝大多数 benchmark 的 sandbox 就是 per-task Docker 镜像。
- SWE-bench 用三层镜像栈:
Base(Ubuntu + conda)→Environment(仓库依赖)→Instance(把 repo checkout 到任务指定 commit)。full ≈ 2,290 个镜像、Verified 500 个。 - Terminal-Bench 每任务一个容器;harness 把任务容器
up起来、在 tmux 里驱动 agent,跑完再把/tests拷进容器跑测试——测试和 oracle 绝不放进 Dockerfile,避免 agent 作弊。
云 / microVM sandbox providers
规模一大,本地 Docker 就不够了,于是有一批 provider(很多正是 Harbor --env 的后端)。按隔离边界从强到弱:Firecracker microVM → gVisor → 普通 Docker 容器。
| Provider | 隔离原语 | 卖点 |
|---|---|---|
| E2B | Firecracker microVM | 跑 LLM 生成代码 / code-interpreter,启动 <200ms,可暂停/恢复(存盘 + 内存) |
| Modal | gVisor(runsc) |
Sandbox 跑不可信代码,秒级启动 |
| Daytona | OCI / Docker 容器 | 「sub 90ms 创建」,可选 VM / GPU 档 |
| Fly Machines | Firecracker microVM | 亚秒起停、REST API 单独寻址,适合临时 agent sandbox |
| Morph / Infinibranch | 自研全 VM 栈 | snapshot + branch 整个环境 <250ms(「Git for compute」) |
| Runloop | micro-VM(VM+容器) | 专为评测 coding agent 的 devbox,万级并发 |
外加 Harbor 文档提到的 Blaxel、Novita Sandbox,以及跑在 Kubernetes 上的 GKE。
取舍:Docker vs gVisor vs microVM vs 全 VM
| 维度 | Docker | gVisor | Firecracker microVM | 全 VM |
|---|---|---|---|---|
| 隔离机制 | namespaces + cgroups + seccomp,共享宿主内核 | 用户态 Go「应用内核」(Sentry)拦截 syscall | KVM 给每个 VM 独立 guest 内核 | 独立内核 + 完整虚拟硬件 |
| 隔离强度 | 最弱(内核漏洞可逃逸) | 中(比容器强、比 VM 弱) | 强(要同时攻破 guest 内核和 hypervisor) | 最强 |
| 启动延迟 | 最快(ms~数秒) | 接近容器 + 少量开销 | 低至 ~125ms | 最慢(秒~数十秒) |
| 密度 / 成本 | 最高 / 最便宜 | 高 / 低 | 很高(每 microVM <5MiB 内存开销) | 最低 / 最贵 |
| 快照 | 镜像层 + docker commit(有限) |
Sentry checkpoint | 一等公民:序列化内存 + vCPU + 设备状态,copy-on-write 恢复 | 成熟但重 |
怎么选:benchmark 之所以标配 Docker,是因为它便宜、密度高、够快、可复现——代码虽不可信,但整个 loop 跑在你自己可丢弃的机器上,操作者掌控全局。而 gVisor / Firecracker 是面向「多租户 / 生产托管」的升级——那里一次容器逃逸就是灾难(Firecracker 正是 AWS Lambda / Fargate 用来大规模跑不可信客户代码的东西)。代价是 gVisor 的每 syscall 开销更高、少数应用兼容性下降。
几个容易过时 / 说错的点:gVisor 现在默认平台是 systrap(seccomp
SIGSYS),早年的 ptrace 已弃用——别再说「gVisor 靠 ptrace」;Morph 是自研全 VM 栈,没有官方来源说它基于 Firecracker;Daytona 是否用 Kata / Sysbox 加固官方未确认。Harbor 的 star / 版本(~2.9k、v0.16.1)是抓取时的数字,引用请复核。
跑一个最小例子
在 E2B 里跑一段模型生成的(不可信)代码:
from e2b_code_interpreter import Sandbox
with Sandbox() as sandbox:
execution = sandbox.run_code("x = 1; x += 1; x") # 模型产出的代码
print(execution.text) # 2
Terminal-Bench 每任务起的容器(docker-compose.yaml 节选)就是让容器 sleep infinity 挂着,好让 harness 挂 tmux、注入 /tests:
services:
client:
build: { dockerfile: Dockerfile }
command: [ "sh", "-c", "sleep infinity" ]
environment: [ TEST_DIR=${T_BENCH_TEST_DIR} ]
SWE-bench 判分则是一条命令把 docker pull → docker run → 打 patch → 跑测试 全干了:
python -m swebench.harness.run_evaluation \
--dataset_name princeton-nlp/SWE-bench_Lite \
--predictions_path <preds> --max_workers <n> --run_id <id>
4. 串起来:最小工作流
把三层拼一起,评一个 coding agent 大概就这四步:
uv tool install harbor # 1. 装 harness
harbor run -d [email protected] -a oracle -n 4 # 2. oracle 冒烟,验证 sandbox
harbor run -d [email protected] -a claude-code \ # 3. 跑你的 agent
-m anthropic/claude-opus-4-1 -n 4
harbor run -d [email protected] -a claude-code \ # 4. 上云 sandbox 拉并发
-m anthropic/claude-opus-4-1 -n 100 --env daytona
要加自己的任务就 harbor init --task,按 task.toml + instruction.md + environment/ + tests/ + solution/ 的结构填,测试往 /logs/verifier/reward.txt 写 0/1;要加自己的 agent 就 subclass BaseInstalledAgent(照 Claude Code adapter 抄),或 --agent-import-path 直接挂。benchmark、harness、sandbox 三层各管一段,替换任意一层都不影响另外两层——这就是这套设计的价值。
参考 · Terminal-Bench:tbench.ai · leaderboard · 论文 arXiv:2601.11868 · github/laude-institute · Harbor:harborframework.com · docs · github/harbor-framework · TRL 集成 · Sandbox:SWE-bench · E2B · Modal · Daytona · Fly · gVisor · Firecracker · Morph