niceeval 0.5.3 → 0.6.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.
- package/INDEX.md +55 -0
- package/dist/agents/types.d.ts +317 -0
- package/dist/context/types.d.ts +335 -0
- package/dist/i18n/core.d.ts +11 -0
- package/dist/i18n/en.d.ts +181 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/zh-CN.d.ts +183 -0
- package/dist/o11y/execution-tree.d.ts +103 -0
- package/dist/o11y/otlp/parse.d.ts +3 -0
- package/dist/o11y/otlp/receiver.d.ts +21 -0
- package/dist/o11y/otlp/select.d.ts +22 -0
- package/dist/o11y/otlp/turn-otel.d.ts +43 -0
- package/dist/o11y/types.d.ts +190 -0
- package/dist/report/aggregate.d.ts +57 -0
- package/dist/report/aggregate.js +212 -0
- package/dist/report/built-ins/cost-pass-rate-comparison.d.ts +1 -0
- package/dist/report/built-ins/cost-pass-rate-comparison.js +17 -0
- package/dist/report/built-ins/index.d.ts +1 -0
- package/dist/report/built-ins/index.js +2 -0
- package/dist/report/components.d.ts +178 -0
- package/dist/report/components.js +132 -0
- package/dist/report/compute.d.ts +106 -0
- package/dist/report/compute.js +618 -0
- package/dist/report/flag.d.ts +6 -0
- package/dist/report/flag.js +8 -0
- package/dist/report/format.d.ts +32 -0
- package/dist/report/format.js +126 -0
- package/dist/report/index.d.ts +19 -0
- package/dist/report/index.js +30 -0
- package/dist/report/load.d.ts +12 -0
- package/dist/report/load.js +56 -0
- package/dist/report/locale.d.ts +83 -0
- package/dist/report/locale.js +160 -0
- package/dist/report/metrics.d.ts +21 -0
- package/dist/report/metrics.js +115 -0
- package/dist/report/primitives.d.ts +64 -0
- package/dist/report/primitives.js +113 -0
- package/dist/report/react/AttemptList.d.ts +23 -0
- package/dist/report/react/AttemptList.js +20 -0
- package/dist/report/react/DeltaTable.d.ts +8 -0
- package/dist/report/react/DeltaTable.js +24 -0
- package/dist/report/react/EvalList.d.ts +10 -0
- package/dist/report/react/EvalList.js +0 -0
- package/dist/report/react/ExperimentList.d.ts +10 -0
- package/dist/report/react/ExperimentList.js +15 -0
- package/dist/report/react/GroupSummary.d.ts +8 -0
- package/dist/report/react/GroupSummary.js +8 -0
- package/dist/report/react/MetricBars.d.ts +10 -0
- package/dist/report/react/MetricBars.js +48 -0
- package/dist/report/react/MetricLine.d.ts +9 -0
- package/dist/report/react/MetricLine.js +68 -0
- package/dist/report/react/MetricMatrix.d.ts +10 -0
- package/dist/report/react/MetricMatrix.js +23 -0
- package/dist/report/react/MetricScatter.d.ts +9 -0
- package/dist/report/react/MetricScatter.js +100 -0
- package/dist/report/react/MetricTable.d.ts +11 -0
- package/dist/report/react/MetricTable.js +26 -0
- package/dist/report/react/RunOverview.d.ts +8 -0
- package/dist/report/react/RunOverview.js +12 -0
- package/dist/report/react/Scoreboard.d.ts +8 -0
- package/dist/report/react/Scoreboard.js +26 -0
- package/dist/report/react/cell.d.ts +9 -0
- package/dist/report/react/cell.js +9 -0
- package/dist/report/react/chart-math.d.ts +14 -0
- package/dist/report/react/chart-math.js +68 -0
- package/dist/report/react/colors.d.ts +14 -0
- package/dist/report/react/colors.js +45 -0
- package/dist/report/react/fixtures.d.ts +19 -0
- package/dist/report/react/fixtures.js +393 -0
- package/dist/report/react/format.d.ts +3 -0
- package/dist/report/react/format.js +7 -0
- package/dist/report/react/index.d.ts +6 -0
- package/dist/report/react/index.js +14 -0
- package/dist/report/report.d.ts +24 -0
- package/dist/report/report.js +49 -0
- package/dist/report/text/faces.d.ts +29 -0
- package/dist/report/text/faces.js +503 -0
- package/dist/report/text/layout.d.ts +75 -0
- package/dist/report/text/layout.js +173 -0
- package/dist/report/text/plot.d.ts +37 -0
- package/dist/report/text/plot.js +81 -0
- package/dist/report/text/table.d.ts +8 -0
- package/dist/report/text/table.js +86 -0
- package/dist/report/tree.d.ts +99 -0
- package/dist/report/tree.js +228 -0
- package/dist/report/types.d.ts +420 -0
- package/dist/report/types.js +4 -0
- package/dist/report/web.d.ts +15 -0
- package/dist/report/web.js +37 -0
- package/dist/results/annotated-source.d.ts +61 -0
- package/dist/results/attempt-evidence.d.ts +69 -0
- package/dist/results/attempt-source.d.ts +15 -0
- package/dist/results/locator.d.ts +75 -0
- package/dist/results/locator.js +0 -0
- package/dist/results/select.d.ts +45 -0
- package/dist/results/select.js +230 -0
- package/dist/results/source-hash.d.ts +9 -0
- package/dist/results/types.d.ts +204 -0
- package/dist/runner/eval-source.d.ts +22 -0
- package/dist/runner/fingerprint.d.ts +23 -0
- package/dist/runner/types.d.ts +332 -0
- package/dist/sandbox/docker-stream.d.ts +24 -0
- package/dist/sandbox/docker.d.ts +84 -0
- package/dist/sandbox/e2b.d.ts +31 -0
- package/dist/sandbox/errors.d.ts +4 -0
- package/dist/sandbox/local-files.d.ts +6 -0
- package/dist/sandbox/paths.d.ts +4 -0
- package/dist/sandbox/registry.d.ts +13 -0
- package/dist/sandbox/resolve.d.ts +40 -0
- package/dist/sandbox/retry.d.ts +10 -0
- package/dist/sandbox/shell.d.ts +11 -0
- package/dist/sandbox/source-files.d.ts +13 -0
- package/dist/sandbox/types.d.ts +203 -0
- package/dist/sandbox/vercel.d.ts +37 -0
- package/dist/scoring/types.d.ts +70 -0
- package/dist/shared/aggregate.d.ts +29 -0
- package/dist/shared/aggregate.js +55 -0
- package/dist/shared/types.d.ts +27 -0
- package/dist/shared/verdict.d.ts +27 -0
- package/dist/shared/verdict.js +34 -0
- package/dist/tty-line.d.ts +6 -0
- package/dist/types.d.ts +7 -0
- package/dist/util.d.ts +22 -0
- package/docs-site/zh/concepts/adapter.mdx +9 -4
- package/docs-site/zh/concepts/assert.mdx +8 -8
- package/docs-site/zh/concepts/evals.mdx +5 -5
- package/docs-site/zh/concepts/experiment.mdx +3 -1
- package/docs-site/zh/concepts/judge.mdx +9 -9
- package/docs-site/zh/concepts/overview.mdx +2 -2
- package/docs-site/zh/concepts/tier.mdx +6 -6
- package/docs-site/zh/example/claude-code-codex-plugin.mdx +9 -5
- package/docs-site/zh/example/claude-code-codex-skill.mdx +12 -7
- package/docs-site/zh/guides/agent-feedback-loop.mdx +235 -78
- package/docs-site/zh/guides/connect-otel.mdx +3 -3
- package/docs-site/zh/guides/connect-your-agent.mdx +2 -2
- package/docs-site/zh/guides/custom-reports.mdx +196 -36
- package/docs-site/zh/guides/experiments.mdx +7 -0
- package/docs-site/zh/guides/fixtures.mdx +1 -1
- package/docs-site/zh/guides/official-adapters.mdx +31 -9
- package/docs-site/zh/guides/publish-report.mdx +4 -4
- package/docs-site/zh/guides/report-components.mdx +194 -55
- package/docs-site/zh/guides/reporters.mdx +2 -2
- package/docs-site/zh/guides/results-data.mdx +35 -34
- package/docs-site/zh/guides/runner.mdx +2 -2
- package/docs-site/zh/guides/sandbox-agent.mdx +10 -5
- package/docs-site/zh/guides/sandbox-providers.mdx +102 -0
- package/docs-site/zh/guides/viewing-results.mdx +199 -79
- package/docs-site/zh/guides/write-experiment.mdx +23 -1
- package/docs-site/zh/guides/write-send.mdx +2 -2
- package/docs-site/zh/index.mdx +4 -3
- package/docs-site/zh/introduction.mdx +2 -1
- package/docs-site/zh/quickstart.mdx +5 -3
- package/docs-site/zh/reference/builtin-agents.mdx +38 -13
- package/docs-site/zh/reference/capabilities.mdx +1 -1
- package/docs-site/zh/reference/cli.mdx +36 -13
- package/docs-site/zh/reference/define-agent.mdx +24 -12
- package/docs-site/zh/reference/define-config.mdx +1 -1
- package/docs-site/zh/reference/define-eval.mdx +3 -3
- package/docs-site/zh/reference/events.mdx +7 -1
- package/docs-site/zh/reference/expect.mdx +3 -3
- package/package.json +10 -6
- package/src/agents/bub.ts +134 -43
- package/src/agents/claude-code.test.ts +152 -0
- package/src/agents/claude-code.ts +127 -13
- package/src/agents/codex.test.ts +192 -0
- package/src/agents/codex.ts +151 -12
- package/src/agents/index.ts +11 -3
- package/src/agents/manifest.ts +41 -0
- package/src/agents/plugin-config.test.ts +94 -0
- package/src/agents/shared.ts +33 -2
- package/src/agents/skills.test.ts +214 -0
- package/src/agents/skills.ts +304 -0
- package/src/agents/streaming.ts +5 -5
- package/src/agents/types.ts +62 -2
- package/src/cli.ts +74 -47
- package/src/context/context.test.ts +20 -2
- package/src/context/context.ts +3 -0
- package/src/context/control-flow.ts +1 -1
- package/src/context/session.test.ts +92 -1
- package/src/context/session.ts +26 -1
- package/src/context/types.ts +4 -4
- package/src/define.test.ts +82 -0
- package/src/define.ts +72 -11
- package/src/expect/index.ts +5 -5
- package/src/i18n/en.ts +36 -15
- package/src/i18n/zh-CN.ts +39 -21
- package/src/index.ts +1 -1
- package/src/o11y/execution-tree.test.ts +452 -0
- package/src/o11y/execution-tree.ts +367 -0
- package/src/o11y/otlp/receiver.ts +1 -1
- package/src/o11y/otlp/select.ts +7 -3
- package/src/o11y/parsers/claude-code.test.ts +175 -0
- package/src/o11y/parsers/claude-code.ts +32 -0
- package/src/o11y/types.ts +6 -1
- package/src/report/aggregate.ts +26 -20
- package/src/report/built-in-user-parity.test.tsx +643 -0
- package/src/report/built-ins/cost-pass-rate-comparison.tsx +23 -0
- package/src/report/built-ins/index.ts +2 -0
- package/src/report/components.tsx +223 -50
- package/src/report/compute.ts +358 -53
- package/src/report/dual-render.test.tsx +1125 -0
- package/src/report/flag.ts +1 -1
- package/src/report/format.ts +50 -2
- package/src/report/index.ts +48 -14
- package/src/report/load.ts +1 -1
- package/src/report/locale.ts +206 -0
- package/src/report/metrics.ts +39 -15
- package/src/report/primitives.tsx +100 -2
- package/src/report/react/AttemptList.tsx +115 -0
- package/src/report/react/DeltaTable.tsx +9 -6
- package/src/report/react/EvalList.tsx +0 -0
- package/src/report/react/ExperimentList.tsx +108 -0
- package/src/report/react/GroupSummary.tsx +66 -0
- package/src/report/react/MetricBars.tsx +24 -13
- package/src/report/react/MetricLine.tsx +21 -21
- package/src/report/react/MetricMatrix.tsx +8 -4
- package/src/report/react/MetricScatter.tsx +136 -96
- package/src/report/react/MetricTable.tsx +115 -15
- package/src/report/react/RunOverview.tsx +48 -26
- package/src/report/react/Scoreboard.tsx +15 -7
- package/src/report/react/cell.tsx +18 -9
- package/src/{view/app/lib/chart.ts → report/react/chart-math.ts} +2 -6
- package/src/report/react/colors.ts +24 -15
- package/src/report/react/enhance.js +129 -0
- package/src/report/react/fixtures.ts +163 -34
- package/src/report/react/format.ts +1 -1
- package/src/report/react/index.tsx +22 -6
- package/src/report/react/render.test.tsx +164 -31
- package/src/report/react/styles.css +405 -213
- package/src/report/report.test.ts +403 -94
- package/src/report/report.ts +29 -11
- package/src/report/text/faces.ts +303 -95
- package/src/report/text/layout.ts +76 -11
- package/src/report/text/table.ts +98 -0
- package/src/report/tree.ts +103 -20
- package/src/report/types.ts +196 -25
- package/src/report/web.ts +46 -19
- package/src/results/annotated-source.test.ts +146 -0
- package/src/results/annotated-source.ts +128 -0
- package/src/results/attempt-evidence.test.ts +158 -0
- package/src/results/attempt-evidence.ts +158 -0
- package/src/results/attempt-source.ts +48 -0
- package/src/results/copy.ts +154 -139
- package/src/results/format.ts +54 -47
- package/src/results/host-equivalence.test.ts +527 -0
- package/src/results/index.ts +56 -14
- package/src/results/locator.test.ts +198 -0
- package/src/results/locator.ts +0 -0
- package/src/results/open.ts +392 -175
- package/src/results/results.test.ts +943 -494
- package/src/results/select.ts +129 -23
- package/src/results/skipped-notice.ts +0 -0
- package/src/results/source-hash.ts +28 -0
- package/src/results/types.ts +98 -62
- package/src/results/writer.ts +304 -176
- package/src/runner/attempt.test.ts +170 -0
- package/src/runner/attempt.ts +97 -15
- package/src/runner/discover.test.ts +65 -0
- package/src/runner/discover.ts +6 -2
- package/src/runner/eval-source.test.ts +121 -0
- package/src/runner/eval-source.ts +45 -0
- package/src/runner/fingerprint.ts +54 -1
- package/src/runner/remote-sandbox.ts +1 -1
- package/src/runner/report.test.ts +6 -8
- package/src/runner/report.ts +5 -4
- package/src/runner/reporters/artifacts.ts +27 -41
- package/src/runner/reporters/braintrust.test.ts +3 -3
- package/src/runner/reporters/braintrust.ts +1 -1
- package/src/runner/reporters/console.ts +6 -6
- package/src/runner/reporters/json.ts +4 -4
- package/src/runner/reporters/live.test.ts +56 -0
- package/src/runner/reporters/live.ts +36 -10
- package/src/runner/reporters/quiet.test.ts +66 -0
- package/src/runner/reporters/quiet.ts +49 -0
- package/src/runner/reporters/shared.ts +5 -5
- package/src/runner/reporters/table.ts +17 -17
- package/src/runner/run.ts +61 -102
- package/src/runner/types.ts +43 -32
- package/src/sandbox/checkpoint.ts +1 -1
- package/src/sandbox/docker.ts +15 -2
- package/src/sandbox/e2b.ts +11 -5
- package/src/sandbox/errors.ts +13 -0
- package/src/sandbox/index.ts +3 -3
- package/src/sandbox/local-files.ts +1 -1
- package/src/sandbox/registry.ts +6 -5
- package/src/sandbox/resolve.ts +38 -23
- package/src/sandbox/retry.test.ts +68 -0
- package/src/sandbox/retry.ts +44 -0
- package/src/sandbox/types.ts +69 -28
- package/src/sandbox/vercel.ts +17 -3
- package/src/scoring/collector.ts +3 -3
- package/src/scoring/judge.ts +2 -2
- package/src/scoring/scoped.ts +13 -1
- package/src/scoring/types.ts +7 -7
- package/src/scoring/verdict.ts +4 -4
- package/src/shared/aggregate.ts +23 -3
- package/src/shared/types.ts +2 -2
- package/src/shared/{outcome.ts → verdict.ts} +15 -15
- package/src/show/compose.ts +12 -132
- package/src/show/index.ts +125 -94
- package/src/show/render.ts +660 -160
- package/src/show/show.test.ts +472 -253
- package/src/source-loc.ts +1 -1
- package/src/tty-line.ts +32 -0
- package/src/types.ts +2 -2
- package/src/util.test.ts +21 -1
- package/src/util.ts +5 -1
- package/src/view/app/App.tsx +33 -198
- package/src/view/app/components/AttemptModal.tsx +6 -6
- package/src/view/app/components/CodeView.tsx +2 -2
- package/src/view/app/components/CopyControls.tsx +32 -36
- package/src/view/app/components/SkippedRunsBanner.tsx +140 -0
- package/src/view/app/components/ui/badge.tsx +1 -1
- package/src/view/app/components/ui/dialog.tsx +2 -2
- package/src/view/app/i18n.ts +40 -31
- package/src/view/app/lib/artifact-url.ts +1 -1
- package/src/view/app/lib/attempt-route.test.ts +37 -47
- package/src/view/app/lib/attempt-route.ts +26 -26
- package/src/view/app/lib/rows.ts +9 -112
- package/src/view/app/lib/verdict.ts +25 -0
- package/src/view/app/main.tsx +9 -12
- package/src/view/app/pages/RunsPage.tsx +4 -4
- package/src/view/app/pages/TracesPage.tsx +3 -3
- package/src/view/app/shared.ts +0 -1
- package/src/view/app/types.ts +6 -51
- package/src/view/artifact-serving.test.ts +136 -0
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +21 -21
- package/src/view/data.test.ts +214 -143
- package/src/view/data.ts +166 -120
- package/src/view/index.ts +23 -11
- package/src/view/server.ts +52 -17
- package/src/view/shared/types.ts +22 -31
- package/src/view/styles.css +117 -327
- package/src/view/view-report.test.ts +146 -98
- package/docs-site/zh/guides/sandbox-backends.mdx +0 -75
- package/src/report/default-report.tsx +0 -222
- package/src/report/dual-face.test.tsx +0 -527
- package/src/report/react/CaseList.tsx +0 -70
- package/src/view/app/components/CostScoreChart.tsx +0 -147
- package/src/view/app/components/ExperimentTable.tsx +0 -284
- package/src/view/app/components/GroupSelector.tsx +0 -61
- package/src/view/app/components/primitives.tsx +0 -43
- package/src/view/app/lib/outcome.ts +0 -82
|
@@ -4,14 +4,36 @@ sidebarTitle: "报告组件"
|
|
|
4
4
|
description: "报告文件里能摆的全部官方双面组件:每个组件回答什么问题、怎么调用、网页面长什么样、终端字符输出长什么样。"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
报告文件里的每个组件都是双面的:网页面是 React 渲染,终端面是字符渲染,两面吃同一份算好的数据,走哪扇门由宿主决定(见[自定义报告](/zh/guides/custom-reports)
|
|
7
|
+
报告文件里的每个组件都是双面的:网页面是 React 渲染,终端面是字符渲染,两面吃同一份算好的数据,走哪扇门由宿主决定(见[自定义报告](/zh/guides/custom-reports))。每个组件的数据算法挂在它自己身上,组件名后打个点就找到,算和画永远配对。本页逐个列出官方组件:它展示哪一层数据、在报告里怎么调用、终端输出长什么样。
|
|
8
|
+
|
|
9
|
+
## 名称与用途
|
|
10
|
+
|
|
11
|
+
英文术语描述组件形态,API 名是代码里的导出名。组件分三类:实体列表逐项展示 experiment、Eval 或 Attempt;汇总组件概括整批 Selection;指标图形把指定维度聚合成值。三个实体列表的 `.data(selection)` 都返回普通数组,报告作者先用 JavaScript `.filter()` 收窄,再把 `items` 交给组件。过滤条件不藏在组件里。中文正文首次提到时用“中文名(`API 名`)”,后续可以只写中文名或 `API 名`。
|
|
12
|
+
|
|
13
|
+
| 分类 | 中文名 | English | API | 主展示单位 |
|
|
14
|
+
| --- | --- | --- | --- | --- |
|
|
15
|
+
| 汇总 | 运行总览 | Run overview | `RunOverview` | 一批 Selection;汇总其中的 experiment、Eval 和 Attempt |
|
|
16
|
+
| 汇总 | 组摘要 | Group summary | `GroupSummary` | 收窄后的一批 Selection;汇总一组 experiment 和 Eval |
|
|
17
|
+
| 实体列表 | 实验列表 | Experiment list | `ExperimentList` | 每项一个 experiment;展开到该 experiment 的 Eval |
|
|
18
|
+
| 实体列表 | Eval 列表 | Eval list | `EvalList` | 每项一个 experiment × Eval;展开到该 Eval 的 Attempt |
|
|
19
|
+
| 实体列表 | Attempt 列表 | Attempt list | `AttemptList` | 每项一个 Attempt;显示断言、错误、Judge 评语和证据 |
|
|
20
|
+
| 指标图形 | 指标表 | Metric table | `MetricTable` | 一个可配置行维度;每格是一个聚合指标值 |
|
|
21
|
+
| 指标图形 | 指标矩阵 | Metric matrix | `MetricMatrix` | 两个可配置维度的交叉格;每格是一个聚合指标值 |
|
|
22
|
+
| 指标图形 | 分组条形图 | Grouped bar chart | `MetricBars` | 两个可配置维度形成分组和系列;每根条是一个聚合指标值 |
|
|
23
|
+
| 指标图形 | 成绩单 | Scoreboard | `Scoreboard` | 每行一个可配置维度值;按 Eval 和 Eval 分组计算分数 |
|
|
24
|
+
| 指标图形 | 指标散点图 | Metric scatter plot | `MetricScatter` | 每点一个可配置维度值,通常是 experiment;坐标是两个聚合指标值 |
|
|
25
|
+
| 指标图形 | 指标趋势图 | Metric line chart | `MetricLine` | 每点一个 experiment;横轴是 experiment flag,纵轴是聚合指标值 |
|
|
26
|
+
| 指标图形 | 成对差异表 | Paired delta table | `DeltaTable` | 每行一对 experiment 或结果快照;格内是指标值及差值 |
|
|
27
|
+
|
|
28
|
+
`Row`、`Col`、`Section`、`Text`、`Style` 和 `Table` 是排版原语,不计算结果,因此不算一种分析图。它们的中文统称分别是行、列、分节、文本、样式和表格;代码里始终使用 API 名。
|
|
8
29
|
|
|
9
30
|
所有组件共守同一套契约,下面不再逐个重复:
|
|
10
31
|
|
|
11
|
-
- **诚实渲染。** 缺数据渲染 `—` 不补 0;覆盖不全的格子带 `12/15`
|
|
32
|
+
- **诚实渲染。** 缺数据渲染 `—` 不补 0;覆盖不全的格子带 `12/15` 角标;截断如实标注剩余数量与原始 artifact 路径。
|
|
12
33
|
- **排序与方向随指标的 `better`。** higher 的指标降序、lower 的升序,「好」的一头恒在上、在右上。
|
|
13
|
-
- **证据室深链。** 网页面的格子、点和条目深链到
|
|
14
|
-
-
|
|
34
|
+
- **证据室深链。** 网页面的格子、点和条目深链到 Attempt 详情;终端面用同一 Attempt locator 交给 `niceeval show @<id>`。
|
|
35
|
+
- **终端输出形成反馈闭环。** 每个 Attempt 有一个以 `@` 开头的短 locator,例如 `@1k2m9qrs`。它唯一指向 experiment、结果快照、Eval 和 Attempt。`✓` / `✗` / `!` / `–` 是 passed / failed / errored / skipped;`[E]` 表示运行时 Eval 源码可用,`[X]` 表示执行步骤可用,`[⏱]` 表示 OTel 时间可用,`[D]` 表示工作区 diff 可用。执行步骤统一包含消息、thinking、tool call/result 和 Skill load;OTel 只给这些步骤补时间,不另开一份输出。
|
|
36
|
+
- **两面同口径,网页面不依赖 JS 也完整。** 排序在计算时由 `sort` 定死,终端和网页看到同一份基准顺序;下钻是普通链接、展开折叠用 `<details>`。网页面另有一层浏览操作:点表头就地重排、榜单行过滤、图表点位悬停看数值——只影响眼前的视图,不改数据口径,刷新即回基准顺序;浏览器禁用 JS 时这些操作消失,内容一样不少(悬停信息退化为图内提示)。
|
|
15
37
|
|
|
16
38
|
## 排版原语
|
|
17
39
|
|
|
@@ -35,28 +57,47 @@ description: "报告文件里能摆的全部官方双面组件:每个组件回
|
|
|
35
57
|
codex 78.0/100 40/50 38/50 │
|
|
36
58
|
```
|
|
37
59
|
|
|
38
|
-
##
|
|
60
|
+
## 表格(`Table`)
|
|
39
61
|
|
|
40
|
-
|
|
62
|
+
第六个排版原语:官方组件摆不出的表,用它摆。它不计算任何东西——列由你定,格子是你算好的显示值,它只负责把两个面都排整齐。
|
|
41
63
|
|
|
42
64
|
```tsx
|
|
43
|
-
<
|
|
65
|
+
<Table
|
|
66
|
+
columns={[
|
|
67
|
+
{ key: "eval", header: "题目" },
|
|
68
|
+
{ key: "pass", header: "通过率", align: "right" },
|
|
69
|
+
{ key: "cost", header: "成本", align: "right" },
|
|
70
|
+
]}
|
|
71
|
+
rows={[
|
|
72
|
+
{
|
|
73
|
+
key: "记忆/写缓存",
|
|
74
|
+
locator: "@160iuj3h",
|
|
75
|
+
cells: { eval: "记忆/写缓存", pass: "87%", cost: "$0.09" },
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
key: "浏览/表单填写",
|
|
79
|
+
locator: "@1qrdcfq8",
|
|
80
|
+
cells: { eval: "浏览/表单填写", pass: null, cost: null },
|
|
81
|
+
},
|
|
82
|
+
]}
|
|
83
|
+
/>
|
|
44
84
|
```
|
|
45
85
|
|
|
46
86
|
```text
|
|
47
|
-
|
|
87
|
+
题目 通过率 成本 attempt
|
|
88
|
+
记忆/写缓存 87% $0.09 @160iuj3h
|
|
89
|
+
浏览/表单填写 — — @1qrdcfq8
|
|
90
|
+
```
|
|
48
91
|
|
|
49
|
-
|
|
50
|
-
compare/bub-gpt-5.4 13/15 87% $0.42 4m 12s
|
|
51
|
-
compare/codex-gpt-5.4 12/15 80% $0.51 5m 03s
|
|
92
|
+
列宽按**终端显示宽度**算,中文和全角字符记 2 列——中文题目名、中文 agent 名都不会把表撕歪。`align: "right"` 让数字列右对齐,小数点自然对齐。格子是 `null` 就渲染 `—`,不补 0。行上带 `locator` 就多出一列 attempt:网页面链到证据室,终端面列出 locator,直接喂给 `niceeval show <locator>`。
|
|
52
93
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
94
|
+
表比终端宽时先压最宽的左对齐列(按显示宽度折行),右对齐列不折行——数字折行读不了;压到下限仍放不下,就从右侧丢列,并在表下如实报丢了几列,不静默截断。
|
|
95
|
+
|
|
96
|
+
指标表、指标矩阵、成绩单和成对差异表的终端面就建在 `Table` 上,所以你的表和官方的表用的是同一把尺子。表格之外的形态要自己排字符时,用[自定义报告](/zh/guides/custom-reports)「换形态」一节里那套文本排版函数。
|
|
56
97
|
|
|
57
|
-
##
|
|
98
|
+
## 运行总览(`RunOverview`)
|
|
58
99
|
|
|
59
|
-
|
|
100
|
+
每张报告开头「这批数据是什么」:几个配置、几道题、通过分布、总成本、何时跑的;Selection 里的挑选警告随数据一起进来、直接显示在条内,诚实不用你另外传一遍。
|
|
60
101
|
|
|
61
102
|
```tsx
|
|
62
103
|
<RunOverview data={await RunOverview.data(selection)} />
|
|
@@ -68,7 +109,122 @@ passed 26 · failed 12 · errored 4 · skipped 0 · $0.93 · 9m 15s
|
|
|
68
109
|
! snapshot compare/codex-gpt-5.4 predates latest run by 2 days
|
|
69
110
|
```
|
|
70
111
|
|
|
71
|
-
##
|
|
112
|
+
## 组摘要(`GroupSummary`)
|
|
113
|
+
|
|
114
|
+
一组 experiment(比如按目录前缀归在同一个 `<Section>` 里的那些 experiment)的紧凑统计块,回答「这组题综合算下来过了多少、花了多少、什么时候跑的」。组件本身不分组——先用 `Selection.filter` 把 Selection 收窄到这一组,再喂给 `GroupSummary.data`:
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
const scoped = selection.filter((s) => s.experimentId.startsWith("compare/"));
|
|
118
|
+
|
|
119
|
+
<Section title="compare">
|
|
120
|
+
<GroupSummary data={await GroupSummary.data(scoped)} />
|
|
121
|
+
</Section>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
Pass rate 73.3% · 2 experiments · 15 evals · failed 3 · errored 1 · $0.93
|
|
126
|
+
latest 2026-07-09T10:00:00Z
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
通过率是 eval 级折叠计票口径:同一 eval 的多轮 attempt 先折成一个判定(任一轮通过则算通过,否则取最严重的),`passed / (passed + failed + errored)`,`skipped` 不进分母——与页头 `RunOverview` 那个两级聚合、带 partial credit 的 `passRate` 是两码事,两者服务不同问题:`GroupSummary` 回答「这组题过了几道」,`RunOverview` 回答「整体质量几分」,不要互相替代。`evals` 按 `experimentId + eval id` 的完整身份键去重——组里两个 experiment 各自的同名 eval 算两道题,不会被误合并成一道。`errored` 为 0 时这一段省略,但 `verdicts.errored` 这个数据字段本身永远在,省略只发生在渲染层。`totalCostUSD` 是组内可测 attempt 成本求和,一个 attempt 都没报成本时是 `null`,两面都渲染缺数据而不是 `$0`。
|
|
130
|
+
|
|
131
|
+
组摘要是普通的公开组件,`GroupSummary.data` 是普通的公开计算函数——想在自己的报告里按目录前缀分组、每组摆一块,按上面的写法收窄 Selection 再调它就行。
|
|
132
|
+
|
|
133
|
+
## 实验列表(`ExperimentList`)
|
|
134
|
+
|
|
135
|
+
每项固定代表一个 experiment。主行显示 experiment id、agent、model、flags、Eval 判定构成、通过率、Tokens、成本和耗时;展开后显示这个 experiment 的 Eval 列表。它不接受列配置——这是 experiment 的诊断视图,不是通用指标表。
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
const experiments = await ExperimentList.data(selection);
|
|
139
|
+
|
|
140
|
+
<ExperimentList
|
|
141
|
+
items={experiments.filter((exp) => exp.verdicts.errored === 0)}
|
|
142
|
+
/>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`niceeval show` 每个 experiment 输出一行汇总,每个 Eval 只占一行。Attempt ID 后的证据位告诉 AI 能读什么,不展开重复命令:
|
|
146
|
+
|
|
147
|
+
```text
|
|
148
|
+
compare/bub-gpt-5.4 · bub · gpt-5.4
|
|
149
|
+
pass 87% · 13 passed / 1 failed / 1 errored · 42 attempts · 4m 12s · $0.42
|
|
150
|
+
✓ algebra/quadratic @12f9k3aq✓[E,X,⏱] @141bm7cx✓[E,X,⏱] 18s avg · $0.02 avg
|
|
151
|
+
✗ weather/brooklyn @1k2m9qrs✗[E,X,⏱] @1nx4dpqr✗[E,X] @19vq2jex✗[E,X] gate calledTool("get_weather")
|
|
152
|
+
! fixtures/button @1c3h6twx![E,D] command timed out after 120s
|
|
153
|
+
|
|
154
|
+
compare/codex-gpt-5.4 · codex · gpt-5.4
|
|
155
|
+
pass 80% · 12 passed / 3 failed · 45 attempts · 5m 03s · $0.51
|
|
156
|
+
✓ algebra/quadratic @1d8p4kwz✓[E,X,⏱] 16s · $0.02
|
|
157
|
+
✗ algebra/matrix @1e2j7mxa✗[E,X] @1f5r9nab✗[E,X,⏱] @1g4w8pcd✗[E,X,⏱] gate equals("42")
|
|
158
|
+
✗ weather/brooklyn @1h6t3vbe✗[E,X,⏱] @1j9m2qfg✗[E,X,⏱] @1k7c5rxh✗[E,X,⏱] gate calledTool("get_weather")
|
|
159
|
+
|
|
160
|
+
inspect: niceeval show @<id> [--eval|--execution|--diff]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
locator 由 `experimentId + snapshot.startedAt + evalId + attempt index` 的不可变身份确定,复制或发布结果后保持不变。宿主在当前结果根解析 locator;不存在或发生冲突时直接报错,不回退到“最新一次”。`@` 前缀让它与 Eval ID 前缀选择器无歧义。
|
|
164
|
+
|
|
165
|
+
`.data(selection)` 返回普通的 `ExperimentListItem[]`,顺序按 experiment id 稳定排列。要只看某个 agent、目录前缀或运行状态,直接过滤数组。组件不提供另一套 `filter` 查询语法。网页面的文本搜索只是临时浏览操作,不改变传入的 items;终端面始终输出完整的传入数组。
|
|
166
|
+
|
|
167
|
+
## Eval 列表(`EvalList`)
|
|
168
|
+
|
|
169
|
+
每项固定代表一个 `experimentId + evalId`,因为同一个 Eval 跑在两个 experiment 上是两条不同结果。主行显示判定、Attempt 数、聚合分数、成本、耗时和失败原因;展开后显示这个 Eval 的 Attempt 列表。
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
const evals = await EvalList.data(selection);
|
|
173
|
+
|
|
174
|
+
<EvalList
|
|
175
|
+
items={evals.filter((item) => item.verdict !== "passed")}
|
|
176
|
+
/>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`niceeval show` 每个 `experiment × Eval` 输出一项,Attempt 仍用同一套紧凑 ID 和证据位。每项只给一条模板:
|
|
180
|
+
|
|
181
|
+
```text
|
|
182
|
+
weather/brooklyn · compare/bub-gpt-5.4 · failed
|
|
183
|
+
score 0.20 · 3 attempts · 41s avg · $0.04 avg
|
|
184
|
+
@1k2m9qrs✗[E,X,⏱] · gate calledTool("get_weather") — tool was never called
|
|
185
|
+
@1nx4dpqr✗[E,X] · gate calledTool("get_weather") — tool was never called
|
|
186
|
+
@19vq2jex✗[E,X] · gate calledTool("get_weather") — tool was never called
|
|
187
|
+
|
|
188
|
+
fixtures/button · compare/bub-gpt-5.4 · errored
|
|
189
|
+
score — · 1 attempt · 2m 00s · $0.09
|
|
190
|
+
@1c3h6twx![E,D] · command timed out after 120s
|
|
191
|
+
|
|
192
|
+
inspect: niceeval show @<id> [--eval|--execution|--diff]
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
`.data(selection)` 返回普通的 `EvalListItem[]`。按 Eval id 前缀、experiment、判定或分数过滤都使用数组 `.filter()`;组件只负责渲染传入的 items。
|
|
196
|
+
|
|
197
|
+
## Attempt 列表(`AttemptList`)
|
|
198
|
+
|
|
199
|
+
每项固定代表一个 Attempt,显示 experiment、Eval、Attempt 序号、判定、耗时、成本、失败断言、error、Judge 评语和证据链接。它既能列失败证据,也能列通过样本,不把 verdict 过滤写死在组件名里。
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
const attempts = await AttemptList.data(selection, { redact });
|
|
203
|
+
|
|
204
|
+
<AttemptList
|
|
205
|
+
items={attempts.filter((item) => item.verdict === "failed" || item.verdict === "errored")}
|
|
206
|
+
/>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
`niceeval show` 每项完整输出一个 Attempt,不再折叠到 Eval 汇总。这里已经是叶子层,只在末尾给一条与该 Attempt 可用证据对应的模板:
|
|
210
|
+
|
|
211
|
+
```text
|
|
212
|
+
✗ @1k2m9qrs · weather/brooklyn · compare/bub-gpt-5.4 · 41s · $0.04 · [E,X,⏱]
|
|
213
|
+
gate calledTool("get_weather") · failed
|
|
214
|
+
tool was never called
|
|
215
|
+
soft judge("回答基于实时数据") · 0.2/1
|
|
216
|
+
reply invents a temperature
|
|
217
|
+
|
|
218
|
+
! @1c3h6twx · fixtures/button · compare/bub-gpt-5.4 · 2m 00s · $0.09 · [E,D]
|
|
219
|
+
command timed out after 120s
|
|
220
|
+
inspect: niceeval show @<id> [--eval|--execution|--diff]
|
|
221
|
+
|
|
222
|
+
(3 more not shown · showing 20 of 23)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
发布前要消毒 error、断言 detail 或 Judge 评语时,把 `redact` 交给 `.data()`;要展示哪些 Attempt,过滤返回的 `AttemptListItem[]`。`limit` 也由报告作者在数组上用 `.slice(0, 20)` 表达,截断时把原始数量交给组件的 `total`,组件据此显示“还有 n 项未展示”,不静默截断。
|
|
226
|
+
|
|
227
|
+
## 指标表(`MetricTable`)
|
|
72
228
|
|
|
73
229
|
一行一个维度值、一列一个指标的对比表,回答「谁整体更好」。行维度、指标列、排序全部可换,自定义指标(`defineMetric`)与内置指标同列。
|
|
74
230
|
|
|
@@ -86,9 +242,11 @@ bub 87% 312 lines $0.42
|
|
|
86
242
|
codex 80% 12/15 355 lines $0.51
|
|
87
243
|
```
|
|
88
244
|
|
|
89
|
-
两面都按 `sort`
|
|
245
|
+
两面都按 `sort` 预排,基准顺序一致——要固定换一种排序,改一行重跑。`12/15` 角标表示该格 15 个 attempt 里只有 12 个测得了这个指标。
|
|
90
246
|
|
|
91
|
-
|
|
247
|
+
`rows: "experiment"` 时每行自动带 agent 与 model 列——结果里现成的元信息,不用配置。`MetricTable` 不展开实体层级:要看 experiment、Eval 或 Attempt 的固定诊断字段,分别使用三个实体列表;要自由换维度和指标列,使用指标表。
|
|
248
|
+
|
|
249
|
+
## 指标矩阵(`MetricMatrix`)
|
|
92
250
|
|
|
93
251
|
行 × 列两个维度、格子里一个指标,回答「哪道题谁挂了」。稀疏渲染:没有样本的格子空着,不编数。
|
|
94
252
|
|
|
@@ -107,7 +265,7 @@ next: niceeval show geometry/area
|
|
|
107
265
|
|
|
108
266
|
网页面点格子深链到该格的 attempt;终端面在表下印下钻命令。
|
|
109
267
|
|
|
110
|
-
##
|
|
268
|
+
## 分组条形图(`MetricBars`)
|
|
111
269
|
|
|
112
270
|
同一份矩阵数据的另一种摆法(`MetricBars.data` 就是 `MetricMatrix.data` 的别名):按组并排比大小,回答「每个科目上谁领先、差多少」。组维度一组条、系列维度一根条、条长是指标值——benchmark 发布图(Terminal-Bench、BrowseComp 各一组,每个 agent 一根柱)就是这个形状。
|
|
113
271
|
|
|
@@ -130,7 +288,7 @@ geometry
|
|
|
130
288
|
|
|
131
289
|
网页面是竖向分组柱:柱顶标数值,系列颜色与其它组件的稳定配色一致,图例自动生成。终端面横向条形,字符宽度即刻度,同组内按值排序(方向随 `better`)。`better: "lower"` 的指标(成本、耗时)条形反向填充,短条恒为「好」。
|
|
132
290
|
|
|
133
|
-
##
|
|
291
|
+
## 成绩单(`Scoreboard`)
|
|
134
292
|
|
|
135
293
|
总分 + 分科小计,回答「这套题它能得几分」。逐题分值制:权重按 eval id 前缀配置,分母对所有被打分者恒定,没跑到的题挣 0 分并如实报 `missing`。
|
|
136
294
|
|
|
@@ -149,19 +307,22 @@ bub 86.5/100 45/50 41.5/50
|
|
|
149
307
|
codex 71.0/100 40/50 31/50 (1 missing)
|
|
150
308
|
```
|
|
151
309
|
|
|
152
|
-
##
|
|
310
|
+
## 指标散点图(`MetricScatter`)
|
|
153
311
|
|
|
154
312
|
每个点一个配置、两个指标各占一轴,回答「又好又便宜的是谁」。`series` 把同 agent 不同档位的点连成线;`better` 驱动轴向——`lower` 的轴反向画,「好」的角落恒在右上。
|
|
155
313
|
|
|
156
314
|
```tsx
|
|
157
|
-
<MetricScatter
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
315
|
+
<MetricScatter
|
|
316
|
+
selection={selection}
|
|
317
|
+
points="experiment" // 每个点 = 一个配置的聚合
|
|
318
|
+
series="agent" // 同 agent 的档位连成线
|
|
319
|
+
x={costUSD}
|
|
320
|
+
y={passRate}
|
|
321
|
+
/>
|
|
163
322
|
```
|
|
164
323
|
|
|
324
|
+
直接把 `selection` 传给它,宿主渲染前替你算好数据——默认报告就是这么写的。要在自己已经跑起来的 React 应用里嵌这张图、或数据是预先算好的,改传 `data`:`<MetricScatter data={await MetricScatter.data(selection, { points: "experiment", series: "agent", x: costUSD, y: passRate })} />`;同时传 `data` 和 `selection`、或两者都不传,类型检查都会报错。
|
|
325
|
+
|
|
165
326
|
```text
|
|
166
327
|
pass ↑ (好 → 右上)
|
|
167
328
|
90%│ A
|
|
@@ -174,11 +335,11 @@ pass ↑ (好 → 右上)
|
|
|
174
335
|
A bub-high B bub-medium C codex-high D codex-low
|
|
175
336
|
```
|
|
176
337
|
|
|
177
|
-
|
|
338
|
+
网页面点带悬停提示(值与 `samples/total`,禁用 JS 时退化为图内提示)、同系列连线、点击深链下钻。终端面用字母标点、图例列在图下;x 或 y 缺数据的点两个面都不画,注脚如实报「n 个点缺数据」;点太密排不下时降级为坐标表,不硬挤。画得出来的点是 0 个(x 或 y 全缺数据)时,两个面都明说这两个指标没有可用数据,不留一片空白;只有 1 个点时,明说「至少要两个实验才能比较」;2 个及以上才正常出图——组件从不因为点不够就整块消失,让你不知道图为什么没了。维度槽也收自定义维度(`{ name, of }`,从 attempt 已有数据算组名)和 `flag()`(experiment 声明的变量),怎么选见[自定义报告](/zh/guides/custom-reports)的「换分组」一节。
|
|
178
339
|
|
|
179
|
-
##
|
|
340
|
+
## 指标趋势图(`MetricLine`)
|
|
180
341
|
|
|
181
|
-
x 是有序变量、每个系列一条线,回答「变量拧大,分数怎么走」——并行 agent 数 × 模拟延迟 × 得分这类 scaling 图就是它。与 `MetricScatter`
|
|
342
|
+
x 是有序变量、每个系列一条线,回答「变量拧大,分数怎么走」——并行 agent 数 × 模拟延迟 × 得分这类 scaling 图就是它。与 `MetricScatter` 的分工:散点图的两轴都是测出来的指标(找优势前沿),趋势图的 x 是你配置的变量(看趋势)。变量在 experiment 的 `flags` 里声明,报告用 `flag()` 直接引用,不从 experiment 命名里解析。
|
|
182
343
|
|
|
183
344
|
```tsx
|
|
184
345
|
<MetricLine data={await MetricLine.data(selection, {
|
|
@@ -205,9 +366,9 @@ A 1 agents B 4 agents C 16 agents
|
|
|
205
366
|
|
|
206
367
|
每个点是一个 experiment 的聚合,与其它组件同一台两级聚合引擎,`samples` / `total` / `refs` 一应俱全;同系列的点按 x 排序连线。网页面每个点可 hover、可深链下钻。终端面同系列共用一个字母、沿 x 排布,趋势肉眼可读;y 缺数据的点不画、注脚报数,点太密降级为坐标表。
|
|
207
368
|
|
|
208
|
-
##
|
|
369
|
+
## 成对差异表(`DeltaTable`)
|
|
209
370
|
|
|
210
|
-
每行一对配置、每列一个指标,格子里 A、B、Δ 三个值,回答「这个开关值不值」「这次修复翻转了什么」。涨跌好坏由 `better` 判定,任一侧缺数据 Δ 显示为缺,不硬算。「这次 vs
|
|
371
|
+
每行一对配置、每列一个指标,格子里 A、B、Δ 三个值,回答「这个开关值不值」「这次修复翻转了什么」。涨跌好坏由 `better` 判定,任一侧缺数据 Δ 显示为缺,不硬算。「这次 vs 上次」(同一配置的两个结果快照)也是它:`pairs` 的 `a` / `b` 除 experiment id 外也收快照键 `<experimentId> @ <startedAt>`——时间轴对比要用到旧快照,`results.latest()` 里没有,配手挑的快照数组(如 `[exp.latest, exp.snapshots[1]]`)用。
|
|
211
372
|
|
|
212
373
|
```tsx
|
|
213
374
|
<DeltaTable data={await DeltaTable.data(selection, {
|
|
@@ -225,28 +386,6 @@ bub 87% → 93% +6% $0.42 → $0.45 +$0.03
|
|
|
225
386
|
codex 80% → 80% ±0 $0.51 → — —
|
|
226
387
|
```
|
|
227
388
|
|
|
228
|
-
## `CaseList` —— 失败案例清单
|
|
229
|
-
|
|
230
|
-
失败与出错的 attempt 逐条列出:失败断言、error 摘要、judge 评语。榜单回答「多少」,它回答「为什么」。
|
|
231
|
-
|
|
232
|
-
```tsx
|
|
233
|
-
<CaseList data={await CaseList.data(selection, { outcomes: ["failed", "errored"], limit: 20 })} />
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
```text
|
|
237
|
-
✗ weather/brooklyn · compare/codex-gpt-5.4 · failed · 41s · $0.04
|
|
238
|
-
gate calledTool("get_weather") — tool was never called
|
|
239
|
-
soft judge("回答基于实时数据") — 0.2/1: reply invents a temperature
|
|
240
|
-
→ niceeval show weather/brooklyn
|
|
241
|
-
✗ fixtures/button · compare/bub-gpt-5.4 · failed · 2m 10s · $0.09
|
|
242
|
-
gate fileChanged("src/components/Button.tsx")
|
|
243
|
-
→ niceeval show fixtures/button
|
|
244
|
-
|
|
245
|
-
(3 more not shown · limit 20)
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
自由文本(error、断言 detail、judge 评语)发布前要消毒时,用 `CaseList.data` 的 `redact` 钩子。
|
|
249
|
-
|
|
250
389
|
## 官方组件之外
|
|
251
390
|
|
|
252
391
|
以上摆法都表达不了时,用 `defineComponent` 写自己的双面组件——网页怎么渲染、终端字符怎么排,两个面你都说了算,写法见[自定义报告](/zh/guides/custom-reports)的「换形态」一节。
|
|
@@ -46,7 +46,7 @@ export default defineEval({
|
|
|
46
46
|
- 每个 attempt 是 Braintrust 里的一行;`runs: 3` 会产生三行,靠 metadata 里的 `attempt` 区分。
|
|
47
47
|
- soft 断言按名字记为 score;gate 断言记在 `gate:` 前缀下。这样在 Braintrust 的实验对比里,gate 回归和 soft 分数回归用同一套 diff 看。
|
|
48
48
|
- metrics 带开始/结束时间、token 用量和估算成本;缺的数据不写,不补零。
|
|
49
|
-
- metadata 带 `agent`、`model`、`experiment`、`flags`、`
|
|
49
|
+
- metadata 带 `agent`、`model`、`experiment`、`flags`、`verdict` 和失败断言明细,方便在 Braintrust 里按维度过滤。
|
|
50
50
|
|
|
51
51
|
### 配置项
|
|
52
52
|
|
|
@@ -95,7 +95,7 @@ import type { Reporter } from "niceeval";
|
|
|
95
95
|
|
|
96
96
|
const notify: Reporter = {
|
|
97
97
|
async onEvalComplete(result) {
|
|
98
|
-
if (result.
|
|
98
|
+
if (result.verdict === "failed") {
|
|
99
99
|
await fetch("http://localhost:3000/notify", {
|
|
100
100
|
method: "POST",
|
|
101
101
|
body: JSON.stringify({ eval: result.id, agent: result.agent }),
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "用 niceeval/results 直接读写结果数据"
|
|
3
3
|
sidebarTitle: "结果数据 API"
|
|
4
|
-
description: "报告积木脚下的数据层:openResults 把 .niceeval/
|
|
4
|
+
description: "报告积木脚下的数据层:openResults 把 .niceeval/ 的落盘 artifact parse 成「实验 → 结果快照 → eval → attempt」的类型化层次,createResultsWriter 把别家结果写成 NiceEval 格式,copySnapshots 负责发布瘦身。"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
[自定义报告](/zh/guides/custom-reports)的积木——指标、计算函数、双面组件——脚下还有一层:`niceeval/results
|
|
7
|
+
[自定义报告](/zh/guides/custom-reports)的积木——指标、计算函数、双面组件——脚下还有一层:`niceeval/results`,落盘 artifact 的 parser。官方两扇门和全部报告积木读的都是这一层,没有私有数据通道;报告表达不了的口径,下到这层直接拿数据算。
|
|
8
8
|
|
|
9
9
|
什么时候下到这层:
|
|
10
10
|
|
|
@@ -16,7 +16,7 @@ description: "报告积木脚下的数据层:openResults 把 .niceeval/ 的落
|
|
|
16
16
|
|
|
17
17
|
## 读:`openResults`
|
|
18
18
|
|
|
19
|
-
输入是 `.niceeval/`
|
|
19
|
+
输入是 `.niceeval/` 目录(或 `copySnapshots` 产出的结果根目录,同一种布局),输出是四层数据:**实验 → 结果快照(单次跑的实验)→ eval → attempt**。你从此不碰路径、不判断文件存在性、不解析 JSON:
|
|
20
20
|
|
|
21
21
|
```typescript
|
|
22
22
|
import { openResults } from "niceeval/results";
|
|
@@ -45,6 +45,8 @@ const snap = exp.latest;
|
|
|
45
45
|
snap.agent; // 本快照自己的 agent
|
|
46
46
|
snap.model;
|
|
47
47
|
snap.startedAt; // 这次实验什么时候跑的
|
|
48
|
+
snap.completedAt; // 收尾时补写;缺失 = 进程中断,已落盘的 attempt 仍可正常读
|
|
49
|
+
snap.dir; // 快照目录的绝对路径 —— 物理落盘就是快照本身,没有更低一层
|
|
48
50
|
snap.producer; // { name: "niceeval", version: "0.4.6" } —— 谁写的这份结果
|
|
49
51
|
snap.schemaVersion; // 结果格式版本
|
|
50
52
|
```
|
|
@@ -59,15 +61,15 @@ for (const ev of snap.evals) {
|
|
|
59
61
|
snap.attempts; // 不关心题目边界时,全部 attempt 平铺
|
|
60
62
|
```
|
|
61
63
|
|
|
62
|
-
**第四层,attempt
|
|
64
|
+
**第四层,attempt**——瘦身条目直接在手上,重 artifact 全部懒加载。唯一带 `Handle` 后缀的类型就是它:上面三层是纯数据,这一层的方法会碰磁盘:
|
|
63
65
|
|
|
64
66
|
```typescript
|
|
65
67
|
const attempt = snap.evals[0].attempts[0];
|
|
66
68
|
|
|
67
69
|
attempt.evalId; // "algebra/quadratic" —— 属于哪道题,不用绕 result
|
|
68
70
|
attempt.experimentId; // 属于哪个实验
|
|
69
|
-
attempt.result; // EvalResult
|
|
70
|
-
attempt.ref; // {
|
|
71
|
+
attempt.result; // EvalResult:判定、断言、用量、成本、experiment 元数据
|
|
72
|
+
attempt.ref; // { snapshot, attempt }:证据引用,与 view 深链、报告格子的 refs 同一身份
|
|
71
73
|
await attempt.events(); // StreamEvent[] | null
|
|
72
74
|
await attempt.trace(); // TraceSpan[] | null
|
|
73
75
|
await attempt.o11y(); // O11ySummary | null
|
|
@@ -77,8 +79,8 @@ await attempt.sources(); // SourceArtifact[] | null
|
|
|
77
79
|
|
|
78
80
|
三条要点:
|
|
79
81
|
|
|
80
|
-
- **懒加载即存在性判断。**
|
|
81
|
-
- **读不了的落盘不静默。**
|
|
82
|
+
- **懒加载即存在性判断。** artifact 缺失返回 `null`,不抛错——remote agent 没有 `diff.json` 时,`await attempt.diff()` 就是 `null`,你据此决定跳过还是报缺。
|
|
83
|
+
- **读不了的落盘不静默。** 版本不兼容、目录损坏的快照进 `skipped` 并带原因;要不要展示由你定,但缺口永远被算出来。
|
|
82
84
|
- **同进程内按句柄记忆化。** 两处都读同一个 `diff()` 不会把上百 MB 读两遍。
|
|
83
85
|
|
|
84
86
|
## 版本:谁写的、读不读得了
|
|
@@ -93,19 +95,17 @@ for (const s of results.skipped) {
|
|
|
93
95
|
}
|
|
94
96
|
```
|
|
95
97
|
|
|
96
|
-
`"incomplete"`
|
|
98
|
+
`"incomplete"` 是极小概率的一种:有 attempt 落盘、却没有 `snapshot.json`——只可能出现在「快照目录建好、元数据还没写完」的极小窗口里进程死亡,或人为删了文件。它和「进程中断」的常态不是一回事:进程中断的常态是**未收尾快照**(`snapshot.json` 在,只是缺 `completedAt`),这种快照能正常读,已落盘的 attempt 全部可见,只是 `results.latest()` 选中它时会带一条 `unfinished-snapshot` 警告(见下文),不会被归进 `skipped`。
|
|
97
99
|
|
|
98
100
|
旧版本的结果不会丢:磁盘上原样还在,用写它的那个工具就能看——`producer.name` 是 `"niceeval"` 时,提示用户 `npx niceeval@<producer.version> view`;是第三方 harness 时如实报出它的名字和版本,别拼一句错误的 npx 命令。`skipped` 给你的信息刚好够做对这个分支。
|
|
99
101
|
|
|
100
|
-
##
|
|
102
|
+
## 快照就是这个目录
|
|
101
103
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
快照把「单次跑的实验」这层身份显式化——**一个 experiment 在一次落盘里的那部分结果**。物理目录仍可通过 `results.runDirs` 与 `snap.runDir` 触达(发布、排查工件路径时用),多数消费方不需要碰。
|
|
104
|
+
**快照 = 单次跑的实验**,物理上就是一个快照目录(`.niceeval/<experiment>/<snapshot>/`),没有更低一层。`niceeval exp compare` 一次 CLI 调用会给涉及的每个实验各开一个独立的快照目录,互相不合并、没有跨实验的聚合落盘——「每个 experiment 最新一次」因此天然是快照粒度:周一跑了整组 compare,周二只重跑 `compare/bub-gpt-5.4`,bub 的最新快照落在周二,codex 的还在周一,`exp.latest` 各自反映各自的历史。
|
|
105
105
|
|
|
106
106
|
## 选快照:`results.latest()`
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
多数场景先回答「现在什么水位」,选择器替你挑「每个实验最新一次」——这也是默认报告的口径。返回的是一个 **Selection**,快照和警告绑在一起走:
|
|
109
109
|
|
|
110
110
|
```typescript
|
|
111
111
|
const latest = results.latest({
|
|
@@ -129,9 +129,9 @@ latest.warnings[0];
|
|
|
129
129
|
// }
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
字段供程序判断——CI 里「覆盖缩水就 fail」直接判 `covered < total`,不解析文本;`message`
|
|
132
|
+
字段供程序判断——CI 里「覆盖缩水就 fail」直接判 `covered < total`,不解析文本;`message` 是渲染好的英文句子,要展示就原样打。渲染与否在你,缺口永远被算出来。警告不止这一种:快照落后于 Selection 中最新的落盘进 `stale-snapshot`、选中的快照没收尾(进程中断)进 `unfinished-snapshot`,每种都带 `kind`、可判断的结构化字段和渲染好的 `message`。
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
Selection 是[报告积木](/zh/guides/custom-reports)和下文 `copySnapshots` 的通用输入:收 `Selection` 时 warnings 随行(`RunOverview` 之类的组件会如实展示),手工挑的 `Snapshot[]` 数组照收。微调官方口径不用降级成裸数组:`latest.filter((s) => s.experimentId !== "compare/broken")` 返回新 Selection——快照被删减,warnings 修剪到幸存的实验,provenance 不丢。`filter` 只做删减;「换成该实验上一个完整快照」这类**替换式**重挑不是它的事,回到 `exp.snapshots` 自己拿——手工挑的数组没有挑选过程,自然没有 warnings 可带,也如实。
|
|
135
135
|
|
|
136
136
|
## 一个真实脚本:分布不是折叠
|
|
137
137
|
|
|
@@ -148,7 +148,7 @@ for (const exp of results.experiments) {
|
|
|
148
148
|
points.push({
|
|
149
149
|
agent: exp.latest.agent,
|
|
150
150
|
eval: attempt.evalId,
|
|
151
|
-
passed: attempt.result.
|
|
151
|
+
passed: attempt.result.verdict === "passed",
|
|
152
152
|
shellCommands: o11y?.shellCommands.length ?? 0,
|
|
153
153
|
});
|
|
154
154
|
}
|
|
@@ -157,7 +157,7 @@ for (const exp of results.experiments) {
|
|
|
157
157
|
|
|
158
158
|
即使在这条最深的路径上也不碰磁盘布局:路径拼接、存在性、版本过滤、快照切分全被库消化。要把这份分布摆进报告页,用 [`defineComponent`](/zh/guides/custom-reports) 包一个双面组件即可。
|
|
159
159
|
|
|
160
|
-
一条跨快照累计时的义务:`--resume`
|
|
160
|
+
一条跨快照累计时的义务:`--resume` 会把上一轮已通过、fingerprint 匹配的结果携带合入新快照,同一个 attempt 因此可能存在于多份落盘。携带条目不是空壳:它带着原快照的 `startedAt`(身份锚)与 `artifactBase`(指向原快照 attempt 目录的相对路径),懒加载按候选顺序回退——先本快照的 attempt 目录,再 `artifactBase` 指向的原快照目录(原快照被清理后如实返回 `null`);`ref` 指向条目所在的落盘,即携带入的那份新快照。身份键 `(experimentId, evalId, attempt, startedAt)` 的四个字段都在数据上——前两个是 attempt 的直达字段,序号与 `startedAt` 在 `attempt.result` 上。reader 忠实反映这份重复;跨快照聚合前用 `dedupeAttempts` 按身份键去重,重复保留最新快照里的那份——报告积木的计算函数内置这条,自己写脚本时记得过一遍:
|
|
161
161
|
|
|
162
162
|
```typescript
|
|
163
163
|
import { dedupeAttempts } from "niceeval/results";
|
|
@@ -166,52 +166,53 @@ const all = exp.snapshots.flatMap((s) => s.attempts); // 跨快照累计:历
|
|
|
166
166
|
const { attempts, warnings } = dedupeAttempts(all); // resume 合入的重复被折掉
|
|
167
167
|
```
|
|
168
168
|
|
|
169
|
-
## 写:`
|
|
169
|
+
## 写:`createResultsWriter`
|
|
170
170
|
|
|
171
|
-
写入面和读取面是同一组类型的两半,签名就是 roundtrip 的证明:reader 的 `attempt.result` 由两部分拼成——快照级字段(agent / model / startedAt / producer)来自 `writer.snapshot()` 的一次声明,其余全部字段就是 `writeAttempt` 第一个参数的类型;第二个参数是 reader
|
|
171
|
+
写入面和读取面是同一组类型的两半,签名就是 roundtrip 的证明:reader 的 `attempt.result` 由两部分拼成——快照级字段(experimentId / agent / model / startedAt / producer)来自 `writer.snapshot()` 的一次声明,其余全部字段就是 `writeAttempt` 第一个参数的类型;第二个参数是 reader 懒加载能拿到的那几样 artifact 的类型。「writeAttempt 参数 + snapshot() 声明 = reader 读回的全部」由类型拼合背书:快照级字段不在 attempt 参数的类型里,不存在「谁的值为准」。用它把别家平台、自研 harness 的结果转成 NiceEval 格式:
|
|
172
172
|
|
|
173
173
|
```typescript
|
|
174
|
-
import {
|
|
174
|
+
import { createResultsWriter } from "niceeval/results";
|
|
175
175
|
|
|
176
|
-
const writer =
|
|
176
|
+
const writer = createResultsWriter(".niceeval", {
|
|
177
177
|
producer: { name: "my-harness", version: "1.0.0" },
|
|
178
178
|
});
|
|
179
179
|
|
|
180
|
-
const snap = writer.snapshot({
|
|
181
|
-
|
|
180
|
+
const snap = await writer.snapshot({ // 建快照目录(独占创建,撞名换后缀重试)+ 写 snapshot.json
|
|
181
|
+
experimentId: "compare/my-agent",
|
|
182
182
|
agent: "my-agent",
|
|
183
183
|
model: "gpt-5.4",
|
|
184
184
|
startedAt: sourceRun.startedAt, // 必填:去重身份键以它为锚
|
|
185
185
|
});
|
|
186
186
|
|
|
187
187
|
for (const r of convertedResults) {
|
|
188
|
-
await snap.writeAttempt(r.result, { //
|
|
189
|
-
events: r.events, // 第二参 =
|
|
188
|
+
await snap.writeAttempt(r.result, { // 写 result.json(判决权威落点,一次写成)+ 拆 artifact 文件
|
|
189
|
+
events: r.events, // 第二参 = artifact,都可选;缺哪样读取面就懒加载出 null
|
|
190
190
|
diff: r.diff,
|
|
191
|
-
}); //
|
|
191
|
+
}); // 拆 artifact 文件、算目录、回填引用,全在库内发生
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
await writer.finish(); //
|
|
194
|
+
await writer.finish(); // 给每个快照补 completedAt,没有任何收尾聚合
|
|
195
195
|
```
|
|
196
196
|
|
|
197
|
-
`writer.snapshot()`
|
|
197
|
+
`writer.snapshot()` 就是读取面「实验 → 快照」层次的镜像:转多个 experiment 就开多个快照目录,experimentId / agent / model / startedAt 这些快照级元数据在这里声明一次,不用塞进每条 attempt;可选的 `knownEvalIds`(该实验已知的 eval 并集)也在这里声明——它是残缺检测的分母,转换只覆盖部分题目时如实交代全集,下游的覆盖警告就能算出来(`copySnapshots` 发布时会自动补记这个字段,见下文)。转完的目录就是标准结果目录:`niceeval show` / `niceeval view` 直接能看,报告积木直接能算,不用抄格式文档;`producer` 会原样出现在读取面的 `snap.producer` 上。**每个文件恰好写入一次**是写入面的核心承诺:`snapshot.json` 开跑即写、收尾只补 `completedAt`;`result.json` 与 artifact 随 attempt 完成落盘。进程中断只丢未完成的 attempt,已完成的判决与 artifact 已经在盘上——真正「有 attempt 落盘却没有 `snapshot.json`」的极端情况才归 `skipped("incomplete")`,未收尾但元数据齐全的快照能正常读,只带一条警告。
|
|
198
198
|
|
|
199
199
|
## 发布:`copySnapshots`
|
|
200
200
|
|
|
201
|
-
|
|
201
|
+
把选中的快照按格式感知地复制到发布目录——只带指定 artifact、只带选中的 attempt:
|
|
202
202
|
|
|
203
203
|
```typescript
|
|
204
204
|
import { openResults, copySnapshots } from "niceeval/results";
|
|
205
205
|
|
|
206
206
|
const results = await openResults(".niceeval");
|
|
207
207
|
await copySnapshots(results.latest(), "site-data/run", {
|
|
208
|
-
artifacts: ["sources", "events", "trace"], // diff 可达百 MB
|
|
209
|
-
});
|
|
208
|
+
artifacts: ["sources", "events", "trace", "o11y"], // diff 可达百 MB,发布时常见地不带;
|
|
209
|
+
}); // o11y 只有几 KB,报告用到 turns 这类
|
|
210
|
+
// 读 o11y 的指标就把它带上,不然渲染成「—」
|
|
210
211
|
```
|
|
211
212
|
|
|
212
213
|
第一个参数收 `Selection` 或手工挑的 `Snapshot[]`——和报告积木同一个输入约定。`artifacts` 的合法值是 `"events" | "trace" | "o11y" | "diff" | "sources"`,缺省全带。目标目录已存在且非空时报错,不静默覆盖——发布脚本要幂等就自己先清目标目录。
|
|
213
214
|
|
|
214
|
-
|
|
215
|
+
复制不改 artifact 内容、不消毒;发布前给自由文本消毒用报告积木 `AttemptList.data` 的 `redact` 钩子。唯一随行补记的是挑选时的**覆盖事实**:`partial-coverage` 警告的分母是实验的历史并集,而发布目录没有历史——所以每个复制出的快照带上 `knownEvalIds`(复制时刻该实验已知的 eval 并集),reader 端把它并进 `exp.evalIds` 的计算(取本地历史与快照携带值的并集)。发布目录上重新 `openResults().latest()`,残缺警告被同一套机制重新算出来,不靠发布者转述。复制出的目录就是标准结果目录,`niceeval view --run <目录>` 直接能看;要让报告站随 push 自动更新,workflow 见[通过 CI 发布报告](/zh/guides/publish-report)。
|
|
215
216
|
|
|
216
217
|
## 分层速览
|
|
217
218
|
|
|
@@ -221,4 +222,4 @@ await copySnapshots(results.latest(), "site-data/run", {
|
|
|
221
222
|
| 报告积木 | [自定义报告](/zh/guides/custom-reports)、[报告组件](/zh/guides/report-components) | 自己的口径与摆法 |
|
|
222
223
|
| 结果数据 API(本页) | `niceeval/results` | 折叠表达不了的算法、接自己的系统、读写格式本身 |
|
|
223
224
|
|
|
224
|
-
|
|
225
|
+
每层都建立在下一层之上,同一份落盘 artifact 是唯一事实来源——上层的派生物删了随时可重算。
|
|
@@ -49,7 +49,7 @@ npx niceeval exp local fixtures/button --runs 5 --early-exit
|
|
|
49
49
|
|
|
50
50
|
## 缓存
|
|
51
51
|
|
|
52
|
-
[NiceEval](https://niceeval.com/) 可以根据输入、配置和相关文件 fingerprint
|
|
52
|
+
[NiceEval](https://niceeval.com/) 可以根据输入、配置和相关文件 fingerprint 跳过已判定为 `passed` 或 `failed` 的结果——两者都是判定确定的终态。`errored`(超时、沙箱异常等框架/环境层面的不确定失败)永远重试。缓存适合加速迭代,但如果你在调试非确定性行为,应该明确关闭(`--force`)或清理相关缓存。
|
|
53
53
|
|
|
54
54
|
## 超时和预算
|
|
55
55
|
|
|
@@ -72,7 +72,7 @@ runner 在 eval 完成后把结果交给 reporters:
|
|
|
72
72
|
|
|
73
73
|
## 输出目录
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
每次运行会写入该实验的结果快照目录 `.niceeval/<experiment>/<快照>/`,包括快照级 `snapshot.json`,以及每个 attempt 的 `result.json`(判决与断言)和按需生成的 `events.json`、`sources.json`、`trace.json`、`o11y.json`、`diff.json` 等拆分 artifact。
|
|
76
76
|
|
|
77
77
|
## 推荐调试流程
|
|
78
78
|
|
|
@@ -67,7 +67,7 @@ export default defineExperiment({
|
|
|
67
67
|
args: ["-y", "@anthropic/mcp-browser"],
|
|
68
68
|
},
|
|
69
69
|
],
|
|
70
|
-
skills: ["Effect-TS/skills"],
|
|
70
|
+
skills: [{ kind: "repo", source: "Effect-TS/skills", ref: "8f3c1a2" }],
|
|
71
71
|
}),
|
|
72
72
|
model: "claude-sonnet-4-6",
|
|
73
73
|
sandbox: dockerSandbox(),
|
|
@@ -86,17 +86,22 @@ export default defineExperiment({
|
|
|
86
86
|
|
|
87
87
|
```text
|
|
88
88
|
createSandbox
|
|
89
|
+
→ sandbox spec 的 .setup() 钩子? # 环境准备(按实验装东西);没挂就跳过
|
|
89
90
|
→ git init && git commit
|
|
90
|
-
→ adapter.setup?
|
|
91
|
-
→ test(t): uploadDirectory(...)
|
|
91
|
+
→ adapter.setup? # 装 CLI / 写 agent 配置
|
|
92
|
+
→ test(t): uploadDirectory(...) # 写入这条 eval 的起始文件
|
|
92
93
|
→ adapter.send(input, ctx)
|
|
93
|
-
→ test(t): runCommand(...)
|
|
94
|
-
→ collectGeneratedFiles()
|
|
94
|
+
→ test(t): runCommand(...) # 手工运行验证命令
|
|
95
|
+
→ collectGeneratedFiles() # git diff HEAD
|
|
96
|
+
→ adapter.teardown? # agent 收尾
|
|
97
|
+
→ sandbox spec 的 .teardown() 钩子? # 环境收尾(如回存状态),销毁前最后跑
|
|
95
98
|
→ sandbox.stop()
|
|
96
99
|
```
|
|
97
100
|
|
|
98
101
|
起始文件和验证命令都写在 `test(t)` 中。agent 执行阶段只能看到你已经写进 sandbox 的文件。
|
|
99
102
|
|
|
103
|
+
环境钩子(`.setup()` / `.teardown()`)挂在 experiment `sandbox` 字段的 spec 上,用来做"按实验变化的环境准备"——装某个实验专属的二进制、预热、跨 attempt 载入和回存状态。它排在 git 基线之前,所以写下的文件不会被算进 agent 产出的 diff。写法和规则见 [Sandbox 后端 · 环境钩子](/zh/guides/sandbox-providers#环境钩子)。
|
|
104
|
+
|
|
100
105
|
## 自定义 sandbox agent
|
|
101
106
|
|
|
102
107
|
```ts
|