niceeval 0.11.1-canary.9 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INDEX.md +1 -0
- package/dist/i18n/en.d.ts +0 -1
- package/dist/i18n/en.js +5 -5
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/index.js +14 -3
- package/dist/i18n/zh-CN.d.ts +1 -2
- package/dist/i18n/zh-CN.js +4 -5
- package/dist/runner/types.d.ts +7 -2
- package/docs-site/zh/examples/integrations/langgraph.mdx +3 -3
- package/docs-site/zh/explanation/judge.mdx +7 -5
- package/docs-site/zh/reference/cli.mdx +11 -17
- package/docs-site/zh/reference/define-config.mdx +14 -1
- package/docs-site/zh/tutorials/agent-onboarding.mdx +8 -22
- package/docs-site/zh/tutorials/ci-integration.mdx +2 -3
- package/docs-site/zh/tutorials/configuration.mdx +165 -0
- package/docs-site/zh/tutorials/custom-reports.mdx +30 -3
- package/package.json +1 -1
- package/src/agents/bub.ts +5 -8
- package/src/cli.ts +22 -17
- package/src/i18n/en.ts +5 -5
- package/src/i18n/index.ts +16 -3
- package/src/i18n/locale.test.ts +41 -0
- package/src/i18n/zh-CN.ts +4 -5
- package/src/report/assets/styles.css +16 -14
- package/src/runner/feedback/io.ts +1 -1
- package/src/runner/types.ts +7 -2
- package/src/scoring/judge.test.ts +47 -6
- package/src/scoring/judge.ts +15 -22
- package/src/show/index.ts +1 -1
- package/src/show/json.test.ts +3 -5
- package/src/show/show.test.ts +3 -5
- package/src/view/app/App.tsx +3 -1
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +1 -1
- package/src/view/site-base.test.ts +51 -0
- package/src/view/site.ts +16 -0
- package/src/view/styles.css +1 -2
- package/src/view/template.html +1 -0
package/INDEX.md
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
- `docs-site/zh/tutorials/agent-onboarding.mdx` — Coding Agent 从零接入项目:给 Coding Agent 的完整接入流程:探索项目、与用户确认路径、配置 Judge、写出 Adapter / Experiment / 评估用例并跑通第一个实验。
|
|
21
21
|
- `docs-site/zh/tutorials/authoring.mdx` — 编写评估用例: 单轮、多轮和数据集模式:用 defineEval 编写评估用例,包括单轮对话、多轮对话、数据驱动测试、Sandbox Workspace 和评估的生命周期与 Fixture。
|
|
22
22
|
- `docs-site/zh/tutorials/ci-integration.mdx` — 在 GitHub Actions 和 CI 中运行 NiceEval:把 NiceEval 接入 GitHub Actions 或任意 CI。评估用例失败时非零退出,输出 JUnit XML,并通过缓存加速重复运行。
|
|
23
|
+
- `docs-site/zh/tutorials/configuration.mdx` — 把配置和密钥各放到该放的地方:跑几次、超时、并发、judge、语言这些值写进代码的哪一层,API key 和 provider token 用哪些环境变量。
|
|
23
24
|
- `docs-site/zh/tutorials/connect-otel.mdx` — OTel 接入:把应用已有的 OTel span 发送给 NiceEval,在 niceeval view 中查看每轮调用瀑布图;评估用例断言仍以 Send 返回的事件和用量为准。
|
|
24
25
|
- `docs-site/zh/tutorials/connect-your-agent.mdx` — 接入你的 Agent:写一个 Adapter、配置一个 Experiment,并运行第一条评估用例;再把配置和参数从 Experiment 传给 Adapter 与被测应用。
|
|
25
26
|
- `docs-site/zh/tutorials/custom-reports.mdx` — 编写自定义报告:用 defineReport、内置报告组件和双面组件编写一份同时用于 niceeval show 与 niceeval view 的自定义报告。
|
package/dist/i18n/en.d.ts
CHANGED
package/dist/i18n/en.js
CHANGED
|
@@ -65,11 +65,10 @@ export const en = {
|
|
|
65
65
|
"runner.gateLeaseWaiting": "waiting on another run for experiment {{experimentId}}'s concurrency slots: all {{effectiveN}} in use ({{holders}}). Concurrent runs share this experiment's slots, and the smallest maxConcurrency in play wins — this run declared {{declaredN}}. Nothing dispatches until a slot frees up; the other run's slots release when its attempts finish, or 30s after it dies.\n",
|
|
66
66
|
"runner.dispatchHaltedExperiment": "experiment halted (dispatch-halted): {{message}}\n",
|
|
67
67
|
"runner.dispatchHaltedEval": "eval halted: {{message}}\n",
|
|
68
|
-
"judge.modelMissing": "No judge model configured. Set it in defineConfig({ judge: { model: \"...\" } })
|
|
68
|
+
"judge.modelMissing": "No judge model configured. Set it in defineConfig({ judge: { model: \"...\" } }) or the eval's judge config (there is no built-in default model, and no environment variable for it).\n" +
|
|
69
69
|
" Docs: node_modules/niceeval/docs-site/zh/tutorials/scoring-guide.mdx",
|
|
70
70
|
"loaders.yamlMissing": "loadYaml(\"{{path}}\") needs a YAML parser: run `pnpm add yaml` first (or switch to loadJson with a JSON dataset).",
|
|
71
71
|
"cli.flag.parseError": "{{message}}\nRun `niceeval --help` for usage.\n",
|
|
72
|
-
"cli.envInvalidNumber": "Environment variable {{name}} is not a number: \"{{value}}\".\n",
|
|
73
72
|
"cli.help": "niceeval — agent-native evals\n\n" +
|
|
74
73
|
"Usage:\n" +
|
|
75
74
|
" niceeval exp [path|experiment] [eval-id-prefix…] run experiments\n" +
|
|
@@ -122,8 +121,9 @@ export const en = {
|
|
|
122
121
|
" --json (machine feed: NDJSON on stdout; default is human text)\n" +
|
|
123
122
|
" --junit path --out dir --port n --open / --no-open -h, --help -v, --version\n\n" +
|
|
124
123
|
"Positional args only select which evals to run (id prefixes); which agent and\n" +
|
|
125
|
-
"how to run come from experiments/ + flags.
|
|
126
|
-
"
|
|
124
|
+
"how to run come from experiments/ + flags. Resolution: flag > experiment >\n" +
|
|
125
|
+
"niceeval.config.ts > built-in default. Configuration has no environment layer;\n" +
|
|
126
|
+
"environment variables hold credentials such as API keys.\n",
|
|
127
127
|
"cli.show.noResults": "No results found under {{root}}. Run `niceeval exp` first, then `niceeval show`.\n",
|
|
128
128
|
"cli.show.runDirMissing": "Results directory not found: {{dir}}\n",
|
|
129
129
|
"cli.show.noEvalMatch": "No results matched: {{patterns}}. Evals with results: {{evals}}\n",
|
|
@@ -274,7 +274,7 @@ export const en = {
|
|
|
274
274
|
"hitl.respondAllEmpty": "There is no pending input.requested request; respond() / respondAll() cannot work. Confirm the turn parked with t.parked(), then answer via t.requireInputRequest() or t.respond().",
|
|
275
275
|
"hitl.respondEmpty": "t.respond(...) requires at least one answer.",
|
|
276
276
|
"hitl.stringAmbiguous": "There are {{count}} pending input requests; a plain-string answer cannot be matched to one. Use the { request, optionId } or { request, text } object form to name it explicitly.",
|
|
277
|
-
"judge.apiKeyMissing": "judge is missing an API key
|
|
277
|
+
"judge.apiKeyMissing": "judge is missing an API key: set NICEEVAL_JUDGE_KEY, or point judge.apiKeyEnv at another environment variable.",
|
|
278
278
|
"judge.httpError": "judge HTTP {{status}}: {{body}}",
|
|
279
279
|
"judge.probeFailed": "judge precheck failed ({{model}}): {{error}}",
|
|
280
280
|
"judge.probeTimeout": "judge precheck timed out after {{seconds}}s ({{model}}): the endpoint accepted the connection but never responded — check the judge baseUrl / gateway, or point NICEEVAL_JUDGE_BASE at a responsive one",
|
package/dist/i18n/index.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { type MessageKey } from "./zh-CN.ts";
|
|
2
2
|
import { type Locale, type Vars } from "./core.ts";
|
|
3
3
|
export type { Locale, Vars } from "./core.ts";
|
|
4
|
+
/**
|
|
5
|
+
* 注入项目配置声明的界面语言。传 undefined(没配 / 没有配置文件)时清空,回到系统 locale 判定。
|
|
6
|
+
* 无法归一的值(如 "C")同样按未声明处理——不为一个装饰性设置让命令失败。
|
|
7
|
+
*/
|
|
8
|
+
export declare function setConfiguredLocale(raw: string | undefined): void;
|
|
4
9
|
export declare function detectLocale(env?: NodeJS.ProcessEnv): Locale;
|
|
5
10
|
export declare function t(key: MessageKey, vars?: Vars): string;
|
package/dist/i18n/index.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
// CLI 侧 i18n:内核(插值/归一)在 core.ts
|
|
1
|
+
// CLI 侧 i18n:内核(插值/归一)在 core.ts;这里只注入来源(config.locale + 系统 locale)与
|
|
2
|
+
// zh-CN 默认值。界面语言是配置,家在 `defineConfig({ locale })`;系统 locale 是「输出到哪个
|
|
3
|
+
// 终端」的环境事实,只作没配时的判定依据。niceeval 自己不发明 NICEEVAL_LANG 这类配置变量
|
|
4
|
+
// (边界见 docs/architecture.md「配置从代码来,凭据从环境来」)。
|
|
2
5
|
import { en } from "./en.js";
|
|
3
6
|
import { zhCN } from "./zh-CN.js";
|
|
4
7
|
import { interpolate, normalizeLocale } from "./core.js";
|
|
@@ -6,9 +9,17 @@ const dictionaries = {
|
|
|
6
9
|
"zh-CN": zhCN,
|
|
7
10
|
en,
|
|
8
11
|
};
|
|
12
|
+
/** `defineConfig({ locale })` 的归一结果;CLI 装载配置后调 setConfiguredLocale 注入一次。 */
|
|
13
|
+
let configuredLocale;
|
|
14
|
+
/**
|
|
15
|
+
* 注入项目配置声明的界面语言。传 undefined(没配 / 没有配置文件)时清空,回到系统 locale 判定。
|
|
16
|
+
* 无法归一的值(如 "C")同样按未声明处理——不为一个装饰性设置让命令失败。
|
|
17
|
+
*/
|
|
18
|
+
export function setConfiguredLocale(raw) {
|
|
19
|
+
configuredLocale = normalizeLocale(raw);
|
|
20
|
+
}
|
|
9
21
|
export function detectLocale(env = process.env) {
|
|
10
|
-
return (
|
|
11
|
-
normalizeLocale(env.NICEEVAL_LOCALE) ??
|
|
22
|
+
return (configuredLocale ??
|
|
12
23
|
normalizeLocale(env.LC_ALL) ??
|
|
13
24
|
normalizeLocale(env.LC_MESSAGES) ??
|
|
14
25
|
normalizeLocale(env.LANG) ??
|
package/dist/i18n/zh-CN.d.ts
CHANGED
|
@@ -63,7 +63,6 @@ export declare const zhCN: {
|
|
|
63
63
|
readonly "judge.modelMissing": string;
|
|
64
64
|
readonly "loaders.yamlMissing": "loadYaml(\"{{path}}\") 需要 YAML 解析器:请先 `pnpm add yaml`(或改用 loadJson + JSON 数据集)。";
|
|
65
65
|
readonly "cli.flag.parseError": "{{message}}\n运行 `niceeval --help` 查看用法。\n";
|
|
66
|
-
readonly "cli.envInvalidNumber": "环境变量 {{name}} 不是数字:\"{{value}}\"。\n";
|
|
67
66
|
readonly "cli.help": string;
|
|
68
67
|
readonly "cli.show.noResults": "{{root}} 下没有结果。先 `niceeval exp` 跑一轮,再 `niceeval show`。\n";
|
|
69
68
|
readonly "cli.show.runDirMissing": "Results directory not found: {{dir}}\n";
|
|
@@ -206,7 +205,7 @@ export declare const zhCN: {
|
|
|
206
205
|
readonly "hitl.respondAllEmpty": "没有待回答的 input.requested 请求,respond() / respondAll() 无法工作;先用 t.parked() 确认停轮,再用 t.requireInputRequest() 或 t.respond() 回答。";
|
|
207
206
|
readonly "hitl.respondEmpty": "t.respond(...) 至少需要一个回答。";
|
|
208
207
|
readonly "hitl.stringAmbiguous": "有 {{count}} 条待回答请求,字符串回答无法对位,请用 { request, optionId } 或 { request, text } 对象形式显式指名。";
|
|
209
|
-
readonly "judge.apiKeyMissing": "judge 缺少 API key
|
|
208
|
+
readonly "judge.apiKeyMissing": "judge 缺少 API key:设置 NICEEVAL_JUDGE_KEY,或用 judge.apiKeyEnv 指向别的环境变量。";
|
|
210
209
|
readonly "judge.httpError": "judge HTTP {{status}}: {{body}}";
|
|
211
210
|
readonly "judge.probeFailed": "judge 预检失败({{model}}): {{error}}";
|
|
212
211
|
readonly "judge.probeTimeout": "judge 预检 {{seconds}}s 超时({{model}}):端点接受了连接但一直不回 —— 检查 judge 的 baseUrl / 网关,或把 NICEEVAL_JUDGE_BASE 指向一个有响应的网关";
|
package/dist/i18n/zh-CN.js
CHANGED
|
@@ -65,11 +65,10 @@ export const zhCN = {
|
|
|
65
65
|
"runner.gateLeaseWaiting": "在等别的运行让出实验 {{experimentId}} 的并发名额:生效的 {{effectiveN}} 个位子全被占着({{holders}})。并行运行共用同一实验的名额,生效值取在场声明里最小的那个——本次运行声明的是 {{declaredN}}。名额腾出来之前不会派发任何 attempt;对方的名额会在它的 attempt 跑完时释放,它若已死则 30s 后过期被接管。\n",
|
|
66
66
|
"runner.dispatchHaltedExperiment": "实验已止损(dispatch-halted):{{message}}\n",
|
|
67
67
|
"runner.dispatchHaltedEval": "eval 已止损:{{message}}\n",
|
|
68
|
-
"judge.modelMissing": "judge 未配置模型:在 defineConfig({ judge: { model: \"...\" } })
|
|
68
|
+
"judge.modelMissing": "judge 未配置模型:在 defineConfig({ judge: { model: \"...\" } }) 或 eval 的 judge 配置里指定裁判模型(没有内置默认模型,也没有对应的环境变量)。\n" +
|
|
69
69
|
" 文档:node_modules/niceeval/docs-site/zh/tutorials/scoring-guide.mdx",
|
|
70
70
|
"loaders.yamlMissing": "loadYaml(\"{{path}}\") 需要 YAML 解析器:请先 `pnpm add yaml`(或改用 loadJson + JSON 数据集)。",
|
|
71
71
|
"cli.flag.parseError": "{{message}}\n运行 `niceeval --help` 查看用法。\n",
|
|
72
|
-
"cli.envInvalidNumber": "环境变量 {{name}} 不是数字:\"{{value}}\"。\n",
|
|
73
72
|
"cli.help": "niceeval — agent-native evals\n\n" +
|
|
74
73
|
"用法:\n" +
|
|
75
74
|
" niceeval exp [路径|实验] [eval-id 前缀…] 跑实验\n" +
|
|
@@ -117,8 +116,8 @@ export const zhCN = {
|
|
|
117
116
|
" --json (机器面:stdout 上的 NDJSON 事件流;默认是人读文本)\n" +
|
|
118
117
|
" --junit path --out dir --port n --open / --no-open -h, --help -v, --version\n\n" +
|
|
119
118
|
"位置参数只选「跑哪些 eval」(id 前缀);对着哪个 agent、怎么跑来自 experiments/ 与\n" +
|
|
120
|
-
"
|
|
121
|
-
"
|
|
119
|
+
"标志。取值优先级:标志 > experiment > niceeval.config.ts > 内置默认;配置项没有\n" +
|
|
120
|
+
"环境变量层,环境变量只放 API key 这类凭据。\n",
|
|
122
121
|
// show 的错误文案保持英文(错误文案英文的仓库约定);noResults 是提示,翻译。
|
|
123
122
|
"cli.show.noResults": "{{root}} 下没有结果。先 `niceeval exp` 跑一轮,再 `niceeval show`。\n",
|
|
124
123
|
"cli.show.runDirMissing": "Results directory not found: {{dir}}\n",
|
|
@@ -270,7 +269,7 @@ export const zhCN = {
|
|
|
270
269
|
"hitl.respondAllEmpty": "没有待回答的 input.requested 请求,respond() / respondAll() 无法工作;先用 t.parked() 确认停轮,再用 t.requireInputRequest() 或 t.respond() 回答。",
|
|
271
270
|
"hitl.respondEmpty": "t.respond(...) 至少需要一个回答。",
|
|
272
271
|
"hitl.stringAmbiguous": "有 {{count}} 条待回答请求,字符串回答无法对位,请用 { request, optionId } 或 { request, text } 对象形式显式指名。",
|
|
273
|
-
"judge.apiKeyMissing": "judge 缺少 API key
|
|
272
|
+
"judge.apiKeyMissing": "judge 缺少 API key:设置 NICEEVAL_JUDGE_KEY,或用 judge.apiKeyEnv 指向别的环境变量。",
|
|
274
273
|
"judge.httpError": "judge HTTP {{status}}: {{body}}",
|
|
275
274
|
"judge.probeFailed": "judge 预检失败({{model}}): {{error}}",
|
|
276
275
|
"judge.probeTimeout": "judge 预检 {{seconds}}s 超时({{model}}):端点接受了连接但一直不回 —— 检查 judge 的 baseUrl / 网关,或把 NICEEVAL_JUDGE_BASE 指向一个有响应的网关",
|
package/dist/runner/types.d.ts
CHANGED
|
@@ -280,7 +280,7 @@ export interface InvocationShape {
|
|
|
280
280
|
configs: number;
|
|
281
281
|
/** 总 attempt 数(evals × configs × runs);逐行输出与汇总计数都按它。 */
|
|
282
282
|
totalAttempts: number;
|
|
283
|
-
/** 本次运行实际生效的全局并发数(flag/
|
|
283
|
+
/** 本次运行实际生效的全局并发数(flag/config/sandbox 默认值解析后的结果);
|
|
284
284
|
* 实验级 maxConcurrency 只在该实验内部限流,不改这个全局值。 */
|
|
285
285
|
maxConcurrency: number;
|
|
286
286
|
/**
|
|
@@ -615,6 +615,11 @@ export interface Config {
|
|
|
615
615
|
* 可传字符串,或按 locale 提供多语言(如 `{ en: "...", "zh-CN": "..." }`),随 view 语言切换。
|
|
616
616
|
*/
|
|
617
617
|
name?: LocalizedText;
|
|
618
|
+
/**
|
|
619
|
+
* CLI 与运行时文案的界面语言(BCP 47,如 `"en"` / `"zh-CN"`);CI 里想让日志恒定一种语言就写这个。
|
|
620
|
+
* 省略则按系统 locale(`LC_ALL` / `LC_MESSAGES` / `LANG`)判定,都没有时用 `zh-CN`。
|
|
621
|
+
*/
|
|
622
|
+
locale?: string;
|
|
618
623
|
/** 项目级默认 Sandbox provider(docker / vercel / e2b / custom);experiment 可覆盖。 */
|
|
619
624
|
sandbox?: SandboxOption;
|
|
620
625
|
/** 上传进 Sandbox 的工作区根目录,省略则用项目根;评估用例的 sandbox 视图从这里起步。 */
|
|
@@ -623,7 +628,7 @@ export interface Config {
|
|
|
623
628
|
judge?: JudgeConfig;
|
|
624
629
|
/** 项目级默认 reporter 列表(如落盘 / 上传结果);EvalDef.reporters 会与它合并。 */
|
|
625
630
|
reporters?: Reporter[];
|
|
626
|
-
/** 项目级默认并发上限;CLI flag /
|
|
631
|
+
/** 项目级默认并发上限;CLI flag / experiment 的同名设置优先级更高(没有环境变量层)。 */
|
|
627
632
|
maxConcurrency?: number;
|
|
628
633
|
/** 项目级默认单次 attempt 超时(毫秒);CLI flag / experiment / EvalDef 的同名设置优先级更高。 */
|
|
629
634
|
timeoutMs?: number;
|
|
@@ -19,7 +19,7 @@ description: "一个纯 Python LangGraph + LangSmith OTel 导出的应用,接
|
|
|
19
19
|
|
|
20
20
|
接入的全部代码变更(生成时从两个目录实测统计):
|
|
21
21
|
|
|
22
|
-
<table className="gd-summary"><tbody><tr><th>{"类别"}</th><th>{"文件数"}</th><th>{"行数"}</th></tr><tr><td>{"评估用例侧 TS 项目脚手架(必要:被测应用是 Python,全新文件)"}</td><td>{"3"}</td><td>{"+42"}</td></tr><tr><td>{"adapter(必要:传输粘合,协议映射在官方包里)"}</td><td>{"2"}</td><td>{"+
|
|
22
|
+
<table className="gd-summary"><tbody><tr><th>{"类别"}</th><th>{"文件数"}</th><th>{"行数"}</th></tr><tr><td>{"评估用例侧 TS 项目脚手架(必要:被测应用是 Python,全新文件)"}</td><td>{"3"}</td><td>{"+42"}</td></tr><tr><td>{"adapter(必要:传输粘合,协议映射在官方包里)"}</td><td>{"2"}</td><td>{"+181"}</td></tr><tr><td>{"evals 与 experiments(评测内容,按需增长)"}</td><td>{"6"}</td><td>{"+118"}</td></tr><tr className="gd-total"><td>{"合计"}</td><td>{"11"}</td><td>{"+341"}</td></tr></tbody></table>
|
|
23
23
|
|
|
24
24
|
## 文件清单
|
|
25
25
|
|
|
@@ -67,9 +67,9 @@ langgraph/
|
|
|
67
67
|
## 新增的 adapter、evals 与 experiments
|
|
68
68
|
|
|
69
69
|
<div className="gd-file">
|
|
70
|
-
<div className="gd-head"><span className="gd-name">{"niceeval.config.ts"}</span><span className="gd-stats"><span className="gd-plus">{"+
|
|
70
|
+
<div className="gd-head"><span className="gd-name">{"niceeval.config.ts"}</span><span className="gd-stats"><span className="gd-plus">{"+13"}</span></span></div>
|
|
71
71
|
<div className="gd-body">
|
|
72
|
-
<table className="gd-table"><tbody><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"1"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" { defineConfig } "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"niceeval\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"2"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"3"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// 注:这个 app 的 .env 把标准的 OPENAI_API_KEY / OPENAI_BASE_URL 挪用给了 DeepSeek"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"4"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// (agent.py 里 ChatOpenAI 直接读这两个 env 名)。niceeval 的 judge(t.judge.autoevals.*)"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"5"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"//
|
|
72
|
+
<table className="gd-table"><tbody><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"1"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" { defineConfig } "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"niceeval\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"2"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"3"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// 注:这个 app 的 .env 把标准的 OPENAI_API_KEY / OPENAI_BASE_URL 挪用给了 DeepSeek"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"4"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// (agent.py 里 ChatOpenAI 直接读这两个 env 名)。niceeval 的 judge(t.judge.autoevals.*)"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"5"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// 不碰这两个名字:端点写在 judge.baseUrl,key 只读 NICEEVAL_JUDGE_KEY(或 judge.apiKeyEnv"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"6"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// 指定的变量名),judge 走独立凭证,不和应用的模型配置互相干扰。"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"7"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"export"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"default"}</span><span className="gdt0">{" "}</span><span className="gdt5">{"defineConfig"}</span><span className="gdt0">{"({"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"8"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" name: { "}</span><span className="gdt2">{"\"zh-CN\""}</span><span className="gdt0">{": "}</span><span className="gdt2">{"\"LangGraph 示例\""}</span><span className="gdt0">{", en: "}</span><span className="gdt2">{"\"LangGraph example\""}</span><span className="gdt0">{" },"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"9"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" judge: { model: "}</span><span className="gdt2">{"\"gpt-5.4\""}</span><span className="gdt0">{" },"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"10"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" timeoutMs: "}</span><span className="gdt1">{"120_000"}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"11"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" "}</span><span className="gdt6">{"// 被测应用是用户自己起的长驻服务,别开太高并发。"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"12"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" maxConcurrency: "}</span><span className="gdt1">{"2"}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"13"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{"});"}</span></td></tr></tbody></table>
|
|
73
73
|
</div>
|
|
74
74
|
</div>
|
|
75
75
|
|
|
@@ -83,7 +83,7 @@ defineEval({
|
|
|
83
83
|
t.judge.autoevals.closedQA("rubric", { on: t.reply, model: "openai/gpt-4o" });
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
三级都没配就是配置错误,调用点直接报错——judge 没有内置默认模型,也没有对应的环境变量(模型是配置,只从代码来)。
|
|
87
87
|
|
|
88
88
|
## 评判端点与 key:OpenAI 兼容协议
|
|
89
89
|
|
|
@@ -100,15 +100,17 @@ defineConfig({
|
|
|
100
100
|
});
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
端点是配置,key 是凭据,两者的来源分开:
|
|
104
104
|
|
|
105
105
|
| 项 | 解析顺序 |
|
|
106
106
|
|---|---|
|
|
107
|
-
| 端点 | `judge.baseUrl` → `
|
|
108
|
-
| key | `judge.apiKeyEnv` 指定的环境变量 → `NICEEVAL_JUDGE_KEY`
|
|
107
|
+
| 端点 | `judge.baseUrl` → `https://api.openai.com/v1` |
|
|
108
|
+
| key | `judge.apiKeyEnv` 指定的环境变量 → `NICEEVAL_JUDGE_KEY` |
|
|
109
|
+
|
|
110
|
+
接自己的网关或 OpenAI 兼容代理时把地址显式写进 `judge.baseUrl`——NiceEval 不去环境里翻 `OPENAI_BASE_URL` 这类变量猜端点。key 同理只读一个名字:`judge.apiKeyEnv` 指定的那个,或默认的 `NICEEVAL_JUDGE_KEY`;judge 有自己的凭据,不会借用被测应用或某个 agent 的 key。
|
|
109
111
|
|
|
110
112
|
<Warning>
|
|
111
|
-
key
|
|
113
|
+
模型或 key 解析不到时,这条 judge 断言记成 `unavailable`(带原因),不会静默消失:除非显式链了 `.optional()`,评不了就让这次 attempt 记 `errored`——评不出来的结论既不算通过,也不该算 agent 答错。配完 key 先跑一条带 `t.judge` 的 eval,在 `niceeval view` 里确认出现了 judge 分数。
|
|
112
114
|
</Warning>
|
|
113
115
|
|
|
114
116
|
## 严重度:judge 默认 soft
|
|
@@ -31,14 +31,17 @@ description: "NiceEval CLI 参考:exp、show、view、init、list 和 clean
|
|
|
31
31
|
|
|
32
32
|
## 输出语言
|
|
33
33
|
|
|
34
|
-
[NiceEval](https://niceeval.com/) 的 CLI
|
|
34
|
+
[NiceEval](https://niceeval.com/) 的 CLI 和运行时文案支持本地化。要固定输出语言(CI 日志尤其需要),写在 `niceeval.config.ts` 里:
|
|
35
35
|
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
```ts
|
|
37
|
+
import { defineConfig } from "niceeval";
|
|
38
|
+
|
|
39
|
+
export default defineConfig({
|
|
40
|
+
locale: "en",
|
|
41
|
+
});
|
|
39
42
|
```
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
没写 `locale` 时按系统 locale 判定,依次看 `LC_ALL`、`LC_MESSAGES`、`LANG`。以 `zh` 开头的值使用 `zh-CN`,其它语言使用 `en`;都没有时默认 `zh-CN`。这只影响终端/runtime 文案,不改变结果 JSON 里的机器字段,也不翻译 LLM judge prompt。
|
|
42
45
|
|
|
43
46
|
## `npx niceeval exp [path|config] [id-prefix...]`
|
|
44
47
|
|
|
@@ -193,20 +196,11 @@ npx niceeval show weather/brooklyn --history
|
|
|
193
196
|
|
|
194
197
|
`--strict` 不是"更严格地报错",而是改变软阈值断言的判定:`.atLeast(n)` 这类软阈值(`soft` severity)平时失败不会把整条评估用例判为 `failed`(只是记一条不达标的断言),加了 `--strict` 之后,软阈值没达标也会让整条评估用例的 verdict 计为 `failed`。CI 中推荐加上,避免"断言分数不够但评估用例显示通过"的情况被放过。
|
|
195
198
|
|
|
196
|
-
##
|
|
197
|
-
|
|
198
|
-
以下环境变量对应部分 flag,用于不便传 CLI 参数的场景(如 CI 环境配置):
|
|
199
|
-
|
|
200
|
-
| 环境变量 | 对应 flag |
|
|
201
|
-
|---|---|
|
|
202
|
-
| `NICEEVAL_RUNS` | `--runs` |
|
|
203
|
-
| `NICEEVAL_MAX_CONCURRENCY` | `--max-concurrency` |
|
|
204
|
-
| `NICEEVAL_TIMEOUT` | `--timeout` |
|
|
205
|
-
| `NICEEVAL_BUDGET` | `--budget` |
|
|
199
|
+
## 环境变量只放凭据
|
|
206
200
|
|
|
207
|
-
|
|
201
|
+
跑几次、超时、并发、预算、judge 模型和端点、界面语言——这些都是配置,只从 CLI flag、`experiments/` 下的 experiment 文件和 `niceeval.config.ts` 读。优先级从高到低:CLI flag > experiment > `niceeval.config.ts` > 内置默认值。没有对应的环境变量,同一个值不会有第三条来路。
|
|
208
202
|
|
|
209
|
-
|
|
203
|
+
环境变量留给凭据(API key、provider token)和终端环境(`NO_COLOR`、系统 locale)。每个 agent / sandbox / judge 只认自己那一个变量名,不会在环境里翻找其它 key;启动时自动加载 cwd 下的 `.env`(不覆盖已经存在的环境变量)。完整清单和迁移对照见[配置与环境变量](/zh/tutorials/configuration)。
|
|
210
204
|
|
|
211
205
|
## 退出码
|
|
212
206
|
|
|
@@ -8,14 +8,18 @@ description: "defineConfig 参考:judge、reporters、并发、超时和 sandb
|
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
10
|
import { defineConfig } from "niceeval";
|
|
11
|
+
import site from "./reports/site";
|
|
11
12
|
|
|
12
13
|
export default defineConfig({
|
|
13
14
|
judge: { model: "gpt-5.4-mini" },
|
|
14
15
|
maxConcurrency: 4,
|
|
15
16
|
timeoutMs: 300_000,
|
|
17
|
+
report: site,
|
|
16
18
|
});
|
|
17
19
|
```
|
|
18
20
|
|
|
21
|
+
`report` 收 `defineReport` 的产物本身(import 自己的报告文件),不是路径字符串。`niceeval show` 与 `niceeval view` 不带 `--report` 时装载它,没写就装载内置的默认报告;`--report` 按次覆盖,`--report standard` 回到内置报告。写法与整套报告能力见[编写自定义报告](/zh/tutorials/custom-reports#设成项目默认)。
|
|
22
|
+
|
|
19
23
|
## Config 字段
|
|
20
24
|
|
|
21
25
|
{/* GENERATED:BEGIN config-fields */}
|
|
@@ -31,6 +35,15 @@ name?: LocalizedText;
|
|
|
31
35
|
项目名,显示在 `niceeval view` 顶部 hero(`<h1>`),省略则回退到通用标题。
|
|
32
36
|
可传字符串,或按 locale 提供多语言(如 `{ en: "...", "zh-CN": "..." }`),随 view 语言切换。
|
|
33
37
|
|
|
38
|
+
#### `locale`
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
locale?: string;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
CLI 与运行时文案的界面语言(BCP 47,如 `"en"` / `"zh-CN"`);CI 里想让日志恒定一种语言就写这个。
|
|
45
|
+
省略则按系统 locale(`LC_ALL` / `LC_MESSAGES` / `LANG`)判定,都没有时用 `zh-CN`。
|
|
46
|
+
|
|
34
47
|
#### `sandbox`
|
|
35
48
|
|
|
36
49
|
```ts
|
|
@@ -69,7 +82,7 @@ reporters?: Reporter[];
|
|
|
69
82
|
maxConcurrency?: number;
|
|
70
83
|
```
|
|
71
84
|
|
|
72
|
-
项目级默认并发上限;CLI flag /
|
|
85
|
+
项目级默认并发上限;CLI flag / experiment 的同名设置优先级更高(没有环境变量层)。
|
|
73
86
|
|
|
74
87
|
#### `timeoutMs`
|
|
75
88
|
|
|
@@ -32,7 +32,7 @@ description: "给 Coding Agent 的完整接入流程:探索项目、与用户
|
|
|
32
32
|
| 用 Vercel AI SDK(`useChat` 后端)写的应用 | [内置 Agent 参考](/zh/reference/builtin-agents):内置 `uiMessageStreamAgent` 无侵入接入,不用手写事件映射 |
|
|
33
33
|
| coding agent CLI(claude-code / codex / bub 等改文件的任务) | [在 Sandbox 中评测 Agent](/zh/tutorials/sandbox-agent):要配 `sandbox`,默认 `dockerSandbox()`,先跟用户确认 Provider |
|
|
34
34
|
| 给 Claude Code / Codex 写的 Skill、Plugin、Hook 或 MCP server | [评测 Coding Agent 扩展](/zh/examples/coding-agent-extensions):同样跑在 Sandbox 里,Provider 确认方式同上 |
|
|
35
|
-
| 其它自研 agent loop、LangGraph、OpenAI Agents SDK、已部署 Agent |
|
|
35
|
+
| 其它自研 agent loop、LangGraph、OpenAI Agents SDK、已部署 Agent | [接入自己的 Agent](/zh/tutorials/connect-your-agent) 起步,手写 `send` 的完整教程在[编写 send](/zh/tutorials/write-send) |
|
|
36
36
|
| 纯函数、没有独立服务的场景 | 先读[接入自己的 Agent](/zh/tutorials/connect-your-agent) 里「为什么不直调」那段,跟用户确认这确实是他们要的边缘用法,再继续 |
|
|
37
37
|
|
|
38
38
|
## 第 2 步:配置 Judge
|
|
@@ -62,17 +62,8 @@ export default defineConfig({
|
|
|
62
62
|
|
|
63
63
|
按第 1 步选中的方向读完对应文档后,依次写:
|
|
64
64
|
|
|
65
|
-
1. **Adapter**(`agents/*.ts` 或用户项目里约定的目录)——只填 `defineAgent` 的 `send`。契约见 [Adapter](/zh/explanation/adapter),API 签名见 [defineAgent 参考](/zh/reference/define-agent)
|
|
66
|
-
|
|
67
|
-
- **把响应归一成事件流**,不要只吐一条最终文本。返回是标准形状(Chat Completions / Responses / AI SDK)就用官方转换器一行搞定(`return fromChatCompletion(res)`);不是标准形状就照事件表把工具调用映射成 `action.called` / `action.result`。只映射最终文本的 Adapter 也能跑绿,但 `t.calledTool()` / `t.toolOrder()` / `t.noFailedActions()` 整族断言从此用不了——评估只能看最后那段话,看不见它中间干了什么。
|
|
68
|
-
- **被测接口支持多轮就接 `ctx.session`**(`history()`,或 `id` + `capture()`)。不接的话每次 `t.send` 都是一场新对话,多轮场景与 `t.newSession()` 的会话隔离全都测不了。
|
|
69
|
-
|
|
70
|
-
两个容易踩的点:**端点/模式要选被测系统核心能力的入口,不是最容易跑通的入口**——比如被测平台既有「纯 LLM 聊天」又有「连库执行」两种模式,接前者等于评了个底层模型代理,没评到产品本身;**`coverage` 按实际映射如实声明**——只把最终文本映射出来就不要声明 complete,声明会被报告采信,虚报比不报更糟。
|
|
71
|
-
|
|
72
|
-
配置归属:URL、鉴权这类静态配置走 Adapter 工厂参数,由实验文件传进来(想给个环境默认值就写成工厂参数的默认值 `process.env.MY_APP_URL ?? "http://localhost:3000"`);**不要在 `send` 内部读 `process.env`**——那样配置藏进了环境,实验文件里看不见,也没法两格连不同实例做对比。
|
|
73
|
-
2. **Experiment**(`experiments/*.ts`)——引用上面的 Adapter,声明 `model`、`flags`、`runs` 等。一个文件一格配置;对比组默认放在 `experiments/compare-models/` 下(目录只负责 id 和批量运行,报告读取每份快照的 `selectedEvalIds`),`evals: (eval) => boolean` 决定各自运行哪些评估用例。
|
|
74
|
-
|
|
75
|
-
做模型对比先**探明被测系统实际支持哪些模型**:读它的 `/models` 接口、配置文件、README 或前端的模型选择器,拿两个真实存在的值各钉一格。探不到就不要硬凑——两格钉同一个模型(包括两格都写成同一个环境变量默认值)等于没有对比,那种情况下要么改用 `flags` 做 prompt / 工具集变体的对比,要么就只写一格基线并在收尾时告诉用户「没做成模型对比,原因是探不到可选模型」。
|
|
65
|
+
1. **Adapter**(`agents/*.ts` 或用户项目里约定的目录)——只填 `defineAgent` 的 `send`,配置走工厂参数,不写死、不读 `process.env`。契约见 [Adapter](/zh/explanation/adapter),API 签名见 [defineAgent 参考](/zh/reference/define-agent),事件映射见[事件参考](/zh/reference/events)。两个容易踩的点:**端点/模式要选被测系统核心能力的入口,不是最容易跑通的入口**——比如被测平台既有「纯 LLM 聊天」又有「连库执行」两种模式,接前者等于评了个底层模型代理,没评到产品本身;**`coverage` 按实际映射如实声明**——只把最终文本映射出来就不要声明 complete,声明会被报告采信,虚报比不报更糟。
|
|
66
|
+
2. **Experiment**(`experiments/*.ts`)——引用上面的 Adapter,声明 `model`、`flags`、`runs` 等。模型对比写两个实验文件,各自钉一个 `model`;`evals: (eval) => boolean` 决定各自运行哪些评估用例。路径只负责 id 和批量运行,报告读取每份快照的 `selectedEvalIds`。
|
|
76
67
|
3. **评估用例**(`evals/*.eval.ts`)——**先探明这个应用是干嘛的,再写一条贴着它真实功能的评估用例**:读它的 README、路由、工具定义或系统提示,找出它的核心用例(客服机器人就问一条真实的客服问题、SQL agent 就给一个真实的查询任务),拿这个用例做第一条评估用例的输入和断言。两类输入都不合格:「你好」这种和应用无关的占位输入,以及「你是什么/你能做什么」这种**问被测系统它自己的元问题**——那不是用户拿它干活的用例。形式上仍从最小写起:一句输入,`t.succeeded()` + 一个针对预期回答的内容断言——但最小形式只是调通的脚手架,不是交付标准,收尾前还要满足两条:
|
|
77
68
|
- **断言在被测系统胡编时要会变红**。不要断言输入里本来就有的词(问「X 是什么」再断言回答含「X」,被测方复读题目就能通过);断言预期回答独有的实质内容——具体事实、结构(`hasSections()`)、真实链接(`includesUrl()`),或用 `t.judge` 做语义判定。
|
|
78
69
|
- **至少一条负例**。喂一个被测系统应该答不了的输入(不存在的表、检索不到的主题),断言它明确说查不到/做不到,而不是编造一个看似合理的结果——对连着真实数据/检索源的 agent,这是最值得先测的失败形态。
|
|
@@ -88,13 +79,11 @@ export default defineConfig({
|
|
|
88
79
|
## 第 4 步:跑通并验证
|
|
89
80
|
|
|
90
81
|
```sh
|
|
91
|
-
<包管理器> exec niceeval exp
|
|
82
|
+
<包管理器> exec niceeval exp models # 按路径运行两个模型配置
|
|
92
83
|
<包管理器> exec niceeval view # 查看器里看对比结果
|
|
93
84
|
```
|
|
94
85
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
**被测系统起不来的时候**:先按它自己的方式起(`pnpm dev`、`docker compose up`、它 README 里那条命令),起不来就把缺什么(端口、密钥、数据库、模型服务)明确告诉用户,请他们起——评估侧不代管被测进程。确实需要先自证 Adapter 链路通(比如没人在场、又必须交付一个跑过的结果)时,可以临时把 Adapter 指向一个最小的桩服务跑一轮,但这**不是有效的评估结果**:桩不进交付物,收尾总结里要写明「链路已验证,但还没对真实的被测系统跑过」。
|
|
86
|
+
按 [Coding Agent 反馈闭环](/zh/tutorials/agent-feedback-loop)用 `niceeval show`、`--transcript`、`--trace` 和 `--diff` 完成运行、观察、修改和重跑;查看器用法见[查看结果](/zh/tutorials/viewing-results)。没跑通分三类定位:`fetch` 直接抛错 → 应用没起来或 URL 不对;`t.succeeded()` 不过 → 应用回了非成功状态;只有内容断言不过 → 接入已经通了,调断言或调应用。
|
|
98
87
|
|
|
99
88
|
## 第 5 步:收尾,告诉用户做了什么
|
|
100
89
|
|
|
@@ -105,9 +94,6 @@ export default defineConfig({
|
|
|
105
94
|
- [ ] 至少有一条负例(应该答不了的输入,断言它明确说做不到)
|
|
106
95
|
- [ ] 有 key 时 Judge 已配置,且在 `niceeval view` 里看到过 Judge 分数;没 key 时总结里说明了
|
|
107
96
|
- [ ] Experiment 里声明的 `model` / `flags` 确实被 Adapter 消费(没有写了没人读的死配置,也没有编造被测系统不存在的 model 值)
|
|
108
|
-
- [ ] 被测系统的响应里有工具调用/过程信息的,Adapter 已把它们映射成事件流,不是只吐一条最终文本
|
|
109
|
-
- [ ] 被测接口支持多轮的,Adapter 已接 `ctx.session`(没接的话在总结里说明,因为多轮场景从此测不了)
|
|
110
|
-
- [ ] **真跑过至少一次,且是绿的**;没绿就说清卡在哪(被测系统没起、断言待调),不要拿没跑过的文件交付
|
|
111
97
|
|
|
112
98
|
跑通之后先总结,再谈下一步。总结要说清:接了什么被测对象、生成了哪几个文件(Adapter / Experiment / 评估用例各在哪)、`niceeval exp compare-models` 和 `niceeval view` 怎么跑、第一次运行的结果是什么样。不要在没被要求的情况下顺手重构用户已有代码,也不要在这几个文件之外新增抽象。
|
|
113
99
|
|
|
@@ -117,10 +103,10 @@ export default defineConfig({
|
|
|
117
103
|
|
|
118
104
|
| 能做什么 | 改动量 | 好处 | 文档 |
|
|
119
105
|
|---|---|---|---|
|
|
120
|
-
|
|
|
106
|
+
| 工具调用断言(`t.calledTool()` 等) | 只改 Adapter:把应用响应映射成标准事件流,约 10–30 行映射代码 | 评估用例能断言「Agent 有没有调对工具、参数对不对」,不再只看最终回复 | [编写 send](/zh/tutorials/write-send)、[事件参考](/zh/reference/events) |
|
|
107
|
+
| 多轮对话、会话隔离 | 只改 Adapter:接上 `ctx.session`(`history()` 或 `id` + `capture()`),几行到十几行 | 评估用例能写多轮场景、`t.newSession()` 验证会话间不串味 | [驱动多轮交互](/zh/explanation/drive)、[编写 send](/zh/tutorials/write-send) |
|
|
108
|
+
| 人工审批流(HITL) | 只改 Adapter:停轮返回 `waiting` + `input.requested`,回答轮续跑,约 10–20 行 | 评估用例能覆盖「批准/拒绝之后 Agent 行为对不对」这类审批场景 | [HITL](/zh/explanation/hitl) |
|
|
121
109
|
| 调用瀑布图(升 Tier 2) | 应用已有 OTel 埋点(第 1 步探过):只是把 span 多发一份给 NiceEval,几行配置;没埋点:补一段通用 OTel 初始化 | `niceeval view` 里看到应用内部每次模型调用、工具执行的耗时和 token 时间线;不影响任何断言 | [配置 OTel](/zh/tutorials/connect-otel) |
|
|
122
110
|
| feature A/B 对比(升 Tier 3) | 改应用:把变体暴露成 `flags` 可切换的配置,改动量取决于应用;已有 A/B 开关(第 1 步探过)就是现成入口 | Experiment 层面直接对比「改 prompt / 换工具集 / 开关 feature 谁更好」 | [组织 Experiment](/zh/tutorials/experiments)、[接入等级](/zh/explanation/tier) |
|
|
123
111
|
|
|
124
|
-
**工具调用断言和多轮会话不在这张表里**——它们是 Tier 1 的一部分,第 3 步写 Adapter 时就该做完(事件流映射、`ctx.session`)。要是当时因为被测接口给不出过程信息或不支持多轮而没做,这里如实告诉用户「这两族断言暂时用不了,原因是接口本身不提供」,而不是把它们说成「以后可以加的功能」。
|
|
125
|
-
|
|
126
112
|
共同点也要讲给用户:这些全是给 Adapter 或应用加增量,**已写的评估用例一行不用改**。三档投入分别买到什么、什么时候值得升级,见[接入等级](/zh/explanation/tier)。第 1 步探到应用已有 OTel 埋点的话,瀑布图那条要主动推荐——成本接近零。
|
|
@@ -11,7 +11,7 @@ description: "把 NiceEval 接入 GitHub Actions 或任意 CI。评估用例失
|
|
|
11
11
|
默认情况下,只要存在失败的 gate,[NiceEval](https://niceeval.com/) 将以非零状态码退出。CI 中通常使用 `--strict`,让失败更明确。
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
|
|
14
|
+
npx niceeval exp ci \
|
|
15
15
|
--strict \
|
|
16
16
|
--junit .niceeval/junit.xml
|
|
17
17
|
```
|
|
@@ -38,7 +38,6 @@ jobs:
|
|
|
38
38
|
- run: npm ci
|
|
39
39
|
- run: npx niceeval exp ci --strict --junit .niceeval/junit.xml
|
|
40
40
|
env:
|
|
41
|
-
NICEEVAL_LANG: en
|
|
42
41
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
43
42
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
44
43
|
```
|
|
@@ -90,7 +89,7 @@ NEXT
|
|
|
90
89
|
需要机器解析运行事件(自建 annotation adapter、实时看板)时把这条命令加上 `--json`,得到同一套事实的 NDJSON 事件流,`start` 打头、`result` 收尾:
|
|
91
90
|
|
|
92
91
|
```bash
|
|
93
|
-
|
|
92
|
+
npx niceeval exp ci --strict --junit .niceeval/junit.xml --json
|
|
94
93
|
```
|
|
95
94
|
|
|
96
95
|
需要一份 JSON 汇总文件时不解析运行日志,运行后单独读结果面:
|