niceeval 0.6.0 → 0.6.2
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.
- package/dist/agents/types.d.ts +72 -6
- package/dist/context/types.d.ts +32 -12
- package/dist/i18n/en.d.ts +54 -0
- package/dist/i18n/zh-CN.d.ts +55 -1
- package/dist/o11y/types.d.ts +16 -2
- package/dist/report/aggregate.d.ts +5 -3
- package/dist/report/aggregate.js +32 -5
- package/dist/report/built-ins/experiment-comparison.d.ts +39 -0
- package/dist/report/built-ins/experiment-comparison.js +119 -0
- package/dist/report/built-ins/index.d.ts +2 -1
- package/dist/report/built-ins/index.js +2 -2
- package/dist/report/components.d.ts +10 -2
- package/dist/report/components.js +3 -3
- package/dist/report/compute.d.ts +11 -18
- package/dist/report/compute.js +68 -66
- package/dist/report/flag.d.ts +16 -1
- package/dist/report/flag.js +19 -1
- package/dist/report/format.d.ts +16 -14
- package/dist/report/format.js +28 -30
- package/dist/report/index.d.ts +5 -4
- package/dist/report/index.js +6 -5
- package/dist/report/locale.d.ts +23 -3
- package/dist/report/locale.js +47 -6
- package/dist/report/metrics.d.ts +13 -1
- package/dist/report/metrics.js +66 -15
- package/dist/report/primitives.d.ts +6 -0
- package/dist/report/react/AttemptList.d.ts +4 -4
- package/dist/report/react/AttemptList.js +8 -10
- package/dist/report/react/EvalList.d.ts +1 -1
- package/dist/report/react/EvalList.js +0 -0
- package/dist/report/react/ExperimentComparison.d.ts +8 -0
- package/dist/report/react/ExperimentComparison.js +11 -0
- package/dist/report/react/ExperimentList.d.ts +4 -2
- package/dist/report/react/ExperimentList.js +57 -7
- package/dist/report/react/MetricScatter.js +6 -16
- package/dist/report/react/chart-math.d.ts +23 -6
- package/dist/report/react/chart-math.js +71 -19
- package/dist/report/react/fixtures.d.ts +3 -3
- package/dist/report/react/fixtures.js +30 -18
- package/dist/report/react/format.d.ts +1 -1
- package/dist/report/react/format.js +1 -1
- package/dist/report/react/index.d.ts +1 -1
- package/dist/report/report.d.ts +5 -1
- package/dist/report/report.js +6 -2
- package/dist/report/text/faces.d.ts +1 -1
- package/dist/report/text/faces.js +100 -61
- package/dist/report/text/table.js +36 -5
- package/dist/report/types.d.ts +40 -34
- package/dist/results/types.d.ts +11 -0
- package/dist/runner/feedback/sink.d.ts +110 -0
- package/dist/runner/types.d.ts +513 -22
- package/dist/sandbox/docker.d.ts +23 -2
- package/dist/sandbox/e2b.d.ts +15 -1
- package/dist/sandbox/errors.d.ts +30 -3
- package/dist/sandbox/io-retry.d.ts +17 -0
- package/dist/sandbox/registry.d.ts +2 -0
- package/dist/sandbox/resolve.d.ts +18 -5
- package/dist/sandbox/retry.d.ts +11 -1
- package/dist/sandbox/types.d.ts +39 -5
- package/dist/sandbox/vercel.d.ts +7 -1
- package/dist/scoring/coverage.d.ts +30 -0
- package/dist/scoring/display.d.ts +21 -0
- package/dist/scoring/display.js +120 -0
- package/dist/scoring/types.d.ts +103 -20
- package/dist/shared/aggregate.d.ts +1 -0
- package/dist/shared/aggregate.js +3 -3
- package/dist/shared/types.d.ts +28 -0
- package/dist/tty-line.d.ts +0 -4
- package/dist/util.d.ts +23 -0
- package/docs-site/zh/concepts/adapter.mdx +24 -6
- package/docs-site/zh/concepts/assert.mdx +11 -10
- package/docs-site/zh/concepts/evals.mdx +7 -6
- package/docs-site/zh/concepts/experiment.mdx +1 -1
- package/docs-site/zh/concepts/overview.mdx +7 -7
- package/docs-site/zh/guides/agent-feedback-loop.mdx +35 -31
- package/docs-site/zh/guides/authoring.mdx +33 -0
- package/docs-site/zh/guides/ci-integration.mdx +23 -12
- package/docs-site/zh/guides/connect-your-agent.mdx +29 -3
- package/docs-site/zh/guides/custom-reports.mdx +29 -34
- package/docs-site/zh/guides/dataset-fanout.mdx +25 -3
- package/docs-site/zh/guides/debug-sandbox.mdx +57 -0
- package/docs-site/zh/guides/debugging.mdx +210 -0
- package/docs-site/zh/guides/experiments.mdx +10 -3
- package/docs-site/zh/guides/fixtures.mdx +3 -1
- package/docs-site/zh/guides/official-adapters.mdx +27 -3
- package/docs-site/zh/guides/publish-report.mdx +30 -16
- package/docs-site/zh/guides/report-components.mdx +49 -37
- package/docs-site/zh/guides/reporters.mdx +2 -2
- package/docs-site/zh/guides/results-data.mdx +42 -8
- package/docs-site/zh/guides/runner.mdx +17 -7
- package/docs-site/zh/guides/sandbox-agent.mdx +57 -7
- package/docs-site/zh/guides/sandbox-providers.mdx +258 -10
- package/docs-site/zh/guides/scoring-guide.mdx +4 -4
- package/docs-site/zh/guides/viewing-results.mdx +85 -41
- package/docs-site/zh/guides/write-experiment.mdx +5 -3
- package/docs-site/zh/guides/write-send.mdx +19 -2
- package/docs-site/zh/index.mdx +1 -1
- package/docs-site/zh/reference/builtin-agents.mdx +27 -0
- package/docs-site/zh/reference/capabilities.mdx +2 -2
- package/docs-site/zh/reference/cli.mdx +35 -9
- package/docs-site/zh/reference/define-agent.mdx +60 -5
- package/docs-site/zh/reference/define-config.mdx +1 -1
- package/docs-site/zh/reference/define-eval.mdx +42 -9
- package/docs-site/zh/reference/events.mdx +2 -2
- package/docs-site/zh/reference/expect.mdx +36 -6
- package/package.json +5 -1
- package/src/agents/ai-sdk-otel.test.ts +1 -0
- package/src/agents/ai-sdk.test.ts +3 -0
- package/src/agents/ai-sdk.ts +3 -0
- package/src/agents/bub-install-spec.test.ts +34 -0
- package/src/agents/bub-install-spec.ts +32 -0
- package/src/agents/bub.ts +31 -32
- package/src/agents/claude-code.test.ts +130 -9
- package/src/agents/claude-code.ts +76 -4
- package/src/agents/codex.test.ts +189 -40
- package/src/agents/codex.ts +155 -14
- package/src/agents/coding-cli-versions.test.ts +15 -0
- package/src/agents/coding-cli-versions.ts +3 -0
- package/src/agents/index.ts +11 -0
- package/src/agents/langgraph.test.ts +204 -0
- package/src/agents/langgraph.ts +495 -0
- package/src/agents/marketplace.ts +85 -0
- package/src/agents/native-config.test.ts +179 -0
- package/src/agents/native-config.ts +267 -0
- package/src/agents/openai-compat.test.ts +1 -0
- package/src/agents/openclaw.test.ts +31 -0
- package/src/agents/openclaw.ts +171 -0
- package/src/agents/plugin-config.test.ts +1 -0
- package/src/agents/sdk-streams.test.ts +79 -0
- package/src/agents/sdk-streams.ts +55 -10
- package/src/agents/skills.test.ts +1 -0
- package/src/agents/streaming.test.ts +3 -9
- package/src/agents/types.ts +73 -6
- package/src/agents/ui-message-stream.test.ts +3 -0
- package/src/cli.ts +411 -108
- package/src/context/context.test.ts +51 -12
- package/src/context/context.ts +161 -29
- package/src/context/session.test.ts +1 -0
- package/src/context/session.ts +114 -6
- package/src/context/types.ts +30 -12
- package/src/define.test.ts +13 -8
- package/src/define.ts +25 -4
- package/src/expect/index.ts +53 -23
- package/src/i18n/en.ts +65 -4
- package/src/i18n/zh-CN.ts +66 -4
- package/src/o11y/cost.test.ts +1 -0
- package/src/o11y/execution-tree.test.ts +1 -20
- package/src/o11y/otlp/mappers/claude-code.test.ts +1 -0
- package/src/o11y/otlp/parse.test.ts +1 -0
- package/src/o11y/otlp/turn-otel.test.ts +1 -0
- package/src/o11y/parsers/bub.test.ts +1 -0
- package/src/o11y/parsers/claude-code.test.ts +1 -34
- package/src/o11y/parsers/openclaw.test.ts +154 -0
- package/src/o11y/parsers/openclaw.ts +310 -0
- package/src/o11y/prices.json +746 -311
- package/src/o11y/tool-names.test.ts +1 -0
- package/src/o11y/types.ts +16 -2
- package/src/report/aggregate.ts +34 -5
- package/src/report/built-in-user-parity.test.tsx +127 -173
- package/src/report/built-ins/experiment-comparison.tsx +179 -0
- package/src/report/built-ins/index.ts +7 -2
- package/src/report/components.tsx +11 -3
- package/src/report/compute.ts +80 -74
- package/src/report/dual-render.test.tsx +222 -91
- package/src/report/flag.ts +30 -2
- package/src/report/format.ts +36 -27
- package/src/report/index.ts +23 -6
- package/src/report/locale.ts +49 -6
- package/src/report/metrics.ts +68 -15
- package/src/report/primitives.tsx +6 -0
- package/src/report/react/AttemptList.tsx +9 -36
- package/src/report/react/EvalList.tsx +0 -0
- package/src/report/react/ExperimentComparison.tsx +68 -0
- package/src/report/react/ExperimentList.tsx +173 -55
- package/src/report/react/MetricScatter.tsx +13 -25
- package/src/report/react/chart-math.test.ts +85 -0
- package/src/report/react/chart-math.ts +101 -22
- package/src/report/react/enhance.js +72 -1
- package/src/report/react/fixtures.ts +34 -21
- package/src/report/react/format.ts +1 -1
- package/src/report/react/index.tsx +0 -1
- package/src/report/react/render.test.tsx +30 -69
- package/src/report/react/styles.css +112 -14
- package/src/report/report.test.ts +308 -105
- package/src/report/report.ts +6 -2
- package/src/report/text/faces.ts +111 -67
- package/src/report/text/table.ts +42 -5
- package/src/report/types.ts +42 -34
- package/src/results/annotated-source.test.ts +62 -9
- package/src/results/annotated-source.ts +64 -6
- package/src/results/attempt-evidence.test.ts +9 -7
- package/src/results/attempt-evidence.ts +15 -8
- package/src/results/attempt-source.ts +6 -3
- package/src/results/copy.ts +145 -55
- package/src/results/host-equivalence.test.ts +11 -9
- package/src/results/index.ts +2 -0
- package/src/results/locator.test.ts +1 -22
- package/src/results/open.ts +7 -1
- package/src/results/publish.ts +149 -0
- package/src/results/results.test.ts +85 -51
- package/src/results/truncate.ts +90 -0
- package/src/results/types.ts +7 -0
- package/src/results/writer.ts +31 -13
- package/src/runner/attempt.test.ts +138 -7
- package/src/runner/attempt.ts +603 -104
- package/src/runner/discover.test.ts +47 -0
- package/src/runner/discover.ts +36 -2
- package/src/runner/eval-source.test.ts +1 -27
- package/src/runner/feedback/agent.test.ts +504 -0
- package/src/runner/feedback/agent.ts +409 -0
- package/src/runner/feedback/ci.test.ts +562 -0
- package/src/runner/feedback/ci.ts +401 -0
- package/src/runner/feedback/coordinator.test.ts +317 -0
- package/src/runner/feedback/coordinator.ts +397 -0
- package/src/runner/feedback/failure.ts +40 -0
- package/src/runner/feedback/human.test.ts +616 -0
- package/src/runner/feedback/human.ts +535 -0
- package/src/runner/feedback/index.ts +66 -0
- package/src/runner/feedback/io.ts +78 -0
- package/src/runner/feedback/profile.test.ts +50 -0
- package/src/runner/feedback/profile.ts +58 -0
- package/src/runner/feedback/reducer.test.ts +395 -0
- package/src/runner/feedback/reducer.ts +260 -0
- package/src/runner/feedback/renderer.ts +82 -0
- package/src/runner/feedback/sink.ts +203 -0
- package/src/runner/feedback/testing.ts +106 -0
- package/src/runner/ledger.test.ts +230 -0
- package/src/runner/ledger.ts +329 -0
- package/src/runner/report.test.ts +128 -3
- package/src/runner/report.ts +33 -9
- package/src/runner/reporters/artifacts.ts +8 -2
- package/src/runner/reporters/braintrust.test.ts +8 -7
- package/src/runner/reporters/braintrust.ts +9 -2
- package/src/runner/reporters/index.ts +2 -2
- package/src/runner/reporters/json.test.ts +162 -0
- package/src/runner/reporters/json.ts +35 -8
- package/src/runner/reporters/shared.ts +1 -5
- package/src/runner/run.test.ts +760 -3
- package/src/runner/run.ts +242 -36
- package/src/runner/sandbox-prep.ts +3 -42
- package/src/runner/timing.ts +158 -0
- package/src/runner/types.ts +518 -22
- package/src/sandbox/checkpoint.test.ts +55 -0
- package/src/sandbox/checkpoint.ts +29 -8
- package/src/sandbox/cli-commands.ts +407 -0
- package/src/sandbox/docker.ts +115 -16
- package/src/sandbox/e2b-agent-template.test.ts +56 -0
- package/src/sandbox/e2b-agent-template.ts +94 -0
- package/src/sandbox/e2b.ts +74 -9
- package/src/sandbox/errors.ts +111 -4
- package/src/sandbox/index.ts +2 -0
- package/src/sandbox/io-retry.test.ts +58 -0
- package/src/sandbox/io-retry.ts +45 -0
- package/src/sandbox/keep-registry.test.ts +86 -0
- package/src/sandbox/keep-registry.ts +142 -0
- package/src/sandbox/keep.ts +178 -0
- package/src/sandbox/paths.test.ts +1 -0
- package/src/sandbox/paths.ts +19 -8
- package/src/sandbox/registry.ts +20 -3
- package/src/sandbox/resolve.ts +76 -11
- package/src/sandbox/retry.test.ts +70 -0
- package/src/sandbox/retry.ts +46 -4
- package/src/sandbox/types.ts +44 -6
- package/src/sandbox/vercel.ts +43 -20
- package/src/scoring/collector.ts +60 -17
- package/src/scoring/coverage.ts +95 -0
- package/src/scoring/diff.ts +81 -0
- package/src/scoring/display.test.ts +121 -0
- package/src/scoring/display.ts +133 -0
- package/src/scoring/evidence.test.ts +189 -0
- package/src/scoring/judge.test.ts +142 -0
- package/src/scoring/judge.ts +15 -18
- package/src/scoring/scoped.ts +217 -50
- package/src/scoring/types.ts +117 -20
- package/src/scoring/verdict.ts +16 -4
- package/src/shared/aggregate.ts +3 -2
- package/src/shared/types.ts +31 -0
- package/src/show/compose.ts +2 -2
- package/src/show/index.ts +29 -16
- package/src/show/render.ts +626 -308
- package/src/show/show.test.ts +251 -36
- package/src/tty-line.ts +8 -26
- package/src/util.test.ts +1 -0
- package/src/util.ts +41 -0
- package/src/view/app/components/AttemptModal.tsx +153 -2
- package/src/view/app/components/CodeView.tsx +32 -11
- package/src/view/app/components/CopyControls.tsx +2 -2
- package/src/view/app/i18n.ts +6 -0
- package/src/view/app/lib/attempt-route.test.ts +1 -0
- package/src/view/app/lib/verdict.ts +7 -9
- package/src/view/artifact-serving.test.ts +2 -1
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +17 -17
- package/src/view/data.test.ts +2 -1
- package/src/view/data.ts +17 -7
- package/src/view/index.ts +12 -1
- package/src/view/server.ts +2 -0
- package/src/view/shared/types.ts +1 -1
- package/src/view/styles.css +3 -0
- package/src/view/view-report.test.ts +11 -10
- package/dist/o11y/execution-tree.d.ts +0 -103
- package/dist/o11y/otlp/select.d.ts +0 -22
- package/dist/report/built-ins/cost-pass-rate-comparison.d.ts +0 -1
- package/dist/report/built-ins/cost-pass-rate-comparison.js +0 -17
- package/dist/results/annotated-source.d.ts +0 -61
- package/dist/results/attempt-evidence.d.ts +0 -69
- package/dist/results/attempt-source.d.ts +0 -15
- package/src/report/built-ins/cost-pass-rate-comparison.tsx +0 -23
- package/src/runner/reporters/console.ts +0 -70
- package/src/runner/reporters/live.test.ts +0 -56
- package/src/runner/reporters/live.ts +0 -247
- package/src/runner/reporters/quiet.test.ts +0 -66
- package/src/runner/reporters/quiet.ts +0 -49
- package/src/runner/reporters/table.ts +0 -277
|
@@ -38,7 +38,7 @@ AI 通常按任务选择这些入口:
|
|
|
38
38
|
<Steps>
|
|
39
39
|
<Step title="运行实验">
|
|
40
40
|
```bash
|
|
41
|
-
npx niceeval exp local
|
|
41
|
+
npx niceeval exp local --output agent
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
先读取退出码:`0` 表示所有 Eval 通过;`1` 表示至少一个 Eval 失败或出错;`2` 表示 NiceEval 自身未能完成运行。退出码决定是否继续,控制台文本用于定位原因。
|
|
@@ -49,7 +49,7 @@ AI 通常按任务选择这些入口:
|
|
|
49
49
|
npx niceeval show @1k2m9qtr
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
第一条命令显示当前各实验的通过率、成本、耗时,以及每个 Eval 的紧凑 Attempt locator
|
|
52
|
+
第一条命令显示当前各实验的通过率、成本、耗时,以及每个 Eval 的紧凑 Attempt locator——locator 本身就是证据入口,不在列表里编码证据可用性。第二条命令直接打开选中的 Attempt,页面末尾的 `available:` 只列出这个 Attempt 实际可用的证据命令。
|
|
53
53
|
</Step>
|
|
54
54
|
<Step title="按问题读取证据">
|
|
55
55
|
```bash
|
|
@@ -59,7 +59,7 @@ AI 通常按任务选择这些入口:
|
|
|
59
59
|
|
|
60
60
|
`--execution` 合并 AI 输出与 trace:标准事件流提供消息、thinking、tool call/result 和 Skill load;OTel 在能够关联时给同一节点补开始时间、耗时、父子关系和错误状态。没有 OTel 时步骤仍完整,只不显示时间。
|
|
61
61
|
|
|
62
|
-
不带证据 flag 时,`show @<id>` 是失败诊断首页。它先列出失败断言的 group、matcher、expected、received
|
|
62
|
+
不带证据 flag 时,`show @<id>` 是失败诊断首页。它先列出失败断言的 group、matcher、expected、received、原因和源码位置,再给执行、生命周期阶段耗时与文件变化摘要。AI 应该先读这一页;只有需要回答“为什么产生这个值”时,才继续打开对应证据。
|
|
63
63
|
|
|
64
64
|
```text
|
|
65
65
|
$ niceeval show @1k2m9qtr
|
|
@@ -68,7 +68,7 @@ AI 通常按任务选择这些入口:
|
|
|
68
68
|
eval source: evals/weather/brooklyn.eval.ts · sha256:8d47…
|
|
69
69
|
|
|
70
70
|
8 export default defineEval({
|
|
71
|
-
9
|
|
71
|
+
9 description: "Brooklyn weather query",
|
|
72
72
|
10 async test(t) {
|
|
73
73
|
11 const turn = await t.send("布鲁克林今天天气怎么样?");
|
|
74
74
|
12 ✗ turn.calledTool("get_weather");
|
|
@@ -88,19 +88,17 @@ AI 通常按任务选择这些入口:
|
|
|
88
88
|
received: 1
|
|
89
89
|
source: evals/memory/swelancer-manager-proposals.eval.ts:40:11
|
|
90
90
|
|
|
91
|
-
execution
|
|
92
|
-
|
|
93
|
-
├─ inference ▕█████░░░░░░░░░░░░░░░▏ 10.1s · "布鲁克林今天大约 24°C,晴。"
|
|
94
|
-
├─ inference ▕░░░░░█████████░░░░░░▏ 18.3s
|
|
95
|
-
└─ inference ▕░░░░░░░░░░░░░░██████▏ 12.4s
|
|
96
|
-
no tool calls
|
|
91
|
+
execution: 3 events · 0 skill loads · 0 tool calls · 1 AI messages
|
|
92
|
+
timing: eval.run 40.4s · scoring.evaluate 0.5s · teardown +0.2s
|
|
97
93
|
|
|
98
94
|
changes · diff unavailable
|
|
99
95
|
reason: this Attempt did not produce workspace file changes
|
|
100
96
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
97
|
+
full eval source: …/weather/brooklyn/a2/sources.json
|
|
98
|
+
available:
|
|
99
|
+
niceeval show @1k2m9qtr --eval
|
|
100
|
+
niceeval show @1k2m9qtr --execution
|
|
101
|
+
niceeval show @1k2m9qtr --timing
|
|
104
102
|
```
|
|
105
103
|
|
|
106
104
|
`--execution` 把 AI 消息、Skill load、工具调用和工具结果排成一棵执行树。它只展示 Agent 可理解的事件;没有关联到这些事件的 SDK / runtime span 不逐行输出,只报告省略数量并保留 `trace.json` 路径。下面的 Attempt 有 OTel,所以能关联的节点同时带相对时间与耗时:
|
|
@@ -155,7 +153,7 @@ AI 通常按任务选择这些入口:
|
|
|
155
153
|
full events: …/weather/brooklyn/a2/events.json
|
|
156
154
|
```
|
|
157
155
|
|
|
158
|
-
diff 是被测 Agent 在 Sandbox 工作区造成的文件变化,不是 Eval
|
|
156
|
+
diff 是被测 Agent 在 Sandbox 工作区造成的文件变化,不是 Eval 源码的新旧差异。只有 sandbox eval 才会收集到 diff——非 sandbox eval 或 agent 确实没碰任何文件时,attempt 页的 `available:` 列表会省略 `--diff`。默认先给文件级摘要,避免把大段补丁塞进 Agent 上下文;`--diff=<文件>` 再展开单个文件,原始 artifact 路径始终保留:
|
|
159
157
|
|
|
160
158
|
```text
|
|
161
159
|
$ niceeval show @1c3h6tbn --diff
|
|
@@ -173,9 +171,10 @@ AI 通常按任务选择这些入口:
|
|
|
173
171
|
|
|
174
172
|
| 要回答的问题 | 入口 | 输出必须包含 |
|
|
175
173
|
| --- | --- | --- |
|
|
176
|
-
| 快速判断一次 Attempt 发生了什么 | `niceeval show @<id>` | Eval
|
|
174
|
+
| 快速判断一次 Attempt 发生了什么 | `niceeval show @<id>` | Eval 断言、执行步骤、生命周期阶段耗时摘要、diff 摘要及各块可用性 |
|
|
177
175
|
| Eval 实际检查了什么,哪条 gate / soft 为什么通过或失败 | `niceeval show @<id> --eval` | 运行时 Eval 源码、源码哈希、断言所在行、严重度、分数与原因 |
|
|
178
|
-
| AI
|
|
176
|
+
| AI 做了什么、调用了什么 | `niceeval show @<id> --execution` | 消息、thinking、Skill load、工具调用与结果;有 OTel 时在同一节点附时间、父子关系和错误状态 |
|
|
177
|
+
| 整个 Attempt 的时间花在哪里 | `niceeval show @<id> --timing` | lifecycle → hook/turn → shell → OTel 的统一时间树;出错的 Attempt 标出已知的最深失败节点 |
|
|
179
178
|
| Sandbox 工作区文件变成什么 | `niceeval show @<id> --diff` | 文件摘要、增删行数、具体补丁和原始 diff 路径;无文件工作区时明确 unavailable |
|
|
180
179
|
</Step>
|
|
181
180
|
<Step title="提出假设并修改">
|
|
@@ -193,7 +192,7 @@ AI 通常按任务选择这些入口:
|
|
|
193
192
|
</Step>
|
|
194
193
|
<Step title="局部重跑并验证假设">
|
|
195
194
|
```bash
|
|
196
|
-
npx niceeval exp local weather/brooklyn --force
|
|
195
|
+
npx niceeval exp local weather/brooklyn --output agent --force
|
|
197
196
|
npx niceeval show weather/brooklyn
|
|
198
197
|
```
|
|
199
198
|
|
|
@@ -201,7 +200,7 @@ AI 通常按任务选择这些入口:
|
|
|
201
200
|
</Step>
|
|
202
201
|
<Step title="全量确认没有回归">
|
|
203
202
|
```bash
|
|
204
|
-
npx niceeval exp local --force
|
|
203
|
+
npx niceeval exp local --output agent --force
|
|
205
204
|
npx niceeval show
|
|
206
205
|
```
|
|
207
206
|
|
|
@@ -211,19 +210,22 @@ AI 通常按任务选择这些入口:
|
|
|
211
210
|
|
|
212
211
|
## AI 应该从输出里读什么
|
|
213
212
|
|
|
214
|
-
`
|
|
213
|
+
`--output agent` 运行中只向 stderr 追加低频 checkpoint(存活信号,不是结果数据源),结束时向 stdout 打印一个有界 handoff block——这才是 AI 应该解析的部分:
|
|
215
214
|
|
|
216
215
|
```text
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
216
|
+
NICEEVAL RESULT failed
|
|
217
|
+
summary: 14 passed, 1 failed, 0 errored (0 reused)
|
|
218
|
+
snapshots:
|
|
219
|
+
- .niceeval/local/2026-07-09T10-00-00-000Z-x1f2/
|
|
220
|
+
failures:
|
|
221
|
+
- @1k2m9qtr weather/brooklyn [local]
|
|
222
|
+
gate: tool was never called
|
|
223
|
+
next:
|
|
224
|
+
niceeval show @1k2m9qtr
|
|
225
|
+
niceeval show @1k2m9qtr --execution
|
|
224
226
|
```
|
|
225
227
|
|
|
226
|
-
AI
|
|
228
|
+
AI 应先从 `failures` 选中 Attempt locator,再按证据位执行 `next` 给出的 `niceeval show @<id>` 或对应证据 flag,不要解析运行期间 stderr 上低频追加的 checkpoint 行——那些只用于判断进程是否存活。失败条数超过上限(默认 5 条)时,handoff 只展开前几条并给出总数,完整清单读结果快照。需要机器读取时,结果快照是事实来源:
|
|
227
229
|
|
|
228
230
|
```text
|
|
229
231
|
.niceeval/<experiment>/<快照>/
|
|
@@ -236,7 +238,7 @@ AI 应先从失败项选中 Attempt locator,再按证据位执行 `niceeval sh
|
|
|
236
238
|
```
|
|
237
239
|
|
|
238
240
|
- `snapshot.json` 记录实验身份、运行配置、格式版本和时间。
|
|
239
|
-
- `result.json` 记录该 Attempt
|
|
241
|
+
- `result.json` 记录该 Attempt 的判定、断言、结构化错误、diagnostics 和用量;瞬时 progress 不落盘。
|
|
240
242
|
- `events.json` 是对话与工具调用事件,`trace.json` 是调用链,`diff.json` 是 Sandbox 文件变化。
|
|
241
243
|
- 某类证据不存在时,对应文件不会生成。先以 `show` 的提示为准,不要假设每个目录都有全部文件。
|
|
242
244
|
|
|
@@ -266,12 +268,14 @@ AI 应先从失败项选中 Attempt locator,再按证据位执行 `niceeval sh
|
|
|
266
268
|
|
|
267
269
|
```text
|
|
268
270
|
读取 node_modules/niceeval/INDEX.md,再按索引读取与任务有关的文档。
|
|
269
|
-
运行 npx niceeval exp local
|
|
271
|
+
运行 npx niceeval exp local --output agent,并根据退出码和失败 locator 决定下一步。
|
|
270
272
|
对每个失败的 Eval,从报告选择一个 Attempt locator;再运行 niceeval show @<id>
|
|
271
|
-
|
|
273
|
+
并按问题选择 --eval、--execution、--timing 或 --diff;--timing 从 lifecycle 展开 setup/teardown
|
|
274
|
+
hook、shell 命令、每轮 send 与可关联的 OTel model/tool,回答整个 Attempt 的时间花在哪里。
|
|
275
|
+
写出失败原因的假设,并判断应该修改被测程序、
|
|
272
276
|
Eval,还是实验环境。修改后用 --force 重跑对应 Eval,比较新的判定和证据。
|
|
273
277
|
同一问题连续三轮没有新证据或改善时停止并汇报,不要靠放宽断言碰绿。
|
|
274
|
-
全部局部失败清零后,用 npx niceeval exp local --force 全量验证;退出码 0 才完成。
|
|
278
|
+
全部局部失败清零后,用 npx niceeval exp local --output agent --force 全量验证;退出码 0 才完成。
|
|
275
279
|
```
|
|
276
280
|
|
|
277
281
|
真实 Agent 的运行可能产生费用。实验阶段可以加 `--budget <美元>` 限制本轮累计成本;预算只能限制单次命令,不能替代上面的停止条件。
|
|
@@ -18,6 +18,7 @@ export default defineEval({
|
|
|
18
18
|
reporters?: Reporter[];
|
|
19
19
|
timeoutMs?: number;
|
|
20
20
|
metadata?: Record<string, unknown>;
|
|
21
|
+
async setup(sandbox, ctx) { /* task fixture + progress/diagnostic */ },
|
|
21
22
|
async test(t) { /* interactions + assertions */ },
|
|
22
23
|
});
|
|
23
24
|
```
|
|
@@ -111,6 +112,38 @@ export default defineEval({
|
|
|
111
112
|
|
|
112
113
|
详见 [Fixtures](/zh/guides/fixtures)。
|
|
113
114
|
|
|
115
|
+
## 从 Eval 报告长步骤和诊断
|
|
116
|
+
|
|
117
|
+
`setup` 用于这条 Eval 的任务夹具。第二个参数绑定到 eval setup 阶段;`test(t)` 里的反馈绑定到 eval run 阶段:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
export default defineEval({
|
|
121
|
+
async setup(sandbox, ctx) {
|
|
122
|
+
ctx.progress({ message: "安装 fixture 依赖" });
|
|
123
|
+
await sandbox.runCommand("npm", ["install"]);
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
async test(t) {
|
|
127
|
+
t.progress({ message: "上传隐藏测试", current: 1, total: 2 });
|
|
128
|
+
await t.sandbox.uploadDirectory("../fixtures/project");
|
|
129
|
+
|
|
130
|
+
const preflight = await inspectFixture();
|
|
131
|
+
if (preflight.usedFallback) {
|
|
132
|
+
t.diagnostic({
|
|
133
|
+
code: "fixture-check-degraded",
|
|
134
|
+
level: "warning",
|
|
135
|
+
message: "Fixture 预检使用了备用检查器",
|
|
136
|
+
data: { checker: preflight.checker },
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
await t.send("完成任务");
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`progress` 只更新运行中的短期状态,不进入结果。`diagnostic` 会写进当前 Attempt 的 `result.json`,但不会代替断言或自动改变判定:业务结论仍用 `t.check` / `t.require` / gate;基础设施无法继续时抛出异常。
|
|
146
|
+
|
|
114
147
|
## 命名约定
|
|
115
148
|
|
|
116
149
|
<CardGroup cols={2}>
|
|
@@ -11,9 +11,15 @@ Evals 应该和测试一样进入 CI。它们能在 PR 阶段发现 agent 行为
|
|
|
11
11
|
默认情况下,只要存在失败的 gate,[NiceEval](https://niceeval.com/) 将以非零状态码退出。CI 中通常使用 `--strict`,让失败更明确。
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
npx niceeval exp ci
|
|
14
|
+
NICEEVAL_LANG=en npx niceeval exp ci \
|
|
15
|
+
--output ci \
|
|
16
|
+
--strict \
|
|
17
|
+
--json .niceeval/ci-summary.json \
|
|
18
|
+
--junit .niceeval/junit.xml
|
|
15
19
|
```
|
|
16
20
|
|
|
21
|
+
CI profile 不输出 ANSI、spinner 或动态表格。日志使用单一有序 stdout 流,只追加 start、低频 heartbeat、失败/错误、diagnostic 和最终 result;通过的 Attempt 不逐条打印。
|
|
22
|
+
|
|
17
23
|
## GitHub Actions 示例
|
|
18
24
|
|
|
19
25
|
```yaml
|
|
@@ -32,8 +38,12 @@ jobs:
|
|
|
32
38
|
node-version: 22
|
|
33
39
|
cache: npm
|
|
34
40
|
- run: npm ci
|
|
35
|
-
- run:
|
|
41
|
+
- run: >-
|
|
42
|
+
npx niceeval exp ci --output ci --strict
|
|
43
|
+
--json .niceeval/ci-summary.json
|
|
44
|
+
--junit .niceeval/junit.xml
|
|
36
45
|
env:
|
|
46
|
+
NICEEVAL_LANG: en
|
|
37
47
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
38
48
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
39
49
|
```
|
|
@@ -52,18 +62,19 @@ jobs:
|
|
|
52
62
|
</Step>
|
|
53
63
|
</Steps>
|
|
54
64
|
|
|
55
|
-
## JUnit
|
|
56
|
-
|
|
57
|
-
```ts
|
|
58
|
-
import { defineConfig } from "niceeval";
|
|
59
|
-
import { Console, JUnit } from "niceeval/reporters";
|
|
65
|
+
## JSON、JUnit 和结构化错误
|
|
60
66
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
```text
|
|
68
|
+
niceeval: start total=24 configs=3 concurrency=10 reused=18
|
|
69
|
+
niceeval: errored locator=@12h8m4k1 eval=fixtures/button experiment=ci/codex phase=sandbox.create reason="E2B sandbox allocation failed after 5 attempts"
|
|
70
|
+
niceeval: result=failed passed=23 failed=0 errored=1 reused=18 duration=128s
|
|
71
|
+
niceeval: json=.niceeval/ci-summary.json
|
|
72
|
+
niceeval: junit=.niceeval/junit.xml
|
|
64
73
|
```
|
|
65
74
|
|
|
66
|
-
|
|
75
|
+
退出码是第一层红绿信号;JSON、JUnit 和结果快照是完整机器接口,日志行只用于搜索和 annotation。`errored` 行带 locator、eval/experiment 身份、已知时的正式 phase,以及一层 `reason` 摘要。详细 cause、stack 和 diagnostics 保存在 Attempt 的 `result.json`,可在保留 artifact 后运行 `niceeval show @<locator>` 回顾。
|
|
76
|
+
|
|
77
|
+
CLI 显式要求的 JSON/JUnit 和默认 results artifact 都是 required 输出:写入失败必须让 job 判红,不能只留 warning 后退出 0。想把结果同时上报到 Braintrust 这类实验平台,见 [Reporter 上报](./reporters)。
|
|
67
78
|
|
|
68
79
|
## 只检查发现
|
|
69
80
|
|
|
@@ -80,7 +91,7 @@ npx niceeval list
|
|
|
80
91
|
## 控制并发
|
|
81
92
|
|
|
82
93
|
```bash
|
|
83
|
-
npx niceeval exp ci --max-concurrency 2
|
|
94
|
+
npx niceeval exp ci --output ci --max-concurrency 2
|
|
84
95
|
```
|
|
85
96
|
|
|
86
97
|
标准 GitHub-hosted runner 上,sandbox eval 并发不宜过高。远程 HTTP eval 可以按服务限流能力调高。
|
|
@@ -101,12 +101,12 @@ npx niceeval exp my-bot refund # 只跑 ID 以 refund 开头的
|
|
|
101
101
|
npx niceeval view # 本地查看器里看结果
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
**跑通的样子**:人在终端运行时会看到一个动态 dashboard,完成和排队数量只在原位更新;失败、错误和 warning 会永久留在上方。结束时只打印摘要、失败 locator 和结果路径。`npx niceeval view` 里能看到每条 Eval 逐轮的输入、事件和评分明细。
|
|
105
105
|
|
|
106
106
|
没跑通时,按报错的位置分三类排查:
|
|
107
107
|
|
|
108
108
|
- **`fetch` 直接抛错**(连接被拒等):应用没起来,或 `send` 里的 URL 不对——先用 `curl` 对那个接口发一次同样的请求确认。
|
|
109
|
-
- **`t.succeeded()` 没过、本轮判定是 failed
|
|
109
|
+
- **`t.succeeded()` 没过、本轮判定是 failed**:请求发出去了,但应用返回的 Turn 是 `failed`。把协议中的失败映射到 `Turn.status` 或标准 `error` event;需要额外保留的有限上下文用 `ctx.diagnostic(...)`,不要打印完整响应体。
|
|
110
110
|
- **只有内容断言没过**:接入本身已经通了——在 `view` 里对照 `t.reply` 的实际值,调断言或调应用。
|
|
111
111
|
|
|
112
112
|
到这里接入已经完成:文本断言和 judge 评分都能用了。想要更多断言(工具、多轮、审批流),见文末的增量地图。
|
|
@@ -173,7 +173,33 @@ export default defineExperiment({
|
|
|
173
173
|
| `flags` | experiment 的 `flags` | 原样转发(请求体、header 都行),应用按参数切换变体 |
|
|
174
174
|
| `telemetry` | 配置了 OTel 接入时出现 | 只碰 `headers`:每轮新的 W3C `traceparent`,spread 进请求头。接收端点每次运行都一样,在 `defineConfig` 里固定、应用启动时指向它,不从 send 传——见 [OTel 接入](/zh/guides/connect-otel) |
|
|
175
175
|
| `session` | 运行器(每条会话线一份) | 会话续接与 HITL 停轮现场的存取器都在它上面:`history()`、`id` / `capture()`、`hold()` / `take()`,见[写 send](/zh/guides/write-send) |
|
|
176
|
-
| `
|
|
176
|
+
| `progress(update)` | 运行器(绑定当前 `agent.run`) | 报告 Turn/tool 的短期状态;Human dashboard 可显示,结果不保存 |
|
|
177
|
+
| `diagnostic(input)` | 运行器(绑定当前 `agent.run`) | 保存协议退化、响应不完整等 warning/error;可由 locator 下钻回顾 |
|
|
178
|
+
|
|
179
|
+
### Adapter 里的进度、诊断和致命错误
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
async send(input, ctx) {
|
|
183
|
+
ctx.progress({ message: "等待上游模型" });
|
|
184
|
+
const response = await callAgent(input, { signal: ctx.signal });
|
|
185
|
+
|
|
186
|
+
if (response.eventsIncomplete) {
|
|
187
|
+
ctx.diagnostic({
|
|
188
|
+
code: "incomplete-event-stream",
|
|
189
|
+
level: "warning",
|
|
190
|
+
message: "上游响应缺少工具结果事件",
|
|
191
|
+
data: { requestId: response.requestId },
|
|
192
|
+
dedupeKey: `incomplete-event-stream:${response.requestId}`,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return toTurn(response);
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
`progress` 是可覆盖的短期状态;`diagnostic` 是运行结束后仍能回顾的有界记录。两者都不能指定 phase 或输出流,也不会自动改变 `Turn.status` 或 Attempt 判定。连接失败、解析无法继续等基础设施错误应抛出异常;正常收到的被测 Agent 失败通过 `Turn.status: "failed"` 表达。
|
|
201
|
+
|
|
202
|
+
终端只显示错误的一层摘要和 locator。完整 code、message、cause、stack 与 diagnostics 在 `result.json` 中,使用 `niceeval show @<locator>` 查看。OTel trace 只补充调用关系和耗时,不是错误记录的前提。
|
|
177
203
|
|
|
178
204
|
同一个 agent 对着本地和生产分别跑,就是两个 experiment 文件、两份工厂参数:
|
|
179
205
|
|
|
@@ -8,19 +8,19 @@ description: "一份报告就是一个报告文件:官方宿主打开结果、
|
|
|
8
8
|
|
|
9
9
|
一份报告就是一个报告文件。你不用打开结果目录、不用写渲染代码、不用起自己的应用:`niceeval show` / `niceeval view` 本体就是宿主——替你打开结果、把数据注入进来,你用官方组件和 `Row` / `Col` 摆版面,写完把文件路径递给 `--report`,终端和网页两扇门就都认它——官方的证据室深链、`--run` 历史切换、静态导出,自定义报告全部原样享有。
|
|
10
10
|
|
|
11
|
-
## view 的默认报告也是一份报告定义
|
|
11
|
+
## show / view 的默认报告也是一份报告定义
|
|
12
12
|
|
|
13
|
-
`niceeval view` 不传 `--report` 时渲染的默认报告不是私有实现,而是 `niceeval/report`
|
|
13
|
+
`niceeval show` / `view` 不传 `--report` 时渲染的默认报告不是私有实现,而是 `niceeval/report` 公开导出的一个值:
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
|
-
import {
|
|
16
|
+
import { ExperimentComparison } from "niceeval/report";
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
`
|
|
19
|
+
`ExperimentComparison` 先按 experiment id 的父目录切成可比组,再为每组分别计算成本 × 端到端成功率散点图和实验明细表。`compare/bub` 与 `compare/codex` 可以同图,`dev-e2b/bub` 必须在另一个组;顶层 experiment 各自成为单例组。网页持有全部组并一次聚焦一组;终端命中多组时只列索引和单组查看命令,命中单组时才展开详情。两面都不会生成跨组总榜。
|
|
20
20
|
|
|
21
21
|
| | 网页(人看) | 终端(agent 和你看) |
|
|
22
22
|
| --- | --- | --- |
|
|
23
|
-
| 官方默认 |
|
|
23
|
+
| 官方默认 | 分组比较报告(网页面) | 同一分组比较报告(文本面) |
|
|
24
24
|
| 自定义摆法 | `niceeval view --report reports/exam.tsx` | `niceeval show --report reports/exam.tsx` |
|
|
25
25
|
|
|
26
26
|
四个格子是同一个宿主。自定义只有三个层次,逐层深入:
|
|
@@ -39,16 +39,13 @@ import { CostPassRateComparison } from "niceeval/report";
|
|
|
39
39
|
// reports/exam.tsx —— 一份定义,两扇门共用
|
|
40
40
|
import {
|
|
41
41
|
defineReport, Col, Section,
|
|
42
|
-
|
|
43
|
-
costUSD, passRate,
|
|
42
|
+
ExperimentComparison, Scoreboard,
|
|
44
43
|
} from "niceeval/report";
|
|
45
44
|
|
|
46
45
|
export default defineReport(async ({ selection }) => {
|
|
47
|
-
const experiments = await ExperimentList.data(selection);
|
|
48
46
|
return (
|
|
49
47
|
<Col>
|
|
50
|
-
<
|
|
51
|
-
<ExperimentList items={experiments} />
|
|
48
|
+
<ExperimentComparison data={await ExperimentComparison.data(selection)} />
|
|
52
49
|
<Section title="考试成绩单">
|
|
53
50
|
<Scoreboard data={await Scoreboard.data(selection, { rows: "agent", subjects: "evalGroup" })} />
|
|
54
51
|
</Section>
|
|
@@ -72,7 +69,7 @@ niceeval view --report reports/exam.tsx # 网页:同一棵树走网页面
|
|
|
72
69
|
|
|
73
70
|
页面里的每个组件都是**双面**的:网页面是 React 渲染,终端面是字符渲染,两面吃同一份算好的数据。实体列表按 experiment → Eval → Attempt 展示事实;指标表、矩阵、条形图、成绩单、散点图、趋势图和差异表展示聚合值。完整清单见[报告组件](/zh/guides/report-components)。网页面的实体、格子和点深链到 Attempt 详情,终端面印出对应的 `niceeval show <eval id>` 下钻命令。
|
|
74
71
|
|
|
75
|
-
|
|
72
|
+
默认报告没有特权:它就是上面导出的 `ExperimentComparison`。你的报告和它同级;需要同样的“按目录分组、组内比较”摆法就直接引用它,不需要就不摆。自己直接组合 `MetricScatter` / `ExperimentList` 时,通用组件只消费你传入的数据,不会自动分组;把跨组 Selection 传进去就表示你明确要做跨组分析。
|
|
76
73
|
|
|
77
74
|
## 排版:Row 与 Col 在两个面都成立
|
|
78
75
|
|
|
@@ -105,17 +102,17 @@ codex 78.0/100 40/50 38/50 │ codex 80% $0.51
|
|
|
105
102
|
|
|
106
103
|
想把实验按目录前缀分组、每组顶部摆一块紧凑统计(通过率、experiment/eval 数、failed/errored、总成本、最后运行时间),用官方组件 `GroupSummary`。它的计算函数 `GroupSummary.data` 只吃一份已经收窄好的 Selection——不管你怎么分组,把对应组的 Selection 传进去,就是同一套折叠口径(eval 级折叠计票、null-safe 总成本、组内最后运行时间的最大值),不是另一套近似公式。
|
|
107
104
|
|
|
108
|
-
常见的分法是按 experiment id 的目录前缀(`compare/bub-low` 属于组 `compare
|
|
105
|
+
常见的分法是按 experiment id 的目录前缀(`compare/bub-low` 属于组 `compare`,顶层实验以自己的完整 id 形成单例组);用 `Selection.filter` 收窄出每组的 Selection,就能一组摆一块:
|
|
109
106
|
|
|
110
107
|
```tsx
|
|
111
108
|
// reports/groups.tsx —— 按 experiment id 前缀分组,每组顶部摆一块统计
|
|
112
109
|
import { defineReport, Col, Section, GroupSummary } from "niceeval/report";
|
|
113
110
|
import type { Snapshot } from "niceeval/report";
|
|
114
111
|
|
|
115
|
-
// experiment id
|
|
116
|
-
function groupOf(snapshot: Snapshot): string
|
|
112
|
+
// experiment id 的完整父路径当组名;顶层 experiment 各自成为单例组。
|
|
113
|
+
function groupOf(snapshot: Snapshot): string {
|
|
117
114
|
const parts = snapshot.experimentId.split("/");
|
|
118
|
-
return parts.length > 1 ? parts.slice(0, -1).join("/") :
|
|
115
|
+
return parts.length > 1 ? parts.slice(0, -1).join("/") : snapshot.experimentId;
|
|
119
116
|
}
|
|
120
117
|
|
|
121
118
|
export default defineReport(async ({ selection }) => {
|
|
@@ -126,9 +123,7 @@ export default defineReport(async ({ selection }) => {
|
|
|
126
123
|
groups.map(async (key) => {
|
|
127
124
|
const scoped = selection.filter((s) => groupOf(s) === key);
|
|
128
125
|
const summary = <GroupSummary data={await GroupSummary.data(scoped)} />;
|
|
129
|
-
return key
|
|
130
|
-
<Section key={key} title={key}>{summary}</Section>
|
|
131
|
-
);
|
|
126
|
+
return <Section key={key} title={key}>{summary}</Section>;
|
|
132
127
|
}),
|
|
133
128
|
)}
|
|
134
129
|
</Col>
|
|
@@ -153,7 +148,7 @@ latest 2026-07-01T11:30:00Z
|
|
|
153
148
|
// reports/golf.tsx —— code-golf:谁写出能用的代码,谁写得短
|
|
154
149
|
import {
|
|
155
150
|
defineReport, Col, MetricTable,
|
|
156
|
-
defineMetric,
|
|
151
|
+
defineMetric, endToEndPassRate, costUSD,
|
|
157
152
|
} from "niceeval/report";
|
|
158
153
|
|
|
159
154
|
// 项目自己的口径:只比能用的代码的行数
|
|
@@ -166,8 +161,8 @@ const codeLines = defineMetric({
|
|
|
166
161
|
async value(attempt) {
|
|
167
162
|
const diff = await attempt.diff();
|
|
168
163
|
if (!diff) return null;
|
|
169
|
-
return Object.
|
|
170
|
-
.reduce((
|
|
164
|
+
return Object.keys(diff.files)
|
|
165
|
+
.reduce((sum, path) => sum + (diff.get(path) ?? "").split("\n").length, 0);
|
|
171
166
|
},
|
|
172
167
|
});
|
|
173
168
|
|
|
@@ -175,8 +170,8 @@ export default defineReport(async ({ selection }) => (
|
|
|
175
170
|
<Col>
|
|
176
171
|
<MetricTable data={await MetricTable.data(selection, {
|
|
177
172
|
rows: "agent",
|
|
178
|
-
columns: [
|
|
179
|
-
sort:
|
|
173
|
+
columns: [endToEndPassRate, codeLines, costUSD],
|
|
174
|
+
sort: endToEndPassRate,
|
|
180
175
|
})} />
|
|
181
176
|
</Col>
|
|
182
177
|
));
|
|
@@ -195,7 +190,7 @@ codex 80% 355 lines $0.51
|
|
|
195
190
|
|
|
196
191
|
`label` 可以是一份文案,也可以按语言给:`label: { en: "Code lines", "zh-CN": "代码行数" }`——查看器界面切语言时,按语言给的 label 跟着切;只给一份就两种语言都用它。指标算出来的数字本身不分语言。
|
|
197
192
|
|
|
198
|
-
内置指标里 `
|
|
193
|
+
内置指标里 `endToEndPassRate` / `taskPassRate` / `executionReliability` / `costUSD` / `durationMs` / `tokens` 只读 Attempt 自带的判定、用量这些字段,任何一份结果目录都算得出。没有限定词的“成功率”使用 `endToEndPassRate`:passed 记 1,failed 和 errored 都记 0。`taskPassRate` 只在形成可信判定的样本上衡量答题质量,errored 不参与;展示它时应明确写“可判定任务通过率”,不能简称成功率。要区分答题质量和执行问题,把 `endToEndPassRate`、`taskPassRate`、`executionReliability` 三列并排。`turns`(Agent 的总轮次)不一样,它读 `attempt.o11y()`——这份数据 `copySnapshots` 缺省会随行,但如果发布脚本显式给了 `artifacts` 列表又没把 `"o11y"` 写进去,它就不在发布根里(见[结果数据 API](/zh/guides/results-data)的「发布」一节),指标渲染成 `—`,不是 0。自己写的指标只要读了 `o11y()` / `diff()` 这类 artifact(就像上面 `codeLines` 读 `attempt.diff()`),发布前都要过一遍同样的检查。
|
|
199
194
|
|
|
200
195
|
## 换分组:三种维度
|
|
201
196
|
|
|
@@ -221,7 +216,7 @@ const vendor: Dimension = {
|
|
|
221
216
|
|
|
222
217
|
<MetricTable data={await MetricTable.data(selection, {
|
|
223
218
|
rows: vendor,
|
|
224
|
-
columns: [
|
|
219
|
+
columns: [endToEndPassRate, costUSD],
|
|
225
220
|
})} />
|
|
226
221
|
```
|
|
227
222
|
|
|
@@ -248,7 +243,7 @@ export default defineExperiment({
|
|
|
248
243
|
<MetricLine data={await MetricLine.data(selection, {
|
|
249
244
|
x: flag("latencyMs", { label: "Simulated latency", unit: "ms" }),
|
|
250
245
|
series: flag("agents", { label: (v) => `${v} agents` }),
|
|
251
|
-
y:
|
|
246
|
+
y: endToEndPassRate,
|
|
252
247
|
})} />
|
|
253
248
|
```
|
|
254
249
|
|
|
@@ -264,12 +259,12 @@ export default defineExperiment({
|
|
|
264
259
|
|
|
265
260
|
```tsx
|
|
266
261
|
// reports/cost-board.tsx
|
|
267
|
-
import { defineReport, Col, Table, MetricTable, costUSD,
|
|
262
|
+
import { defineReport, Col, Table, MetricTable, costUSD, endToEndPassRate } from "niceeval/report";
|
|
268
263
|
|
|
269
264
|
export default defineReport(async ({ selection }) => {
|
|
270
265
|
const board = await MetricTable.data(selection, {
|
|
271
266
|
rows: "agent",
|
|
272
|
-
columns: [
|
|
267
|
+
columns: [endToEndPassRate, costUSD],
|
|
273
268
|
});
|
|
274
269
|
return (
|
|
275
270
|
<Col>
|
|
@@ -283,7 +278,7 @@ export default defineReport(async ({ selection }) => {
|
|
|
283
278
|
key: r.key,
|
|
284
279
|
cells: {
|
|
285
280
|
agent: r.key,
|
|
286
|
-
pass: r.cells[
|
|
281
|
+
pass: r.cells[endToEndPassRate.name].display,
|
|
287
282
|
// 缺数据交 null,组件渲染成 —;不要自己填 0
|
|
288
283
|
cost: r.cells[costUSD.name].value === null ? null : r.cells[costUSD.name].display,
|
|
289
284
|
},
|
|
@@ -324,7 +319,7 @@ codex 80% $0.51
|
|
|
324
319
|
// reports/passbars.tsx
|
|
325
320
|
import {
|
|
326
321
|
defineReport, defineComponent, Col, Style, MetricTable,
|
|
327
|
-
bar, padEnd, stringWidth,
|
|
322
|
+
bar, padEnd, stringWidth, endToEndPassRate,
|
|
328
323
|
} from "niceeval/report";
|
|
329
324
|
|
|
330
325
|
interface BarRow { key: string; ratio: number | null; display: string }
|
|
@@ -356,11 +351,11 @@ const PassBars = defineComponent<{ rows: BarRow[] }>({
|
|
|
356
351
|
});
|
|
357
352
|
|
|
358
353
|
export default defineReport(async ({ selection }) => {
|
|
359
|
-
const board = await MetricTable.data(selection, { rows: "agent", columns: [
|
|
354
|
+
const board = await MetricTable.data(selection, { rows: "agent", columns: [endToEndPassRate] });
|
|
360
355
|
const rows = board.rows.map((r) => ({
|
|
361
356
|
key: r.key,
|
|
362
|
-
ratio: r.cells[
|
|
363
|
-
display: r.cells[
|
|
357
|
+
ratio: r.cells[endToEndPassRate.name].value, // 格子键锚在指标对象上,不裸写字符串
|
|
358
|
+
display: r.cells[endToEndPassRate.name].display,
|
|
364
359
|
}));
|
|
365
360
|
return (
|
|
366
361
|
<Col>
|
|
@@ -400,4 +395,4 @@ niceeval view --report reports/exam.tsx --out site
|
|
|
400
395
|
|
|
401
396
|
## 界线:内置命令不长配置
|
|
402
397
|
|
|
403
|
-
一次只渲染一份报告,`--report` 收显式文件路径——没有 `reports/` 目录自动发现、没有插件注册表、没有配置文件。自定义指标和自定义组件都住在你的报告文件里,随文件一起递入,宿主不为它们长任何注册面。不传 `--report` 时渲染的就是内置的 `
|
|
398
|
+
一次只渲染一份报告,`--report` 收显式文件路径——没有 `reports/` 目录自动发现、没有插件注册表、没有配置文件。自定义指标和自定义组件都住在你的报告文件里,随文件一起递入,宿主不为它们长任何注册面。不传 `--report` 时渲染的就是内置的 `ExperimentComparison`,你的报告和它是同级实现。
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "数据驱动测试(dataset fan-out):用多份数据运行同一套 eval"
|
|
3
3
|
sidebarTitle: "数据驱动测试"
|
|
4
|
-
description: "从 .eval.ts
|
|
4
|
+
description: "从 .eval.ts 文件导出数组或 keyed record,将一套 eval 逻辑展开为多个 case。用 loadYaml 或 loadJson 读取外部数据集,并获得稳定 ID。"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
数据驱动测试(dataset fan-out)适合大量结构相同、输入不同的测试。例如 SQL 生成、意图分类、检索问答和工具选择。
|
|
8
8
|
|
|
9
9
|
## 工作原理
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
没有外部业务 ID 时,一个 `.eval.ts` 文件默认导出数组:
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
14
|
import { defineEval } from "niceeval";
|
|
@@ -41,6 +41,25 @@ sql/0001
|
|
|
41
41
|
|
|
42
42
|
序号零填充,便于稳定引用和过滤。
|
|
43
43
|
|
|
44
|
+
数据源已经带稳定 case、issue 或 benchmark ID 时,默认导出 keyed record:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
export default Object.fromEntries(
|
|
48
|
+
rows.map((row) => [
|
|
49
|
+
row.issueId,
|
|
50
|
+
defineEval({
|
|
51
|
+
description: row.title,
|
|
52
|
+
async test(t) {
|
|
53
|
+
await t.send(row.prompt);
|
|
54
|
+
t.succeeded();
|
|
55
|
+
},
|
|
56
|
+
}),
|
|
57
|
+
]),
|
|
58
|
+
);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
文件是 `evals/swelancer.eval.ts`、key 是 `15193` 时,ID 就是 `swelancer/15193`。key 必须是一个非空路径片段:不能是 `.` / `..`,不能含 `/`、`\\` 或控制字符。NiceEval 按 key 字典序发现,数据源返回顺序变化不会改变运行顺序。
|
|
62
|
+
|
|
44
63
|
## 从 YAML / JSON 加载
|
|
45
64
|
|
|
46
65
|
```ts
|
|
@@ -63,8 +82,11 @@ cases:
|
|
|
63
82
|
# 运行整个数据集
|
|
64
83
|
npx niceeval exp local sql
|
|
65
84
|
|
|
66
|
-
#
|
|
85
|
+
# 只运行第一个数组 case
|
|
67
86
|
npx niceeval exp local sql/0000
|
|
87
|
+
|
|
88
|
+
# 运行一个 keyed case
|
|
89
|
+
npx niceeval exp local swelancer/15193
|
|
68
90
|
```
|
|
69
91
|
|
|
70
92
|
## 数据集 vs 独立文件
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "保留沙箱现场排查问题"
|
|
3
|
+
sidebarTitle: "保留沙箱现场"
|
|
4
|
+
description: "用 --keep-sandbox 把失败 Attempt 的沙箱保留成可随时唤醒的现场,用 niceeval sandbox enter 进去手动排查,用 sandbox list / stop 查看和清理。"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
沙箱默认在每个 Attempt 结束后销毁,排查依据是落盘的 artifact:`niceeval show` 能看到判定、断言、diff 和事件流。大多数问题到这里就够了——完整的排查路线见 [Debug 手册](/zh/guides/debugging)。
|
|
8
|
+
|
|
9
|
+
但有些问题只能进活的环境里看:
|
|
10
|
+
|
|
11
|
+
- **环境起不来**——setup 阶段装依赖失败、agent CLI 启动不了。这时 agent 还没开始跑,事件流是空的,最快的办法是进沙箱手动重跑一遍安装命令。
|
|
12
|
+
- **改动落在 `git diff` 之外**——全局装了什么包、`$HOME` 下写了什么配置、`PATH` 实际是什么,artifact 里没有。
|
|
13
|
+
- **重跑太慢**——冷启动加安装要几分钟,想逐条验证猜测时,留着现场比每次重跑快得多。
|
|
14
|
+
|
|
15
|
+
## 跑的时候保留现场
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx niceeval exp local onboarding/tool-first --keep-sandbox # 等价 --keep-sandbox=failed
|
|
19
|
+
npx niceeval exp local onboarding/tool-first --keep-sandbox=all # 通过的也保留
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`--keep-sandbox` 是 `niceeval exp` 的运行参数,两档:`failed`(缺省值)保留判定为 `failed` 或 `errored` 的 Attempt(包括超时打断的);`all` 连通过的也保留——调 setup 钩子、核对通过环境的真实状态时用它,不用故意弄挂一条 eval。不带这个参数时全部销毁。
|
|
23
|
+
|
|
24
|
+
运行结束后,摘要里会列出保留了哪些沙箱、怎么进去:
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
Kept sandboxes (1)
|
|
28
|
+
@1x7f3q9k onboarding/tool-first #1 errored docker · a3f9c2d1
|
|
29
|
+
enter: niceeval sandbox enter a3f9c2d1
|
|
30
|
+
Stop them with: niceeval sandbox stop --all
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
每行给三样东西:Attempt 定位符(用 `niceeval show @1x7f3q9k` 看落盘证据)、沙箱实例 id、进入现场的命令。保留下来的沙箱不会一直跑着烧资源——Docker 容器停在磁盘上,E2B 微 VM 暂停计费,Vercel 保存文件系统。`niceeval sandbox enter` 会先唤醒再进入,在 workdir 打开 shell;退出 shell 后现场自动回到休眠(想让它保持运行,加 `--leave-running`)。进去之后就是这次 Attempt 跑完时的环境,可以手动执行命令、翻文件、复现失败。
|
|
34
|
+
|
|
35
|
+
## 查看和清理
|
|
36
|
+
|
|
37
|
+
保留下来的沙箱逐条记录在 `.niceeval/sandboxes/` 里,用 `niceeval sandbox` 管理:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
niceeval sandbox list # 列出保留的沙箱和现场状态
|
|
41
|
+
niceeval sandbox enter a3f9c2d1 # 唤醒并进入;退出后自动回到休眠
|
|
42
|
+
niceeval sandbox stop a3f9c2d1 # 销毁指定沙箱(id 可以只写唯一前缀)
|
|
43
|
+
niceeval sandbox stop --all # 全部销毁
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`stop` 是幂等的:沙箱已经不在了(手动删过、云端过期)不算错误,只会把记录移掉并说明。如果 provider 销毁失败,命令会保留记录并返回错误,方便稍后重试,不会把仍活着的资源从列表里藏掉。忘了清也有提醒——下次运行开始时,如果还有上次保留的沙箱,会打一行提示。
|
|
47
|
+
|
|
48
|
+
## 各 Provider 的差别
|
|
49
|
+
|
|
50
|
+
- **Docker**:保留 = 容器停在磁盘上(不占内存,重启 Docker 也还在),进入时自动启动。容器不会自己消失,是唯一需要主动清理的 provider。除了 `niceeval sandbox stop`,也可以用 `docker ps -a -f label=niceeval.keep-candidate=true` 直接核对。
|
|
51
|
+
- **E2B**:保留 = 暂停微 VM——文件和内存整体保存,暂停期间停止计费、无限期保留,进入时自动恢复。
|
|
52
|
+
- **Vercel Sandbox**:保留 = 停止微 VM——文件系统保存、之后可恢复,但内存状态不保留,唤醒后进程要重新启动;超过 provider 的保留期限后 `niceeval sandbox list` 标成 `expired`。
|
|
53
|
+
- **自定义 Provider**:`defineSandbox` 产出的 provider 不支持留存,因为事后的 `sandbox stop` 不加载用户配置,无法在新进程里安全找回自定义销毁函数。
|
|
54
|
+
|
|
55
|
+
## 边界
|
|
56
|
+
|
|
57
|
+
保留的沙箱只用来排查,不能续跑或重新评分;判定、断言、diff 这些结论仍以 artifact 为准。查看 artifact 的方法见[查看结果](/zh/guides/viewing-results)。
|