跳到主要内容

第 9 章:组织、版本化与发布 Dataset

团队已经有了两个可独立运行的任务:分析日志、修复 Python 端口问题。某次回归测试却出现了一个尴尬的问题:同事用同一条 harbor run -p ... 命令得到三个 Trial,而 dataset.toml 明明只列了两个任务;本地目录旁的 metric.py 也没有出现在最终指标中。另一次测试写着“使用 stable 版本”,但没人能确认这个标签一个月前是否指向相同内容。

这些现象不是评分算法的问题,而是发布边界没有定义清楚。一个可复现的 Dataset 至少要回答四个问题:选了哪些 Task、每个 Task 是哪份内容、Reward 如何聚合、运行时引用能否回到同一版本

本章把前面完成的 Task 组织成一个可审计的 Dataset,并分别给出本地目录、Harbor Registry 包和 Git 仓库三种使用方式。所有行为均以 Harbor v0.18.0(提交 527d50deb63a5d279e8c20593c18a2cbc7f61f9e)为准。

完成本章后,你应该能够:从 Task 清单生成并维护 dataset.toml;解释 Task digest、Dataset digest、revision 与标签各自解决的问题;判断本地、Registry 和 Git 来源的选择边界;编写不会混淆 Trial Reward 的聚合 Metric;在升级、回滚和调整可见性时留下足以复查的证据。更重要的是,面对“这个结果能否重现”时,你将知道应该查看具体引用、锁文件和实际指标,而不是只回答“我们用了同一个目录”。

9.1 Dataset 是选择与发布契约

先为本书项目确定 Dataset 边界:

Task主要能力Task Reward 约定
llmkb/log-incident-report从日志定位故障并形成报告reward,0 到 1
llmkb/python-port-repair修复 Python 服务的端口配置reward,0 到 1

dataset.toml 保存 Dataset 元数据、Task 名称与摘要,以及 metric.py 等 Dataset 文件的摘要。Task 引用必须采用 组织/名称 形式;非空摘要必须是 sha256: 加 64 位小写十六进制字符。Dataset 自身的内容哈希由排序后的 Task 摘要和文件的“路径:摘要”共同计算,因此调整描述不会改变内容哈希,而更换 Task 内容或指标文件会改变它。1

Dataset 不会把多个 Task 合并为一个共享环境。Harbor 展开 Job 时会为每个“Task × Agent × Attempt”创建独立 Trial;每个 Trial 又有独立名称、UUID、环境会话 ID 和 Environment 实例。这个边界隔离的是 Trial 的环境状态,不会自动隔离外部数据库、云账户或显式挂载的宿主目录。需要共享外部服务时,应为每个 Trial 使用唯一命名空间,并在清理阶段删除它。2

Dataset 是任务选择和聚合契约,不是事务、权限边界或完整沙箱。

从复现角度看,可以把 Dataset 看成一条引用链:Dataset 版本指向若干 Task digest,并可指向 metric.py 的文件摘要;Job 又指向某个 Dataset 版本,最终把解析后的 Task 写进锁文件。链条中的每一层都有不同职责。Task digest 回答“任务内容是否相同”,Dataset digest 回答“选集和 Dataset 文件是否相同”,锁文件回答“这次 Job 实际解析并准备运行了什么”。任何一层都不能代替另外两层。

这也带来一个实用判断:若只改了 Dataset 描述,内容哈希可以不变;若更换 Task digest,即使 Task 名称不变,Dataset 内容哈希也会变化;若仍使用可移动标签启动 Job,则必须以解析后的 digest 和锁文件确认实际版本。发布说明适合讲“为什么变”,摘要适合确认“内容是不是那一份”,两者应一起保存。

建立契约前还要统一 Reward 语义。把“能运行”但评分范围、键名或缺失值政策不同的 Task 直接塞进同一均值,会得到格式正确却无法解释的数字。本章选择两个都输出 reward 且范围为 0 到 1 的 Task,是有意缩小发布面。如果某个新 Task 输出 accuracy 或多维 Reward,应先迁移其 Verifier,或为它设计能够保留维度含义的 Metric;不能在 Dataset 层悄悄把未知字段当成 0。

同理,Dataset 版本应代表一个可以整体说明的能力切片。加入新 Task、修改评分契约或改变缺失值政策,都会影响结果可比性,通常应形成新发布版本。只修正文案未必改变内容哈希,但仍应在发布说明里记录。这个约束让团队能回答:两个均值的差异来自 Agent,还是来自题目集合、评分器或聚合政策。若答案不明确,就不应把两次运行放在同一趋势图上。

发布负责人还应为每次变更指定审阅者:Task 所有者确认内容,指标所有者确认统计含义,Registry 所有者确认权限。三项检查都留下记录后,版本号才真正具有团队语境。

9.2 建立本地 Dataset

在项目根目录执行:

mkdir -p datasets

harbor dataset init llmkb/system-diagnosis \
-o datasets/system-diagnosis \
--description "系统配置与故障诊断 Agent Benchmark" \
--author "LLMKB Team" \
--with-metric

harbor add tasks/log-incident-report \
tasks/python-port-repair \
--to datasets/system-diagnosis

dataset init 创建 dataset.toml 和 README;--with-metric 还会复制指标模板并登记其摘要。harbor add 会计算 Task 内容摘要,同名 Task 已存在时更新摘要。它只允许通过该命令登记与清单同目录的 metric.py3

推荐目录如下:

project/
├── datasets/
│ └── system-diagnosis/
│ ├── dataset.toml
│ ├── metric.py
│ └── README.md
└── tasks/
├── log-incident-report/
└── python-port-repair/

生成后的 dataset.toml 结构类似下面这样。这里的省略号只是示意,不能提交或发布;实际文件应包含完整摘要。

[dataset]
name = "llmkb/system-diagnosis"
description = "系统配置与故障诊断 Agent Benchmark"

[[dataset.authors]]
name = "LLMKB Team"

[[tasks]]
name = "llmkb/log-incident-report"
digest = "sha256:完整的64位小写十六进制摘要"

[[tasks]]
name = "llmkb/python-port-repair"
digest = "sha256:完整的64位小写十六进制摘要"

[[files]]
path = "metric.py"
digest = "sha256:完整的64位小写十六进制摘要"

模型中 schema_version 默认是 1.0,但 v0.18.0 的 to_toml() 并不把它写回文件。因此,不要因为生成文件里没有该字段就手工补一个未经工具验证的结构。1

Task 摘要并不是压缩包字节的哈希。Harbor 对纳入包的文件按相对路径排序,把每个“相对路径 + 文件 SHA-256”加入最终哈希;被 .gitignore 或默认忽略规则排除的文件不参与计算,纳入包的其余文件参与。这样,相同内容可以获得稳定身份,也能用于变更检测和缓存键。它不等同于数字签名、发布者身份证明或恶意代码扫描;而且 v0.18.0 的 Registry 下载路径没有展示对解包后 Task 做一次完整的客户端重算。因此,应把 digest 理解为内容寻址标识,而不是端到端安全证明。4

9.3 分清 Reward 与 Metric

Verifier 为每个 Trial 产生 Reward;它可以包含多个分量。本章两个 Task 统一输出单一 reward,便于 Dataset 聚合。Metric 接收一组 Trial Reward(失败或缺失时可能是 None),返回 Job/Dataset 级指标。Metric 不会反过来创造或修改某个 Trial 的 Reward。5

将生成的 metric.py 改为以下内容:

import argparse
import json
import math
from pathlib import Path


def aggregate(rewards: list[dict[str, float] | None]) -> dict[str, float | int]:
values: list[float] = []
missing = 0

for item in rewards:
if item is None or set(item) != {"reward"}:
values.append(0.0)
missing += 1
continue

value = item["reward"]
if not isinstance(value, (int, float)) or isinstance(value, bool):
raise ValueError("reward must be numeric")
value = float(value)
if not math.isfinite(value) or not 0.0 <= value <= 1.0:
raise ValueError("reward must be finite and between 0.0 and 1.0")
values.append(value)

count = len(values)
return {
"mean_reward": sum(values) / count if count else 0.0,
"missing_reward_rate": missing / count if count else 0.0,
"trial_count": count,
}


def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input", required=True)
parser.add_argument("-o", "--output", required=True)
args = parser.parse_args()

rewards = [json.loads(line) for line in Path(args.input).read_text().splitlines()]
Path(args.output).write_text(json.dumps(aggregate(rewards)))


if __name__ == "__main__":
main()

把缺失 Reward 计为 0,可避免“只统计成功完成的 Trial”带来的幸存者偏差;同时输出缺失率,让读者知道均值下降来自能力不足还是执行故障。代价是基础设施偶发失败也会降低均值,所以实验报告还应并列记录 Trial 异常类型。非缺失 Reward 必须是 0 到 1 的有限数值;单纯检查 Python 数值类型还会放过负数、超过 1 的值以及 NaN/Infinity。尤其 json.loads() 默认能够解析 NaN,所以聚合层必须用 math.isfinite() 主动拒绝它。

Harbor 的脚本指标通过宿主机子进程执行 uv run metric.py -i ... -o ...。对 Registry Dataset,下载的 metric.py 也会这样运行,而不是在 Trial 容器里运行。因此只应执行可信 Dataset 的指标代码。5

修改指标或 Task 后,同步摘要:

harbor sync datasets/system-diagnosis

普通 sync 只重算 Dataset 文件,以及位于 Dataset 目录内、名称匹配的直接子 Task。由于本书把 Task 放在 tasks/,应再次执行 harbor add ... --to ... 来刷新外部 Task 摘要。harbor sync --upgrade 还会把 Registry Task 引用更新到其 latest,属于依赖升级;运行前必须提交或备份清单,审核差异后再接受。发布流程也会自动同步可发现的本地内容。6

9.4 三种来源不是同一种执行模型

9.4.1 本地目录:最快,但边界最容易误判

harbor run \
-p tasks \
-a oracle \
-i log-incident-report \
-i python-port-repair

v0.18.0 有两个值得显式记录的实现事实:

  1. harbor run -p DIR 不读取 DIR/dataset.toml 来选择 Task,而是枚举该目录下直接存在的有效 Task 子目录。
  2. 本地 -p 路径不会自动载入相邻的 metric.py;若 Job 没有另配指标,会退回内置 Mean。发布为 Registry Dataset 后,包内登记的 metric.py 才会被解析为脚本 Metric。7

这解释了开头的失败案例:有人把 draft-new-verifier/ 留在 tasks/ 父目录中,它虽然没有写进另一个目录里的清单,仍被本地枚举;团队又以为 Dataset 目录里的自定义 Metric 已生效。修复方式是让“本地 Task 父目录”只包含获准 Task,或像上例一样显式使用 -i/--include-task-name 过滤,并在 Job 配置/结果中核对实际 Metrics。这个过滤列表是在手工镜像 manifest 的选择,仍没有读取 dataset.toml。若要验证发布契约,应使用固定版本的 Registry Dataset,而不是把本地 -p 当作清单执行器。

排查这类问题时,不要先改 Verifier。先把“期望集合”和“实际集合”分别列出:清单中的 tasks[].name 是发布期望,本地目录枚举结果是 -p 的实际候选,-i 过滤后的集合才是本次 Job 的输入。随后执行同一命令并追加 --print-config 查看解析后的 JobConfig;实际运行后,再检查 Job 根目录和每个 Trial 目录里的 lock.json。若 Task 数量已经不一致,评分差异只是后果;若 Task 一致而 Metrics 仍不同,再检查 Job 是否解析到 UvScript 或内置 Mean

过滤标识取决于来源,不能机械复制:本地 LocalTaskId.get_name() 返回目录 basename,因此上例使用 log-incident-report;Registry 包的 PackageTaskId.get_name() 返回完整 org/name,所以正式配置必须使用 llmkb/log-incident-report;Git Task 通常按路径名过滤。过滤后没有匹配项时,v0.18.0 会报错并列出候选,而不是悄悄运行全部 Task。7

这个顺序能区分三类常被混在一起的问题:选择错误会改变 Trial 数量,版本错误会让相同名称对应不同内容,聚合错误则在 Trial Reward 不变时改变 Job 级结果。把三者分别验证,比用“再跑一次看看”更快,也更容易形成可审查证据。

9.4.2 Registry Dataset:适合正式发布

Registry 引用写作 组织/名称@refref 可以是标签、纯整数 revision 或 sha256: Dataset digest;省略时默认为 latest。服务端返回 revision,发布器总会附加 latest,并可同时附加自定义标签。标签便于人读,却可能移动;revision 和 digest 更适合冻结实验。Registry 在解析 Dataset 后,会把其 Task 引用固定到各自完整 Task digest。8

以下是正式联网阶段的操作示例,本章没有实际执行登录、上传或远程同步:

# 先重新登记外部 Task,再检查 dataset.toml 的差异
harbor add tasks/log-incident-report \
tasks/python-port-repair \
--to datasets/system-diagnosis
harbor sync datasets/system-diagnosis

# 需要 Registry 凭据和网络;显式提供外部 Task,默认发布为 private
harbor publish \
tasks/log-incident-report \
tasks/python-port-repair \
datasets/system-diagnosis \
--tag v1.0

# 用发布输出中的真实 revision 或 digest 替换示例值
harbor run -d llmkb/system-diagnosis@3 -a oracle
harbor run -d 'llmkb/system-diagnosis@sha256:<真实Dataset摘要>' -a oracle

正式回归最好保存项目级 Job 配置,而不是让命令历史承担全部记录。v0.18.0 的 --config 接受 YAML 或 JSON,不接受 TOML。下面的 configs/system-diagnosis-v1.json 展示一个冻结配置;使用前必须把 ref 替换为发布输出中的完整真实摘要:

{
"job_name": "system-diagnosis-v1",
"jobs_dir": "jobs",
"n_attempts": 3,
"n_concurrent_trials": 3,
"agents": [
{"name": "oracle"}
],
"datasets": [
{
"name": "llmkb/system-diagnosis",
"ref": "sha256:<真实Dataset摘要>",
"task_names": [
"llmkb/log-incident-report",
"llmkb/python-port-repair"
]
}
]
}

先只解析配置,不启动容器:

harbor run --config configs/system-diagnosis-v1.json --print-config

确认 datasets[0].ref、两个过滤名称、Agent 与尝试次数后,再去掉 --print-config 启动。两个 Task、三次尝试、一个 Agent 理论上应产生六个 Trial;若实际数量不同,应停止结果比较并检查过滤条件、重试记录和提前失败。n_concurrent_trials 只控制并发上限,不改变实验样本数。JobConfig 还允许显式配置 Metrics,但本例故意留空,以便使用已发布 Dataset 的 Metric;若同时增加 Job 级 Metrics,结果中会出现额外聚合项,不能再把所有字段都归因于 metric.py9

publish 会先处理这次命令显式提供的 Task,再发布 Dataset。它也会自动发现 Dataset 目录里的直属 Task,但本章两个 Task 位于外部 tasks/,不能指望发布器从 manifest digest 反向找回本地路径;因此示例把三个路径都传入。另一种做法是先确认两个 Task 的对应 digest 已在 Registry,再单独发布 Dataset。--no-tasks 可跳过 Dataset 目录内的 Task 发布;--public 会造成外部可见性变化,不能作为随手的调试选项。10

9.4.3 Git Dataset:固定仓库提交

Git 来源由仓库根目录(或指定路径)的 registry.json 定义命名 Dataset,而不是读取 dataset.toml。一个最小示例如下:

[
{
"name": "system-diagnosis",
"version": "1.0",
"description": "系统配置与故障诊断 Agent Benchmark",
"tasks": [
{"name": "log-incident-report", "path": "tasks/log-incident-report"},
{"name": "python-port-repair", "path": "tasks/python-port-repair"}
],
"metrics": [{"type": "mean", "kwargs": {}}]
}
]
harbor run \
--repo 'https://github.com/example/benchmark.git@<完整提交SHA>' \
-d system-diagnosis \
-a oracle

Harbor 会把分支、标签或 SHA 通过 git ls-remote 解析成提交 SHA,并使用稀疏检出和缓存。分支或标签会在新运行时重新解析;正式实验应保存完整提交 SHA。Git 命名 Dataset 的 Metrics 来自 registry.json 的内置 Metric 配置,不能假定仓库里的自定义 metric.py 会像 Registry 包那样自动传递。11

来源Task 选择依据自定义 metric.py适用场景
本地 -p直接子目录;可用 -i 过滤不自动载入开发与快速诊断
Registry -d已发布 Dataset 版本中的 Task digest登记后自动载入正式基线、共享与复现
Git --repo -dregistry.json + 解析后的提交 SHA不按 Registry 包机制载入代码审查驱动、无需 Harbor 发布

选择来源时,重点不是哪种“更高级”,而是哪种证据与工作流匹配。Task 正在频繁编辑时,本地路径反馈最快,但必须显式控制目录和过滤器;团队以代码审查批准 Task 变更、又不需要 Harbor Registry 时,Git 提交天然适合做审查锚点,但自定义脚本 Metric 要另行设计;要把选集、Task 包和指标文件作为一个可发现版本分发时,Registry Dataset 的语义最完整,同时也引入了凭据、组织权限和远程可用性依赖。

不要在同一组正式结果里无记录地混用来源。即使三个来源表面上都指向同名 Task,本地内容、Git 提交和 Registry digest 仍可能不同。若必须迁移来源,应先做一个桥接运行:逐项比较 Task digest 或受控文件差异,使用同一 Agent 和尝试次数运行 smoke Job,并确认 Trial Reward 与聚合字段的契约一致。迁移记录应明确写出旧引用、新引用和差异原因。

9.5 版本、标签、同步与回滚

一次发布至少记录四类标识:Harbor 版本与提交、Dataset revision、Dataset digest、每个 Task digest。建议把它们和 Job 配置、Job 根目录的 lock.json 以及各 Trial 目录的 lock.json 一同归档。根文件是 JobLock,汇总 Harbor 信息、全局重放设置和各 Trial 锁;逐 Trial 文件是 TrialLock,保存该次实际 Task digest,Git Task 还会保存解析后的提交。12

版本选择可遵循以下规则:

  • latest:只用于开发者快速查看当前版本。
  • v1.0 等标签:用于发布说明和人工沟通,不作为唯一复现锚点。
  • revision:Registry 内简短、不可混淆的历史定位。
  • digest:内容身份最明确,适合论文、报告和回归基线。

v0.18.0 没有专门的 Dataset 回滚或“移动标签”CLI。远程回滚的含义不是删除新版本,而是把运行重新指向已知的旧 revision 或 digest:

# 示例中的 2 必须替换为发布记录里确认过的旧 revision
harbor run -d llmkb/system-diagnosis@2 -a oracle

# 最严格的做法是使用旧版本的真实 digest
harbor run -d 'llmkb/system-diagnosis@sha256:<旧版本真实摘要>' -a oracle

本地回滚则从版本控制恢复上一份 dataset.tomlmetric.py,再重新核对 Task 内容。不要用一次 harbor sync --upgrade 代替回滚:它的语义是把 Registry 依赖推进到 latest,方向恰好相反。

一次可靠升级应分成“发现、审核、发布、采用”四步。发现阶段运行普通 sync 或重新 add,只生成候选差异;审核阶段确认 Task digest 变化是否对应批准的源码变更,并单独检查 metric.py 的输入输出契约;发布阶段获得新 revision 和 Dataset digest,但不覆盖旧实验记录;采用阶段才把项目 JobConfig 从旧 digest 改到新 digest。这样,新版本出问题时只需恢复一个引用,而不是试图倒推 latest 当时指向哪里。

回滚也不能只看聚合均值恢复了没有。应确认解析到旧 Dataset digest、Task digest 与归档记录一致、Trial 数量符合笛卡尔积、Metric 字段没有变化,并检查外部共享服务是否残留新版本状态。若旧 Task 依赖的镜像、包仓库或外部 API 已不可用,digest 只能证明 Harbor 选择的内容身份,不能让消失的依赖重新出现;这正是兼容性记录必须覆盖运行环境的原因。

9.6 可见性与兼容性记录

查询和调整 Registry Dataset 可见性:

harbor dataset visibility llmkb/system-diagnosis
harbor dataset visibility llmkb/system-diagnosis --public
harbor dataset visibility llmkb/system-diagnosis --private

私有 Dataset 对组织成员可见,公开 Dataset 对所有人可见。把 Dataset 改为公开时,CLI 会检查私有 Task,并提示是否连带公开;改回私有时,代码没有对关联 Task 做对称级联。因此“Dataset 已私有”不代表曾公开的 Task 也已私有,必须逐项审核。--public--private--toggle 互斥。13

dataset.toml 没有兼容性矩阵字段。团队应在 README 中补充一份治理记录;这是项目约定,不是 Harbor 自动校验:

Harbor: v0.18.0 / 527d50d...
Task schema: 1.3
Dataset manifest schema: 1.0
Tasks: 名称 + digest
Reward contract: 仅包含数值 reward,范围 0..1
Metric contract: mean_reward / missing_reward_rate / trial_count
Runtime: OS、架构、容器提供者、网络策略
Known limits: 外部共享服务、宿主挂载、Verifier 模式

9.7 发布前验证

先做不需要 Docker 和网络的检查。以下命令假定第 3 章的锁定源码仍位于 ~/harbor-lab/harbor-v0.18.0--project 明确从该源码项目加载 Harbor,而不是依赖当前项目目录恰好能够 import harbor

HARBOR_SOURCE="${HARBOR_SOURCE:-$HOME/harbor-lab/harbor-v0.18.0}"
test -f "$HARBOR_SOURCE/pyproject.toml" || {
echo "Harbor v0.18.0 source not found: $HARBOR_SOURCE" >&2
exit 1
}

# 1. Harbor 能解析清单并校验摘要格式
uv run --project "$HARBOR_SOURCE" python - <<'PY'
from pathlib import Path
from harbor.models.dataset.manifest import DatasetManifest

path = Path("datasets/system-diagnosis/dataset.toml")
manifest = DatasetManifest.from_toml(path.read_text())
print(manifest.dataset.name)
print(manifest.compute_content_hash())
print([(task.name, task.digest) for task in manifest.tasks])
PY

# 2. 自定义 Metric 的最小契约测试
tmpdir="$(mktemp -d)"
printf '%s\n' '{"reward": 1}' '{"reward": 0}' 'null' > "$tmpdir/rewards.jsonl"
uv run --project "$HARBOR_SOURCE" datasets/system-diagnosis/metric.py \
-i "$tmpdir/rewards.jsonl" \
-o "$tmpdir/metrics.json"
cat "$tmpdir/metrics.json"

# 3. 范围与有限值负例;三次都必须以非零状态退出
for invalid in '-0.1' '1.1' 'NaN'; do
printf '{"reward": %s}\n' "$invalid" > "$tmpdir/invalid.jsonl"
if uv run --project "$HARBOR_SOURCE" datasets/system-diagnosis/metric.py \
-i "$tmpdir/invalid.jsonl" \
-o "$tmpdir/invalid-metrics.json" >/dev/null 2>&1; then
echo "metric unexpectedly accepted reward=$invalid" >&2
exit 1
fi
echo "rejected reward=$invalid"
done

rm -rf "$tmpdir"

第二段预期得到等价于:

{"mean_reward": 0.3333333333333333, "missing_reward_rate": 0.3333333333333333, "trial_count": 3}

负例循环应依次打印 rejected reward=-0.1rejected reward=1.1rejected reward=NaN。正例先成功,能降低“因为脚本或路径整体损坏而导致所有负例恰好失败”的误判风险。

然后做人工门禁:

  1. 比较 dataset.toml 的 Task 名单与批准清单,确认没有草稿 Task。
  2. 对外部 Task 重新执行 harbor add ... --to ...,审核摘要差异。
  3. 保存发布前后的 Dataset digest、revision、标签和每个 Task digest。
  4. 私有发布后,从一个干净缓存环境按 digest 下载并运行最小 smoke Job。
  5. 核对 Job 实际 Metrics、Trial 数量和锁文件,再决定是否公开。

第 4、5 步需要 Registry、容器运行时和凭据;本章写作验证没有伪造这些远程结果。

把门禁结果写成机器可比对的发布记录,而不是聊天消息。至少保存:候选 dataset.toml 的差异、Metric 最小测试输出、harbor run --print-config 输出、发布返回的 revision/digest、可见性查询结果和 smoke Job 路径。下一次升级时,这些材料就是旧基线;下一次事故时,它们能快速回答“内容变了、选择变了,还是运行环境变了”。

还应设定明确的失败条件:出现短摘要或非法摘要就不发布;期望与实际 Trial 数不一致就不比较指标;missing_reward_rate 超过团队阈值就先调查基础设施;公开操作涉及私有 Task 时必须由拥有者复核;无法从记录定位到不可变 Dataset 引用时,该结果只能标记为探索性结果。门禁的目的不是消灭所有失败,而是防止失败被悄悄包装成可复现结论。

本章小结

Dataset 的价值不只是把几个 Task 放在一起,而是把任务选择、内容身份、聚合语义和发布引用写成可审计契约。本地目录适合开发,但 v0.18.0 不把 dataset.toml 当作本地 Task 选择器,也不自动载入相邻 metric.py;Registry Dataset 才提供按 Dataset 版本解析并钉住 Task digest 的正式路径;Git Dataset 则用 registry.json 和提交 SHA 建立代码仓库边界。

稳定复现依赖不可变引用和完整记录:用 digest 或 revision 冻结正式运行,用标签沟通发布意图,用 sync 或重新 add 更新正确范围的摘要,并把可见性、外部状态与指标代码的信任边界纳入发布审查。

练习

  1. 在本地 tasks/ 父目录加入一个未登记的有效 Task,分别用不带和带 -i 的命令检查实际选择差异;解释为什么另一个目录中的 dataset.toml 没有阻止它运行。
  2. 扩展 metric.py,在保留缺失率的同时输出两个 Task 各自的均值。说明你需要怎样的输入分组信息,不能凭 Reward 本身推断哪些事实。
  3. 修改某个被纳入打包的 Task 文件,再修改一个被忽略的文件,比较两次 digest;说明内容寻址边界与完整工作树之间的差异。
  4. 为团队写一份 v1.0 发布清单,要求同时记录标签、revision、Dataset digest、Task digest、Harbor 提交与可见性审核结果。
  5. 设计一次回滚演练:从新版本切换到旧 digest,列出验证 Trial 数量、Metrics、锁文件和外部共享状态的步骤。

参考资料

Footnotes

  1. Harbor Framework Team,Harbor v0.18.0,DatasetManifest、Task/File 引用校验与内容哈希,访问日期:2026-07-16。 2

  2. Harbor Framework Team,Harbor v0.18.0,Job 展开 TrialTrial 创建独立 Environment,访问日期:2026-07-16。

  3. Harbor Framework Team,Harbor v0.18.0,Dataset 初始化向 Dataset 添加 Task 或文件,访问日期:2026-07-16。

  4. Harbor Framework Team,Harbor v0.18.0,Task 打包内容哈希实现Registry 包 Task 下载实现,访问日期:2026-07-16。

  5. Harbor Framework Team,Harbor v0.18.0,Dataset Metrics 文档UvScript Metric 实现,访问日期:2026-07-16。 2

  6. Harbor Framework Team,Harbor v0.18.0,Dataset 同步实现Dataset 发布文档,访问日期:2026-07-16。

  7. Harbor Framework Team,Harbor v0.18.0,本地 Dataset Task 发现配置Job Metric 解析,访问日期:2026-07-16。 2

  8. Harbor Framework Team,Harbor v0.18.0,包版本引用解析Registry Dataset 解析并固定 Task,访问日期:2026-07-16。

  9. Harbor Framework Team,Harbor v0.18.0,JobConfig 与 DatasetConfigharbor run --config 解析与覆盖,访问日期:2026-07-16。

  10. Harbor Framework Team,Harbor v0.18.0,发布 CLIPublisher 的 Dataset 发布流程,访问日期:2026-07-16。

  11. Harbor Framework Team,Harbor v0.18.0,Git 仓库 Dataset 文档Git Registry 客户端,访问日期:2026-07-16。

  12. Harbor Framework Team,Harbor v0.18.0,Job 锁文件模型,访问日期:2026-07-16。

  13. Harbor Framework Team,Harbor v0.18.0,Dataset 可见性 CLI发布与可见性文档,访问日期:2026-07-16。