跳转到主要内容
Experiment 是可签入的运行配置:同一批评估用例对着哪个 adapter、哪个模型、开哪些 flags、跑几次、预算多少,都写在 experiments/ 里。CLI 的位置参数只负责筛评估用例,不负责临时改 agent 或运行配置。

最小实验

agent 放的是已经配置好的 agent 实例。被测系统的 URL、鉴权、协议细节通常传给 adapter 工厂;运行器不会单独保存一个 agentConfig 字段。

评估不同的 System Prompt 对于 Agent 的影响

使用 flag 机制,配置不同的 Flag,设置两个实验,对比不同 Prompt 的区别。
model 会作为 ctx.model 传给 Adapter;如果你的 Agent 支持模型选择,自己构建请求 flags 会作为 ctx.flags 传给 Adapter,也会作为 t.flags 出现在评估用例里。 语义就是产品 A/B 测试里的 feature flag,应该编写 Adapter 把 Flag 发给你的 Agent,Agent 根据 Flag 切换不同的 System Prompt 或者行为。

编写一组实验

一个实验文件是一格配置。要比较多个模型、agent 或 flag 取值,就写多个文件;目录只负责 id:
报告直接比较当前 Scope 中的这些 experiments,不需要额外分组字段。 只想验证某一个配置时,把位置参数写成该配置的完整 id:
用于逐配置排查——确认某一格改动是否达标,不用先跑完整组,也不用把其它配置文件挪出目录。

常用字段

启动 Experiment 共享服务

有些资源是「一个实验一份、所有 Attempt 共用」的:一条到内网记忆服务的隧道、一个实验专用的 mock server、一个 license 租约。这类资源写进一对实验级 Hook setup / teardown:整场至多各跑一次。setup 在这个实验第一个要派发的 Attempt 前执行;teardown 在全部 Attempt 收尾后执行(运行被中断也执行),当且仅当 setup 的时点已经走到才触发——setup 抛错同样要走到 teardown,收尾代码要对可能未赋值的变量做防御。上一次的结果全部被复用、这个实验一个 Attempt 都不需要真正运行时,setupteardown 都不会执行:
setup 在跑的时候,终端的 ACTIVE 区会显示一行 experiment setup · <实验 id>ctx.progress(...) 的消息就更新在这一行末尾;等它的 Attempt 计入排队数,这不是卡住。在 CI 或 agent 输出里,setup / teardown 的开始和结束各追加一行。 setup 抛错时,这个实验的每条 Attempt 都记为 errored(错误码 experiment-setup-failed)、逐条进报告;同一批的其它实验照常跑——环境起不来不该伪装成绿,也不该连坐别人。 teardown 里资源释放是必达底线:用 try/finally 包住,不管前面的观测代码是否出错都要执行。观测类动作(health probe、指标上报)只是 best-effort——给它自己的短超时、失败不要拦住释放,并在 ctx.signal.aborted 时直接跳过;中断路径上,一次可能挂起的观测不该挡在「拆隧道、退租约」前面:
setup / teardown 只管「在你机器上、一个实验一份」的服务。要在跑 agent 前按实验在Sandbox 里准备环境(装二进制、预热、跨 attempt 载入和回存状态),挂在 sandbox 字段的 spec 上:
固定的 Agent CLI、系统包和大模型缓存应先做进 image/template/snapshot;.setup() 不应在每个 Attempt 重建同一套环境。从官方 Docker 镜像、E2B 模板或 Vercel runtime 派生预制环境的步骤见 Sandbox Provider · 从官方基线继续构建以提速 Hook 的执行时机、多 Hook 顺序和失败语义见 Sandbox provider · 生命周期

与 Sandbox Hook 协作

实验级 Hook 起宿主机侧服务,Sandbox Hook 每个 Sandbox 把坐标写进去、收尾时回存状态——两层在同一个文件里靠模块变量衔接,时序由 runner 保证:实验级 setup 早于本实验任何 Sandbox Hook,Sandbox Hook 读到的变量一定已经赋好值:
一份实验文件从上往下读就是完整的运行说明:整场一次的宿主机资源在实验级 Hook 对里;每 Sandbox 的写入与回存在 sandbox 链式 Hook 里,读实验级产物;agent 怎么连自己、评估用例的任务夹具各在 agent 定义与 EvalDef 里,不进实验文件。

多个实验共享同一套生命周期代码

对比组里常常是几个实验对着同一类基础设施——同一个记忆产品,claude 与 codex 各一格对照,起停机制完全一样。把起停写成一个工厂函数,返回共享同一闭包的整套件:实验级 Hook 对、给 agent / MCP 工厂读坐标的 getter、把坐标写进 Sandbox 的 sandbox Hook。每个实验文件各自调用一次工厂,同一套代码、各自的实例与坐标:
实验文件里换 agent 只换 agent 那几行,生命周期四行接完:
两条纪律保证多个实验并发跑同一套代码不互相踩踏:
  • 工厂在 import 期只创建闭包,不做 I/O、不读配置——实验文件在 niceeval exp 的发现阶段就会被 import,import 抛错会连累同批无关实验;所有硬失败留给 setup
  • 运行时坐标活在工厂闭包里,不放模块级单例——同批并行的两个实验各持一份,互不覆写;坐标在 setup 之后才存在。
服务起多份太贵、必须让同批实验共享一份实例时,用「首进启动、末出关停」的引用计数代替按实验各建一份:
计数能保持平衡,靠的是成对触发规则本身:teardown 当且仅当同层 setup 时点走到过才执行、setup 抛错也照样配对触发 teardownrefs 不会泄漏。 边界在生命周期:同批共享的服务活不过这次 run。要跨 run 存在的服务(先起好、连续跑多次 niceeval exp)仍归外部编排(docker compose 之类)起停,URL 经环境变量传入。

让不同评估用例使用不同预制环境

一批真实任务可能需要不同版本的运行时和依赖。评估用例用 environment 声明一个与 provider 无关的 profile ID;sandbox spec 的 environments 表再把它映射到具体模板或快照:
environment 是非空的稳定字符串,不是包版本约束。environments 表的值就是该 provider 预制产物字段的覆盖(Docker 的 image、E2B 的 template、Vercel 的 snapshotId)。NiceEval 在启动任何 Sandbox 前对所有选中的评估用例完成查表;某条评估用例声明的 profile 缺表项会在启动时一次性报出全部缺项。remote Agent 不创建 Sandbox,不参与查表。因为映射是随 spec 复用的数据,同一个实验能覆盖全部评估用例——分数和对比表不会因为环境不同被拆成多个实验。 跨配置比较的设计建议见实验矩阵。Adapter 对 ctx.modelctx.flags 的用法见 Adapter