niceeval 0.5.2 → 0.5.3
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 +6 -6
- package/docs-site/zh/concepts/overview.mdx +1 -1
- package/docs-site/zh/concepts/tier.mdx +6 -6
- package/docs-site/zh/guides/ci-integration.mdx +2 -0
- package/docs-site/zh/guides/connect-your-agent.mdx +7 -7
- package/docs-site/zh/guides/custom-reports.mdx +7 -7
- package/docs-site/zh/guides/experiments.mdx +3 -3
- package/docs-site/zh/guides/publish-report.mdx +91 -0
- package/docs-site/zh/guides/report-components.mdx +4 -4
- package/docs-site/zh/guides/reporters.mdx +1 -1
- package/docs-site/zh/guides/results-data.mdx +1 -1
- 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 +3 -19
- 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/builtin-agents.mdx +4 -0
- package/docs-site/zh/reference/cli.mdx +9 -6
- package/docs-site/zh/reference/define-agent.mdx +7 -3
- package/docs-site/zh/reference/define-config.mdx +3 -1
- package/docs-site/zh/reference/define-eval.mdx +9 -3
- package/docs-site/zh/reference/events.mdx +4 -0
- package/docs-site/zh/reference/expect.mdx +4 -0
- 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 +2 -2
- package/src/agents/ui-message-stream.test.ts +1 -1
- package/src/cli.ts +38 -14
- 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 +2 -2
- package/src/i18n/zh-CN.ts +2 -2
- package/src/report/aggregate.ts +16 -16
- package/src/report/components.tsx +1 -1
- package/src/report/compute.ts +7 -7
- package/src/report/dual-face.test.tsx +1 -1
- package/src/report/{param.ts → flag.ts} +6 -6
- package/src/report/index.ts +3 -3
- package/src/report/react/MetricLine.tsx +1 -1
- package/src/report/react/fixtures.ts +1 -1
- package/src/report/report.test.ts +17 -17
- package/src/report/types.ts +11 -11
- package/src/results/results.test.ts +2 -2
- package/src/runner/attempt.ts +3 -3
- package/src/runner/fingerprint.ts +1 -1
- 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 +8 -8
- package/src/view/app/css.d.ts +3 -0
- package/src/view/app/i18n.ts +3 -3
- package/src/view/app/lib/rows.ts +4 -4
- package/src/view/client-dist/app.js +2 -2
- package/src/view/data.test.ts +25 -1
- package/src/view/data.ts +27 -3
|
@@ -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.flags`、`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
|
+
flags: ctx.flags,
|
|
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
|
+
flags: { 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 flags**(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 flags: Readonly<Record<string, unknown>>; // Tier 3 的 feature A/B:experiment.flags 透传;没配时是 {}
|
|
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`、`flags` 由 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、哪个模型、开哪些 flags、跑几次。eval 与 experiment 分开的原因——晚绑定,让同一份 eval 换着对象跑。"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
**experiment** 对着哪个 agent、用哪个模型、开哪些
|
|
7
|
+
**experiment** 对着哪个 agent、用哪个模型、开哪些 flags、跑几次。用于做 AB 测试、模型对比、feature 对比——`flags` 就是 A/B 测试里的 feature flag,一个 experiment 是一组 flag 取值。
|
|
8
8
|
|
|
9
9
|
## 一个 experiment 里有什么
|
|
10
10
|
|
|
@@ -16,13 +16,13 @@ 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
|
+
flags: { promptVariant: "concise" }, // 经 ctx.flags 透传;eval 里是 t.flags
|
|
20
20
|
runs: 3, // 每条 eval 跑几次(对抗随机性)
|
|
21
21
|
});
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
- `agent`:评谁。放的是已经配置好的实例——被测系统的 URL、鉴权传给 Adapter 工厂,不进 experiment 的其它字段。
|
|
25
|
-
- `model` / `
|
|
25
|
+
- `model` / `flags`:透传语义。[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
|
## 矩阵对比
|
|
@@ -32,6 +32,6 @@ export default defineExperiment({
|
|
|
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 / flags 的对比怎么组织、怎么读结果。
|
|
36
36
|
- [评估](/zh/concepts/evals) — 另一半:eval 是什么、生命周期与 outcome。
|
|
37
|
-
- [Tier](/zh/concepts/tier) — `model` / `
|
|
37
|
+
- [Tier](/zh/concepts/tier) — `model` / `flags` 各在哪一档生效。
|
|
@@ -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 flags。每档买到什么、什么时候升级。"
|
|
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 flags
|
|
26
26
|
|
|
27
|
-
要评「改 prompt / 换工具集 / 开关某个 feature 谁更好」这类 **feature A/B test** 时,对照点在应用内部,前两档拿不到。这一档改应用内部代码,把变体暴露成外部可选的配置:experiment 的 `
|
|
27
|
+
要评「改 prompt / 换工具集 / 开关某个 feature 谁更好」这类 **feature A/B test** 时,对照点在应用内部,前两档拿不到。这一档改应用内部代码,把变体暴露成外部可选的配置:experiment 的 `flags` 经 `ctx.flags` 透传给 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.flags` 按档位出现。
|
|
43
43
|
- [OTel 接入](/zh/guides/connect-otel) — Tier 2 的完整走法。
|
|
44
|
-
- [实验](/zh/concepts/experiment) — model /
|
|
44
|
+
- [实验](/zh/concepts/experiment) — model / flags 对比在 experiment 侧怎么声明。
|
|
@@ -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`、`flags`,运行器每轮经 `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`、`flags` 每轮从 `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
|
+
flags: ctx.flags, // ← experiment.flags:原样交给应用,应用按参数切换
|
|
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
|
+
flags: { promptVariant: "concise" }, // ← 动态值:经 ctx.flags 到 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
|
+
| `flags` | experiment 的 `flags` | 原样转发(请求体、header 都行),应用按参数切换变体 |
|
|
174
174
|
| `telemetry` | 配置了 OTel 接入时出现 | 只碰 `headers`:每轮新的 W3C `traceparent`,spread 进请求头。接收端点每次运行都一样,在 `defineConfig` 里固定、应用启动时指向它,不从 send 传——见 [OTel 接入](/zh/guides/connect-otel) |
|
|
175
175
|
| `session` | 运行器(每条会话线一份) | 会话续接与 HITL 停轮现场的存取器都在它上面:`history()`、`id` / `capture()`、`hold()` / `take()`,见[写 send](/zh/guides/write-send) |
|
|
176
176
|
| `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 对比 | 应用把变体暴露成 `flags` 可切换的配置 | [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、flags。
|
|
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) —— 三档接入各投入什么、买到什么。
|
|
@@ -20,7 +20,7 @@ description: "一份报告就是一个报告文件:官方宿主打开结果、
|
|
|
20
20
|
四个格子是同一个宿主。自定义只有三个层次,逐层深入:
|
|
21
21
|
|
|
22
22
|
1. **换摆法**:用官方组件和 `Row` / `Col` 重摆版面——大多数「要自己的口径」到这层就够了。
|
|
23
|
-
2. **换口径**:`defineMetric` 定义自己的指标,`
|
|
23
|
+
2. **换口径**:`defineMetric` 定义自己的指标,`flag()` 把 experiment 声明的变量当维度和轴。
|
|
24
24
|
3. **换形态**:官方组件表达不了的展示,用 `defineComponent` 写自己的组件——网页怎么渲染、终端字符怎么排,两个面你都说了算。
|
|
25
25
|
|
|
26
26
|
## 一份报告 = 一个报告文件
|
|
@@ -138,7 +138,7 @@ codex 80% 355 lines $0.51
|
|
|
138
138
|
|
|
139
139
|
## 变量来自配置,不来自命名
|
|
140
140
|
|
|
141
|
-
画「并行 agent 数 × 模拟延迟 × 得分」这类 scaling 趋势时,图上的变量不该编码进 experiment id(`ultra-16agents-300ms`),再在报告里解析字符串抠出来——那是约定不是配置,改个命名整张图就散。变量的家是 experiment 文件的 `
|
|
141
|
+
画「并行 agent 数 × 模拟延迟 × 得分」这类 scaling 趋势时,图上的变量不该编码进 experiment id(`ultra-16agents-300ms`),再在报告里解析字符串抠出来——那是约定不是配置,改个命名整张图就散。变量的家是 experiment 文件的 `flags`:一文件一个档位,把变量声明成数据:
|
|
142
142
|
|
|
143
143
|
```ts
|
|
144
144
|
// experiments/ultra/agents-16.ts
|
|
@@ -147,21 +147,21 @@ import { bub } from "../../agents/bub.ts";
|
|
|
147
147
|
|
|
148
148
|
export default defineExperiment({
|
|
149
149
|
agent: bub({ mode: "ultra" }),
|
|
150
|
-
|
|
150
|
+
flags: { agents: 16, latencyMs: 300 },
|
|
151
151
|
});
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
-
报告侧用 `
|
|
154
|
+
报告侧用 `flag()` 把声明的值直接当维度或轴用,不解析任何名字:
|
|
155
155
|
|
|
156
156
|
```tsx
|
|
157
157
|
<MetricLine data={await MetricLine.data(selection, {
|
|
158
|
-
x:
|
|
159
|
-
series:
|
|
158
|
+
x: flag("latencyMs", { label: "Simulated latency", unit: "ms" }),
|
|
159
|
+
series: flag("agents", { label: (v) => `${v} agents` }),
|
|
160
160
|
y: passRate,
|
|
161
161
|
})} />
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
-
`
|
|
164
|
+
`flag()` 用在维度槽(`series` / `rows` / `columns` / `points`)就按声明值分组,用在轴槽(`x`)就要求数值并驱动刻度;内置维度(`agent`、`model`、`experiment`、`evalGroup`……)覆盖不了的变量都走它。没有声明该 flag 的 experiment 不猜:分组时如实归入「未配置」一组,作轴时该点不画、注脚报数。flags 随快照落盘,所以历史 run 也能按当时声明的变量重摆——报告读的是配置的事实,不是命名的巧合。
|
|
165
165
|
|
|
166
166
|
## 换形态:自定义组件,一个定义两个面
|
|
167
167
|
|
|
@@ -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 和 flags,比较 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` 的字段怎么写、`flags` 怎么透传到 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 flags(要求 Tier 3 接入:变体在应用内部,需要应用把它暴露成 experiment 可选的 flag,经 `flags` → `ctx.flags` 透传)。
|
|
47
47
|
- 不同 sandbox 后端。
|
|
48
48
|
- 同一任务的 pass@N。
|
|
49
49
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "通过 CI 发布报告"
|
|
3
|
+
sidebarTitle: "CI 发布报告"
|
|
4
|
+
description: "把报告站接进 CI:.niceeval/ 提交进仓库,构建命令一行 view --out,push 即发布。发布的站和本地 niceeval view 完全一样;零可读结果时构建失败,保住上一次部署。"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
`niceeval view --out <目录>` 把查看器导出成一个纯静态目录:榜单、快照列表、transcript、trace 瀑布,和本地 `niceeval view` 看到的完全一样(导出行为见[查看结果 · 导出与静态托管](/zh/guides/viewing-results#导出与静态托管))。CI 发布只需要让结果数据到构建机手里——把 `.niceeval/` 提交进仓库,仓库本身就是数据源。本地跑完 eval,提交并 push 就是发布,不需要任何脚本或本地构建命令。
|
|
8
|
+
|
|
9
|
+
## `.gitignore` 只排除 `diff.json`
|
|
10
|
+
|
|
11
|
+
```gitignore
|
|
12
|
+
# .niceeval/ 提交进仓库:它是线上报告的数据源,CI 构建时直接从这里生成整站。
|
|
13
|
+
# 只排除 diff.json —— 单轮可达上百 MB,查看器也不读它。
|
|
14
|
+
.niceeval/**/diff.json
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 构建命令就是导出命令
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx niceeval view --out site
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`view` 对零可读结果直接报错、非零退出,不会导出一张空报告——`.niceeval/` checkout 坏掉,或所有落盘与当前 niceeval 的 schemaVersion 不兼容被整批跳过时,构建失败,Vercel / GitHub Pages 保留上一次部署。错误逐条列出被跳过的 run 目录与原因,schemaVersion 场景还给出能直接查看旧落盘的 `npx niceeval@<版本> view` 命令。
|
|
24
|
+
|
|
25
|
+
## 发布自定义报告
|
|
26
|
+
|
|
27
|
+
不传 `--report` 时,发布出来的首页是官方榜单。想让首页换成自己的报告,把 [`defineReport` 报告文件](/zh/guides/custom-reports)传给 `--report` 就行——attempt 证据页(transcript、trace、代码视图)仍在同一个站里,报告里的每个数字点进去就是对应证据,和本地 `view --report` 看到的一模一样:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx niceeval view --report reports/exam.tsx --out site
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
报告文件和 `.niceeval/` 一样提交在仓库里,改完版面 push,线上就跟着更新。用下面的 `vercel.json` / workflow 时,把构建命令换成这一行即可,其余配置不用动。
|
|
34
|
+
|
|
35
|
+
## 接托管平台
|
|
36
|
+
|
|
37
|
+
**Vercel**:仓库根放一个 `vercel.json`,导入项目后 push 即部署。
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"installCommand": "pnpm install --frozen-lockfile",
|
|
42
|
+
"buildCommand": "npx niceeval view --out site",
|
|
43
|
+
"outputDirectory": "site"
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**GitHub Pages**:仓库 Settings → Pages 把 Source 设为 GitHub Actions,再加 workflow:
|
|
48
|
+
|
|
49
|
+
```yaml
|
|
50
|
+
# .github/workflows/report.yml
|
|
51
|
+
name: report
|
|
52
|
+
on:
|
|
53
|
+
push:
|
|
54
|
+
branches: [main]
|
|
55
|
+
|
|
56
|
+
permissions:
|
|
57
|
+
contents: read
|
|
58
|
+
pages: write
|
|
59
|
+
id-token: write
|
|
60
|
+
|
|
61
|
+
jobs:
|
|
62
|
+
build:
|
|
63
|
+
runs-on: ubuntu-latest
|
|
64
|
+
steps:
|
|
65
|
+
- uses: actions/checkout@v4
|
|
66
|
+
- uses: pnpm/action-setup@v4
|
|
67
|
+
- uses: actions/setup-node@v4
|
|
68
|
+
with:
|
|
69
|
+
node-version: 22
|
|
70
|
+
cache: pnpm
|
|
71
|
+
- run: pnpm install --frozen-lockfile
|
|
72
|
+
- run: npx niceeval view --out site
|
|
73
|
+
- uses: actions/upload-pages-artifact@v3
|
|
74
|
+
with:
|
|
75
|
+
path: site
|
|
76
|
+
deploy:
|
|
77
|
+
needs: build
|
|
78
|
+
runs-on: ubuntu-latest
|
|
79
|
+
environment:
|
|
80
|
+
name: github-pages
|
|
81
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
82
|
+
steps:
|
|
83
|
+
- id: deployment
|
|
84
|
+
uses: actions/deploy-pages@v4
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
日常循环从此只有两步:本地跑 eval,提交 `.niceeval/` 并 push。发布出去的站带完整历史——同事打开链接,看到的和你本地 `niceeval view` 一模一样;仓库、本地查看器、线上报告三处同一个真相源。仓库体积随落盘增长(`diff.json` 已排除,增长主要来自 transcript 和 trace 工件),不想保留的旧 run 直接删对应目录即可,查看器和榜单跟着仓库走。
|
|
88
|
+
|
|
89
|
+
## 发布的是完整证据
|
|
90
|
+
|
|
91
|
+
整站导出带完整证据:transcript、源码快照、trace 原样进站点,不消毒。发布到公网前确认结果里没有密钥或敏感数据;自定义报告页的自由文本消毒见[结果数据层](/zh/guides/results-data)的 `redact` 钩子。
|
|
@@ -174,16 +174,16 @@ pass ↑ (好 → 右上)
|
|
|
174
174
|
A bub-high B bub-medium C codex-high D codex-low
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
-
网页面点带 hover(值与 `samples/total`)、同系列连线、点击深链下钻。终端面用字母标点、图例列在图下;x 或 y 缺数据的点两个面都不画,注脚如实报「n 个点缺数据」;点太密排不下时降级为坐标表,不硬挤。维度槽也收 `
|
|
177
|
+
网页面点带 hover(值与 `samples/total`)、同系列连线、点击深链下钻。终端面用字母标点、图例列在图下;x 或 y 缺数据的点两个面都不画,注脚如实报「n 个点缺数据」;点太密排不下时降级为坐标表,不硬挤。维度槽也收 `flag()`——用 experiment 声明的变量分组,见[自定义报告](/zh/guides/custom-reports)的「变量来自配置」一节。
|
|
178
178
|
|
|
179
179
|
## `MetricLine` —— 趋势线
|
|
180
180
|
|
|
181
|
-
x 是有序变量、每个系列一条线,回答「变量拧大,分数怎么走」——并行 agent 数 × 模拟延迟 × 得分这类 scaling 图就是它。与 `MetricScatter` 的分工:scatter 的两轴都是测出来的指标(找 frontier),line 的 x 是你配置的变量(看趋势)。变量在 experiment 的 `
|
|
181
|
+
x 是有序变量、每个系列一条线,回答「变量拧大,分数怎么走」——并行 agent 数 × 模拟延迟 × 得分这类 scaling 图就是它。与 `MetricScatter` 的分工:scatter 的两轴都是测出来的指标(找 frontier),line 的 x 是你配置的变量(看趋势)。变量在 experiment 的 `flags` 里声明,报告用 `flag()` 直接引用,不从 experiment 命名里解析。
|
|
182
182
|
|
|
183
183
|
```tsx
|
|
184
184
|
<MetricLine data={await MetricLine.data(selection, {
|
|
185
|
-
x:
|
|
186
|
-
series:
|
|
185
|
+
x: flag("latencyMs", { label: "Simulated latency", unit: "ms" }),
|
|
186
|
+
series: flag("agents", { label: (v) => `${v} agents` }),
|
|
187
187
|
y: passRate,
|
|
188
188
|
})} />
|
|
189
189
|
```
|
|
@@ -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`、`flags`、`outcome` 和失败断言明细,方便在 Braintrust 里按维度过滤。
|
|
50
50
|
|
|
51
51
|
### 配置项
|
|
52
52
|
|
|
@@ -211,7 +211,7 @@ await copySnapshots(results.latest(), "site-data/run", {
|
|
|
211
211
|
|
|
212
212
|
第一个参数收 `Selection` 或手工挑的 `Snapshot[]`——和报告积木同一个输入约定。`artifacts` 的合法值是 `"events" | "trace" | "o11y" | "diff" | "sources"`,缺省全带。目标目录已存在且非空时报错,不静默覆盖——发布脚本要幂等就自己先清目标目录。
|
|
213
213
|
|
|
214
|
-
复制不改工件内容、不消毒;发布前给自由文本消毒用报告积木 `CaseList.data` 的 `redact` 钩子。唯一随行补记的是挑选时的**覆盖事实**:`partial-coverage` 警告的分母是实验的历史并集,而发布目录没有历史——所以每个复制出的快照带上 `knownEvalIds`(复制时刻该实验已知的 eval 并集),reader 端把它并进 `exp.evalIds` 的计算(取本地历史与快照携带值的并集)。发布目录上重新 `openResults().latest()
|
|
214
|
+
复制不改工件内容、不消毒;发布前给自由文本消毒用报告积木 `CaseList.data` 的 `redact` 钩子。唯一随行补记的是挑选时的**覆盖事实**:`partial-coverage` 警告的分母是实验的历史并集,而发布目录没有历史——所以每个复制出的快照带上 `knownEvalIds`(复制时刻该实验已知的 eval 并集),reader 端把它并进 `exp.evalIds` 的计算(取本地历史与快照携带值的并集)。发布目录上重新 `openResults().latest()`,残缺警告被同一套机制重新算出来,不靠发布者转述。复制出的目录就是标准结果目录,`niceeval view --run <目录>` 直接能看;要让报告站随 push 自动更新,workflow 见[通过 CI 发布报告](/zh/guides/publish-report)。
|
|
215
215
|
|
|
216
216
|
## 分层速览
|
|
217
217
|
|
|
@@ -115,9 +115,9 @@ export default defineSandboxAgent({
|
|
|
115
115
|
});
|
|
116
116
|
```
|
|
117
117
|
|
|
118
|
-
## `ctx.model` 与 `ctx.
|
|
118
|
+
## `ctx.model` 与 `ctx.flags`
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
experiment 声明的 model 和 flags 会出现在 adapter context 中。adapter 可以决定如何把它们转成 CLI 参数或 HTTP payload。
|
|
121
121
|
|
|
122
122
|
## 在 experiment 中使用自定义 agent
|
|
123
123
|
|
|
@@ -195,30 +195,14 @@ NiceEval 写入 `<dir>/index.html`,并把查看器要读取的工件(`source
|
|
|
195
195
|
|
|
196
196
|
`--out` 只接受目录。没有单文件导出:代码视图、transcript 和 trace 依赖工件文件,单个 HTML 装不下完整证据。要把结果发给别人,托管整站发链接即可。
|
|
197
197
|
|
|
198
|
+
零可读结果时 `view` 直接报错、非零退出——本地服务起不来,`--out` 不导出空页面。错误逐条列出被跳过的 run 目录与原因;落盘 schemaVersion 与当前版本不兼容时,还给出能直接查看旧落盘的 `npx niceeval@<版本> view` 命令。
|
|
199
|
+
|
|
198
200
|
两点限制:
|
|
199
201
|
|
|
200
202
|
- `diff.json` 和 `o11y.json` 不会被复制。查看器不读取它们,且 diff 可能达到上百 MB。
|
|
201
203
|
- 用 `file://` 直接打开 `index.html` 时浏览器不允许 fetch 工件,代码视图会提示源码不可用。本地预览用 http 服务打开。
|
|
202
204
|
|
|
203
|
-
最简单的流程是在跑过 eval
|
|
204
|
-
|
|
205
|
-
```typescript
|
|
206
|
-
// scripts/snapshot.ts —— 跑完 eval 后执行,产物提交进仓库
|
|
207
|
-
import { openResults, copySnapshots } from "niceeval/results";
|
|
208
|
-
|
|
209
|
-
const results = await openResults(".niceeval");
|
|
210
|
-
await copySnapshots(results.latest(), "site-data/run", {
|
|
211
|
-
artifacts: ["sources", "events", "trace"],
|
|
212
|
-
});
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
```bash
|
|
216
|
-
npx niceeval view --run site-data/run --out site # CI 上对着快照构建
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
结果目录经 `--run` 递入,与 `show --run` 同一语义——位置参数永远留给 eval id 前缀。
|
|
220
|
-
|
|
221
|
-
`results.latest()` 按实验挑最新快照,而不是挑最新的 run——某次运行只跑了部分实验时,其它实验仍保留上一轮的结果。
|
|
205
|
+
最简单的流程是在跑过 eval 的机器上导出,把产物目录直接部署,不需要额外步骤。要让站点随 push 自动更新,把 `.niceeval/` 提交进仓库、CI 上一行 `view --out` 导出——workflow 与平台接线见[通过 CI 发布报告](/zh/guides/publish-report)。
|
|
222
206
|
|
|
223
207
|
## Artifact 说明
|
|
224
208
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "写实验:用 defineExperiment 固定运行配置"
|
|
3
3
|
sidebarTitle: "写实验"
|
|
4
|
-
description: "学习如何在 experiments/ 里用 defineExperiment 选择 agent、传 model 和
|
|
4
|
+
description: "学习如何在 experiments/ 里用 defineExperiment 选择 agent、传 model 和 flags、设置 runs、预算、并发与 sandbox。"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Experiment 是可签入的运行配置:同一批 eval 对着哪个 adapter
|
|
7
|
+
Experiment 是可签入的运行配置:同一批 eval 对着哪个 adapter、哪个模型、开哪些 flags、跑几次、预算多少,都写在 `experiments/` 里。CLI 的位置参数只负责筛 eval,不负责临时改 agent 或运行配置。
|
|
8
8
|
|
|
9
9
|
## 最小实验
|
|
10
10
|
|
|
@@ -20,7 +20,7 @@ export default defineExperiment({
|
|
|
20
20
|
|
|
21
21
|
`agent` 放的是已经配置好的 agent 实例。被测系统的 URL、鉴权、协议细节通常传给 adapter 工厂;运行器不会单独保存一个 `agentConfig` 字段。
|
|
22
22
|
|
|
23
|
-
## 传 model 和
|
|
23
|
+
## 传 model 和 flags
|
|
24
24
|
|
|
25
25
|
```ts
|
|
26
26
|
// experiments/concise.ts
|
|
@@ -34,7 +34,7 @@ export default defineExperiment({
|
|
|
34
34
|
apiKey: process.env.STAGING_AGENT_API_KEY,
|
|
35
35
|
}),
|
|
36
36
|
model: "gpt-5.4",
|
|
37
|
-
|
|
37
|
+
flags: {
|
|
38
38
|
promptVariant: "concise",
|
|
39
39
|
webResearch: false,
|
|
40
40
|
},
|
|
@@ -46,7 +46,7 @@ export default defineExperiment({
|
|
|
46
46
|
|
|
47
47
|
`model` 会作为 `ctx.model` 传给 adapter;应用接口支持模型选择时,adapter 把它放进请求体、header 或 CLI 参数即可。
|
|
48
48
|
|
|
49
|
-
`
|
|
49
|
+
`flags` 会作为 `ctx.flags` 传给 adapter,也会作为 `t.flags` 出现在 eval 里。语义就是产品 A/B 测试里的 feature flag:一个 experiment 就是一组 flag 取值,例如 prompt 版本、工具集、检索开关;值可以是任意 JSON(变体名、数字、配置对象),不限于布尔。真正按 flag 切换行为的是你的应用或 adapter,[NiceEval](https://niceeval.com/) 只负责原样透传。它与命令行开关是两回事——后者在文档里一律写作 CLI flag 或字面的 `--xxx`。
|
|
50
50
|
|
|
51
51
|
```ts
|
|
52
52
|
// agents/web-agent.ts
|
|
@@ -57,7 +57,7 @@ async send(input, ctx) {
|
|
|
57
57
|
body: JSON.stringify({
|
|
58
58
|
message: input.text,
|
|
59
59
|
model: ctx.model,
|
|
60
|
-
|
|
60
|
+
flags: ctx.flags,
|
|
61
61
|
}),
|
|
62
62
|
signal: ctx.signal,
|
|
63
63
|
});
|
|
@@ -66,7 +66,7 @@ async send(input, ctx) {
|
|
|
66
66
|
|
|
67
67
|
## 一组实验怎么写
|
|
68
68
|
|
|
69
|
-
一个实验文件是一格配置。要比较多个模型、agent 或
|
|
69
|
+
一个实验文件是一格配置。要比较多个模型、agent 或 flag 取值,就把多个文件放进同一个文件夹:
|
|
70
70
|
|
|
71
71
|
```text
|
|
72
72
|
experiments/
|
|
@@ -89,7 +89,7 @@ npx niceeval exp prompt-variants
|
|
|
89
89
|
| `agent` | 选择并配置 adapter;必填 |
|
|
90
90
|
| `model` | 单个模型名,经 `ctx.model` 透传 |
|
|
91
91
|
| `reasoningEffort` | 单个推理努力程度(如 `"high"`),经 `ctx.reasoningEffort` / `t.reasoningEffort` 透传,归属与 `model` 一致 |
|
|
92
|
-
| `
|
|
92
|
+
| `flags` | 实验条件(A/B 里的 feature flag),任意 JSON 对象,经 `ctx.flags` / `t.flags` 透传 |
|
|
93
93
|
| `runs` | 每个 eval × 配置最多跑几次 |
|
|
94
94
|
| `earlyExit` | 多次运行里通过一次就提前停止 |
|
|
95
95
|
| `evals` | 限定这一格配置跑哪些 eval |
|
|
@@ -98,4 +98,4 @@ npx niceeval exp prompt-variants
|
|
|
98
98
|
| `maxConcurrency` | 这一格配置的并发上限 |
|
|
99
99
|
| `sandbox` | sandbox agent 使用的后端,如 `dockerSandbox()` / `e2bSandbox()` |
|
|
100
100
|
|
|
101
|
-
跨配置比较的设计建议见[实验矩阵](/zh/guides/experiments)。adapter 如何消费 `ctx.model` 和 `ctx.
|
|
101
|
+
跨配置比较的设计建议见[实验矩阵](/zh/guides/experiments)。adapter 如何消费 `ctx.model` 和 `ctx.flags` 见[Adapter](/zh/concepts/adapter)。
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "如何写好 Send"
|
|
3
3
|
sidebarTitle: "如何写好 Send"
|
|
4
|
-
description: "手写 Adapter 的 send 函数,一条主线七步走:发消息拿回复、接上之前的消息、记录消费、把工具解析成事件、人工介入(HITL)、接上 OTel trace、透传 experiment 的
|
|
4
|
+
description: "手写 Adapter 的 send 函数,一条主线七步走:发消息拿回复、接上之前的消息、记录消费、把工具解析成事件、人工介入(HITL)、接上 OTel trace、透传 experiment 的 flags。每一步只在上一步的代码上多几行,每一步解锁一组断言。"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
[Adapter](/zh/concepts/adapter) 讲了契约:`send` 传入 `TurnInput` 和 `AgentContext`,返回 `Turn`。这一页讲怎么写它——从"发一条消息拿到回复"的最小形态起步,一步一步加到完整接入。每一步只在上一步的代码上多几行,前面已有的部分在代码里用 `// ……省略` 标出;每一步末尾说明这几行解锁了哪些断言。改完任何一步都能立刻验证:把新解锁的断言写进一条 eval 重跑 `npx niceeval exp`,`niceeval view` 里能看到这一步新增的数据——多轮轨迹、用量、工具事件、待回答请求。
|
|
@@ -303,16 +303,16 @@ OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318/v1/traces node server.js
|
|
|
303
303
|
|
|
304
304
|
**这一步解锁**:`niceeval view` 里的调用瀑布图——应用内部每次模型调用、每次工具执行的耗时与 token,按轮铺成时间线。断言不变:判决依据在前几步的事件映射里已经齐了,span 只进瀑布图、不喂断言。接收器怎么起、span 怎么归属到轮,见 [OTel 接入](/zh/guides/connect-otel)。
|
|
305
305
|
|
|
306
|
-
## 第七步:透传 experiment 的
|
|
306
|
+
## 第七步:透传 experiment 的 flags(A/B 对比)
|
|
307
307
|
|
|
308
|
-
应用把变体暴露成可切换的配置后,experiment 声明 `
|
|
308
|
+
应用把变体暴露成可切换的配置后,experiment 声明 `flags`,运行器每轮经 `ctx.flags` 原样递给 `send`;Adapter 不解释它们的含义,只随请求转发——应用按参数切换变体:
|
|
309
309
|
|
|
310
310
|
```ts
|
|
311
311
|
// ……send 其余部分同前,省略
|
|
312
312
|
body: JSON.stringify({
|
|
313
313
|
messages,
|
|
314
314
|
model: ctx.model, // ← 第一步就转发的 experiment.model;这里一起列出
|
|
315
|
-
|
|
315
|
+
flags: ctx.flags, // ← experiment.flags 原样透传;没配时是 {}
|
|
316
316
|
}),
|
|
317
317
|
```
|
|
318
318
|
|
|
@@ -324,11 +324,11 @@ import chatApp from "../agents/chat-app.ts";
|
|
|
324
324
|
export default defineExperiment({
|
|
325
325
|
agent: chatApp,
|
|
326
326
|
model: "gpt-5.4", // ← 经 ctx.model 到 send
|
|
327
|
-
|
|
327
|
+
flags: { promptVariant: "concise" }, // ← 每轮经 ctx.flags 到 send
|
|
328
328
|
});
|
|
329
329
|
```
|
|
330
330
|
|
|
331
|
-
两个 experiment 文件各声明一份 `
|
|
331
|
+
两个 experiment 文件各声明一份 `flags`,`npx niceeval exp` 分别跑同一批 eval,就是一组 A/B 对比。这是三档接入里的 Tier 3(要求应用配合暴露开关),投入与回报见 [Tier](/zh/concepts/tier);`flags` 与 `model`、`runs` 等其余 experiment 字段见[写实验](/zh/guides/write-experiment)。
|
|
332
332
|
|
|
333
333
|
**这一步解锁**:同一批 eval 跨变体的成绩对比。
|
|
334
334
|
|
package/docs-site/zh/index.mdx
CHANGED
|
@@ -87,7 +87,7 @@ eval 运行完成后,[NiceEval](https://niceeval.com/) 会生成易读报告
|
|
|
87
87
|
| 概念 | 一句话 |
|
|
88
88
|
|---|---|
|
|
89
89
|
| [Eval](/zh/concepts/evals) | 一个测试用例:写在 `evals/*.eval.ts` 里,描述测什么。 |
|
|
90
|
-
| [Experiment](/zh/concepts/experiment) | 可签入的运行配置:决定连哪个 Adapter、什么 model、什么
|
|
90
|
+
| [Experiment](/zh/concepts/experiment) | 可签入的运行配置:决定连哪个 Adapter、什么 model、什么 flags。 |
|
|
91
91
|
| [Adapter](/zh/concepts/adapter) | 连接被测系统的适配层:实现一个 `send`,把返回翻译成标准事件流。 |
|
|
92
92
|
| [Sandbox](/zh/guides/sandbox-backends) | 需要隔离工作区的 coding agent 才用得到;直连 Web Agent 不需要。 |
|
|
93
93
|
| [Tier](/zh/concepts/tier) | 接入 Adapter 的三档投入:Tier 1 只接 send,Tier 2 加 OTel 换调用瀑布图,Tier 3 侵入改造做 feature A/B。 |
|
|
@@ -114,7 +114,7 @@ NiceEval 是一个 Agent-Native 的评估工具。Dataset / golden 那一套「
|
|
|
114
114
|
| 概念 | 一句话 |
|
|
115
115
|
|---|---|
|
|
116
116
|
| Eval | 一个测试用例:写在 `evals/*.eval.ts` 里,描述测什么。 |
|
|
117
|
-
| Experiment | 可签入的运行配置:决定连哪个 Adapter、什么 model、什么
|
|
117
|
+
| Experiment | 可签入的运行配置:决定连哪个 Adapter、什么 model、什么 flags。 |
|
|
118
118
|
| Adapter | 连接被测系统的适配层:实现一个 `send`,把返回翻译成标准事件流。 |
|
|
119
119
|
| Sandbox | 需要隔离工作区的 coding agent 才用得到;直连 Web Agent 不需要。 |
|
|
120
120
|
| Tier | 接入 Adapter 的三档投入:Tier 1 只接 send,Tier 2 加 OTel 换调用瀑布图,Tier 3 侵入改造做 feature A/B。 |
|