跳转到主要内容
快速开始里那条评估用例只检查了回复里有没有“30 天”。真实的 Agent 要做的事更多:调用对的工具、在多轮对话里记住上下文、改对代码。这一页从一条最简单的用例出发,逐步加上这些检查。

一条评估用例的结构

评估用例写在 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 打分:
Judge 给出 0 到 1 的分数,.gate(0.7) 表示低于 0.7 就不通过。能用确定规则检查的,例如调用了哪个工具、文件有没有改,优先用确定规则;Judge 留给真正开放的质量。配置 Judge 用哪个模型,见配置并验证 Judge。

一份测试集生成多条用例

几十条用例逻辑相同、只有输入不同时,不用写几十个文件。从一个文件导出数组:
生成的 ID 是 sql/0000、sql/0001。从 YAML 或 JSON 读测试集、给每条起稳定的 ID,见数据驱动测试。

给 Coding Agent 准备工作区

评估 Coding Agent 时,用例照样是普通的 .eval.ts,只是要先把一个项目放进 Sandbox,再让 Agent 去改:
Sandbox 从哪来、怎样用项目自己的测试判分,见 Fixture 和 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 一份,互不覆盖。
这些步骤在一次 Attempt 里的完整顺序: 默认每次 Attempt 独占一个 Sandbox,“每个 Sandbox 一次”和“每次 Attempt”是同一回事。开启复用后两者才会分开,见复用 Sandbox。

接下来