第 2 章:任务解剖——一个 Task 由什么构成
第 2 章:任务解剖——一个 Task 由什么构成
Task 是 Harbor 评测体系的最小构件,也是任务作者唯一需要亲手编写的东西。本章拆解一个 Task 目录的五个组成部分,讲清 reward 落盘的路径约定,并完整走一遍一个 Trial 从启动到出分的生命周期。
一个 Task 就是一个目录
Harbor 对任务的定义非常朴素:一条指令(instruction)、一个环境(environment)、一个测试脚本(test script)。三者被打包成一个目录,目录本身就是任务,没有任何额外的打包格式或中心化注册要求。
一个标准任务目录长这样:
my-task/
├── instruction.md
├── task.toml
├── environment/
│ ├── Dockerfile
│ └── ...
├── solution/
│ ├── solve.sh
│ └── ...
└── tests/
├── test.sh
└── ...其中 environment/ 里的环境规格(spec)不限于 Dockerfile——只要消费方(比如 Harbor 框架)支持,任何规格都可以。常见的有:
Dockerfile:单容器环境,最常用的选择;docker-compose.yaml:多容器环境,适合需要数据库、API mock 等副服务的任务;Apptainer.def:面向 HPC(高性能计算)环境。
五个组成部分各自的角色如下表:
| 组件 | 作用 |
|---|---|
instruction.md |
传给 agent 的任务指令,Markdown 格式 |
task.toml |
任务配置与元数据(超时、资源、网络、环境变量等) |
environment/ |
定义 agent 和 verifier 运行所在的环境 |
solution/ |
可选的参考解答,供 Oracle agent 验证任务可解 |
tests/ |
测试脚本,判定任务是否完成并产出 reward |
reward 落盘约定
任务在 tests/test.sh 脚本中定义"如何验证指令是否被完成",并把结果写成数值型的 reward。Harbor 约定了一个刚性的落盘路径:测试脚本必须在环境内把 reward 写到:
/logs/verifier/reward.txt当任务需要多维或带标签的评分时,则写 JSON 格式:
/logs/verifier/reward.json这个约定非常重要:verifier 的输出与框架的解耦就靠它完成。无论你的测试逻辑是跑单元测试、比对文件哈希,还是调用一个 LLM judge,最终交付给 Harbor 的都只是这两个路径之一里的数字或结构化结果。第 4 章会再次从"特殊路径"的角度梳理 /logs/ 下的完整约定。
一个 Trial 的生命周期
一个 Trial(单次尝试)就是任务各组件协同运转的一个回合,流程如下:
flowchart LR
S["Start 启动环境"] --> E1["Environment 就绪"]
E1 --> A["Agent 执行指令"]
A --> T["Trajectory 记录轨迹"]
T --> E2["Environment 最终状态"]
E2 --> V["Verifier 跑 tests/ 打分"]
V --> R["Reward 产出"]
R --> AGG["跨 Trial 聚合"]用文字描述一遍:
- 启动环境:Harbor 依据
environment/(或task.toml里的docker_image)启动沙箱。 - Agent 执行:agent 收到
instruction.md的内容,在沙箱内自由操作——写代码、跑命令、改文件。整个对话与动作历史沉淀为 Trajectory。 - Verifier 打分:agent 结束后,Harbor 在环境中运行
tests/里的测试脚本,由它判定任务完成度。 - Reward 落盘:测试脚本把数值 reward 写入
/logs/verifier/reward.txt(或reward.json),Harbor 读取并归档。 - 聚合:同一 Job 下的所有 Trial 结果汇聚成整个评测的分数,可用于横向对比、RL 与上下文优化、SFT 数据构造和失败分析。
注意一个容易忽略的细节:agent 在沙箱里的改动直接反映在环境最终状态上,verifier 检查的正是这个状态——所以测试脚本天然可以检查"agent 有没有把文件改对",而不需要 agent 自己汇报结果。
任务的独立性、隔离性与可复现性
Harbor 官方把任务类比为软件包:任务应当是自包含、有版本、可维护、可长期演进的独立代码单元。这意味着:
- 无框架依赖:Harbor 任务对 Harbor 框架本身零依赖,可以插进任何支持 Harbor 格式的框架里使用。目录即接口。
- 隔离执行:每次 Trial 都在独立的沙箱环境中进行,agent 之间、任务之间互不干扰。
- 可复现:环境由 Dockerfile 等规格文件锁定,配置由
task.toml记录,轨迹与得分完整归档。
如果你曾经维护过一个"题目和环境定义散落在十几个脚本里"的内部评测集,这套约束带来的收益是立竿见影的。
多步任务一瞥
标准任务只有一条指令,但 Harbor 支持把一个任务拆成多个步骤(multi-step),每一步有自己的指令、测试和解答,适合在长周期任务中设置里程碑、测试记忆类的持续学习方法,或观察 agent 在已有工作之上继续构建的能力。目录结构大致演化为:
my-task/
├── task.toml
├── environment/
│ └── Dockerfile
├── tests/
│ ├── helpers.py
│ └── ...
└── steps/
├── step-1/
│ ├── instruction.md
│ ├── tests/test.sh
│ ├── solution/solve.sh
│ └── workdir/setup.sh
└── step-2/
└── ...几个关键点:tests/helpers.py 等共享评分工具可被每一步的测试复用;每步的 workdir/setup.sh 为可选的准备工作脚本;任务根部的基础 tests/test.sh 可以作为没有自带测试脚本的步骤的兜底,步骤专属的 test.sh 会覆盖它。多步配置的细节(如 [[steps]] 与 trial 级 reward 汇总策略)将在第 3 章的字段参考中提及。
本章小结
- Task = instruction + environment + test script,整体是一个自包含目录,目录即接口。
- 五个组成部分:
instruction.md(指令)、task.toml(配置)、environment/(环境)、solution/(可选参考解答)、tests/(判分脚本)。 - 环境规格不限于 Dockerfile,
docker-compose.yaml与Apptainer.def也是常见选择。 - reward 约定路径:单值写
/logs/verifier/reward.txt,多维或标签化评分写/logs/verifier/reward.json。 - Trial 生命周期:启动环境 → agent 执行并留下 Trajectory → verifier 跑
tests/→ reward 落盘 → 跨 Trial 聚合。 - 任务对 Harbor 框架零依赖,可插入任何支持该格式的框架;官方建议像维护软件包一样维护任务。
- 多步任务通过
steps/目录组织,每步可有独立指令、测试、解答,根部test.sh充当兜底。