第 6 章:Verifier——从 test.sh 到 Reward
第 6 章:Verifier——从 test.sh 到 Reward
Agent 跑完一轮之后,谁来判定它"做对了"?本章讲解 Harbor 的 verifier 机制:
tests/test.sh如何被执行、reward 文件的路径与格式约定、脚本失败与 reward 缺失意味着什么,以及为什么判分逻辑必须确定、可复现。读完本章,你就能为一个 Harbor task 写出可靠的判分脚本。
verifier 的执行机制与时机
Harbor task 的判分入口是一个 shell 脚本:Linux 下为 tests/test.sh,Windows 下为 tests/test.bat。它的执行时机和流程是固定的:
- agent 先运行。Agent 在沙箱环境中按照 instruction 完成任务。
- Harbor 上传测试目录。Agent 运行结束后,Harbor 把 task 目录下的
tests/上传到沙箱内的/tests/(除非使用了专用 verifier 镜像或构建定义,见后文"隔离的 verifier 环境")。 - 执行判分脚本。
tests/test.sh在 task 的工作目录(working directory)中被执行,负责验证任务是否完成。 - 产出数值 reward。脚本必须把一个数值形式的 reward 写入
/logs/verifier/reward.txt或/logs/verifier/reward.json,Harbor 读取它作为该次 trial 的得分。
整个流程可以用下图概括:
flowchart LR
A[Agent 运行] --> B[Harbor 上传 tests/ 到 /tests/]
B --> C[执行 tests/test.sh]
C --> D{写入 reward?}
D -->|reward.txt 或 reward.json| E[Harbor 读取得分]
D -->|脚本异常退出| F[该 trial 无 verifier 结果]一个合格的判分脚本应该做三件事:
- 安装测试所需的依赖(如果需要);
- 检查 agent 是否满足了 instruction 的要求;
- 在
/logs/verifier/下写出 reward。
一个实践建议:脚本内部尽量使用绝对路径,避免工作目录变化带来的 cwd 惊喜。
reward 文件约定
Harbor 支持两种 reward 输出格式,路径是固定的:
| 文件 | 格式 | 典型用途 |
|---|---|---|
/logs/verifier/reward.txt |
单个数字,通常为 1 或 0 |
二值判定:通过 / 不通过 |
/logs/verifier/reward.json |
JSON 键值对象,值为带标签的数值指标 | 多指标评分、LLM 评审等细分打分 |
如果两个文件同时存在,Harbor 优先读取 reward.json。
最简单的判分脚本长这样:
#!/bin/bash
set -euo pipefail
uvx pytest /tests/test_outputs.py
if [ $? -eq 0 ]; then
echo 1 > /logs/verifier/reward.txt
else
echo 0 > /logs/verifier/reward.txt
fi这个脚本用 uvx pytest 运行测试文件,pytest 通过就写 1,失败就写 0。注意它直接引用绝对路径 /tests/test_outputs.py,并依赖 set -euo pipefail 让失败尽早暴露。
如果你的任务需要多个维度的分数,或者要用 LLM 来评审,可以写一个结构化的 reward.json,例如把不同指标分别打分后落盘。对于多指标或 LLM-as-a-judge 场景,Harbor 官方还维护了一个包 harbor-rewardkit(即 RewardKit),它是定义和运行常见 verifier(程序化判定标准、LLM/agent 评审)的最省事方式,后文会再提到。
失败模式与"无 reward"的含义
判分环节有两种截然不同的"失败",写 task 时必须区分清楚:
- 判定失败(有 reward):脚本正常运行,只是发现 agent 没有完成任务。这时你应当主动写出
0(或对应维度的低分)。这是评测语义内的正常结果。 - 判分器自身出错(无 reward):脚本崩溃、依赖没装上、路径写错、提前退出,导致
/logs/verifier/下根本没有产出 reward 文件。此时这次 trial 就没有 verifier 结果,而不是"得了 0 分"。
两者的后果不同:在多步任务中,"步骤出错且没有 verifier 结果"依然会终止整个任务(详见第 7 章);而在按 "mean" 策略聚合 reward 时,缺失的指标键会被计为 0。换句话说,reward 缺失会污染聚合结果、让这轮 trial 无法与其他轮公平比较——所以判分脚本要尽量"总能在最后写出 reward",用 set -euo pipefail 加显式的落盘逻辑来兜底。
为什么 verifier 要保持确定性、可复现
verifier 是整个评测体系的"度量衡",它必须对相同的环境终态给出相同的分数。这背后有两个具体的功能性原因:
- Regrade(重判)。Harbor 支持对已有 trial 重跑 verifier(前提是 task 使用了隔离的 verifier 环境)。当你迭代判分逻辑时,可以在旧结果上直接重新打分而不用重跑 agent。如果 verifier 依赖随机性、外部网络状态或时间戳,重判结果就失去了可比性。
- 跨 run 比较。评测的意义在于横向对比不同 agent、不同模型。判分脚本只应依赖环境的最终状态(文件内容、命令输出等可检查的事实),而不应引入噪声。
配套地,在数据集层面 Harbor 也建议把远程任务钉到完整 commit SHA 以获得可复现的 run(见第 8 章)。判分端与环境端都锁死,分数才真正可复现。
隔离的 verifier 环境(separate verifier)
默认情况下,判分脚本与 agent 共用同一个沙箱——verifier 能看到 agent 留下的文件系统改动,这正是"检查 agent 干了什么"的依据。但如果你希望在 agent 与 verifier 之间划出更清晰的安全边界,可以让 Harbor 用独立的沙箱来跑判分。
在 task.toml 中设置:
[verifier]
environment_mode = "separate"
[verifier.environment]
cpus = 2[verifier.environment] 与 [environment] 使用同一套 schema。镜像来源的优先级是:
[verifier.environment]中的docker_image;tests/目录里的构建定义(Dockerfile或docker-compose.yaml);- 都没有时,Harbor 启动一份 agent 环境的全新副本并把
tests/上传到/tests/——不会继承 agent 的文件系统改动。
使用专用 verifier 镜像或构建定义时,有一个重要约束:镜像里必须自带 /tests/test.sh(或 /tests/test.bat),Harbor 不会在运行时把 tests 上传进去。例如:
FROM ubuntu:24.04
WORKDIR /app
COPY test.sh /tests/test.sh另外两点值得记住:task.toml 中 [artifacts] 声明的产物会以相同路径复制进 verifier 沙箱;在多步任务中,step 级的 verifier 定义优先于任务级定义。
给 verifier 传环境变量
判分脚本有时需要密钥或模型名(比如用 LLM 评审)。通过 task.toml 的 [verifier.env] 段即可传入:
[verifier.env]
ANTHROPIC_API_KEY = "${ANTHROPIC_API_KEY}"
MODEL_NAME = "claude-haiku-4-5"${VAR} 语法表示从宿主机环境读取变量值。出于安全考虑,Harbor 在把这些环境变量传给 verifier 之前会先请求用户确认。
LLM 评审与 RewardKit
test.sh 只是一个普通脚本——你想用什么验证方式都可以,包括 LLM-as-a-judge 或 agent-as-a-judge。为了避免重复造轮子,Harbor 团队维护了 harbor-rewardkit 包,用它定义程序化判定标准和评审型判分器,然后在 test.sh 里调用即可。多指标场景下,让 RewardKit 把各维度分数写进 reward.json,就能被 Harbor 直接识别。
verifier 与 solution 的关系
task 目录中还有一个可选的 solution/ 文件夹(Linux 下入口为 solution/solve.sh),它存放参考解法,供 oracle agent 使用。两者的分工是:
| 目录 | 谁来执行 | 回答的问题 |
|---|---|---|
tests/test.sh |
被评测的 agent 跑完后由 Harbor 执行 | "agent 做对了吗?" |
solution/solve.sh |
Oracle agent 执行 | "这个任务本身可解吗?" |
编写 task 时,推荐用 harbor run 配合 oracle agent 跑一遍 solve.sh 做健全性检查——如果连参考解法都拿不到满分,说明任务或沙箱集成有问题。发布公开 benchmark 时 solution 是可选的,不希望暴露参考实现可以省略;但没有 solution/,oracle agent 就无法运行。运行时 Harbor 会把 solution/ 复制到 /solution 并从 task 工作目录执行 solve 脚本,[solution] 段配置的环境变量会在此时生效。
一句话总结:verifier 判 agent 的答卷,solution 保证题目本身没问题。一个高质量 task 两者兼备。
本章小结
- 判分入口是
tests/test.sh(Windows 为tests/test.bat),在 agent 运行结束后、由 Harbor 上传到/tests/后执行,工作目录为 task 的 working directory。 - reward 只认两个固定路径:
/logs/verifier/reward.txt(单数字,通常 1/0)与/logs/verifier/reward.json(带标签的多指标对象);两者并存时优先reward.json。 - 区分"判定失败"(写出 0 分)与"判分器出错"(没有 reward 文件、trial 无 verifier 结果),后者会破坏聚合与比较。
- verifier 必须确定性、可复现:regrade 会对旧 trial 重跑判分,跨 run 对比也要求相同终态得到相同分数。
environment_mode = "separate"可用独立沙箱跑判分,镜像优先级为docker_image>tests/内构建定义 > agent 环境副本;专用镜像必须自带判分脚本。[verifier.env]负责向 verifier 传环境变量,${VAR}从宿主读取且需要用户确认。- LLM/agent-as-a-judge 完全合法,推荐用
harbor-rewardkit(RewardKit)少写样板代码。 solution/solve.sh是给 oracle agent 的参考解法,用于验证任务可解;它与 verifier 互补,共同构成 task 质量的两道保险。
延伸阅读
- Verifier——本章主源页面
- Solution——参考解法与 oracle agent
- Separate verifier——隔离判分环境的完整说明
- Regrade——对已有 trial 重跑 verifier
- RewardKit Quick Start——评审型判分器的官方工具包