数据截至 (上游 commit 8b292c9f1b14)
第 2 章 执行面 —— Harness 契约与四种 Runtime
本章讲什么: verifiers 怎么做到「让 Claude Code / Codex 这些真程序来做题」——harness 适配器要遵守的最小契约、能力旗标怎么声明程序的本事、以及 rollout 跑在哪四种隔离级别的盒子里。
1. 为什么不自己写 agent 循环
评测框架最常见的做法是内置一个简化 agent 循环。verifiers 反着来:被评的就该是用户真实在用的那个程序——它的 prompt、它的工具、它的怪癖都在里面,评出来才作数。于是 harness 的职责非常窄(verifiers/v1/harness.py):
setup(runtime):把程序装进 rollout 的盒子(harness.py:95-96);launch(...):把程序跑到退出,返回ProgramResult(抽象方法,harness.py:262-284);resume(..., messages):用户说话了,让程序接着已有的对话答下一段(默认实现:把累积对话当 Messages prompt 重新拉起,harness.py:208-256)。
契约里真正的硬约束只有一个(launch 的 docstring,harness.py:262-284):程序的一切模型调用必须打向拦截服务器的 endpoint(bearer token 是 secret)。至于是不是真起了一个进程,框架不关心——也可以进程内跑循环,只要流量过境,最后返回一个合成的成功 ProgramResult 即可。「拦截是契约,进程不是」。
2. 能力旗标:harness 自报家门
Harness 类上一排 ClassVar[bool] 旗标(harness.py:33-54),框架按旗标决定能对这台程序做什么:
| 旗标 | 默认 | 含义 |
|---|---|---|
APPENDS_SYSTEM_PROMPT | False | 能单独发 system 消息;否则把 system_prompt 折进首条 user(resolve_prompt,:59-83) |
SUPPORTS_MCP | False | 能装 MCP 工具服务器(taskset 导出的 toolset 才挂得上) |
SUPPORTS_TOOL_INTERCEPTION | False | 工具流量也能走拦截(改写工具返回) |
SUPPORTS_RESUME | False | 默认 resume() 能用累积对话重放续跑 |
EXECUTES_CODE | True | 程序会把模型的代码放到本地执行——subprocess 警告和 judge 的沙箱要求都看它 |
SUPPORTS_SKILLS | False | 程序能发现 SKILL.md 技能;setup 里调 install_skills 上传到固定发现目录(:98-118) |
NEEDS_CONTAINER | True | 必须跑在容器里——只有自家的极简循环(bash、null)豁免 |
install_skills 有个细到可爱的细节(harness.py:98-118):runtime.write 只搬字节不搬权限位,所以上传完要对可执行文件补一遍 chmod +x(:114-118)。
3. 一段交换 = 若干段「段」
verifiers 把一次人机交换切成段(segment):Harness.run 跑一次 launch 或 resume 到程序让出控制权为止,HarnessSession 是跨段持有状态的手柄(harness.py:287-359)。切段的回报是:「等用户输入」不烧 agent 的时间预算——每段只花自己运行的时间(见第 4 章 rollout 的预算记账)。
错误归因也在这里做(_check_result,harness.py:145-161):程序退出码非零时,先看是盒子死了(SandboxError)还是程序自己崩了(HarnessError);若某轮被 @stop 拒了,退出非零反而是预期内的。
4. 四种 runtime:盒子在哪开
每个 rollout 跑在一个 runtime 里(docs/v1/architecture.md:9-13 + verifiers/v1/runtimes/):
| runtime | 盒子是什么 | 适用 |
|---|---|---|
subprocess | 本地 Python 子进程 | 只用于调试——副作用会互相串门(改到 harness 的全局配置就污染别的 rollout) |
docker | 本机 Docker 容器 | 本地隔离跑 |
modal | 远端 Modal 沙箱 | 生产/高并发 |
prime | Prime Intellect 沙箱 | 生产、训练,支持 host 级网络策略 |
框架对「敢把真程序跑在 subprocess 里」有明确态度:只要 harness EXECUTES_CODE=True 且 runtime 是 subprocess,env 初始化时就警告——本地文件和配置会影响评测,subprocess 仅供调试(env.py:123-137)。
runtime 还有一条职责链:start() 起盒子 → prepare_setup() → (harness/工具装好) → prepare_execution([endpoint, *tool_urls]) 落下执行期网络策略,但放过框架自己回调用的路由(rollout.py:209-264)。也就是说:题目声明的 network_allow/network_block(第 1 章)到这一步才真正生效。
5. 内置 harness 族
verifiers/v1/harnesses/ 下自带 14 个适配器,分三类:
- 厂商 CLI:claude_code、codex、kimi_code、openclaw、hermes_agent、pi、mini_swe_agent、terminus_2——把各家真实编码/终端 agent CLI 装进盒子;
- 基础循环:
bash(带工具的极简循环)、null(无工具纯聊天,唯一EXECUTES_CODE=False的)、compact、rlm; - 特殊通道:
browser_use(CDP 驱动浏览器)、pool(池化复用)、node.py。
选一个 harness 就是一行配置:--env.agent.harness.id codex 或 TOML [env.agent.harness](docs/v1/evaluation.md:20-23)。工具太多的话几乎所有 harness 都支持 disabled_tools 名单(evaluation.md:50-59)。