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
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "报告组件一览"
|
|
3
|
+
sidebarTitle: "报告组件"
|
|
4
|
+
description: "报告文件里能摆的全部官方双面组件:每个组件回答什么问题、怎么调用、网页面长什么样、终端字符输出长什么样。"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
报告文件里的每个组件都是双面的:网页面是 React 渲染,终端面是字符渲染,两面吃同一份算好的数据,走哪扇门由宿主决定(见[自定义报告](/zh/guides/custom-reports))。每个组件的数据算法挂在它自己身上——`<MetricTable data={await MetricTable.data(selection, …)} />`,组件名后打个点就找到,算和画永远配对。本页逐个列出官方组件:它回答什么问题、在报告里怎么调用、终端输出长什么样。
|
|
8
|
+
|
|
9
|
+
所有组件共守同一套契约,下面不再逐个重复:
|
|
10
|
+
|
|
11
|
+
- **诚实渲染。** 缺数据渲染 `—` 不补 0;覆盖不全的格子带 `12/15` 角标;截断如实标注剩余数量与原始工件路径。
|
|
12
|
+
- **排序与方向随指标的 `better`。** higher 的指标降序、lower 的升序,「好」的一头恒在上、在右上。
|
|
13
|
+
- **证据室深链。** 网页面的格子、点和条目深链到 attempt 弹窗;终端面在条目下印 `niceeval show <eval id>` 下钻命令。
|
|
14
|
+
- **两面同口径,网页面不 hydrate 也完整。** 排序在计算时由 `sort` 定死,两面预排一致;下钻是普通链接、展开折叠用 `<details>`;网页面不提供终端面没有的口径操作,静态导出零客户端 JS。
|
|
15
|
+
|
|
16
|
+
## 排版原语
|
|
17
|
+
|
|
18
|
+
`Row` / `Col` / `Section` / `Text` 负责摆版面,一次摆放两个面各自成立:`Col` 纵向堆叠;`Row` 网页横排、终端字符分栏(宽度不够自动降级纵向);`Section` 是带标题的块;`Text` 是说明文字。另有 `<Style>{css}</Style>` 给自定义组件带样式:web 面吐 `<style>` 标签、text 面渲染为空——静态导出不打包用户代码,`className` 引用的 CSS 靠它随树走。
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
<Col>
|
|
22
|
+
<Section title="考试成绩单">
|
|
23
|
+
<Row>
|
|
24
|
+
<Scoreboard data={board} />
|
|
25
|
+
<Text>algebra 权重 ×2,满分 100。</Text>
|
|
26
|
+
</Row>
|
|
27
|
+
</Section>
|
|
28
|
+
</Col>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
考试成绩单
|
|
33
|
+
agent 总分 algebra geometry │ algebra 权重 ×2,
|
|
34
|
+
bub 86.5/100 45/50 41.5/50 │ 满分 100。
|
|
35
|
+
codex 78.0/100 40/50 38/50 │
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## `DefaultReport` —— 官方水位整块
|
|
39
|
+
|
|
40
|
+
官方两扇门裸跑时渲染的就是它:跨 run 合成的现刻榜单加失败清单,表头如实标注合成来源。零 props、纯声明——官方水位只读瘦身条目,宿主总是替你备好,经上下文注入。把官方水位铺进自己的报告时摆上它,不要就不摆:
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
<DefaultReport />
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
Current verdicts · 2 experiments · composed from 3 runs · latest 2026-07-09T10-00-00-000Z
|
|
48
|
+
|
|
49
|
+
experiment evals pass cost duration
|
|
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
|
|
52
|
+
|
|
53
|
+
Failing:
|
|
54
|
+
✗ weather/brooklyn compare/codex-gpt-5.4 gate calledTool("get_weather") 41s ago
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## `RunOverview` —— 页头 KPI 条
|
|
58
|
+
|
|
59
|
+
每张报告开头「这批数据是什么」:几个配置、几道题、通过分布、总成本、何时跑的;选集里的挑选警告随数据一起进来、直接显示在条内,诚实不靠你记得接线。
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
<RunOverview data={await RunOverview.data(selection)} />
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
2 experiments · 15 evals · 42 attempts · composed from 3 runs · latest 2026-07-09T10-00
|
|
67
|
+
passed 26 · failed 12 · errored 4 · skipped 0 · $0.93 · 9m 15s
|
|
68
|
+
! snapshot compare/codex-gpt-5.4 predates latest run by 2 days
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## `MetricTable` —— 榜单
|
|
72
|
+
|
|
73
|
+
一行一个维度值、一列一个指标的对比表,回答「谁整体更好」。行维度、指标列、排序全部可换,自定义指标(`defineMetric`)与内置指标同列。
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
<MetricTable data={await MetricTable.data(selection, {
|
|
77
|
+
rows: "agent",
|
|
78
|
+
columns: [passRate, codeLines, costUSD],
|
|
79
|
+
sort: passRate,
|
|
80
|
+
})} />
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
agent pass rate code lines cost
|
|
85
|
+
bub 87% 312 lines $0.42
|
|
86
|
+
codex 80% 12/15 355 lines $0.51
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
两面都按 `sort` 预排,看到同一份顺序——要换排序改一行重跑,不做单面才有的交互口径。`12/15` 角标表示该格 15 个 attempt 里只有 12 个测得了这个指标。
|
|
90
|
+
|
|
91
|
+
## `MetricMatrix` —— 逐题格子
|
|
92
|
+
|
|
93
|
+
行 × 列两个维度、格子里一个指标,回答「哪道题谁挂了」。稀疏渲染:没有样本的格子空着,不编数。
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
<MetricMatrix data={await MetricMatrix.data(selection, { rows: "eval", columns: "agent", cell: passRate })} />
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
eval bub codex
|
|
101
|
+
algebra/quadratic 100% 100%
|
|
102
|
+
algebra/matrix 100% 67%
|
|
103
|
+
geometry/area 50% —
|
|
104
|
+
|
|
105
|
+
next: niceeval show geometry/area
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
网页面点格子深链到该格的 attempt;终端面在表下印下钻命令。
|
|
109
|
+
|
|
110
|
+
## `MetricBars` —— 分组条形
|
|
111
|
+
|
|
112
|
+
同一份矩阵数据的另一种摆法(`MetricBars.data` 就是 `MetricMatrix.data` 的别名):按组并排比大小,回答「每个科目上谁领先、差多少」。组维度一组条、系列维度一根条、条长是指标值——benchmark 发布图(Terminal-Bench、BrowseComp 各一组,每个 agent 一根柱)就是这个形状。
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
<MetricBars data={await MetricBars.data(selection, {
|
|
116
|
+
rows: "evalGroup", // 一组条 = 一个科目/benchmark
|
|
117
|
+
columns: "agent", // 一根条 = 一个 agent
|
|
118
|
+
cell: passRate,
|
|
119
|
+
})} />
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```text
|
|
123
|
+
algebra
|
|
124
|
+
bub ██████████████████░░ 91.9%
|
|
125
|
+
codex █████████████████░░░ 88.0%
|
|
126
|
+
geometry
|
|
127
|
+
bub ██████████░░░░░░░░░░ 50.0%
|
|
128
|
+
codex —
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
网页面是竖向分组柱:柱顶标数值,系列颜色与其它组件的稳定配色一致,图例自动生成。终端面横向条形,字符宽度即刻度,同组内按值排序(方向随 `better`)。`better: "lower"` 的指标(成本、耗时)条形反向填充,短条恒为「好」。
|
|
132
|
+
|
|
133
|
+
## `Scoreboard` —— 考试成绩单
|
|
134
|
+
|
|
135
|
+
总分 + 分科小计,回答「这套题它能得几分」。逐题分值制:权重按 eval id 前缀配置,分母对所有被打分者恒定,没跑到的题挣 0 分并如实报 `missing`。
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
<Scoreboard data={await Scoreboard.data(selection, {
|
|
139
|
+
rows: "agent",
|
|
140
|
+
subjects: "evalGroup",
|
|
141
|
+
weights: { "algebra/": 2 },
|
|
142
|
+
fullMarks: 100,
|
|
143
|
+
})} />
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```text
|
|
147
|
+
agent total algebra geometry
|
|
148
|
+
bub 86.5/100 45/50 41.5/50
|
|
149
|
+
codex 71.0/100 40/50 31/50 (1 missing)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## `MetricScatter` —— 质量 × 成本 frontier
|
|
153
|
+
|
|
154
|
+
每个点一个配置、两个指标各占一轴,回答「又好又便宜的是谁」。`series` 把同 agent 不同档位的点连成线;`better` 驱动轴向——`lower` 的轴反向画,「好」的角落恒在右上。
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
<MetricScatter data={await MetricScatter.data(selection, {
|
|
158
|
+
points: "experiment", // 每个点 = 一个配置的聚合
|
|
159
|
+
series: "agent", // 同 agent 的档位连成线
|
|
160
|
+
x: costUSD,
|
|
161
|
+
y: passRate,
|
|
162
|
+
})} />
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```text
|
|
166
|
+
pass ↑ (好 → 右上)
|
|
167
|
+
90%│ A
|
|
168
|
+
│ B
|
|
169
|
+
75%│ C
|
|
170
|
+
60%│ D
|
|
171
|
+
└──────────────────────────────────────→ cost(轴反向:越右越省)
|
|
172
|
+
$0.60 $0.45 $0.30 $0.15
|
|
173
|
+
|
|
174
|
+
A bub-high B bub-medium C codex-high D codex-low
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
网页面点带 hover(值与 `samples/total`)、同系列连线、点击深链下钻。终端面用字母标点、图例列在图下;x 或 y 缺数据的点两个面都不画,注脚如实报「n 个点缺数据」;点太密排不下时降级为坐标表,不硬挤。维度槽也收 `param()`——用 experiment 声明的变量分组,见[自定义报告](/zh/guides/custom-reports)的「变量来自配置」一节。
|
|
178
|
+
|
|
179
|
+
## `MetricLine` —— 趋势线
|
|
180
|
+
|
|
181
|
+
x 是有序变量、每个系列一条线,回答「变量拧大,分数怎么走」——并行 agent 数 × 模拟延迟 × 得分这类 scaling 图就是它。与 `MetricScatter` 的分工:scatter 的两轴都是测出来的指标(找 frontier),line 的 x 是你配置的变量(看趋势)。变量在 experiment 的 `params` 里声明,报告用 `param()` 直接引用,不从 experiment 命名里解析。
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
<MetricLine data={await MetricLine.data(selection, {
|
|
185
|
+
x: param("latencyMs", { label: "Simulated latency", unit: "ms" }),
|
|
186
|
+
series: param("agents", { label: (v) => `${v} agents` }),
|
|
187
|
+
y: passRate,
|
|
188
|
+
})} />
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
```text
|
|
192
|
+
pass ↑
|
|
193
|
+
80%│ C···C
|
|
194
|
+
│ C···C B
|
|
195
|
+
60%│ C B···B
|
|
196
|
+
│ B···B A
|
|
197
|
+
40%│ A···A
|
|
198
|
+
│ A
|
|
199
|
+
20%│
|
|
200
|
+
└───────────────────────────────────────────→ Simulated latency
|
|
201
|
+
1 min 5 min 10 min 20 min
|
|
202
|
+
|
|
203
|
+
A 1 agents B 4 agents C 16 agents
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
每个点是一个 experiment 的聚合,与其它组件同一台两级聚合引擎,`samples` / `total` / `refs` 一应俱全;同系列的点按 x 排序连线。网页面每个点可 hover、可深链下钻。终端面同系列共用一个字母、沿 x 排布,趋势肉眼可读;y 缺数据的点不画、注脚报数,点太密降级为坐标表。
|
|
207
|
+
|
|
208
|
+
## `DeltaTable` —— 成对对比
|
|
209
|
+
|
|
210
|
+
每行一对配置、每列一个指标,格子里 A、B、Δ 三个值,回答「这个开关值不值」「这次修复翻转了什么」。涨跌好坏由 `better` 判定,任一侧缺数据 Δ 显示为缺,不硬算。「这次 vs 上次」(同一配置的两个快照)也是它:`pairs` 的 `a` / `b` 除 experiment id 外也收快照键 `<experimentId> @ <startedAt>`——时间轴对比要用到旧快照,`results.latest()` 里没有,配手挑的快照数组(如 `[exp.latest, exp.snapshots[1]]`)用。
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
<DeltaTable data={await DeltaTable.data(selection, {
|
|
214
|
+
pairs: [
|
|
215
|
+
{ a: "compare/bub", b: "compare/bub--agents-md", label: "bub" },
|
|
216
|
+
{ a: "compare/codex", b: "compare/codex--agents-md", label: "codex" },
|
|
217
|
+
],
|
|
218
|
+
metrics: [passRate, costUSD],
|
|
219
|
+
})} />
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
```text
|
|
223
|
+
pair pass rate cost
|
|
224
|
+
bub 87% → 93% +6% $0.42 → $0.45 +$0.03
|
|
225
|
+
codex 80% → 80% ±0 $0.51 → — —
|
|
226
|
+
```
|
|
227
|
+
|
|
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
|
+
## 官方组件之外
|
|
251
|
+
|
|
252
|
+
以上摆法都表达不了时,用 `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`、`
|
|
49
|
+
- metadata 带 `agent`、`model`、`experiment`、`params`、`outcome` 和失败断言明细,方便在 Braintrust 里按维度过滤。
|
|
50
50
|
|
|
51
51
|
### 配置项
|
|
52
52
|
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "用 niceeval/results 直接读写结果数据"
|
|
3
|
+
sidebarTitle: "结果数据 API"
|
|
4
|
+
description: "报告积木脚下的数据层:openResults 把 .niceeval/ 的落盘工件 parse 成「实验 → 快照 → eval → attempt」的类型化层次,createRunWriter 把别家结果写成 NiceEval 格式,copySnapshots 负责发布瘦身。"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
[自定义报告](/zh/guides/custom-reports)的积木——指标、计算函数、双面组件——脚下还有一层:`niceeval/results`,落盘工件的 parser。官方两扇门和全部报告积木读的都是这一层,没有私有数据通道;报告表达不了的口径,下到这层直接拿数据算。
|
|
8
|
+
|
|
9
|
+
什么时候下到这层:
|
|
10
|
+
|
|
11
|
+
- **口径连计算函数都折不出。** 表格、矩阵、成绩单、散点都是「折叠」;分布类的看法(直方图、逐事件分析)不是折叠,要自己遍历 attempt。
|
|
12
|
+
- **把结果喂进自己的系统。** 数据仓库、告警、内部平台——吃类型化数据,不吃磁盘布局。
|
|
13
|
+
- **把别家平台的结果转成 NiceEval 格式。** 写入面保证「写出去的就是读得回的」,转完 `niceeval show` / `view` / 报告积木全套直接能用。
|
|
14
|
+
|
|
15
|
+
这一层只管数据的读与写,不预设任何看法:不合并、不聚合、不去重,忠实反映磁盘。「怎么看」归上层的报告积木。
|
|
16
|
+
|
|
17
|
+
## 读:`openResults`
|
|
18
|
+
|
|
19
|
+
输入是 `.niceeval/` 目录(或单个 run 目录),输出是四层数据:**实验 → 快照(单次跑的实验)→ eval → attempt**。你从此不碰路径、不判断文件存在性、不解析 JSON:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { openResults } from "niceeval/results";
|
|
23
|
+
|
|
24
|
+
const results = await openResults(".niceeval");
|
|
25
|
+
|
|
26
|
+
results.experiments; // Experiment[]:每个实验一项,挂着自己的全部历史
|
|
27
|
+
results.skipped; // 读不了的落盘:{ dir, reason, schemaVersion?, producer? }[]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**第一层,实验**——同一个 experiment id 的历次运行归在一起:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
const exp = results.experiments.find((e) => e.id === "compare/bub-gpt-5.4")!;
|
|
34
|
+
|
|
35
|
+
exp.snapshots; // Snapshot[]:历次快照,最新在前
|
|
36
|
+
exp.latest; // 最新一次(= snapshots[0])
|
|
37
|
+
exp.evalIds; // 历史覆盖过的 eval 并集,残缺检测的依据
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**第二层,快照**——单次跑的实验。它是谁、什么时候跑的、**用什么写的**,都在这一层:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const snap = exp.latest;
|
|
44
|
+
|
|
45
|
+
snap.agent; // 本快照自己的 agent
|
|
46
|
+
snap.model;
|
|
47
|
+
snap.startedAt; // 这次实验什么时候跑的
|
|
48
|
+
snap.producer; // { name: "niceeval", version: "0.4.6" } —— 谁写的这份结果
|
|
49
|
+
snap.schemaVersion; // 结果格式版本
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**第三层,eval**——这次实验里的每道题,attempt(重试历史)挂在题下面:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
for (const ev of snap.evals) {
|
|
56
|
+
ev.id; // "algebra/quadratic"
|
|
57
|
+
ev.attempts; // AttemptHandle[]:该题的全部 attempt
|
|
58
|
+
}
|
|
59
|
+
snap.attempts; // 不关心题目边界时,全部 attempt 平铺
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**第四层,attempt**——瘦身条目直接在手上,重工件全部懒加载。唯一带 `Handle` 后缀的类型就是它:上面三层是纯数据,这一层的方法会碰磁盘:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
const attempt = snap.evals[0].attempts[0];
|
|
66
|
+
|
|
67
|
+
attempt.evalId; // "algebra/quadratic" —— 属于哪道题,不用绕 result
|
|
68
|
+
attempt.experimentId; // 属于哪个实验
|
|
69
|
+
attempt.result; // EvalResult:判决、断言、用量、成本、experiment 元数据
|
|
70
|
+
attempt.ref; // { run, result }:证据引用,与 view 深链、报告格子的 refs 同一身份
|
|
71
|
+
await attempt.events(); // StreamEvent[] | null
|
|
72
|
+
await attempt.trace(); // TraceSpan[] | null
|
|
73
|
+
await attempt.o11y(); // O11ySummary | null
|
|
74
|
+
await attempt.diff(); // DiffData | null(可达上百 MB,所以必须懒)
|
|
75
|
+
await attempt.sources(); // SourceArtifact[] | null
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
三条要点:
|
|
79
|
+
|
|
80
|
+
- **懒加载即存在性判断。** 工件缺失返回 `null`,不抛错——remote agent 没有 `diff.json` 时,`await attempt.diff()` 就是 `null`,你据此决定跳过还是报缺。
|
|
81
|
+
- **读不了的落盘不静默。** 版本不兼容、目录损坏的 run 进 `skipped` 并带原因;要不要展示由你定,但缺口永远被算出来。
|
|
82
|
+
- **同进程内按句柄记忆化。** 两处都读同一个 `diff()` 不会把上百 MB 读两遍。
|
|
83
|
+
|
|
84
|
+
## 版本:谁写的、读不读得了
|
|
85
|
+
|
|
86
|
+
每个快照都带自己的出身:`producer` 是写这份结果的工具与版本(niceeval 自己,或经写入面转换的第三方 harness),`schemaVersion` 是磁盘格式版本。格式只在破坏兼容时递增版本,读取器只认相同版本——**版本不兼容的落盘不解析、不迁移、不猜**,整个 run 进 `skipped`:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
for (const s of results.skipped) {
|
|
90
|
+
s.reason; // "incompatible-version" | "malformed" | "incomplete"
|
|
91
|
+
s.schemaVersion; // 那份结果声明的格式版本
|
|
92
|
+
s.producer; // { name, version }:谁写的它
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`"incomplete"` 与版本无关:run 中途 crash、没走到收尾写出 summary 的目录进这一类——已完成的 attempt 工件仍在盘上供手工排查,但没有 summary 的落盘对读取面不可见。
|
|
97
|
+
|
|
98
|
+
旧版本的结果不会丢:磁盘上原样还在,用写它的那个工具就能看——`producer.name` 是 `"niceeval"` 时,提示用户 `npx niceeval@<producer.version> view`;是第三方 harness 时如实报出它的名字和版本,别拼一句错误的 npx 命令。`skipped` 给你的信息刚好够做对这个分支。
|
|
99
|
+
|
|
100
|
+
## 为什么快照不是 run 目录
|
|
101
|
+
|
|
102
|
+
一次 CLI 调用写一个 run 目录,但一个 run 目录里可以装多个 experiment(`niceeval exp compare` 整组对照进同一份汇总)。「每个 experiment 最新一次」因此没法用目录粒度表达:周一跑了整组,周二只重跑 `compare/bub-gpt-5.4`,bub 的最新在周二的目录里,codex 的还在周一的目录里。
|
|
103
|
+
|
|
104
|
+
快照把「单次跑的实验」这层身份显式化——**一个 experiment 在一次落盘里的那部分结果**。物理目录仍可通过 `results.runDirs` 与 `snap.runDir` 触达(发布、排查工件路径时用),多数消费方不需要碰。
|
|
105
|
+
|
|
106
|
+
## 选快照:`results.latest()`
|
|
107
|
+
|
|
108
|
+
多数场景先回答「现在什么水位」,选择器替你挑「每个实验最新一次」——这也是官方榜单的口径。返回的是一个**选集(Selection)**,快照和警告绑在一起走:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const latest = results.latest({
|
|
112
|
+
experiments: "compare/", // 可选:experiment id 前缀过滤(string | string[]),同 CLI 前缀语义
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
latest.snapshots; // Snapshot[]:每个实验最新一次
|
|
116
|
+
latest.warnings; // 结构化警告,不是渲染好的文本
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
「最新」可能残缺:只重跑一道题是正常的 debug 姿势,它产出的最新快照就只有一道题。选择器把每个选中快照的覆盖与该实验的历史并集(`exp.evalIds`)对比,缩水就写进 `warnings`:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
latest.warnings[0];
|
|
123
|
+
// {
|
|
124
|
+
// kind: "partial-coverage",
|
|
125
|
+
// experimentId: "midterm/bub-gpt-5.4",
|
|
126
|
+
// covered: 1,
|
|
127
|
+
// total: 50,
|
|
128
|
+
// message: "snapshot covers 1 of 50 evals seen in history; re-run `niceeval exp midterm/bub-gpt-5.4` for a full snapshot",
|
|
129
|
+
// }
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
字段供程序判断——CI 里「覆盖缩水就 fail」直接判 `covered < total`,不解析文本;`message` 是渲染好的英文句子,要展示就原样打。渲染与否在你,缺口永远被算出来。警告不止这一种:快照落后于选集中最新的落盘、experimentId 缺失被合成键等同样进 `warnings`,每种都带 `kind`、可判断的结构化字段和渲染好的 `message`。
|
|
133
|
+
|
|
134
|
+
选集是[报告积木](/zh/guides/custom-reports)和下文 `copySnapshots` 的通用输入:收 `Selection` 时 warnings 随行(`RunOverview` 之类的组件会如实展示),手工挑的 `Snapshot[]` 数组照收。微调官方口径不用降级成裸数组:`latest.filter((s) => s.experimentId !== "compare/broken")` 返回新选集——快照被删减,warnings 修剪到幸存的实验,provenance 不丢。`filter` 只做删减;「换成该实验上一个完整快照」这类**替换式**重挑不是它的事,回到 `exp.snapshots` 自己拿——手工挑的数组没有挑选过程,自然没有 warnings 可带,也如实。
|
|
135
|
+
|
|
136
|
+
## 一个真实脚本:分布不是折叠
|
|
137
|
+
|
|
138
|
+
「每个 agent 的 shell 命令数分布」画不成表格——那是分布,不是折叠。直接遍历:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
import { openResults } from "niceeval/results";
|
|
142
|
+
|
|
143
|
+
const results = await openResults(".niceeval");
|
|
144
|
+
const points = [];
|
|
145
|
+
for (const exp of results.experiments) {
|
|
146
|
+
for (const attempt of exp.latest.attempts) {
|
|
147
|
+
const o11y = await attempt.o11y();
|
|
148
|
+
points.push({
|
|
149
|
+
agent: exp.latest.agent,
|
|
150
|
+
eval: attempt.evalId,
|
|
151
|
+
passed: attempt.result.outcome === "passed",
|
|
152
|
+
shellCommands: o11y?.shellCommands.length ?? 0,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
即使在这条最深的路径上也不碰磁盘布局:路径拼接、存在性、版本过滤、快照切分全被库消化。要把这份分布摆进报告页,用 [`defineComponent`](/zh/guides/custom-reports) 包一个双面组件即可。
|
|
159
|
+
|
|
160
|
+
一条跨快照累计时的义务:`--resume` 会把上一轮已通过的结果原样合入新 run,同一个 attempt 因此可能存在于多份落盘。合入的条目不是空壳:它的工件仍在原 run 目录,懒加载会自动回退到那里读(原 run 目录被清理后如实返回 `null`);`ref` 指向条目所在的落盘。身份键 `(experimentId, evalId, attempt, startedAt)` 的四个字段都在数据上——前两个是 attempt 的直达字段,序号与 `startedAt` 在 `attempt.result` 上。reader 忠实反映这份重复;跨快照聚合前用 `dedupeAttempts` 按身份键去重,重复保留最新 run 目录里的那份——报告积木的计算函数内置这条,自己写脚本时记得过一遍:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { dedupeAttempts } from "niceeval/results";
|
|
164
|
+
|
|
165
|
+
const all = exp.snapshots.flatMap((s) => s.attempts); // 跨快照累计:历史全量
|
|
166
|
+
const { attempts, warnings } = dedupeAttempts(all); // resume 合入的重复被折掉
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## 写:`createRunWriter`
|
|
170
|
+
|
|
171
|
+
写入面和读取面是同一组类型的两半,签名就是 roundtrip 的证明:reader 的 `attempt.result` 由两部分拼成——快照级字段(agent / model / startedAt / producer)来自 `writer.snapshot()` 的一次声明,其余全部字段就是 `writeAttempt` 第一个参数的类型;第二个参数是 reader 懒加载能拿到的那几样工件的类型。「writeAttempt 参数 + snapshot() 声明 = reader 读回的全部」由类型拼合背书:快照级字段不在 attempt 参数的类型里,不存在「谁的值为准」。用它把别家平台、自研 harness 的结果转成 NiceEval 格式:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
import { createRunWriter } from "niceeval/results";
|
|
175
|
+
|
|
176
|
+
const writer = await createRunWriter(".niceeval", {
|
|
177
|
+
producer: { name: "my-harness", version: "1.0.0" },
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const snap = writer.snapshot({ // 快照级元数据的家:一个 experiment 开一个
|
|
181
|
+
experiment: "compare/my-agent",
|
|
182
|
+
agent: "my-agent",
|
|
183
|
+
model: "gpt-5.4",
|
|
184
|
+
startedAt: sourceRun.startedAt, // 必填:去重身份键以它为锚
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
for (const r of convertedResults) {
|
|
188
|
+
await snap.writeAttempt(r.result, { // 第一参 = attempt 级条目;快照级字段在 snapshot() 声明
|
|
189
|
+
events: r.events, // 第二参 = 工件,都可选;缺哪样读取面就懒加载出 null
|
|
190
|
+
diff: r.diff,
|
|
191
|
+
}); // 拆工件文件、算目录、回填引用,全在库内发生
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
await writer.finish(); // summary 从已写入的 attempt 推导,计数永远和条目对得上
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`writer.snapshot()` 就是读取面「一个 run 装多份快照」的镜像:转多个 experiment 就开多个快照,agent / model / startedAt 这些快照级元数据在这里声明,不用塞进每条 attempt;可选的 `knownEvalIds`(该实验已知的 eval 并集)也在这里声明——它是残缺检测的分母,转换只覆盖部分题目时如实交代全集,下游的覆盖警告就能算出来(`copySnapshots` 发布时会自动补记这个字段,见下文)。转完的目录就是标准结果目录:`niceeval show` / `niceeval view` 直接能看,报告积木直接能算,不用抄格式文档;`producer` 会原样出现在读取面的 `snap.producer` 上。attempt 工件按完成增量落盘,但 summary 是 `finish()` 写出的收尾事实:中途 crash、没走到 `finish()` 的目录,已完成的工件仍在盘上供手工排查,读取面则把它归入 `skipped("incomplete")`,不认半份落盘。
|
|
198
|
+
|
|
199
|
+
## 发布:`copySnapshots`
|
|
200
|
+
|
|
201
|
+
把选中的快照按格式感知地复制到发布目录——只带指定工件、只带选中的 attempt:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
import { openResults, copySnapshots } from "niceeval/results";
|
|
205
|
+
|
|
206
|
+
const results = await openResults(".niceeval");
|
|
207
|
+
await copySnapshots(results.latest(), "site-data/run", {
|
|
208
|
+
artifacts: ["sources", "events", "trace"], // diff 可达百 MB,发布时常见地不带
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
第一个参数收 `Selection` 或手工挑的 `Snapshot[]`——和报告积木同一个输入约定。`artifacts` 的合法值是 `"events" | "trace" | "o11y" | "diff" | "sources"`,缺省全带。目标目录已存在且非空时报错,不静默覆盖——发布脚本要幂等就自己先清目标目录。
|
|
213
|
+
|
|
214
|
+
复制不改工件内容、不消毒;发布前给自由文本消毒用报告积木 `CaseList.data` 的 `redact` 钩子。唯一随行补记的是挑选时的**覆盖事实**:`partial-coverage` 警告的分母是实验的历史并集,而发布目录没有历史——所以每个复制出的快照带上 `knownEvalIds`(复制时刻该实验已知的 eval 并集),reader 端把它并进 `exp.evalIds` 的计算(取本地历史与快照携带值的并集)。发布目录上重新 `openResults().latest()`,残缺警告被同一套机制重新算出来,不靠发布者转述。CI 静态托管的完整流程见[查看结果 · 导出与静态托管](/zh/guides/viewing-results#导出与静态托管)。
|
|
215
|
+
|
|
216
|
+
## 分层速览
|
|
217
|
+
|
|
218
|
+
| 层 | 入口 | 回答 |
|
|
219
|
+
| --- | --- | --- |
|
|
220
|
+
| 官方两扇门 | `niceeval show` / `niceeval view` | 零代码看官方摆法 |
|
|
221
|
+
| 报告积木 | [自定义报告](/zh/guides/custom-reports)、[报告组件](/zh/guides/report-components) | 自己的口径与摆法 |
|
|
222
|
+
| 结果数据 API(本页) | `niceeval/results` | 折叠表达不了的算法、接自己的系统、读写格式本身 |
|
|
223
|
+
|
|
224
|
+
每层都建立在下一层之上,同一份落盘工件是唯一事实来源——上层的派生物删了随时可重算。
|
|
@@ -115,9 +115,9 @@ export default defineSandboxAgent({
|
|
|
115
115
|
});
|
|
116
116
|
```
|
|
117
117
|
|
|
118
|
-
## `ctx.model` 与 `ctx.
|
|
118
|
+
## `ctx.model` 与 `ctx.params`
|
|
119
119
|
|
|
120
|
-
CLI 传入的模型和自定义
|
|
120
|
+
CLI 传入的模型和自定义 params 会出现在 adapter context 中。adapter 可以决定如何把它们转成 CLI 参数或 HTTP payload。
|
|
121
121
|
|
|
122
122
|
## 在 experiment 中使用自定义 agent
|
|
123
123
|
|