第 2 章

第 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 聚合"]

用文字描述一遍:

  1. 启动环境:Harbor 依据 environment/(或 task.toml 里的 docker_image)启动沙箱。
  2. Agent 执行:agent 收到 instruction.md 的内容,在沙箱内自由操作——写代码、跑命令、改文件。整个对话与动作历史沉淀为 Trajectory。
  3. Verifier 打分:agent 结束后,Harbor 在环境中运行 tests/ 里的测试脚本,由它判定任务完成度。
  4. Reward 落盘:测试脚本把数值 reward 写入 /logs/verifier/reward.txt(或 reward.json),Harbor 读取并归档。
  5. 聚合:同一 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 充当兜底。

延伸阅读