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:
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,方便你拆成两个组。 - 一个评估用例最多属于一个组,同一组里也不能重复列出。
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。确认后运行:
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 之外,而且每道题都能独立得出判定时,才放进同一组。
常见错误
接着看
- 用 Plugin 复用完整评估条件——把组、实验或成员都要的一整套条件写一次,挂到需要的地方。
- 复用 Sandbox——整批评估都适合共用 Sandbox 时,
sandboxReuse: true的生命周期和残留边界。 - 调好并发——评估组怎样和全局、Experiment 并发上限一起工作。
- Sandbox Provider 配置——选择
template、设置寿命并编写准备步骤。 - 重跑与沿用——哪些 Attempt 会进入这次的组内队列。