niceeval 0.4.6 → 0.5.1
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/docs-site/zh/concepts/adapter.mdx +6 -6
- package/docs-site/zh/concepts/experiment.mdx +7 -7
- package/docs-site/zh/concepts/overview.mdx +1 -1
- package/docs-site/zh/concepts/tier.mdx +6 -6
- package/docs-site/zh/guides/agent-feedback-loop.mdx +64 -21
- package/docs-site/zh/guides/connect-your-agent.mdx +7 -7
- package/docs-site/zh/guides/custom-reports.mdx +243 -0
- package/docs-site/zh/guides/experiments.mdx +3 -3
- package/docs-site/zh/guides/report-components.mdx +252 -0
- package/docs-site/zh/guides/reporters.mdx +1 -1
- package/docs-site/zh/guides/results-data.mdx +224 -0
- package/docs-site/zh/guides/runner.mdx +1 -1
- package/docs-site/zh/guides/sandbox-agent.mdx +2 -2
- package/docs-site/zh/guides/viewing-results.mdx +147 -77
- package/docs-site/zh/guides/write-experiment.mdx +9 -9
- package/docs-site/zh/guides/write-send.mdx +6 -6
- package/docs-site/zh/index.mdx +1 -1
- package/docs-site/zh/introduction.mdx +1 -1
- package/docs-site/zh/reference/cli.mdx +11 -3
- package/docs-site/zh/reference/define-agent.mdx +5 -5
- package/docs-site/zh/reference/define-config.mdx +1 -1
- package/docs-site/zh/reference/define-eval.mdx +3 -3
- package/package.json +3 -2
- package/src/agents/ai-sdk.test.ts +1 -1
- package/src/agents/ai-sdk.ts +2 -2
- package/src/agents/claude-code.ts +1 -1
- package/src/agents/codex.ts +2 -2
- package/src/agents/streaming.test.ts +1 -1
- package/src/agents/types.ts +4 -4
- package/src/agents/ui-message-stream.test.ts +1 -1
- package/src/cli.ts +120 -18
- package/src/context/context.test.ts +1 -1
- package/src/context/context.ts +3 -3
- package/src/context/session.ts +2 -2
- package/src/context/types.ts +2 -2
- package/src/i18n/en.ts +29 -7
- package/src/i18n/zh-CN.ts +24 -2
- package/src/report/aggregate.ts +79 -50
- package/src/report/components.tsx +190 -0
- package/src/report/compute.ts +149 -84
- package/src/report/default-report.tsx +222 -0
- package/src/report/dual-face.test.tsx +527 -0
- package/src/report/format.ts +29 -0
- package/src/report/index.ts +79 -24
- package/src/report/load.ts +67 -0
- package/src/report/metrics.ts +2 -1
- package/src/report/param.ts +18 -0
- package/src/report/primitives.tsx +117 -0
- package/src/report/react/CaseList.tsx +2 -2
- package/src/report/react/DeltaTable.tsx +2 -2
- package/src/report/react/MetricBars.tsx +109 -0
- package/src/report/react/MetricLine.tsx +200 -0
- package/src/report/react/MetricMatrix.tsx +1 -1
- package/src/report/react/MetricScatter.tsx +1 -1
- package/src/report/react/MetricTable.tsx +1 -1
- package/src/report/react/RunOverview.tsx +4 -4
- package/src/report/react/Scoreboard.tsx +3 -3
- package/src/report/react/cell.tsx +1 -1
- package/src/report/react/fixtures.ts +98 -36
- package/src/report/react/format.ts +3 -28
- package/src/report/react/index.tsx +41 -24
- package/src/report/react/render.test.tsx +8 -8
- package/src/report/react/styles.css +77 -0
- package/src/report/report.test.ts +248 -128
- package/src/report/report.ts +69 -0
- package/src/report/text/faces.ts +356 -0
- package/src/report/text/layout.ts +125 -0
- package/src/report/text/plot.ts +127 -0
- package/src/report/tree.ts +220 -0
- package/src/report/types.ts +75 -27
- package/src/report/web.ts +40 -0
- package/src/results/copy.ts +95 -41
- package/src/results/format.ts +12 -11
- package/src/results/index.ts +34 -17
- package/src/results/open.ts +201 -81
- package/src/results/results.test.ts +595 -191
- package/src/results/select.ts +107 -55
- package/src/results/types.ts +137 -45
- package/src/results/writer.ts +258 -0
- package/src/runner/attempt.ts +3 -3
- package/src/runner/fingerprint.ts +1 -1
- package/src/runner/reporters/artifacts.ts +38 -78
- package/src/runner/reporters/braintrust.test.ts +2 -2
- package/src/runner/reporters/braintrust.ts +3 -3
- package/src/runner/run.ts +1 -1
- package/src/runner/types.ts +18 -12
- package/src/show/compose.ts +215 -0
- package/src/show/index.ts +263 -0
- package/src/show/render.ts +456 -0
- package/src/show/show.test.ts +505 -0
- package/src/view/app/App.tsx +85 -24
- package/src/view/app/components/CostScoreChart.tsx +29 -10
- package/src/view/app/components/ExperimentTable.tsx +27 -9
- package/src/view/app/components/GroupSelector.tsx +1 -1
- package/src/view/app/i18n.ts +18 -3
- package/src/view/app/lib/attempt-route.test.ts +9 -9
- package/src/view/app/lib/attempt-route.ts +5 -5
- package/src/view/app/lib/outcome.ts +1 -3
- package/src/view/app/lib/rows.ts +95 -15
- package/src/view/app/main.tsx +18 -6
- package/src/view/app/pages/RunsPage.tsx +4 -7
- package/src/view/app/pages/TracesPage.tsx +3 -8
- package/src/view/app/types.ts +50 -6
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +20 -20
- package/src/view/data.test.ts +254 -0
- package/src/view/data.ts +336 -0
- package/src/view/index.ts +64 -10
- package/src/view/server.ts +31 -9
- package/src/view/shared/types.ts +47 -41
- package/src/view/styles.css +5 -0
- package/src/view/template.html +1 -0
- package/src/view/view-report.test.ts +308 -0
- package/docs-site/.mintignore +0 -13
- package/docs-site/AGENTS.md +0 -46
- package/docs-site/concepts/adapter.mdx +0 -287
- package/docs-site/concepts/assert.mdx +0 -243
- package/docs-site/concepts/drive.mdx +0 -118
- package/docs-site/concepts/evals.mdx +0 -108
- package/docs-site/concepts/experiment.mdx +0 -37
- package/docs-site/concepts/hitl.mdx +0 -83
- package/docs-site/concepts/judge.mdx +0 -129
- package/docs-site/concepts/overview.mdx +0 -98
- package/docs-site/concepts/tier.mdx +0 -42
- package/docs-site/docs-ref/00-index.md +0 -23
- package/docs-site/docs-ref/01-page-types.md +0 -43
- package/docs-site/docs-ref/03-style-rules.md +0 -45
- package/docs-site/docs-ref/04-code-examples.md +0 -37
- package/docs-site/docs-ref/05-dx-failure-paths.md +0 -45
- package/docs-site/docs-ref/06-checklists.md +0 -53
- package/docs-site/docs.json +0 -256
- package/docs-site/example/ai-agent-application.mdx +0 -148
- package/docs-site/example/claude-code-codex-plugin.mdx +0 -162
- package/docs-site/example/claude-code-codex-skill.mdx +0 -147
- package/docs-site/example/showcase.mdx +0 -39
- package/docs-site/example/tier1-ai-sdk-v7.mdx +0 -136
- package/docs-site/example/tier1-claude-sdk.mdx +0 -119
- package/docs-site/example/tier1-codex-sdk.mdx +0 -111
- package/docs-site/example/tier1-langgraph.mdx +0 -119
- package/docs-site/example/tier1-pi-sdk.mdx +0 -119
- package/docs-site/favicon.svg +0 -5
- package/docs-site/github-diff.css +0 -135
- package/docs-site/github-diff.js +0 -11
- package/docs-site/guides/authoring.mdx +0 -129
- package/docs-site/guides/ci-integration.mdx +0 -97
- package/docs-site/guides/connect-otel.mdx +0 -209
- package/docs-site/guides/connect-your-agent.mdx +0 -221
- package/docs-site/guides/dataset-fanout.mdx +0 -98
- package/docs-site/guides/experiments.mdx +0 -71
- package/docs-site/guides/fixtures.mdx +0 -147
- package/docs-site/guides/reporters.mdx +0 -113
- package/docs-site/guides/runner.mdx +0 -79
- package/docs-site/guides/sandbox-agent.mdx +0 -138
- package/docs-site/guides/sandbox-backends.mdx +0 -64
- package/docs-site/guides/scoring-guide.mdx +0 -92
- package/docs-site/guides/viewing-results.mdx +0 -195
- package/docs-site/guides/write-experiment.mdx +0 -81
- package/docs-site/guides/write-send.mdx +0 -347
- package/docs-site/index.mdx +0 -181
- package/docs-site/introduction.mdx +0 -141
- package/docs-site/quickstart.mdx +0 -136
- package/docs-site/reference/builtin-agents.mdx +0 -161
- package/docs-site/reference/capabilities.mdx +0 -76
- package/docs-site/reference/cli.mdx +0 -120
- package/docs-site/reference/define-agent.mdx +0 -168
- package/docs-site/reference/define-config.mdx +0 -41
- package/docs-site/reference/define-eval.mdx +0 -160
- package/docs-site/reference/events.mdx +0 -131
- package/docs-site/reference/expect.mdx +0 -112
- package/docs-site/tracker.js +0 -8
- package/src/report/react/data.ts +0 -17
- package/src/view/aggregate.ts +0 -103
- package/src/view/loader.test.ts +0 -61
- package/src/view/loader.ts +0 -257
|
@@ -19,7 +19,7 @@ description: "Adapter 是你写的适配器。本文讲清楚 send 函数传入
|
|
|
19
19
|
|
|
20
20
|
回程:Adapter 把应用的原始返回翻译成 `Turn`——其中 `events` 是一个按发生顺序排列的事件对象数组(下文有实际值)
|
|
21
21
|
|
|
22
|
-
被测系统的 URL、鉴权、协议细节通过 Adapter 工厂的配置传入。`defineExperiment` 里放的是已经配置好的 agent 实例;Adapter 在 `send` 闭包里消费这些配置,再把每轮动态值(如 `ctx.model`、`ctx.
|
|
22
|
+
被测系统的 URL、鉴权、协议细节通过 Adapter 工厂的配置传入。`defineExperiment` 里放的是已经配置好的 agent 实例;Adapter 在 `send` 闭包里消费这些配置,再把每轮动态值(如 `ctx.model`、`ctx.params`、`ctx.telemetry`)随请求转发给应用:
|
|
23
23
|
|
|
24
24
|
```ts
|
|
25
25
|
// agents/web-agent.ts
|
|
@@ -48,7 +48,7 @@ export function webAgent(options: WebAgentOptions): Agent {
|
|
|
48
48
|
message: input.text,
|
|
49
49
|
files: input.files,
|
|
50
50
|
model: ctx.model,
|
|
51
|
-
|
|
51
|
+
params: ctx.params,
|
|
52
52
|
}),
|
|
53
53
|
signal: ctx.signal,
|
|
54
54
|
});
|
|
@@ -81,13 +81,13 @@ export default defineExperiment({
|
|
|
81
81
|
apiKey: process.env.STAGING_AGENT_API_KEY,
|
|
82
82
|
}),
|
|
83
83
|
model: "gpt-5.4",
|
|
84
|
-
|
|
84
|
+
params: { promptVariant: "concise" },
|
|
85
85
|
});
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
## 接入 Tier
|
|
89
89
|
|
|
90
|
-
按「Adapter 接到哪里、额外拿到什么观测数据」,接入分三档:**Tier 1 只接 send**(应用代码一行不改,全套断言在这一档就齐了)、**Tier 2 send + OTel**(应用把 span 发给 [NiceEval](https://niceeval.com/),换 `niceeval view` 的调用瀑布图)、**Tier 3 侵入改造 + experiment
|
|
90
|
+
按「Adapter 接到哪里、额外拿到什么观测数据」,接入分三档:**Tier 1 只接 send**(应用代码一行不改,全套断言在这一档就齐了)、**Tier 2 send + OTel**(应用把 span 发给 [NiceEval](https://niceeval.com/),换 `niceeval view` 的调用瀑布图)、**Tier 3 侵入改造 + experiment params**(feature A/B)。每档投入什么、买到什么、什么时候升级,见 [Tier](/zh/concepts/tier)。
|
|
91
91
|
|
|
92
92
|
eval 侧的驱动 API——`t.send()`、`t.sendFile()`、`t.newSession()`、HITL 的 `t.respond()` / `t.respondAll()`
|
|
93
93
|
统一都是调用 Adapter 的 `send`。
|
|
@@ -187,7 +187,7 @@ interface AgentContext {
|
|
|
187
187
|
readonly model?: string; // Tier 1 的模型对比:experiment.model 透传;应用接口收模型选择就转发
|
|
188
188
|
readonly reasoningEffort?: string; // 推理努力程度:experiment.reasoningEffort 透传,归属与 model 一致
|
|
189
189
|
readonly telemetry?: Telemetry; // Tier 2:本轮的 W3C trace context(headers,每轮一个新 traceparent)
|
|
190
|
-
readonly
|
|
190
|
+
readonly params: Readonly<Record<string, unknown>>; // Tier 3 的 feature A/B:experiment.params 透传;没配时是 {}
|
|
191
191
|
|
|
192
192
|
readonly sandbox?: Sandbox; // 仅 defineSandboxAgent 构造的 Agent 会拿到
|
|
193
193
|
}
|
|
@@ -209,7 +209,7 @@ interface AgentSession {
|
|
|
209
209
|
}
|
|
210
210
|
```
|
|
211
211
|
|
|
212
|
-
`ctx` 里没有要你检查的标志位。三个档位字段都是"透传"语义:`model`、`
|
|
212
|
+
`ctx` 里没有要你检查的标志位。三个档位字段都是"透传"语义:`model`、`params` 由 experiment 声明、运行器原样递过来,Adapter 只负责随请求转发给应用,不解释它们的含义;`telemetry` 仅在配置了 OTel 接入时出现,`send` 里只需要把 `headers` spread 进请求头——接收端点在 `defineConfig` 里固定、由应用启动时指向,不从这里传,见[OTel 接入](/zh/guides/connect-otel)。
|
|
213
213
|
|
|
214
214
|
`session` 是本条会话线的自有状态,[NiceEval](https://niceeval.com/) 对它只承诺一件事:**同一条会话线的每次 `send` 拿到同一个 `ctx.session`,新会话线(eval 的第一轮,或 `t.newSession()` 之后)拿到一个全新的。** 会话续接(`id`/`capture`、`history`)和 HITL 停轮现场(`hold`/`take`)的存取器都在它上面,"第一轮"就是新会话线的自然形态——`id` 是 `undefined`、`history.get()` 是空数组,没有要判断的分支;`state` 是这些存取器之外的逃生舱,框架从不往里写数据。
|
|
215
215
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "实验(Experiment):评谁、怎么跑"
|
|
3
3
|
sidebarTitle: "实验"
|
|
4
|
-
description: "Experiment 是可签入的运行配置:同一批 eval 对着哪个 agent、哪个模型、哪些
|
|
4
|
+
description: "Experiment 是可签入的运行配置:同一批 eval 对着哪个 agent、哪个模型、哪些 params、跑几次。eval 与 experiment 分开的原因——晚绑定,让同一份 eval 换着对象跑。"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
**experiment** 对着哪个 agent、用哪个模型、开哪些
|
|
7
|
+
**experiment** 对着哪个 agent、用哪个模型、开哪些 params、跑几次。用于做 AB 测试、模型对比、feature 对比。
|
|
8
8
|
|
|
9
9
|
## 一个 experiment 里有什么
|
|
10
10
|
|
|
@@ -16,22 +16,22 @@ export default defineExperiment({
|
|
|
16
16
|
description: "简洁 prompt 变体",
|
|
17
17
|
agent: webAgent({ baseUrl: "https://staging.example.com" }), // 已配置好的 agent 实例
|
|
18
18
|
model: "gpt-5.4", // 经 ctx.model 透传给 Adapter
|
|
19
|
-
|
|
19
|
+
params: { promptVariant: "concise" }, // 经 ctx.params 透传;eval 里是 t.params
|
|
20
20
|
runs: 3, // 每条 eval 跑几次(对抗随机性)
|
|
21
21
|
});
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
- `agent`:评谁。放的是已经配置好的实例——被测系统的 URL、鉴权传给 Adapter 工厂,不进 experiment 的其它字段。
|
|
25
|
-
- `model` / `
|
|
25
|
+
- `model` / `params`:透传语义。[NiceEval](https://niceeval.com/) 不解释它们的含义,原样经 `ctx` 递给 Adapter,由 Adapter 随请求转发、应用按需切换——这正是 [Tier](/zh/concepts/tier) 里模型对比(Tier 1)和 feature A/B(Tier 3)的通道。
|
|
26
26
|
- `runs`、`budget`、并发、`sandbox` 等运行参数:怎么跑、跑多少。完整字段见[写实验](/zh/guides/write-experiment)。
|
|
27
27
|
|
|
28
28
|
## 矩阵对比
|
|
29
29
|
|
|
30
|
-
要比较的每个变体写一个 experiment 文件:两个模型就是两个文件,只差 `model` 一行;prompt A/B
|
|
30
|
+
要比较的每个变体写一个 experiment 文件:两个模型就是两个文件,只差 `model` 一行;prompt A/B 就是只差一个参数。同一批 eval 在多个 experiment 下各跑一遍,pass rate、成本、延迟就有了可比的横截面——`niceeval view` 里叠着看。适合比什么、结果怎么读,见[实验矩阵](/zh/guides/experiments)。
|
|
31
31
|
|
|
32
32
|
## 相关阅读
|
|
33
33
|
|
|
34
34
|
- [写实验](/zh/guides/write-experiment) — `defineExperiment` 的完整字段:runs、预算、并发与 sandbox。
|
|
35
|
-
- [实验矩阵](/zh/guides/experiments) — 跨 agent / model /
|
|
35
|
+
- [实验矩阵](/zh/guides/experiments) — 跨 agent / model / params 的对比怎么组织、怎么读结果。
|
|
36
36
|
- [评估](/zh/concepts/evals) — 另一半:eval 是什么、生命周期与 outcome。
|
|
37
|
-
- [Tier](/zh/concepts/tier) — `model` / `
|
|
37
|
+
- [Tier](/zh/concepts/tier) — `model` / `params` 各在哪一档生效。
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "接入 Tier:三档投入,三组能力"
|
|
3
3
|
sidebarTitle: "Tier"
|
|
4
|
-
description: "按「Adapter 接到哪里、额外拿到什么观测数据」接入分三档:Tier 1 只接 send,Tier 2 加 OTel,Tier 3 侵入改造暴露 experiment
|
|
4
|
+
description: "按「Adapter 接到哪里、额外拿到什么观测数据」接入分三档:Tier 1 只接 send,Tier 2 加 OTel,Tier 3 侵入改造暴露 experiment params。每档买到什么、什么时候升级。"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
接入 [NiceEval](https://niceeval.com/) 不是全有或全无的一次性工程。按「Adapter 接到哪里、额外拿到什么观测数据」,接入分三档——每一档在上一档之上加一种投入、换一组新能力,已写的 eval 全程复用,不用改。
|
|
8
8
|
|
|
9
|
-

|
|
10
10
|
|
|
11
11
|
## Tier 1:只接 send
|
|
12
12
|
|
|
@@ -22,9 +22,9 @@ description: "按「Adapter 接到哪里、额外拿到什么观测数据」接
|
|
|
22
22
|
|
|
23
23
|
这一档买到的是**观测**:`niceeval view` 的调用瀑布图——应用内部每次模型调用、每次工具执行、各自的耗时与 token,按轮铺成时间线。断言不受影响:span 只进瀑布图,不进事件流、不喂断言。走法见[OTel 接入](/zh/guides/connect-otel)。
|
|
24
24
|
|
|
25
|
-
## Tier 3:侵入改造 + experiment
|
|
25
|
+
## Tier 3:侵入改造 + experiment params
|
|
26
26
|
|
|
27
|
-
要评「改 prompt / 换工具集 / 开关某个 feature 谁更好」这类 **feature A/B test** 时,对照点在应用内部,前两档拿不到。这一档改应用内部代码,把变体暴露成外部可选的配置:experiment 的 `
|
|
27
|
+
要评「改 prompt / 换工具集 / 开关某个 feature 谁更好」这类 **feature A/B test** 时,对照点在应用内部,前两档拿不到。这一档改应用内部代码,把变体暴露成外部可选的配置:experiment 的 `params` 经 `ctx.params` 透传给 Adapter,Adapter 随请求交给应用(HTTP header、请求体字段、环境变量都行),应用按参数切换变体。
|
|
28
28
|
|
|
29
29
|
注意侵入的是应用(把变体暴露成配置),不是接入面——Adapter 本身不变,依然只经对外接口调用应用。
|
|
30
30
|
|
|
@@ -39,6 +39,6 @@ description: "按「Adapter 接到哪里、额外拿到什么观测数据」接
|
|
|
39
39
|
## 相关阅读
|
|
40
40
|
|
|
41
41
|
- [接入你的 Agent](/zh/guides/connect-your-agent) — 接入全景:最小接入与参数通道。
|
|
42
|
-
- [Adapter](/zh/concepts/adapter) — 契约本身:`send` 传入什么返回什么,`ctx.model` / `ctx.telemetry` / `ctx.
|
|
42
|
+
- [Adapter](/zh/concepts/adapter) — 契约本身:`send` 传入什么返回什么,`ctx.model` / `ctx.telemetry` / `ctx.params` 按档位出现。
|
|
43
43
|
- [OTel 接入](/zh/guides/connect-otel) — Tier 2 的完整走法。
|
|
44
|
-
- [实验](/zh/concepts/experiment) — model /
|
|
44
|
+
- [实验](/zh/concepts/experiment) — model / params 对比在 experiment 侧怎么声明。
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "让 coding agent 读结果并自主迭代"
|
|
3
3
|
sidebarTitle: "Agent 反馈闭环"
|
|
4
|
-
description: "
|
|
4
|
+
description: "niceeval exp 跑完直接给出失败清单和下钻命令,agent 用 niceeval show 在终端读断言、transcript、trace 与 diff,修完按前缀重跑——跑→读→修→再跑全程不出终端。"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Eval
|
|
7
|
+
Eval 的价值在闭环:不只是人看报告,而是让 coding agent(Claude Code、Codex 等)自己跑 eval、自己读失败原因、自己改被测程序、再跑验证。这个循环成立的前提是信息对等:**人在 `niceeval view` 里能看到的证据,agent 都能用 `niceeval show` 在终端看到**(两扇门的完整用法见[查看结果](/zh/guides/viewing-results))。agent 不需要浏览器,不需要解析人类向的流式输出,也不需要一口气吞下可能上百 MB 的原始 JSON——CLI 输出是浓缩过的读法,原始工件永远在盘上,要多细读多细。
|
|
8
8
|
|
|
9
9
|
## 一次完整的循环
|
|
10
10
|
|
|
@@ -15,65 +15,108 @@ Eval 的价值在闭环:不只是人看报告,而是让 coding agent(Claude Code
|
|
|
15
15
|
```
|
|
16
16
|
</Step>
|
|
17
17
|
<Step title="读">
|
|
18
|
-
|
|
18
|
+
运行中每个 eval 一条流式行即时反馈;运行结束时 CLI 打出收尾块——失败清单逐条带 eval id、失败断言和下钻命令,末尾是结构化结果入口。完整输出长这样:
|
|
19
19
|
|
|
20
20
|
```text
|
|
21
|
+
$ npx niceeval exp local
|
|
22
|
+
Discovered 15 evals · experiment local
|
|
23
|
+
|
|
24
|
+
✓ classify (12ms)
|
|
25
|
+
✓ weather/tokyo (31s · $0.02)
|
|
26
|
+
✗ weather/brooklyn (41s · $0.04 · 3 attempts)
|
|
27
|
+
✓ fixtures/button (2m 10s · $0.11)
|
|
28
|
+
…
|
|
29
|
+
|
|
30
|
+
Failing:
|
|
31
|
+
✗ weather/brooklyn
|
|
32
|
+
gate calledTool("get_weather"): tool was never called
|
|
33
|
+
soft judge("回答基于实时数据"): 0.2/1
|
|
34
|
+
→ niceeval show weather/brooklyn
|
|
35
|
+
|
|
36
|
+
Results: 14 passed, 1 failed, 0 errored, 0 skipped · $0.31 · 6m 02s
|
|
21
37
|
Structured results: .niceeval/2026-07-09T10-00-00-000Z/summary.json
|
|
22
38
|
```
|
|
23
39
|
|
|
24
|
-
`
|
|
40
|
+
流式行是给盯着终端的人看的,长运行会把它们刷出屏幕;收尾块专为「只看最后一屏」设计——失败集中重放一遍,每条自带 `niceeval show <eval id>`,agent 不用回滚历史输出就能开始下钻。跑多 experiment 的组(如 `exp compare`)时,收尾块还会多一张 per-experiment 汇总表,与 `niceeval show` 榜单是同一份渲染。
|
|
41
|
+
|
|
42
|
+
退出码是给循环用的判决,不用解析任何文本:全绿退 `0`;有失败或错误退 `1`(按 eval 级折叠——`runs` 重试吸收掉的单次抖动不算红);框架自身崩溃退 `2`。agent 或脚本判断「还要不要再来一轮」,看退出码就够。
|
|
43
|
+
|
|
44
|
+
`summary.json` 与 attempt 级工件是结构化事实源,字段说明见[查看结果](/zh/guides/viewing-results);`niceeval show` 是对同一份工件的终端读法,逐级下钻见下一节。
|
|
25
45
|
</Step>
|
|
26
46
|
<Step title="修">
|
|
27
|
-
|
|
47
|
+
先判断缺陷在哪一侧:被测程序,还是 eval 本身(断言过紧、fixture 错误、setup 缺失)。修对应的一侧,不要为了变绿而放宽断言。
|
|
28
48
|
</Step>
|
|
29
49
|
<Step title="再跑">
|
|
30
50
|
```bash
|
|
31
51
|
npx niceeval exp local weather
|
|
32
52
|
```
|
|
33
53
|
|
|
34
|
-
位置参数按 eval id
|
|
54
|
+
位置参数按 eval id 前缀只重跑失败的部分,已通过的 eval 会被指纹缓存跳过。注意缓存的边界:指纹只看 eval 源码和运行配置,**看不到被测程序**——修完被测程序后,旧的「通过」仍是对旧代码的判定,这次修复若引入回归,缓存不会替你发现。所以迭代中按前缀快速重跑,等失败清零后再用 `--force` 全量跑一遍,确认没把别的 eval 修坏,才算真正收工。
|
|
35
55
|
</Step>
|
|
36
56
|
</Steps>
|
|
37
57
|
|
|
38
|
-
给 agent
|
|
58
|
+
给 agent 的提示词可以直接这样写:
|
|
39
59
|
|
|
40
60
|
```text
|
|
41
|
-
跑 npx niceeval exp local
|
|
42
|
-
|
|
61
|
+
跑 npx niceeval exp local。对每个失败的 eval 用 niceeval show <eval id> 看失败断言,
|
|
62
|
+
需要证据时加 --transcript / --trace / --diff。判断缺陷在被测程序还是 eval 本身,
|
|
63
|
+
修掉后重跑对应前缀。失败清零后用 --force 全量重跑一次确认没有回归,退出码 0 才算完成。
|
|
64
|
+
连续三轮同一个 eval 没有进展就停下来汇报,不要为了变绿放宽断言。
|
|
43
65
|
```
|
|
44
66
|
|
|
45
|
-
|
|
67
|
+
跑真实 agent 的 eval 每轮都花钱;不放心 agent 的循环成本时,给 `exp` 加 `--budget <美元>`——累计花费到顶后剩余 attempt 直接跳过,agent 循环再失控也有硬上限。
|
|
68
|
+
|
|
69
|
+
## 在终端下钻
|
|
70
|
+
|
|
71
|
+
`niceeval show` 的位置参数与 `exp` 同一语义——eval id 前缀选「看哪些 eval」;flag 选「看哪个切面」:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
niceeval show # 榜单:每个 experiment 的通过率、成本、耗时与失败清单
|
|
75
|
+
niceeval show weather/brooklyn # 单个 eval:attempt、断言明细、错误与用量
|
|
76
|
+
niceeval show weather/brooklyn --transcript # agent 的完整对话与工具调用
|
|
77
|
+
niceeval show weather/brooklyn --trace # trace 瀑布的文本版
|
|
78
|
+
niceeval show fixtures/button --diff # sandbox 里的文件改动
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
默认永远对准闭环要的东西:判决取每个 eval 跨 run 合成的最新一份,证据切面挑最新一次失败的 attempt。三个切面对长内容都会截断,但截断永远如实标注剩余数量和原始工件路径——输出对上下文窗口友好,事实源一字不少地留在盘上。
|
|
82
|
+
|
|
83
|
+
榜单、单个 eval 与各证据切面的输出长什么样、历史与抖动怎么看(`--history`)、多次 run 的合成口径,都在[查看结果](/zh/guides/viewing-results);要项目自己的口径(考试成绩单、代码行数榜),写一个打到 stdout 的报告脚本,见[自定义报告](/zh/guides/custom-reports)——给 agent 的指引只多一行:跑 `npx tsx scripts/report.ts`,读 stdout。
|
|
84
|
+
|
|
85
|
+
## AGENTS.md / CLAUDE.md 指引区块
|
|
46
86
|
|
|
47
|
-
模型的训练数据里没有 NiceEval。`npx niceeval init`
|
|
87
|
+
模型的训练数据里没有 NiceEval。`npx niceeval init` 会写入一个托管区块,告诉 agent 先读随包文档再动手:项目已有 `AGENTS.md` 就写那份;只有 `CLAUDE.md`(没有 `AGENTS.md`)就直接写进 `CLAUDE.md`,不另建重复文件;两者都没有则新建 `AGENTS.md`。`CLAUDE.md` 是指向 `AGENTS.md` 的符号链接时,写入自动落到链接目标,不会分裂成两份内容。
|
|
48
88
|
|
|
49
89
|
```md
|
|
50
90
|
<!-- BEGIN:niceeval-agent-rules -->
|
|
51
91
|
# niceeval is NOT in your training data
|
|
52
92
|
|
|
53
93
|
Its APIs and conventions may differ from anything you have seen. Read the relevant
|
|
54
|
-
guide in `node_modules/niceeval/docs-site/`
|
|
55
|
-
|
|
56
|
-
|
|
94
|
+
guide in `node_modules/niceeval/docs-site/zh/` before writing any eval, experiment,
|
|
95
|
+
adapter, or niceeval config. The bundled docs are Chinese-only — that is the single
|
|
96
|
+
authoritative, always-current version; read it regardless of your working language.
|
|
97
|
+
After a run, drill into failures with `niceeval show <eval id>` (add `--transcript` /
|
|
98
|
+
`--trace` / `--diff` for evidence); the `summary.json` path the CLI prints and the
|
|
99
|
+
artifact files it references are the structured source of truth.
|
|
57
100
|
<!-- END:niceeval-agent-rules -->
|
|
58
101
|
```
|
|
59
102
|
|
|
60
|
-
|
|
103
|
+
标记之外的内容不会被改动;升级 NiceEval 后重跑 `npx niceeval init` 会刷新标记之内的指引,且每次都会重新按上述规则判断该写哪份文件。npm 包只随包发布中文文档(`node_modules/niceeval/docs-site/zh/`)——中文是唯一始终与代码同步的准绳版本,agent 读中文没有障碍,读到的永远是和安装版本一致的行为,而不是训练数据里的旧记忆。
|
|
61
104
|
|
|
62
105
|
## 报错自带修法
|
|
63
106
|
|
|
64
|
-
|
|
107
|
+
配置和运行期错误直接给出修法与随包文档路径,agent 读到报错就能自修,不必先搜索:
|
|
65
108
|
|
|
66
109
|
```text
|
|
67
110
|
Could not find niceeval.config.ts.
|
|
68
111
|
Ways to fix:
|
|
69
112
|
- [init] Run `npx niceeval init` to scaffold niceeval.config.ts and evals/
|
|
70
113
|
- [cd] Run from the project root that contains niceeval.config.ts
|
|
71
|
-
Docs: node_modules/niceeval/docs-site/quickstart.mdx
|
|
114
|
+
Docs: node_modules/niceeval/docs-site/zh/quickstart.mdx
|
|
72
115
|
```
|
|
73
116
|
|
|
74
|
-
##
|
|
117
|
+
## 人与 agent 看同一份工件
|
|
75
118
|
|
|
76
|
-
|
|
119
|
+
`niceeval show` 和 `niceeval view` 读的是同一批落盘工件,人和 agent 随时可以互相接手:
|
|
77
120
|
|
|
78
|
-
-
|
|
79
|
-
-
|
|
121
|
+
- 人在 `npx niceeval view` 里看到失败时,榜单右上角的 **Copy fix prompt** 把全部失败打包成一段可直接粘给 agent 的修复 prompt——失败清单、artifacts 路径、判断与重跑步骤都在里面,教的读法与本页同一套:`niceeval show` 下钻、判断缺陷在哪一侧、重跑前缀、`--force` 收尾。打开单个 attempt 的弹窗,同名按钮只打包这一条失败,适合逐条转交。
|
|
122
|
+
- 反过来,agent 修完之后人想核对现场,`npx niceeval view` 打开的就是 agent 刚刚在终端里读过的那份结果。
|
|
@@ -116,9 +116,9 @@ npx niceeval view # 本地查看器里看结果
|
|
|
116
116
|
配置归属只有两条通道,分清它们,接入就不会乱:
|
|
117
117
|
|
|
118
118
|
1. **静态配置走 Adapter 工厂。** URL、鉴权、协议细节这类"这个环境长什么样"的配置,作为工厂参数写在 experiment 文件里——`defineExperiment` 的 `agent` 字段放的是**已经配置好的实例**。
|
|
119
|
-
2. **每轮动态值走 `ctx`。** experiment 声明的 `model`、`
|
|
119
|
+
2. **每轮动态值走 `ctx`。** experiment 声明的 `model`、`params`,运行器每轮经 `ctx` 原样递给 `send`;Adapter 不解释它们的含义,只随请求转发给应用。
|
|
120
120
|
|
|
121
|
-
两条通道拼在一起,就是把第一步的 my-bot 从「写死 URL 的实例」改成「收配置的工厂」:default export 换成一个返回 `defineAgent(...)` 的函数,`send` 里写死的地方换成读工厂参数;experiment 声明的 `model`、`
|
|
121
|
+
两条通道拼在一起,就是把第一步的 my-bot 从「写死 URL 的实例」改成「收配置的工厂」:default export 换成一个返回 `defineAgent(...)` 的函数,`send` 里写死的地方换成读工厂参数;experiment 声明的 `model`、`params` 每轮从 `ctx` 到达,`send` 里随请求转发:
|
|
122
122
|
|
|
123
123
|
```ts
|
|
124
124
|
// agents/my-bot.ts —— 工厂:静态配置进闭包
|
|
@@ -135,7 +135,7 @@ export function myBot(options: { baseUrl: string }): Agent {
|
|
|
135
135
|
body: JSON.stringify({
|
|
136
136
|
message: input.text,
|
|
137
137
|
model: ctx.model, // ← experiment.model:应用接口收模型选择就转发
|
|
138
|
-
|
|
138
|
+
params: ctx.params, // ← experiment.params:原样交给应用,应用按参数切换
|
|
139
139
|
}),
|
|
140
140
|
signal: ctx.signal, // ← 运行器的超时与取消:挂到每个请求上
|
|
141
141
|
});
|
|
@@ -160,7 +160,7 @@ export default defineExperiment({
|
|
|
160
160
|
description: "my-bot 基线",
|
|
161
161
|
agent: myBot({ baseUrl: "http://localhost:3000" }), // ← 静态配置:实例化时传
|
|
162
162
|
model: "gpt-5.4", // ← 动态值:经 ctx.model 到 send
|
|
163
|
-
|
|
163
|
+
params: { promptVariant: "concise" }, // ← 动态值:经 ctx.params 到 send
|
|
164
164
|
});
|
|
165
165
|
```
|
|
166
166
|
|
|
@@ -170,7 +170,7 @@ export default defineExperiment({
|
|
|
170
170
|
|---|---|---|
|
|
171
171
|
| `signal` | 运行器(超时与取消) | 挂到发出的每个请求上 |
|
|
172
172
|
| `model` | experiment 的 `model` | 应用接口收模型选择就随请求转发;不收就忽略 |
|
|
173
|
-
| `
|
|
173
|
+
| `params` | experiment 的 `params` | 原样转发(请求体、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
|
| `log(msg)` | 运行器 | 写进本轮日志,`niceeval view` 里可见 |
|
|
@@ -208,7 +208,7 @@ npx niceeval exp prod
|
|
|
208
208
|
| 多轮对话、`t.newSession()` 隔离 | 接上 `ctx.session`:`history()` 或 `id` + `capture()` | [写 send](/zh/guides/write-send) |
|
|
209
209
|
| 审批流(HITL,人工介入) | 停轮返回 `waiting` + `input.requested`,回答轮续跑 | [HITL](/zh/concepts/hitl) |
|
|
210
210
|
| `niceeval view` 的调用瀑布图 | 应用把 OTel span 发给 NiceEval(不影响断言) | [OTel 接入](/zh/guides/connect-otel) |
|
|
211
|
-
| feature A/B 对比 | 应用把变体暴露成 `
|
|
211
|
+
| feature A/B 对比 | 应用把变体暴露成 `params` 可切换的配置 | [Tier](/zh/concepts/tier)、[写实验](/zh/guides/write-experiment) |
|
|
212
212
|
|
|
213
213
|
这些增量落在哪一档、各档买到什么,见 [Tier](/zh/concepts/tier)。
|
|
214
214
|
|
|
@@ -219,7 +219,7 @@ npx niceeval exp prod
|
|
|
219
219
|
## 相关阅读
|
|
220
220
|
|
|
221
221
|
- [官方适配器一览](/zh/guides/official-adapters) —— 被测对象能直接用官方 Adapter 时,Sandbox / 非 Sandbox 各有哪些、配置项怎么写。
|
|
222
|
-
- [写 send](/zh/guides/write-send) —— 手写 Adapter 的完整教程:七步递进,从发一条消息到 HITL、OTel、
|
|
222
|
+
- [写 send](/zh/guides/write-send) —— 手写 Adapter 的完整教程:七步递进,从发一条消息到 HITL、OTel、params。
|
|
223
223
|
- [Adapter](/zh/concepts/adapter) —— `send` 的契约:`TurnInput` / `AgentContext` / `Turn` 逐字段。
|
|
224
224
|
- [OTel 接入](/zh/guides/connect-otel) —— 把应用的 span 也发给 [NiceEval](https://niceeval.com/),换 `niceeval view` 的调用瀑布图。
|
|
225
225
|
- [Tier](/zh/concepts/tier) —— 三档接入各投入什么、买到什么。
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "用报告积木搭自己的口径"
|
|
3
|
+
sidebarTitle: "自定义报告"
|
|
4
|
+
description: "一份报告就是一个报告文件:官方宿主打开结果、注入数据,Row / Col 负责排版,双面组件同时渲染网页与终端——niceeval show 与 niceeval view 渲染的官方榜单,就是出厂预装的那份报告。"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
[查看结果](/zh/guides/viewing-results)讲「用」:官方两扇门 `niceeval show`(终端)和 `niceeval view`(网页)怎么看。本页讲「写」:官方摆法不够时,怎么写一份自己的报告——考试成绩单、代码行数榜、质量 × 成本 frontier。
|
|
8
|
+
|
|
9
|
+
一份报告就是一个报告文件。你不用打开结果目录、不用写渲染代码、不用起自己的应用:`niceeval show` / `niceeval view` 本体就是宿主——替你打开结果、把数据注入进来,你用官方组件和 `Row` / `Col` 摆版面,写完把文件路径递给 `--report`,终端和网页两扇门就都认它——官方的证据室深链、`--run` 历史切换、静态导出,自定义报告全部原样享有。
|
|
10
|
+
|
|
11
|
+
## 官方默认报告也是一份报告文件
|
|
12
|
+
|
|
13
|
+
`niceeval view` 和 `niceeval show` 渲染的官方榜单不是私有实现,而是出厂预装的一份报告:`niceeval view` 就等于 `niceeval view --report <内置默认报告>`,`show` 同理。我们自己用的零件和给你的完全一样,没有私有数据通道,官方榜单上的每个数字你都能用同样几行代码原样重算出来。
|
|
14
|
+
|
|
15
|
+
| | 网页(人看) | 终端(agent 和你看) |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| 官方摆法 | `niceeval view` | `niceeval show` |
|
|
18
|
+
| 自定义摆法 | `niceeval view --report reports/exam.tsx` | `niceeval show --report reports/exam.tsx` |
|
|
19
|
+
|
|
20
|
+
四个格子是同一个宿主。自定义只有三个层次,逐层深入:
|
|
21
|
+
|
|
22
|
+
1. **换摆法**:用官方组件和 `Row` / `Col` 重摆版面——大多数「要自己的口径」到这层就够了。
|
|
23
|
+
2. **换口径**:`defineMetric` 定义自己的指标,`param()` 把 experiment 声明的变量当维度和轴。
|
|
24
|
+
3. **换形态**:官方组件表达不了的展示,用 `defineComponent` 写自己的组件——网页怎么渲染、终端字符怎么排,两个面你都说了算。
|
|
25
|
+
|
|
26
|
+
## 一份报告 = 一个报告文件
|
|
27
|
+
|
|
28
|
+
先交代唯一的前置:报告文件是 `.tsx`,写它的项目要装 `react`(写自定义组件的 web 面还要 `@types/react`),tsconfig 里 `compilerOptions.jsx` 设为 `"react-jsx"`。裸跑 `niceeval show` / `niceeval view` 不需要这些——只有自己写报告文件才需要。
|
|
29
|
+
|
|
30
|
+
报告基座是 `defineReport`:宿主打开结果目录(含 `--run` 指定历史 run)、按官方口径挑好快照,注入给你的函数;你只负责折数据和摆积木:
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
// reports/exam.tsx —— 一份定义,两扇门共用
|
|
34
|
+
import {
|
|
35
|
+
defineReport, Col, Section,
|
|
36
|
+
DefaultReport, Scoreboard,
|
|
37
|
+
} from "niceeval/report";
|
|
38
|
+
|
|
39
|
+
export default defineReport(async ({ selection }) => (
|
|
40
|
+
<Col>
|
|
41
|
+
<DefaultReport />
|
|
42
|
+
{/* 官方水位原样铺进来,不要就删这行 */}
|
|
43
|
+
<Section title="考试成绩单">
|
|
44
|
+
<Scoreboard data={await Scoreboard.data(selection, { rows: "agent", subjects: "evalGroup" })} />
|
|
45
|
+
</Section>
|
|
46
|
+
</Col>
|
|
47
|
+
));
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
niceeval show --report reports/exam.tsx # 终端:同一棵树走文本面
|
|
52
|
+
niceeval view --report reports/exam.tsx # 网页:同一棵树走网页面,attempt 深链直达证据室
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
注入的上下文只有两样,全部是[结果数据 API](/zh/guides/results-data) 的原语,宿主没有私有通道:`results` 就是 `openResults(".niceeval")` 的返回(实验、历次快照、attempt 级 `diff()`、`trace()` 都在上面);`selection` 就是 `results.latest()` 的返回选集——宿主按「每个实验取最新」挑好的现刻水位,与官方榜单同口径,快照在 `selection.snapshots`,挑选警告在 `selection.warnings`。组件的 `data` 函数直接吃选集,警告随行——`RunOverview` 会把残缺快照的警告如实显示,不用你另外接线;默认挑法不合口径时,拿 `results` 自己挑,手工挑的 `Snapshot[]` 数组同样能喂给每个 `data` 函数。
|
|
56
|
+
|
|
57
|
+
命令行的范围先作用在挑选上,报告拿到的就是范围内的水位:位置参数的 eval id 前缀收窄选集覆盖的 eval(覆盖警告的分母同样收窄到范围内),`--run` 把结果根换成指定目录,`--experiment` 让选集只留该实验。`--history` 与 `--report` 互斥——趋势在报告里用 `exp.snapshots` 自己摆;证据切面(`--transcript` / `--trace` / `--diff`)走证据室,不渲染报告槽。
|
|
58
|
+
|
|
59
|
+
`<DefaultReport />` 零 props、纯声明:官方水位只读瘦身条目(不碰 diff 这类重工件),宿主总是替你备好,摆不摆在你。它渲染的是**宿主注入的那份选集**的官方水位——跟随命令行范围,但不跟随你在报告里自己另挑的口径;这正是它的意义:当官方锚点,和你的口径并排对照。要「官方摆法 + 自选口径」,用它脚下的同一批组件自己摆——它没有任何私有零件。
|
|
60
|
+
|
|
61
|
+
页面里的每个组件都是**双面**的:网页面是 React 渲染,终端面是字符渲染,两面吃同一份算好的数据。你在报告文件里只见到组件本身,从不碰某一面——走哪扇门由宿主决定。官方组件覆盖常见摆法——榜单 `MetricTable`、逐题格子 `MetricMatrix`、分组条形 `MetricBars`、成绩单 `Scoreboard`、质量 × 成本 frontier `MetricScatter`、趋势线 `MetricLine`、成对对比 `DeltaTable`、失败清单 `CaseList`——每个组件回答什么问题、怎么调用、终端字符输出长什么样,逐个见[报告组件](/zh/guides/report-components)。它们在宿主里自动接上证据室:网页面的格子深链到 attempt 弹窗,终端面在条目下印出 `niceeval show <eval id>` 下钻命令。
|
|
62
|
+
|
|
63
|
+
默认榜单没有特权:报告槽只有一个,`<DefaultReport />` 只是出厂填充。「官方水位 + 自己的口径」像上面那样把它摆进自己的树;不要官方水位就不摆——组合发生在你的 tsx 里,没有 tab、没有注册表、没有配置。
|
|
64
|
+
|
|
65
|
+
## 排版:Row 与 Col 在两个面都成立
|
|
66
|
+
|
|
67
|
+
排版原语也是双面组件,一次摆放,两个面各自成立:
|
|
68
|
+
|
|
69
|
+
- **`<Col>`**:纵向依次排列——网页是块级堆叠,终端是逐块输出。
|
|
70
|
+
- **`<Row>`**:并排——网页是横向排布,终端是字符分栏;终端宽度不够时自动降级为纵向,不硬挤。
|
|
71
|
+
- **`<Section title="…">`**:带标题的块,网页是标题层级,终端是标题行加缩进。
|
|
72
|
+
- **`<Text>`**:说明文字,网页是段落,终端是折行文本。
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
<Row>
|
|
76
|
+
<Scoreboard data={board} />
|
|
77
|
+
<MetricTable data={bench} />
|
|
78
|
+
</Row>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
$ niceeval show --report reports/exam.tsx
|
|
83
|
+
考试成绩单 │ benchmark 榜
|
|
84
|
+
agent 总分 algebra geometry │ agent pass cost
|
|
85
|
+
bub 86.5/100 45/50 41.5/50 │ bub 87% $0.42
|
|
86
|
+
codex 78.0/100 40/50 38/50 │ codex 80% $0.51
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
页面树里只放双面组件和排版原语,不放裸 HTML 标签——终端面没法渲染一个 `<div>`。要自由内容,说明文字用 `<Text>`,更自由的走下面的自定义组件。
|
|
90
|
+
|
|
91
|
+
## 换口径:自定义指标
|
|
92
|
+
|
|
93
|
+
官方榜单的口径拆开就是 `MetricTable.data(selection, { rows: "experiment", columns: [passRate, costUSD, durationMs] })`——每个组件的数据算法挂在它自己身上,组件名后打个点就找到;换口径只是把 columns 换成自己的指标:
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
// reports/golf.tsx —— code-golf:谁写出能用的代码,谁写得短
|
|
97
|
+
import {
|
|
98
|
+
defineReport, Col, MetricTable,
|
|
99
|
+
defineMetric, passRate, costUSD,
|
|
100
|
+
} from "niceeval/report";
|
|
101
|
+
|
|
102
|
+
// 项目自己的口径:只比能用的代码的行数
|
|
103
|
+
const codeLines = defineMetric({
|
|
104
|
+
name: "code-lines",
|
|
105
|
+
label: "代码行数",
|
|
106
|
+
better: "lower",
|
|
107
|
+
unit: "lines",
|
|
108
|
+
where: (a) => a.result.outcome === "passed",
|
|
109
|
+
async value(attempt) {
|
|
110
|
+
const diff = await attempt.diff();
|
|
111
|
+
if (!diff) return null;
|
|
112
|
+
return Object.values(diff.generatedFiles)
|
|
113
|
+
.reduce((n, src) => n + src.split("\n").length, 0);
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
export default defineReport(async ({ selection }) => (
|
|
118
|
+
<Col>
|
|
119
|
+
<MetricTable data={await MetricTable.data(selection, {
|
|
120
|
+
rows: "agent",
|
|
121
|
+
columns: [passRate, codeLines, costUSD],
|
|
122
|
+
sort: passRate,
|
|
123
|
+
})} />
|
|
124
|
+
</Col>
|
|
125
|
+
));
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
$ niceeval show --report reports/golf.tsx
|
|
130
|
+
agent pass rate code lines cost
|
|
131
|
+
bub 87% 312 lines $0.42
|
|
132
|
+
codex 80% 355 lines $0.51
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
格子里的终值是**两级折叠**出来的:同一道题的多个 attempt 先折成题级值,再跨题折成格子值,两级默认都是平均。分两级不是形式主义——失败的题天然比通过的题 attempt 多(重试跑满、通过即停),平铺求均值会让分数和重试策略纠缠在一起。两级都能在 `defineMetric` 的 `aggregate` 里换:`aggregate: { perEval: "max", across: "mean" }` 就是 pass@k(题内取最好一次,跨题取占比)。
|
|
136
|
+
|
|
137
|
+
两个面继承同一套诚实契约:排序方向随指标的 `better`,覆盖不全的格子带 `12/15` 角标(15 个 attempt 里 12 个测得了该指标),缺数据渲染成 `—` 而不是 0。指标只定义一次,两个面共用——人在网页上看到的数字,就是 agent 在 stdout 里读到的数字,判断口径永远一致。给 agent 的指引也只多一行:跑 `niceeval show --report reports/golf.tsx`,读 stdout。
|
|
138
|
+
|
|
139
|
+
## 变量来自配置,不来自命名
|
|
140
|
+
|
|
141
|
+
画「并行 agent 数 × 模拟延迟 × 得分」这类 scaling 趋势时,图上的变量不该编码进 experiment id(`ultra-16agents-300ms`),再在报告里解析字符串抠出来——那是约定不是配置,改个命名整张图就散。变量的家是 experiment 文件的 `params`:一文件一个档位,把变量声明成数据:
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
// experiments/ultra/agents-16.ts
|
|
145
|
+
import { defineExperiment } from "niceeval";
|
|
146
|
+
import { bub } from "../../agents/bub.ts";
|
|
147
|
+
|
|
148
|
+
export default defineExperiment({
|
|
149
|
+
agent: bub({ mode: "ultra" }),
|
|
150
|
+
params: { agents: 16, latencyMs: 300 },
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
报告侧用 `param()` 把声明的值直接当维度或轴用,不解析任何名字:
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
<MetricLine data={await MetricLine.data(selection, {
|
|
158
|
+
x: param("latencyMs", { label: "Simulated latency", unit: "ms" }),
|
|
159
|
+
series: param("agents", { label: (v) => `${v} agents` }),
|
|
160
|
+
y: passRate,
|
|
161
|
+
})} />
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`param()` 用在维度槽(`series` / `rows` / `columns` / `points`)就按声明值分组,用在轴槽(`x`)就要求数值并驱动刻度;内置维度(`agent`、`model`、`experiment`、`evalGroup`……)覆盖不了的变量都走它。没有声明该 param 的 experiment 不猜:分组时如实归入「未配置」一组,作轴时该点不画、注脚报数。params 随快照落盘,所以历史 run 也能按当时声明的变量重摆——报告读的是配置的事实,不是命名的巧合。
|
|
165
|
+
|
|
166
|
+
## 换形态:自定义组件,一个定义两个面
|
|
167
|
+
|
|
168
|
+
官方组件表达不了的展示——通过率条形、预算燃尽、项目自己的徽章——用 `defineComponent` 声明两个渲染面,和 `defineExperiment` / `defineMetric` / `defineReport` 同一个家族:
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
// reports/passbars.tsx
|
|
172
|
+
import { defineReport, defineComponent, Col, Style, MetricTable, passRate } from "niceeval/report";
|
|
173
|
+
|
|
174
|
+
interface BarRow { key: string; ratio: number | null; display: string }
|
|
175
|
+
|
|
176
|
+
const PassBars = defineComponent<{ rows: BarRow[] }>({
|
|
177
|
+
web({ rows }) {
|
|
178
|
+
return (
|
|
179
|
+
<ul className="passbars">
|
|
180
|
+
{rows.map((r) => (
|
|
181
|
+
<li key={r.key}>
|
|
182
|
+
<span>{r.key}</span>
|
|
183
|
+
<i style={{ width: `${(r.ratio ?? 0) * 100}%` }} />
|
|
184
|
+
<b>{r.ratio === null ? "—" : r.display}</b>
|
|
185
|
+
</li>
|
|
186
|
+
))}
|
|
187
|
+
</ul>
|
|
188
|
+
);
|
|
189
|
+
},
|
|
190
|
+
text({ rows }, { width }) {
|
|
191
|
+
const bar = (n: number) => "█".repeat(Math.round(n * 10)).padEnd(10, "░");
|
|
192
|
+
return rows
|
|
193
|
+
.map((r) => `${r.key.padEnd(8)} ${r.ratio === null ? "—".padEnd(10) : bar(r.ratio)} ${r.display}`)
|
|
194
|
+
.join("\n");
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
export default defineReport(async ({ selection }) => {
|
|
199
|
+
const board = await MetricTable.data(selection, { rows: "agent", columns: [passRate] });
|
|
200
|
+
const rows = board.rows.map((r) => ({
|
|
201
|
+
key: r.key,
|
|
202
|
+
ratio: r.cells[passRate.name].value, // 格子键锚在指标对象上,不裸写字符串
|
|
203
|
+
display: r.cells[passRate.name].display,
|
|
204
|
+
}));
|
|
205
|
+
return (
|
|
206
|
+
<Col>
|
|
207
|
+
<Style>{`.passbars li { display: flex; gap: 8px; } .passbars i { background: #4a7; height: 12px; }`}</Style>
|
|
208
|
+
<PassBars rows={rows} />
|
|
209
|
+
</Col>
|
|
210
|
+
);
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
```text
|
|
215
|
+
$ niceeval show --report reports/passbars.tsx
|
|
216
|
+
bub █████████░ 87%
|
|
217
|
+
codex ████████░░ 80%
|
|
218
|
+
claude — —
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
组件的契约:
|
|
222
|
+
|
|
223
|
+
- **计算在报告函数体里,渲染面是纯函数。** 读 attempt 句柄、`await` 折数据都发生在 `defineReport` 的函数体里;`web` 和 `text` 只认算好的数据,零 IO、同步——这条边界让同一棵树能被烘进静态导出。
|
|
224
|
+
- **面收第二参上下文。** `text(props, ctx)` 的 `ctx.width` 是可用列宽(`Row` 分栏后会变窄),`ctx.attemptCommand(ref)` 生成下钻命令;`web(props, ctx)` 的 `ctx.attemptHref(ref)` 生成证据室深链——自定义组件和官方组件通到同一间证据室。
|
|
225
|
+
- **网页面静态渲染,不 hydrate。** 宿主在计算侧把 `web` 面渲染成静态 HTML,不打包你的代码进查看器:交互用普通链接和 `<details>`,与官方组件同一条静态契约。
|
|
226
|
+
- **样式随树带走。** 静态导出不打包你的代码,`className` 引用的 CSS 用内置原语 `<Style>{css}</Style>` 放进页面树——web 面吐 `<style>` 标签,text 面渲染为空,上面的例子就是这么给 `.passbars` 上样式的。
|
|
227
|
+
- **诚实契约同样适用。** 缺数据渲染 `—` 不补 0,截断如实标注剩余数量——上面例子里 `claude` 没有样本就是 `—`。
|
|
228
|
+
|
|
229
|
+
## 发布:导出即静态站
|
|
230
|
+
|
|
231
|
+
发布自定义报告和发布官方查看器是同一个动作——`--out` 加上 `--report`:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
niceeval view --report reports/exam.tsx --out site
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
产物是纯静态文件:你的报告页是首页,证据室(transcript、trace、代码视图)在同一站内,报告里的每个数字点进去就是证据。组件不 hydrate,产物不需要任何客户端 JS,交给任何静态托管即可。CI 上没有 `.niceeval/` 时,先用 `copySnapshots` 把快照瘦身进仓库再导出,流程见[查看结果](/zh/guides/viewing-results#导出与静态托管)。
|
|
238
|
+
|
|
239
|
+
双面组件的网页面就是普通 React 组件,`data` 函数就是普通 TS 函数——想把某一块指标表嵌进已有的内部面板,import 组件喂数据就行。那是零件的复用,不是另一套报告系统:报告的家在官方宿主。算数据与渲染分离部署时(CI 落 JSON、另一个应用 fetch),两侧锁同一个 niceeval 版本是硬要求——组件数据不带版本戳,兼容性跟随包版本。
|
|
240
|
+
|
|
241
|
+
## 界线:内置命令不长配置
|
|
242
|
+
|
|
243
|
+
报告槽只有一个,`--report` 收显式文件路径——没有 `reports/` 目录自动发现、没有插件注册表、没有配置文件。自定义指标和自定义组件都住在你的报告文件里,随文件一起递入,宿主不为它们长任何注册面。默认报告只是报告槽的出厂填充,你的报告和它是同级实现。
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "实验矩阵:用运行矩阵比较 agents 和 models"
|
|
3
3
|
sidebarTitle: "实验矩阵"
|
|
4
|
-
description: "使用 NiceEval experiments 让同一批 eval 横跨多个 agents、models 和
|
|
4
|
+
description: "使用 NiceEval experiments 让同一批 eval 横跨多个 agents、models 和 params,比较 pass rate、成本和延迟。"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
Experiment 用于比较多组运行配置。典型问题包括:Claude Code 和 Codex 谁在同一批 coding-agent 任务上 pass rate 更高?某个 prompt 改动是否降低成本?不同模型的延迟和质量如何取舍?
|
|
@@ -37,13 +37,13 @@ npx niceeval exp compare-models # 把同组各 model 并排出报告
|
|
|
37
37
|
|
|
38
38
|
这样每个配置独立成文件:可命名、可 diff、可单独 review,"这一组就是对照"在文件结构上就讲清楚了。
|
|
39
39
|
|
|
40
|
-
`defineExperiment` 的字段怎么写、`
|
|
40
|
+
`defineExperiment` 的字段怎么写、`params` 怎么透传到 adapter,见[写实验](/zh/guides/write-experiment)。
|
|
41
41
|
|
|
42
42
|
## 适合比较什么
|
|
43
43
|
|
|
44
44
|
- 不同 Adapters。
|
|
45
45
|
- 不同模型(Tier 1 接入即可:应用接口暴露模型选择,`model` 经 `ctx.model` 透传)。
|
|
46
|
-
- 不同 prompts 或 feature
|
|
46
|
+
- 不同 prompts 或 feature 开关(要求 Tier 3 接入:变体在应用内部,需要应用把它暴露成 experiment 可选的配置,经 `params` → `ctx.params` 透传)。
|
|
47
47
|
- 不同 sandbox 后端。
|
|
48
48
|
- 同一任务的 pass@N。
|
|
49
49
|
|