Skip to main content
你的评估集里有 20 道 Rust 题和 30 道 Python 题。Rust 题都要一套装好 cargo 和依赖的环境,装一次要五分钟。给每道题都装一遍太慢;用 sandboxReuse 把整批 50 道题串起来又太慢,Python 题根本不需要等 Rust 题。 评估组解决这个问题。用 defineEvalGroup() 把 20 道 Rust 题划进同一组:组内共用一台 Sandbox,按 ID 依次执行;Python 题和其它组照常并行。 评估组只安排这次真正要跑的 Attempt。沿用上次结果的题不会进入组内 Sandbox;--rerun、attempts 和首过即停的规则和不分组时一样。

先选对复用方式

同一个 Experiment 不能既选中评估组、又声明 sandboxReuse: true。两者都会决定哪些题共用 Sandbox,同时出现时,运行会在创建 Sandbox 前报 eval-group-sandbox-reuse-conflict。

第 1 步:把组文件放在成员旁边

在 evals/ 下建一个目录,把成员和 eval-group.ts 放进去。这个目录的路径就是组 ID:
NiceEval 只认 evals/**/eval-group.ts 这个文件名。写成 *.eval-group.ts 不会被发现;放在 evals/eval-group.ts 也不行,因为它没有所在目录可以当组 ID。

第 2 步:在组文件里列出成员

每个成员照常默认导出 defineEval() 或 defineScoreEval() 的返回值。组文件导入这些返回值,把它们列进 evals:
写 evals 时注意:
  • 不能为空,只接受导入进来的返回值。评估用例 ID、目录前缀、glob、tag 和 selector 都不行。
  • NiceEval 不会自动收集同目录的文件。成员增删都会在 eval-group.ts 的 diff 里看得到。
  • 成员要么全是 defineEval(),要么全是 defineScoreEval()。混在一起时,运行在规划 Sandbox 前就会报错,并列出两类评估用例的 ID,方便你拆成两个组。
  • 一个评估用例最多属于一个组,同一组里也不能重复列出。
数组的顺序不决定执行顺序。组内总是按评估用例 ID 排序执行,调整数组位置不会改变调度。需要“先构建、后查询”这种前后依赖时,把两步写进同一个评估用例。按业务顺序排列成员的写法还不能用。 onUnavailable 必须写,它决定组内 Sandbox 无法创建、重置或准备时怎么办:
  • "stop-group":这个组剩下的题不再派发。
  • "replace-sandbox":丢掉当前 Sandbox,下一个 Attempt 尝试新建一台。
不写时,加载 eval-group.ts 就会报错。失败后要多花钱重建还是停下,由你决定,NiceEval 不替你猜。 挑哪些题跑仍由 Experiment 和命令行决定。只选中 workflow/02-query 时,workflow/01-index 不会被自动带进这次运行。

第 3 步:让 Experiment 提供 Sandbox

大多数项目由 Experiment 选择 Provider 和 template,评估组只负责“哪些题共用 Sandbox”:
这里不要写 sandboxReuse: true。maxConcurrency: 4 是整个 Experiment 的上限,不会让同组同时跑四个;剩下的并发位留给其它组和没分组的评估用例。 评估组只能和在 Sandbox 里运行的 Agent、支持复用的 Provider 一起用。不带 Sandbox 的 Agent 不能运行评估组。

第 4 步:运行前确认分组

先用 --dry 看计划,它不会创建 Sandbox,也不调用模型:
确认计划里出现了两道成员题,再看它们是否属于同一个组。机器可读的计划里,每一项带 evalGroupId:
两道题的 evalGroupId 都应该是 workflow。没有这个字段时,先检查组文件名和位置是不是 evals/workflow/eval-group.ts。确认后运行:
组内按评估用例 ID 排队。attempts 大于 1 时,同一道题的几次 Attempt 连续跑完,再轮到下一道题。首过即停或沿用上次结果而不需要跑的位置会直接跳过。这只是稳定的排队规则,不能用来在题与题之间传数据。 跑完后用 pnpm exec niceeval show 在终端查看结果,或用 pnpm exec niceeval view 在浏览器里查看。

把公共准备放到评估组

Sandbox 的准备可以分三处写:Experiment、评估组和评估用例。三处中只能有一处选择 template。常见分工如下: 评估组的准备步骤写成 sandboxLayer():
成员不能选 template,但可以加自己的 .before() / .after() 命令。Experiment、评估组、成员和 Agent 的准备步骤排进同一个顺序:先满足依赖,再按 changeFrequency 从小到大执行。所以评估组里很少变的步骤,可以排在 Experiment 里经常变的步骤前面。 组内的生命周期回调和 Agent Context 都能读到 ctx.evalGroup.id 和 ctx.evalGroup.definitionHash。同一个函数也可能服务没分组的评估,所以 evalGroup 是可选字段。需要在 workdir 之外按组隔离缓存或服务名时,先检查这个字段是否存在,再用组 ID 拼名字。

不要靠组内顺序传递结果

每个 Attempt 结束后,NiceEval 把 workdir 恢复到公共准备完成时的状态。前一道题写进 workdir 的文件,后一道题看不到;$HOME、/tmp、全局安装和后台进程则可能留下来。接受不了这些残留时,不要复用 Sandbox。 前一道题也不一定会跑。沿用上次结果、命令行过滤、预算耗尽和中断,都可能让某个成员这次不进入 Sandbox。后一步必须读前一步的文件时,把两步写进同一个评估用例。只有共享的东西本来就在 workdir 之外,而且每道题都能独立得出判定时,才放进同一组。

常见错误

接着看