niceeval 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/INDEX.md +1 -0
  2. package/dist/adapter.cjs +4 -0
  3. package/dist/adapter.cjs.map +1 -1
  4. package/dist/adapter.d.cts +25 -14
  5. package/dist/adapter.d.mts +25 -14
  6. package/dist/adapter.d.ts +25 -14
  7. package/dist/assertions/measurements.cjs +45 -0
  8. package/dist/assertions/measurements.cjs.map +1 -0
  9. package/dist/assertions/measurements.d.cts +17 -0
  10. package/dist/assertions/measurements.d.mts +17 -0
  11. package/dist/assertions/measurements.d.ts +17 -0
  12. package/dist/assertions/measurements.js +8 -0
  13. package/dist/assertions/measurements.js.map +1 -0
  14. package/dist/define.cjs +2 -0
  15. package/dist/define.cjs.map +1 -1
  16. package/dist/expect/index.cjs +4 -1
  17. package/dist/expect/index.cjs.map +1 -1
  18. package/dist/expect/index.d.cts +3 -1
  19. package/dist/expect/index.d.mts +3 -1
  20. package/dist/expect/index.d.ts +3 -1
  21. package/dist/expect/index.js +20 -16
  22. package/dist/expect/index.mjs +20 -16
  23. package/dist/experiment/host/operations.cjs +3 -1
  24. package/dist/experiment/host/operations.cjs.map +1 -1
  25. package/dist/experiment/host/runtime.cjs +3 -1
  26. package/dist/experiment/host/runtime.cjs.map +1 -1
  27. package/dist/experiment/host/teardown.cjs +1 -0
  28. package/dist/experiment/host/teardown.cjs.map +1 -1
  29. package/dist/index.cjs +3 -19
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +3 -7
  32. package/dist/index.d.mts +3 -7
  33. package/dist/index.d.ts +3 -7
  34. package/dist/index.js +12 -34
  35. package/dist/index.mjs +12 -34
  36. package/dist/model-slots.cjs +19 -0
  37. package/dist/model-slots.cjs.map +1 -1
  38. package/dist/model-slots.d.cts +7 -0
  39. package/dist/model-slots.d.mts +7 -0
  40. package/dist/model-slots.d.ts +7 -0
  41. package/dist/model-slots.js +8 -2
  42. package/dist/o11y/eval-usage.cjs +20 -2
  43. package/dist/o11y/eval-usage.cjs.map +1 -1
  44. package/dist/o11y/eval-usage.d.cts +7 -2
  45. package/dist/o11y/eval-usage.d.mts +7 -2
  46. package/dist/o11y/eval-usage.d.ts +7 -2
  47. package/dist/runner/adoption.cjs +1 -0
  48. package/dist/runner/adoption.cjs.map +1 -1
  49. package/dist/runner/eval-selection.cjs +15 -1
  50. package/dist/runner/eval-selection.cjs.map +1 -1
  51. package/dist/runner/eval-selection.d.cts +4 -0
  52. package/dist/runner/eval-selection.d.mts +4 -0
  53. package/dist/runner/eval-selection.d.ts +4 -0
  54. package/dist/runner/lock.cjs +1 -3
  55. package/dist/runner/lock.cjs.map +1 -1
  56. package/dist/runner/lock.d.cts +0 -2
  57. package/dist/runner/lock.d.mts +0 -2
  58. package/dist/runner/lock.d.ts +0 -2
  59. package/dist/runner/lock.js +14 -16
  60. package/dist/runner/types.cjs.map +1 -1
  61. package/dist/runner/types.d.cts +1 -0
  62. package/dist/runner/types.d.mts +1 -0
  63. package/dist/runner/types.d.ts +1 -0
  64. package/dist/sandbox/docker-profile/registry.cjs +1 -2
  65. package/dist/sandbox/docker-profile/registry.cjs.map +1 -1
  66. package/dist/sandbox/docker-profile/registry.d.cts +0 -1
  67. package/dist/sandbox/docker-profile/registry.d.mts +0 -1
  68. package/dist/sandbox/docker-profile/registry.d.ts +0 -1
  69. package/dist/sandbox/docker-profile/registry.js +2 -4
  70. package/dist/sandbox/index.cjs +1 -2
  71. package/dist/sandbox/index.cjs.map +1 -1
  72. package/dist/sandbox/index.d.cts +1 -1
  73. package/dist/sandbox/index.d.mts +1 -1
  74. package/dist/sandbox/index.d.ts +1 -1
  75. package/dist/sandbox/index.js +32 -34
  76. package/dist/sandbox/index.mjs +32 -34
  77. package/docs-site/zh/examples/integrations/ai-sdk-v7.mdx +5 -5
  78. package/docs-site/zh/examples/integrations/claude-sdk.mdx +5 -5
  79. package/docs-site/zh/examples/integrations/langgraph.mdx +3 -3
  80. package/docs-site/zh/examples/integrations/pi-sdk.mdx +5 -5
  81. package/docs-site/zh/explanation/judge.mdx +1 -1
  82. package/docs-site/zh/index.mdx +18 -0
  83. package/docs-site/zh/introduction.mdx +2 -1
  84. package/docs-site/zh/reference/define-agent.mdx +2 -2
  85. package/docs-site/zh/tutorials/authoring.mdx +2 -1
  86. package/docs-site/zh/tutorials/control-run-cost.mdx +84 -5
  87. package/docs-site/zh/tutorials/upgrade-0-18.mdx +64 -0
  88. package/docs-site/zh/tutorials/verify-judge.mdx +2 -1
  89. package/package.json +1 -1
@@ -20,7 +20,7 @@ Adapter 只需填写应用地址和审批接口地址。示例中的 `calculate`
20
20
 
21
21
  接入的全部代码变更(生成时从两个目录实测统计):
22
22
 
23
- <table className="gd-summary"><tbody><tr><th>{"类别"}</th><th>{"文件数"}</th><th>{"行数"}</th></tr><tr><td>{"应用侧配置(必要:依赖声明)"}</td><td>{"3"}</td><td>{"+6 −1"}</td></tr><tr><td>{"Adapter(必要:连接应用,协议映射由 NiceEval 提供)"}</td><td>{"2"}</td><td>{"+112"}</td></tr><tr><td>{"评估用例与实验(按需增长)"}</td><td>{"6"}</td><td>{"+128"}</td></tr><tr className="gd-total"><td>{"合计"}</td><td>{"11"}</td><td>{"+246 −1"}</td></tr></tbody></table>
23
+ <table className="gd-summary"><tbody><tr><th>{"类别"}</th><th>{"文件数"}</th><th>{"行数"}</th></tr><tr><td>{"应用侧配置(必要:依赖声明)"}</td><td>{"3"}</td><td>{"+6 −1"}</td></tr><tr><td>{"Adapter(必要:连接应用,协议映射由 NiceEval 提供)"}</td><td>{"2"}</td><td>{"+112"}</td></tr><tr><td>{"评估用例与实验(按需增长)"}</td><td>{"6"}</td><td>{"+130"}</td></tr><tr className="gd-total"><td>{"合计"}</td><td>{"11"}</td><td>{"+248 −1"}</td></tr></tbody></table>
24
24
 
25
25
  ## 文件清单
26
26
 
@@ -82,9 +82,9 @@ pi-sdk/
82
82
  </div>
83
83
 
84
84
  <div className="gd-file">
85
- <div className="gd-head"><span className="gd-name">{"evals/basic-qa.eval.ts"}</span><span className="gd-stats"><span className="gd-plus">{"+27"}</span></span></div>
85
+ <div className="gd-head"><span className="gd-name">{"evals/basic-qa.eval.ts"}</span><span className="gd-stats"><span className="gd-plus">{"+28"}</span></span></div>
86
86
  <div className="gd-body">
87
- <table className="gd-table"><tbody><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"1"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" { defineEval, defineJudge } "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"niceeval\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"2"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"3"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"const"}</span><span className="gdt0">{" "}</span><span className="gdt1">{"answerQuality"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"="}</span><span className="gdt0">{" "}</span><span className="gdt5">{"defineJudge"}</span><span className="gdt0">{"({"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"4"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" name: "}</span><span className="gdt2">{"\"answer-quality\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"5"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" rubric: "}</span><span className="gdt2">{"\"助手是否用一两句话正常介绍了自己,而不是报错或答非所问?\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"6"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{"});"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"7"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"8"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// 这条 eval 验证 agent 能正常问答、不瞎调工具,顺带冒烟 usage 有没有正确从"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"9"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// message_end 的 AssistantMessage.usage 累加进 Turn.usage。"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"10"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"export"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"default"}</span><span className="gdt0">{" "}</span><span className="gdt5">{"defineEval"}</span><span className="gdt0">{"({"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"11"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" description: "}</span><span className="gdt2">{"\"测试 agent 能正常问答且不瞎调工具\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"12"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"13"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt4">{"async"}</span><span className="gdt0">{" "}</span><span className="gdt5">{"test"}</span><span className="gdt0">{"("}</span><span className="gdt7">{"t"}</span><span className="gdt0">{") {"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"14"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt4">{"const"}</span><span className="gdt0">{" "}</span><span className="gdt1">{"turn"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"="}</span><span className="gdt0">{" "}</span><span className="gdt4">{"await"}</span><span className="gdt0">{" t."}</span><span className="gdt5">{"send"}</span><span className="gdt0">{"("}</span><span className="gdt2">{"\"用一句话介绍一下你自己,这轮不用查天气也不用算数。\""}</span><span className="gdt0">{");"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"15"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt4">{"await"}</span><span className="gdt0">{" turn."}</span><span className="gdt5">{"succeeded"}</span><span className="gdt0">{"()."}</span><span className="gdt5">{"orStop"}</span><span className="gdt0">{"();"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"16"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"17"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt4">{"await"}</span><span className="gdt0">{" t."}</span><span className="gdt5">{"group"}</span><span className="gdt0">{"("}</span><span className="gdt2">{"\"正常收发、没有多余工具调用\""}</span><span className="gdt0">{", () "}</span><span className="gdt4">{"=>"}</span><span className="gdt0">{" {"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"18"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" t."}</span><span className="gdt5">{"succeeded"}</span><span className="gdt0">{"();"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"19"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" t."}</span><span className="gdt5">{"usedNoTools"}</span><span className="gdt0">{"();"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"20"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" });"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"21"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"22"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt6">{"// usage 冒烟:拿不到就应该是 0,不该抛错;上限给得宽松,只为证明数字不是编的。"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"23"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" t."}</span><span className="gdt5">{"maxTokens"}</span><span className="gdt0">{"("}</span><span className="gdt1">{"20_000"}</span><span className="gdt0">{");"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"24"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"25"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" t."}</span><span className="gdt5">{"judge"}</span><span className="gdt0">{"({ prompt: turn.input, answer: turn.message }, answerQuality)."}</span><span className="gdt5">{"gate"}</span><span className="gdt0">{"("}</span><span className="gdt1">{"0.6"}</span><span className="gdt0">{");"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"26"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" },"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"27"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{"});"}</span></td></tr></tbody></table>
87
+ <table className="gd-table"><tbody><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"1"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" { defineEval } "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"niceeval\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"2"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" { defineJudge } "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"niceeval/expect\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"3"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"4"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"const"}</span><span className="gdt0">{" "}</span><span className="gdt1">{"answerQuality"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"="}</span><span className="gdt0">{" "}</span><span className="gdt5">{"defineJudge"}</span><span className="gdt0">{"({"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"5"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" name: "}</span><span className="gdt2">{"\"answer-quality\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"6"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" rubric: "}</span><span className="gdt2">{"\"助手是否用一两句话正常介绍了自己,而不是报错或答非所问?\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"7"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{"});"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"8"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"9"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// 这条 eval 验证 agent 能正常问答、不瞎调工具,顺带冒烟 usage 有没有正确从"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"10"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// message_end 的 AssistantMessage.usage 累加进 Turn.usage。"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"11"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"export"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"default"}</span><span className="gdt0">{" "}</span><span className="gdt5">{"defineEval"}</span><span className="gdt0">{"({"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"12"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" description: "}</span><span className="gdt2">{"\"测试 agent 能正常问答且不瞎调工具\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"13"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"14"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt4">{"async"}</span><span className="gdt0">{" "}</span><span className="gdt5">{"test"}</span><span className="gdt0">{"("}</span><span className="gdt7">{"t"}</span><span className="gdt0">{") {"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"15"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt4">{"const"}</span><span className="gdt0">{" "}</span><span className="gdt1">{"turn"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"="}</span><span className="gdt0">{" "}</span><span className="gdt4">{"await"}</span><span className="gdt0">{" t."}</span><span className="gdt5">{"send"}</span><span className="gdt0">{"("}</span><span className="gdt2">{"\"用一句话介绍一下你自己,这轮不用查天气也不用算数。\""}</span><span className="gdt0">{");"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"16"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt4">{"await"}</span><span className="gdt0">{" turn."}</span><span className="gdt5">{"succeeded"}</span><span className="gdt0">{"()."}</span><span className="gdt5">{"orStop"}</span><span className="gdt0">{"();"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"17"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"18"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt4">{"await"}</span><span className="gdt0">{" t."}</span><span className="gdt5">{"group"}</span><span className="gdt0">{"("}</span><span className="gdt2">{"\"正常收发、没有多余工具调用\""}</span><span className="gdt0">{", () "}</span><span className="gdt4">{"=>"}</span><span className="gdt0">{" {"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"19"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" t."}</span><span className="gdt5">{"succeeded"}</span><span className="gdt0">{"();"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"20"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" t."}</span><span className="gdt5">{"usedNoTools"}</span><span className="gdt0">{"();"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"21"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" });"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"22"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"23"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt6">{"// usage 冒烟:拿不到就应该是 0,不该抛错;上限给得宽松,只为证明数字不是编的。"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"24"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" t."}</span><span className="gdt5">{"maxTokens"}</span><span className="gdt0">{"("}</span><span className="gdt1">{"20_000"}</span><span className="gdt0">{");"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"25"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"26"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" t."}</span><span className="gdt5">{"judge"}</span><span className="gdt0">{"({ prompt: turn.input, answer: turn.message }, answerQuality)."}</span><span className="gdt5">{"gate"}</span><span className="gdt0">{"("}</span><span className="gdt1">{"0.6"}</span><span className="gdt0">{");"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"27"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" },"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"28"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{"});"}</span></td></tr></tbody></table>
88
88
  </div>
89
89
  </div>
90
90
 
@@ -110,9 +110,9 @@ pi-sdk/
110
110
  </div>
111
111
 
112
112
  <div className="gd-file">
113
- <div className="gd-head"><span className="gd-name">{"evals/weather-tool.eval.ts"}</span><span className="gd-stats"><span className="gd-plus">{"+25"}</span></span></div>
113
+ <div className="gd-head"><span className="gd-name">{"evals/weather-tool.eval.ts"}</span><span className="gd-stats"><span className="gd-plus">{"+26"}</span></span></div>
114
114
  <div className="gd-body">
115
- <table className="gd-table"><tbody><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"1"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" { defineEval, defineJudge } "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"niceeval\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"2"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" { jsonMatch, pattern, toolMatch } "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"niceeval/expect\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"3"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"4"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"const"}</span><span className="gdt0">{" "}</span><span className="gdt1">{"answerQuality"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"="}</span><span className="gdt0">{" "}</span><span className="gdt5">{"defineJudge"}</span><span className="gdt0">{"({"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"5"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" name: "}</span><span className="gdt2">{"\"answer-quality\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"6"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" rubric: "}</span><span className="gdt2">{"\"助手是否给出了具体的天气数据(温度或天气状况),而不是拒绝回答或含糊其辞?\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"7"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{"});"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"8"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"9"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// 这条 eval 验证 agent 遇到实时天气问题时会调 get_weather,而不是直接编一个答案。"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"10"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"export"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"default"}</span><span className="gdt0">{" "}</span><span className="gdt5">{"defineEval"}</span><span className="gdt0">{"({"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"11"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" description: "}</span><span className="gdt2">{"\"测试 agent 在天气问题中正确调用 get_weather 并基于结果作答\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"12"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"13"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt4">{"async"}</span><span className="gdt0">{" "}</span><span className="gdt5">{"test"}</span><span className="gdt0">{"("}</span><span className="gdt7">{"t"}</span><span className="gdt0">{") {"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"14"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt4">{"const"}</span><span className="gdt0">{" "}</span><span className="gdt1">{"turn"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"="}</span><span className="gdt0">{" "}</span><span className="gdt4">{"await"}</span><span className="gdt0">{" t."}</span><span className="gdt5">{"send"}</span><span className="gdt0">{"("}</span><span className="gdt2">{"\"北京今天天气怎么样?\""}</span><span className="gdt0">{");"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"15"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt4">{"await"}</span><span className="gdt0">{" turn."}</span><span className="gdt5">{"succeeded"}</span><span className="gdt0">{"()."}</span><span className="gdt5">{"orStop"}</span><span className="gdt0">{"();"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"16"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"17"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt4">{"await"}</span><span className="gdt0">{" t."}</span><span className="gdt5">{"group"}</span><span className="gdt0">{"("}</span><span className="gdt2">{"\"调用 get_weather 且城市正确\""}</span><span className="gdt0">{", () "}</span><span className="gdt4">{"=>"}</span><span className="gdt0">{" {"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"18"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" t."}</span><span className="gdt5">{"calledTool"}</span><span className="gdt0">{"("}</span><span className="gdt5">{"toolMatch"}</span><span className="gdt0">{"("}</span><span className="gdt2">{"\"get_weather\""}</span><span className="gdt0">{", { input: "}</span><span className="gdt5">{"jsonMatch"}</span><span className="gdt0">{"({ city: "}</span><span className="gdt2">{"\"北京\""}</span><span className="gdt0">{" }) }));"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"19"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" t."}</span><span className="gdt5">{"check"}</span><span className="gdt0">{"(turn.message, "}</span><span className="gdt5">{"pattern"}</span><span className="gdt0">{"("}</span><span className="gdt2">{"/"}</span><span className="gdt8">{"°C"}</span><span className="gdt4">{"|"}</span><span className="gdt8">{"气温"}</span><span className="gdt4">{"|"}</span><span className="gdt8">{"天气"}</span><span className="gdt4">{"|"}</span><span className="gdt8">{"晴"}</span><span className="gdt4">{"|"}</span><span className="gdt8">{"多云"}</span><span className="gdt4">{"|"}</span><span className="gdt8">{"雨"}</span><span className="gdt4">{"|"}</span><span className="gdt8">{"阴"}</span><span className="gdt2">{"/"}</span><span className="gdt0">{"));"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"20"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" });"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"21"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"22"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt6">{"// 「是否走了工具」由上面的 t.calledTool 确定性把关;judge 只评回复本身的质量。"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"23"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" t."}</span><span className="gdt5">{"judge"}</span><span className="gdt0">{"({ prompt: turn.input, answer: turn.message }, answerQuality)."}</span><span className="gdt5">{"gate"}</span><span className="gdt0">{"("}</span><span className="gdt1">{"0.7"}</span><span className="gdt0">{");"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"24"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" },"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"25"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{"});"}</span></td></tr></tbody></table>
115
+ <table className="gd-table"><tbody><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"1"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" { defineEval } "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"niceeval\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"2"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" { defineJudge } "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"niceeval/expect\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"3"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" { jsonMatch, pattern, toolMatch } "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"niceeval/expect\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"4"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"5"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"const"}</span><span className="gdt0">{" "}</span><span className="gdt1">{"answerQuality"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"="}</span><span className="gdt0">{" "}</span><span className="gdt5">{"defineJudge"}</span><span className="gdt0">{"({"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"6"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" name: "}</span><span className="gdt2">{"\"answer-quality\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"7"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" rubric: "}</span><span className="gdt2">{"\"助手是否给出了具体的天气数据(温度或天气状况),而不是拒绝回答或含糊其辞?\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"8"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{"});"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"9"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"10"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// 这条 eval 验证 agent 遇到实时天气问题时会调 get_weather,而不是直接编一个答案。"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"11"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"export"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"default"}</span><span className="gdt0">{" "}</span><span className="gdt5">{"defineEval"}</span><span className="gdt0">{"({"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"12"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" description: "}</span><span className="gdt2">{"\"测试 agent 在天气问题中正确调用 get_weather 并基于结果作答\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"13"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"14"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt4">{"async"}</span><span className="gdt0">{" "}</span><span className="gdt5">{"test"}</span><span className="gdt0">{"("}</span><span className="gdt7">{"t"}</span><span className="gdt0">{") {"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"15"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt4">{"const"}</span><span className="gdt0">{" "}</span><span className="gdt1">{"turn"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"="}</span><span className="gdt0">{" "}</span><span className="gdt4">{"await"}</span><span className="gdt0">{" t."}</span><span className="gdt5">{"send"}</span><span className="gdt0">{"("}</span><span className="gdt2">{"\"北京今天天气怎么样?\""}</span><span className="gdt0">{");"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"16"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt4">{"await"}</span><span className="gdt0">{" turn."}</span><span className="gdt5">{"succeeded"}</span><span className="gdt0">{"()."}</span><span className="gdt5">{"orStop"}</span><span className="gdt0">{"();"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"17"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"18"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt4">{"await"}</span><span className="gdt0">{" t."}</span><span className="gdt5">{"group"}</span><span className="gdt0">{"("}</span><span className="gdt2">{"\"调用 get_weather 且城市正确\""}</span><span className="gdt0">{", () "}</span><span className="gdt4">{"=>"}</span><span className="gdt0">{" {"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"19"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" t."}</span><span className="gdt5">{"calledTool"}</span><span className="gdt0">{"("}</span><span className="gdt5">{"toolMatch"}</span><span className="gdt0">{"("}</span><span className="gdt2">{"\"get_weather\""}</span><span className="gdt0">{", { input: "}</span><span className="gdt5">{"jsonMatch"}</span><span className="gdt0">{"({ city: "}</span><span className="gdt2">{"\"北京\""}</span><span className="gdt0">{" }) }));"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"20"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" t."}</span><span className="gdt5">{"check"}</span><span className="gdt0">{"(turn.message, "}</span><span className="gdt5">{"pattern"}</span><span className="gdt0">{"("}</span><span className="gdt2">{"/"}</span><span className="gdt8">{"°C"}</span><span className="gdt4">{"|"}</span><span className="gdt8">{"气温"}</span><span className="gdt4">{"|"}</span><span className="gdt8">{"天气"}</span><span className="gdt4">{"|"}</span><span className="gdt8">{"晴"}</span><span className="gdt4">{"|"}</span><span className="gdt8">{"多云"}</span><span className="gdt4">{"|"}</span><span className="gdt8">{"雨"}</span><span className="gdt4">{"|"}</span><span className="gdt8">{"阴"}</span><span className="gdt2">{"/"}</span><span className="gdt0">{"));"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"21"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" });"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"22"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"23"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt6">{"// 「是否走了工具」由上面的 t.calledTool 确定性把关;judge 只评回复本身的质量。"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"24"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" t."}</span><span className="gdt5">{"judge"}</span><span className="gdt0">{"({ prompt: turn.input, answer: turn.message }, answerQuality)."}</span><span className="gdt5">{"gate"}</span><span className="gdt0">{"("}</span><span className="gdt1">{"0.7"}</span><span className="gdt0">{");"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"25"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" },"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"26"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{"});"}</span></td></tr></tbody></table>
116
116
  </div>
117
117
  </div>
118
118
 
@@ -12,7 +12,7 @@ Judge 用独立裁判模型评估事实一致性、摘要忠实度和说明质
12
12
  在模块顶层写出标准。`rubric` 说明评价维度,`anchors` 给连续质量量尺提供可校准的落点:
13
13
 
14
14
  ```ts
15
- import { defineJudge } from "niceeval";
15
+ import { defineJudge } from "niceeval/expect";
16
16
 
17
17
  const explainsRisk = defineJudge({
18
18
  name: "explains-risk",
@@ -16,6 +16,24 @@ description: "像写单元测试一样写评估,像做实验一样比较 Agent
16
16
 
17
17
  所有运行都在你自己的机器和 CI 里完成,不需要注册账号。
18
18
 
19
+ ## 直接检查数值和比例
20
+
21
+ 在 `defineScoreEval` 的 `test(t)` 中,已计算好的 0 到 1 比例可以直接计分,不需要自定义 fraction matcher:
22
+
23
+ ```ts
24
+ import { atMost, isTrue, measured, quantile } from "niceeval/expect";
25
+
26
+ t.score(completionRate, { weight: 20 });
27
+ t.score(measured(optionalRate, { unavailable: "no-samples" }), { weight: 10 });
28
+ t.check(ready, isTrue()).gate();
29
+ t.check(measured(latencyMs, { unavailable: "no-actions" }), atMost(1200)).gate();
30
+ t.check(quantile(intervalsMs, 0.9), atMost(3000)).gate();
31
+ ```
32
+
33
+ `measured` 将 null 保留为带原因的不可判定材料,不会当成零。`t.score` 要求比例在 0 到 1 之间,不隐式截断;领域确需限定范围时请显式处理。
34
+ `quantile` 默认采用线性插值,也可传 `{ method: "nearest-rank" }`。结果的 `statistics` 提供样本数、分位点和算法;空样本返回 `no-samples`,无效样本返回 `non-finite-sample`。
35
+ 纯统计报告可以由 Adapter 用 `ctx.attach` 保存为 Markdown 或 JSON 附件,无需创建恒真 check。
36
+
19
37
  ## 从这里开始
20
38
 
21
39
  <CardGroup cols={3}>
@@ -26,7 +26,8 @@ NiceEval 用来回答这个问题。它以 Agent 为主要场景,也能评任
26
26
 
27
27
  ```ts
28
28
  // evals/weather-tool.eval.ts
29
- import { defineEval, defineJudge } from "niceeval";
29
+ import { defineEval } from "niceeval";
30
+ import { defineJudge } from "niceeval/expect";
30
31
  import { includes } from "niceeval/expect";
31
32
 
32
33
  const groundedAnswer = defineJudge({
@@ -144,7 +144,7 @@ readonly signal: AbortSignal;
144
144
  #### `models`
145
145
 
146
146
  ```ts
147
- readonly models: ResolvedModelSlots;
147
+ readonly models: Readonly<Record<Slots, ResolvedModelSlot>>;
148
148
  ```
149
149
 
150
150
  Frozen application model selections for this Attempt.
@@ -152,7 +152,7 @@ Frozen application model selections for this Attempt.
152
152
  #### `recordUsage`
153
153
 
154
154
  ```ts
155
- recordUsage(input: import("./adapter-usage.ts").AdapterUsageInput): void;
155
+ recordUsage(input: Omit<import("./adapter-usage.ts").AdapterUsageInput, "modelSlot"> & { readonly modelSlot?: Slots | null }): void;
156
156
  ```
157
157
 
158
158
  #### `sealUsage`
@@ -100,7 +100,8 @@ export default defineEval({
100
100
  “回答是否有依据”“语气是否适合小学生”这类问题,正则写不出来。把它交给裁判模型 Judge 打分:
101
101
 
102
102
  ```ts
103
- import { defineEval, defineJudge } from "niceeval";
103
+ import { defineEval } from "niceeval";
104
+ import { defineJudge } from "niceeval/expect";
104
105
 
105
106
  const grounded = defineJudge({
106
107
  name: "grounded-answer",
@@ -115,11 +115,9 @@ Headless Adapter 使用 `ctx.recordUsage` 登记真实调用,并在采集结
115
115
  ```ts
116
116
  import { atLeast } from "niceeval/expect";
117
117
 
118
- const planner = t.usage.cacheRead.modelSlots.find(group => group.modelSlot === "planner");
119
- t.check(
120
- planner?.rate ?? { state: "unavailable", reason: "model-slot-not-recorded" },
121
- atLeast(0.6),
122
- ).gate();
118
+ t.check(t.usage.cacheRead.rate("planner"), atLeast(0.6)).gate();
119
+ // 不区分用途时,检查所有已登记调用的合计。
120
+ t.check(t.usage.cacheRead.rate(), atLeast(0.6)).gate();
123
121
  ```
124
122
 
125
123
  读取率是同组调用的 **缓存读取 token 总量 / 含缓存输入 token 总量**,包括失败调用与重试。它不是逐次百分比的平均值。每次调用优先使用 `inputTotalTokens`;没有独立总量时,必须同时提供 `inputTokens`、`cacheReadTokens`、`cacheWriteTokens` 三个互斥桶。
@@ -129,3 +127,84 @@ t.check(
129
127
  缺失输入、未 seal、partial seal、unknown 调用状态、空组或零分母均返回 `unavailable`,`reasons` 列出原因。缺失不按零处理;完整且有正输入总量的零缓存读取率才是数值 0。旧账本没有明确的生产者完整性声明时,也返回不可判定。
130
128
 
131
129
  使用 `niceeval show <Attempt locator> --usage` 或 View 查看读取率、覆盖情况与原因;脚本通过 `attempt.usage` 的 `usage.cacheRead` 读取相同报告。已有 `recordUsage` 调用无需改名,但必须登记缓存 token,并在采集完成后诚实 seal。无需迁移历史结果文件。
130
+
131
+
132
+ 槽位名来自 Experiment 的 `models` 键,不是框架内置名单。`model` 单模型简写对应 `default`。
133
+ 传入未声明的槽位会在调用处报错,并列出可用名称。已声明但完整记录中没有调用的槽位返回 `slot-has-no-calls`;采集不完整时仍返回 `collection-incomplete`。
134
+ Eval 可以被多个 Experiment 复用,因此此入口在运行回调里校验槽位,不在定义期检查动态字符串。
135
+
136
+ ### 登记命令行 Agent 的用量
137
+
138
+ Adapter 应按外部回执记录真实调用,并将 `modelSlot` 设为 Experiment 声明的用途,例如 `mind`。
139
+ 同一请求的流式增量和最终回执不能各登记一次;先按请求 ID 合并,收到最终回执后再登记。
140
+
141
+ ```ts
142
+ // 单次 Claude 请求的最终 usage;字段缺失时保留 null。
143
+ ctx.recordUsage({
144
+ callId: requestId,
145
+ modelSlot: "mind",
146
+ provider: observedProvider,
147
+ model: observedModel,
148
+ status: observedStatus,
149
+ inputTokens: usage.input_tokens ?? null,
150
+ cacheReadTokens: usage.cache_read_input_tokens ?? null,
151
+ cacheWriteTokens: usage.cache_creation_input_tokens ?? null,
152
+ outputTokens: usage.output_tokens ?? null,
153
+ });
154
+ ```
155
+
156
+ Claude 的 `input_tokens` 是未命中缓存的输入桶,含缓存总量需加上读取和写入桶,见 [Claude 缓存用量说明](https://platform.claude.com/docs/en/build-with-claude/prompt-caching)。
157
+ Codex 的 usage 若提供含缓存的 `input_tokens`,应映射到 `inputTotalTokens`,`cached_input_tokens` 映射到 `cacheReadTokens`,不要再把缓存加进总量。
158
+ 不能仅因命令成功退出就认定全部模型调用都已被记录;需核对所用 CLI 版本输出的是逐请求、Turn 还是会话合计。
159
+
160
+ 只有会话总量时,不要把多次物理调用伪装成一次调用,也不要把合计与明细同时登记。
161
+ 当前 `recordUsage` 的单位是物理调用;将会话合计保存在统计附件中,并声明 `partial`,缓存判据会诚实地显示不可判定。
162
+ 完全没有 usage 时同样声明 `partial`,不要填零或声明完整空账本:
163
+
164
+ ```ts
165
+ ctx.sealUsage({ state: "partial", reason: "usage-not-exposed" });
166
+ // 只提供会话总量、不能证明逐调用完整性时:
167
+ // ctx.sealUsage({ state: "partial", reason: "session-totals-only" });
168
+ ```
169
+
170
+ 确实拿到全部物理调用的最终记录后,再调用 `ctx.sealUsage({ state: "complete" })`。
171
+
172
+ ## 让模型用途名称参与类型检查
173
+
174
+ 从 0.18 起,共享声明让同一评估可用于多个实验,同时在编译时检查用途名称:
175
+
176
+ ```ts title="models.ts"
177
+ import { defineModelSlots } from "niceeval";
178
+
179
+ export const slots = defineModelSlots({
180
+ mind: { model: "provider/planner" },
181
+ speech: { model: "provider/speaker" },
182
+ });
183
+ ```
184
+
185
+ 在已有 Adapter 的绑定工厂上声明要求。也可解构 `adapter.defineScoreEval`,但根入口的同名工厂不接受 `requires`:
186
+
187
+ ```ts title="evals/cache.eval.ts"
188
+ import { atLeast } from "niceeval/expect";
189
+ import { adapter } from "../adapter";
190
+ import { slots } from "../models";
191
+
192
+ export default adapter.defineScoreEval({
193
+ requires: { models: slots },
194
+ test(t) {
195
+ t.check(t.usage.cacheRead.rate("mind"), atLeast(0.6)).gate();
196
+ // rate("mimd") 会产生 TypeScript 错误;rate() 仍查询全部用途的合计。
197
+ },
198
+ });
199
+ ```
200
+
201
+ 实验使用 `defineExperiment({ adapter, models: slots, evals: ["cache"] })`。
202
+ 其它实验可为这些用途选择不同模型;`requires` 要求用途存在,不要求模型值相同。
203
+ 加载实验时会检查它选中的全部评估,CLI 的标签或前缀过滤不能隐藏缺失用途。
204
+
205
+ 给 `defineAdapter` 也传入 `requires: { models: slots }`,即可同时收窄 `ctx.models` 和 `ctx.recordUsage` 的 `modelSlot`。
206
+ `null` 或省略的 `modelSlot` 仍表示未归属到用途的调用。已声明但本次没有调用时,`rate` 返回原因 `slot-has-no-calls` 的不可用材料。
207
+
208
+ 0.17.x 中省略 `requires` 的写法在 0.18 继续兼容,名称仍在调用时校验。
209
+ 迁移时先提取共享声明,再给 Adapter 和绑定评估分别添加 `requires`。
210
+ 不要先把声明标注成 `Record<string, ModelSlotSelection>`,否则 TypeScript 已丢失具体键,无法检测拼写错误。
@@ -0,0 +1,64 @@
1
+ ---
2
+ title: "升级到 0.18"
3
+ description: "共享模型用途、原生数值材料与公开入口调整"
4
+ ---
5
+
6
+ 0.18 保留 0.17 的 `t.usage.cacheRead.rate()` 与按用途查询方式,并增加可复用的用途类型声明。
7
+ 在评估项目中升级依赖:
8
+
9
+ ```sh
10
+ pnpm add niceeval@^0.18.0
11
+ ```
12
+
13
+ ## 共享模型用途
14
+
15
+ 用 `defineModelSlots` 提取声明,实验的 `models` 与 Adapter 绑定评估的 `requires.models` 引用它。
16
+ 给 `defineAdapter` 同样添加 `requires.models` 后,`ctx.models` 与 `ctx.recordUsage` 的用途键也会被收窄。
17
+ 完整示例见[按用途检查缓存读取率](/zh/tutorials/control-run-cost#让模型用途名称参与类型检查)。
18
+
19
+ 0.18 继续接受省略 `requires` 的 0.17 写法,按调用时校验名称。
20
+ 根入口的 `defineEval`、`defineScoreEval` 属于 Agent 路径;共享声明只扩展 Adapter 绑定工厂。
21
+ 用途要求不会绑定具体实验或固定模型值。
22
+
23
+ ## 调整重复的导入入口
24
+
25
+ | 原来的入口 | 0.18 写法 |
26
+ | --- | --- |
27
+ | 根入口的 `defineJudge`、`defineScoreMatch`、`defineMaterialMatch`、`defineContextMatch` | 从 `niceeval/expect` 导入 |
28
+ | 根入口的 `factuality`、`faithfulness`、`instructionFollowing`、`pairwisePreference` | 从 `niceeval/expect` 导入 |
29
+ | 根入口的 `readScoreMatchAudit` | 从 `niceeval/inspection` 导入 |
30
+ | `niceeval/sandbox` 的 `resolveDockerProfileAlias` | 使用同入口的 `resolveDockerProfile` |
31
+ | 根入口的 `requireEnv`、`getEnv`、`stripComments` | 取消公开导出,由项目自行提供需要的工具 |
32
+
33
+ 例如,自定义评分判据改为:
34
+
35
+ ```ts
36
+ import { defineScoreEval } from "niceeval";
37
+ import { defineScoreMatch } from "niceeval/expect";
38
+ ```
39
+
40
+ 判据的执行语义不变,原有根类型导出与错误类保留。
41
+ 环境工具原先将空字符串当作缺失;改用 `process.env` 时请明确保留还是改变这一行为。
42
+ 文本 matcher 的 `stripComments` 选项仍可使用,不受同名通用函数移除影响。
43
+
44
+ ## 替换项目数值工具
45
+
46
+ ```ts
47
+ import { atMost, isTrue, measured, quantile } from "niceeval/expect";
48
+
49
+ // value 为 null 时保留原因,不会把缺失转换成 0。
50
+ t.check(measured(durationMs, { unavailable: "record-incomplete" }), atMost(3000)).gate();
51
+
52
+ // 默认 linear;也可显式选择 nearest-rank。
53
+ t.check(quantile(intervalsMs, 0.5), atMost(1200)).gate();
54
+ t.check(quantile(intervalsMs, 0.9, { method: "nearest-rank" }), atMost(3000)).gate();
55
+
56
+ // fraction 直接作为评分材料;无样本继续显示不可判定。
57
+ t.score(measured(successRate, { unavailable: "no-samples" }), { weight: 1 });
58
+ t.check(completed, isTrue()).gate();
59
+ ```
60
+
61
+ `quantile` 返回值的 `statistics` 包含 `sampleCount`、`probability` 与 `method`。
62
+ 空集合返回 `no-samples`,非有限样本返回 `non-finite-sample`;非法概率或算法参数直接报错。
63
+ `measured` 对非有限数值返回 `non-finite-number`。
64
+ `fraction` 必须位于 `[0, 1]`,`t.score` 不会替作者截断越界值。
@@ -68,7 +68,8 @@ export default defineEval({
68
68
 
69
69
  ```ts
70
70
  import { readFile } from "node:fs/promises";
71
- import { defineEval, defineJudge } from "niceeval";
71
+ import { defineEval } from "niceeval";
72
+ import { defineJudge } from "niceeval/expect";
72
73
  import { judgeImage } from "niceeval/judge";
73
74
 
74
75
  const actionQuality = defineJudge({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "niceeval",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Agent-native eval tool — eval agents, services, functions, and coding-agent fixtures",
5
5
  "type": "module",
6
6
  "license": "MIT",