跳转到主要内容
你写了一版新的 system prompt,想知道它比旧版好还是差。评估用例已经有了,现在需要对着两个版本各跑一遍,再把结果放在一起看。 这就是 Experiment 的用处。评估用例只说“发什么、怎样算做对”;Experiment 说“对着谁跑”:哪个 Agent、哪个模型、开哪些 flags、跑几次、花多少钱为止。两者分开,同一批评估用例就能拿来比较任意两个配置。

最小实验

文件路径就是 Experiment ID,这里是 local。agent 放的是已经配置好的 Adapter 实例,被测应用的地址、鉴权这类连接参数传给 Adapter 工厂,不写在别处。

比较两个模型

一个 Experiment 文件就是一个配置。要比较两个模型,写两个文件,只改 model 那一行:
model 会作为 ctx.model 传给 Adapter。Adapter 要把它放进发给应用的请求里,应用才真的会换模型,写法见接入你的 Agent。 attempts: 3 让每条评估用例跑 3 次。Agent 每次的回答都不一样,只跑一次的通过率说明不了多少问题。

比较两版 prompt

prompt 版本在应用内部,NiceEval 看不到。做法和产品里的 A/B 测试一样:应用接受一个 flag,按 flag 切换 prompt;Experiment 负责给这个 flag 赋值。
experiments/prompts/v2.ts 把 promptVariant 改成 "v2",其余逐字相同。两个配置之间差得越少,分数的差别就越能归因到 prompt 上。 Adapter 把 ctx.flags 原样转给应用:
flags 是扁平对象,值只能是字符串、有限数字或布尔值。嵌套对象、数组、null 和显式 undefined 都会在运行前被拒绝。评估用例里也能用 t.flags 读到同一份值。 需要检查 flag 是否合法或补默认值时,在 Adapter 上声明同步的 parseFlags(input: unknown)。它的返回值决定实验里 flags 的类型,规范化后的值参与结果沿用的判定。 最常见的失误是两个文件写成了同一个 flags 或同一个 model,例如两边都读同一个环境变量的默认值。运行和报告都正常,但两列数字比的是同一套配置。

跑一组,或者只跑一格

位置参数按 ID 前缀选择 Experiment。目录只用来组织 ID 和批量选择:
某一格结果反常、需要单独复现时,写它的完整 ID 就行,不用先跑完整组,也不用把其它文件挪走。后面再跟评估用例 ID 前缀,可以进一步只跑某几道题:
想在运行前确认会跑哪些评估用例、多少次,加 --dry,见选择并预览运行范围。

看对比结果

两个配置都跑完后,在终端分别查看:
每个 Experiment 的 Summary 里会列出这几项:
Observed 是有结果的 Attempt 数和计划数。两个数不相等时,说明有些 Attempt 还没有结果,这时的通过率不能直接和另一边比较。 除了通过率,还要看耗时、token、成本和失败的类型。一个配置通过率高一点,成本却翻了一倍,不一定值得换。把两次 Run 并排比较,见比较多个固定 Run;在浏览器里看,用 niceeval view。

让比较有意义

  • 比较期间保持评估用例集合不变,否则两边比的不是同一批题。
  • 非确定性强的 Agent,尤其是 Coding Agent,每个配置至少跑 3 次。
  • 每次只改一个变量。同时换模型又换 prompt,就分不清是谁的功劳。
  • 失败要按原因归类看,不只看总分。

只跑部分评估用例

每个 Experiment 用 evals 选择要跑哪些评估用例,默认是全部。可以写 ID 前缀数组,也可以写一个函数:
eval.id 是从文件路径推导出的项目内 ID,不是绝对路径,可以直接用 startsWith 判断。

常用字段

字段全集见 defineExperiment 参考。

不同评估用例需要不同环境

一批真实任务可能需要不同版本的运行时,例如有的题要 Python 3.9。这时把 Sandbox 直接写在评估用例上,让任务和它需要的环境放在一起:
多条评估用例共用同一个环境时,用普通函数复用:
Experiment 这时只需要写 Agent,同一个 Experiment 仍然能覆盖所有评估用例。NiceEval 在创建任何 Sandbox 之前,会先检查每条评估用例的环境是否和 Experiment 的配置兼容。

一个应用用多个模型

应用内部有规划、回复、审查等不同角色,各用一个模型时,用 models 按用途分别指定:
Adapter 从 ctx.models 读取这份配置交给应用,每次请求模型时在 ctx.recordUsage 里写上对应的 modelSlot。这样用量和费用能按用途分开统计;没被调用的角色不会产生费用。 models 不能和 model、reasoningEffort 同时写。Agent 类的 Adapter 只用单个 model。 运行后用 niceeval show @<locator> --usage 查看一次运行里各个模型的用量和费用。Judge 的用量没有上报时会显示为不可用,总费用只给出已知部分。