一条评估用例的结构
评估用例写在evals/ 下,文件名以 .eval.ts 结尾,默认导出 defineEval(...):
- 文件路径决定 ID:
evals/billing/refund.eval.ts的 ID 是billing/refund。只有.eval.ts和.eval.tsx文件会被发现。 description会显示在结果里,写成一句“这条用例验证什么”。tags用于按标签选择,例如niceeval exp local --tag billing,Experiment 的evals函数也能读到它。test(t)是用例主体。t上有发送消息、断言和操作 Sandbox 的全部方法。
judge、timeoutMs、sandbox、metadata,见 defineEval 参考。
单轮:发一条消息,检查结果
t.succeeded():这一轮正常完成,没有失败或停在等待状态。t.calledTool(...):调用了get_weather,参数里的城市是 Brooklyn,而且只调用了一次。工具调用要 Adapter 上报对应事件才看得到,见编写 Send。t.check(value, match):对一个具体的值做检查,这里是回复文本。
await t.check(...).orStop() 在失败时停下。
给检查加上 .label("..."),结果里显示的就是你写的说明,比显示一段代码好读:
t.send() 返回这一轮的 Turn。需要分别检查某一轮时,保存它:
多轮:检查上下文接不接得上
t 上连续 send,就是同一段对话。draft.succeeded() 只检查第一轮;t.calledTool 检查整个用例里有没有调用过。需要两段互不相干的对话,例如模拟两个用户,用 t.newSession() 开一个新会话。
多轮对话要 Adapter 把上一轮的上下文带给应用,见编写 Send。
开放式的回答交给 Judge
“回答是否有依据”“语气是否适合小学生”这类问题,正则写不出来。把它交给裁判模型 Judge 打分:.gate(0.7) 表示低于 0.7 就不通过。能用确定规则检查的,例如调用了哪个工具、文件有没有改,优先用确定规则;Judge 留给真正开放的质量。配置 Judge 用哪个模型,见配置并验证 Judge。
一份测试集生成多条用例
几十条用例逻辑相同、只有输入不同时,不用写几十个文件。从一个文件导出数组:sql/0000、sql/0001。从 YAML 或 JSON 读测试集、给每条起稳定的 ID,见数据驱动测试。
给 Coding Agent 准备工作区
评估 Coding Agent 时,用例照样是普通的.eval.ts,只是要先把一个项目放进 Sandbox,再让 Agent 去改:
运行中提示进度和问题
准备步骤比较慢时,用t.progress 在终端显示当前在做什么;遇到值得记下、但不影响结论的情况,用 t.diagnostic 记一条诊断:
progress 只在运行时显示,不会保存。diagnostic 会随这次 Attempt 保存,事后能在结果里看到,但它不会改变通过与否。业务结论写成断言;准备工作失败、没法继续时,直接抛出异常,这次运行会记为 errored。
准备和收尾外部资源
有的用例要在 Sandbox 之外建临时资源,例如在共享的 Git 服务上建一个临时仓库。这时用setup 准备、teardown 收尾,每个 Attempt 各执行一次:
- 大多数用例不需要
teardown。写进 Sandbox 的文件、装的依赖,会随 Sandbox 销毁一起消失。只有 Sandbox 之外的资源才需要手动清理。 setup开始过,teardown就一定会执行,setup抛错也一样,所以要防御资源没建成的情况。teardown抛错或超过 30 秒只记一条teardown-failed诊断,不会改变这次 Attempt 已经得出的结论。想让某个检查影响结论,放进setup或test。attempts大于 1 时,同一条用例的多个 Attempt 并发执行。把句柄存进以sandbox为键的WeakMap,每个 Attempt 一份,互不覆盖。
接下来
- 任务有部分完成度,想按完成了几步给分:读通过制与计分制。
- 想把判据写得清楚、不误判:读 NiceEval Taste。
- 全部匹配器:查 expect 参考。