niceeval 0.6.1 → 0.6.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/dist/agents/types.d.ts +67 -5
- package/dist/context/types.d.ts +32 -12
- package/dist/i18n/en.d.ts +54 -0
- package/dist/i18n/zh-CN.d.ts +55 -1
- package/dist/o11y/types.d.ts +16 -2
- package/dist/report/aggregate.d.ts +5 -3
- package/dist/report/aggregate.js +32 -5
- package/dist/report/built-ins/experiment-comparison.d.ts +39 -1
- package/dist/report/built-ins/experiment-comparison.js +116 -10
- package/dist/report/built-ins/index.d.ts +1 -0
- package/dist/report/built-ins/index.js +1 -1
- package/dist/report/components.d.ts +8 -2
- package/dist/report/components.js +3 -3
- package/dist/report/compute.d.ts +11 -18
- package/dist/report/compute.js +54 -34
- package/dist/report/flag.d.ts +16 -1
- package/dist/report/flag.js +19 -1
- package/dist/report/format.d.ts +16 -8
- package/dist/report/format.js +27 -12
- package/dist/report/index.d.ts +4 -3
- package/dist/report/index.js +5 -4
- package/dist/report/locale.d.ts +11 -2
- package/dist/report/locale.js +23 -5
- package/dist/report/metrics.d.ts +13 -1
- package/dist/report/metrics.js +65 -14
- package/dist/report/primitives.d.ts +6 -0
- package/dist/report/react/AttemptList.d.ts +2 -2
- package/dist/report/react/AttemptList.js +5 -6
- package/dist/report/react/EvalList.d.ts +1 -1
- package/dist/report/react/EvalList.js +0 -0
- package/dist/report/react/ExperimentComparison.d.ts +8 -0
- package/dist/report/react/ExperimentComparison.js +11 -0
- package/dist/report/react/ExperimentList.d.ts +2 -1
- package/dist/report/react/ExperimentList.js +8 -10
- package/dist/report/react/MetricScatter.js +5 -11
- package/dist/report/react/chart-math.d.ts +23 -6
- package/dist/report/react/chart-math.js +71 -19
- package/dist/report/react/fixtures.d.ts +3 -3
- package/dist/report/react/fixtures.js +21 -14
- package/dist/report/report.d.ts +5 -1
- package/dist/report/report.js +6 -2
- package/dist/report/text/faces.d.ts +1 -1
- package/dist/report/text/faces.js +42 -41
- package/dist/report/text/table.js +36 -5
- package/dist/report/types.d.ts +39 -21
- package/dist/results/types.d.ts +11 -0
- package/dist/runner/feedback/sink.d.ts +110 -0
- package/dist/runner/types.d.ts +513 -22
- package/dist/sandbox/docker.d.ts +23 -2
- package/dist/sandbox/e2b.d.ts +15 -1
- package/dist/sandbox/errors.d.ts +30 -3
- package/dist/sandbox/io-retry.d.ts +17 -0
- package/dist/sandbox/registry.d.ts +2 -0
- package/dist/sandbox/resolve.d.ts +18 -5
- package/dist/sandbox/retry.d.ts +11 -1
- package/dist/sandbox/types.d.ts +39 -5
- package/dist/sandbox/vercel.d.ts +7 -1
- package/dist/scoring/coverage.d.ts +30 -0
- package/dist/scoring/display.d.ts +21 -0
- package/dist/scoring/display.js +120 -0
- package/dist/scoring/types.d.ts +103 -20
- package/dist/shared/aggregate.d.ts +1 -0
- package/dist/shared/aggregate.js +3 -3
- package/dist/shared/types.d.ts +28 -0
- package/dist/tty-line.d.ts +0 -4
- package/dist/util.d.ts +23 -0
- package/docs-site/zh/concepts/adapter.mdx +22 -4
- package/docs-site/zh/concepts/experiment.mdx +1 -1
- package/docs-site/zh/concepts/overview.mdx +6 -6
- package/docs-site/zh/guides/agent-feedback-loop.mdx +28 -26
- package/docs-site/zh/guides/authoring.mdx +33 -0
- package/docs-site/zh/guides/ci-integration.mdx +23 -12
- package/docs-site/zh/guides/connect-your-agent.mdx +29 -3
- package/docs-site/zh/guides/custom-reports.mdx +29 -34
- package/docs-site/zh/guides/dataset-fanout.mdx +25 -3
- package/docs-site/zh/guides/debug-sandbox.mdx +57 -0
- package/docs-site/zh/guides/debugging.mdx +210 -0
- package/docs-site/zh/guides/experiments.mdx +10 -3
- package/docs-site/zh/guides/official-adapters.mdx +26 -2
- package/docs-site/zh/guides/publish-report.mdx +30 -16
- package/docs-site/zh/guides/report-components.mdx +42 -30
- package/docs-site/zh/guides/reporters.mdx +2 -2
- package/docs-site/zh/guides/results-data.mdx +17 -9
- package/docs-site/zh/guides/runner.mdx +17 -7
- package/docs-site/zh/guides/sandbox-agent.mdx +56 -7
- package/docs-site/zh/guides/sandbox-providers.mdx +257 -9
- package/docs-site/zh/guides/scoring-guide.mdx +4 -4
- package/docs-site/zh/guides/viewing-results.mdx +79 -36
- package/docs-site/zh/guides/write-experiment.mdx +5 -3
- package/docs-site/zh/guides/write-send.mdx +17 -1
- package/docs-site/zh/index.mdx +1 -1
- package/docs-site/zh/reference/builtin-agents.mdx +27 -0
- package/docs-site/zh/reference/capabilities.mdx +2 -2
- package/docs-site/zh/reference/cli.mdx +33 -7
- package/docs-site/zh/reference/define-agent.mdx +57 -4
- package/docs-site/zh/reference/define-config.mdx +1 -1
- package/docs-site/zh/reference/define-eval.mdx +42 -9
- package/docs-site/zh/reference/expect.mdx +26 -1
- package/package.json +5 -1
- package/src/agents/ai-sdk-otel.test.ts +1 -0
- package/src/agents/ai-sdk.test.ts +3 -0
- package/src/agents/ai-sdk.ts +3 -0
- package/src/agents/bub-install-spec.test.ts +34 -0
- package/src/agents/bub-install-spec.ts +32 -0
- package/src/agents/bub.ts +31 -32
- package/src/agents/claude-code.test.ts +130 -9
- package/src/agents/claude-code.ts +76 -4
- package/src/agents/codex.test.ts +189 -40
- package/src/agents/codex.ts +155 -14
- package/src/agents/coding-cli-versions.test.ts +15 -0
- package/src/agents/coding-cli-versions.ts +3 -0
- package/src/agents/index.ts +11 -0
- package/src/agents/langgraph.test.ts +204 -0
- package/src/agents/langgraph.ts +495 -0
- package/src/agents/marketplace.ts +85 -0
- package/src/agents/native-config.test.ts +179 -0
- package/src/agents/native-config.ts +267 -0
- package/src/agents/openai-compat.test.ts +1 -0
- package/src/agents/openclaw.test.ts +31 -0
- package/src/agents/openclaw.ts +171 -0
- package/src/agents/plugin-config.test.ts +1 -0
- package/src/agents/sdk-streams.test.ts +79 -0
- package/src/agents/sdk-streams.ts +55 -10
- package/src/agents/skills.test.ts +1 -0
- package/src/agents/streaming.test.ts +3 -9
- package/src/agents/types.ts +68 -5
- package/src/agents/ui-message-stream.test.ts +3 -0
- package/src/cli.ts +411 -108
- package/src/context/context.test.ts +51 -12
- package/src/context/context.ts +161 -29
- package/src/context/session.test.ts +1 -0
- package/src/context/session.ts +114 -6
- package/src/context/types.ts +30 -12
- package/src/define.test.ts +13 -8
- package/src/define.ts +25 -4
- package/src/expect/index.ts +53 -23
- package/src/i18n/en.ts +64 -2
- package/src/i18n/zh-CN.ts +65 -3
- package/src/o11y/cost.test.ts +1 -0
- package/src/o11y/execution-tree.test.ts +1 -20
- package/src/o11y/otlp/mappers/claude-code.test.ts +1 -0
- package/src/o11y/otlp/parse.test.ts +1 -0
- package/src/o11y/otlp/turn-otel.test.ts +1 -0
- package/src/o11y/parsers/bub.test.ts +1 -0
- package/src/o11y/parsers/claude-code.test.ts +1 -34
- package/src/o11y/parsers/openclaw.test.ts +154 -0
- package/src/o11y/parsers/openclaw.ts +310 -0
- package/src/o11y/prices.json +746 -311
- package/src/o11y/tool-names.test.ts +1 -0
- package/src/o11y/types.ts +16 -2
- package/src/report/aggregate.ts +34 -5
- package/src/report/built-in-user-parity.test.tsx +110 -153
- package/src/report/built-ins/experiment-comparison.tsx +173 -13
- package/src/report/built-ins/index.ts +6 -1
- package/src/report/components.tsx +9 -3
- package/src/report/compute.ts +70 -40
- package/src/report/dual-render.test.tsx +194 -67
- package/src/report/flag.ts +30 -2
- package/src/report/format.ts +35 -11
- package/src/report/index.ts +22 -4
- package/src/report/locale.ts +25 -5
- package/src/report/metrics.ts +67 -14
- package/src/report/primitives.tsx +6 -0
- package/src/report/react/AttemptList.tsx +6 -31
- package/src/report/react/EvalList.tsx +0 -0
- package/src/report/react/ExperimentComparison.tsx +68 -0
- package/src/report/react/ExperimentList.tsx +15 -9
- package/src/report/react/MetricScatter.tsx +12 -14
- package/src/report/react/chart-math.test.ts +85 -0
- package/src/report/react/chart-math.ts +101 -22
- package/src/report/react/enhance.js +33 -1
- package/src/report/react/fixtures.ts +24 -17
- package/src/report/react/render.test.tsx +9 -64
- package/src/report/react/styles.css +73 -2
- package/src/report/report.test.ts +306 -98
- package/src/report/report.ts +6 -2
- package/src/report/text/faces.ts +47 -43
- package/src/report/text/table.ts +42 -5
- package/src/report/types.ts +41 -21
- package/src/results/annotated-source.test.ts +62 -9
- package/src/results/annotated-source.ts +64 -6
- package/src/results/attempt-evidence.test.ts +9 -7
- package/src/results/attempt-evidence.ts +15 -8
- package/src/results/attempt-source.ts +6 -3
- package/src/results/copy.ts +145 -55
- package/src/results/host-equivalence.test.ts +8 -6
- package/src/results/index.ts +2 -0
- package/src/results/locator.test.ts +1 -22
- package/src/results/open.ts +7 -1
- package/src/results/publish.ts +149 -0
- package/src/results/results.test.ts +85 -51
- package/src/results/truncate.ts +90 -0
- package/src/results/types.ts +7 -0
- package/src/results/writer.ts +31 -13
- package/src/runner/attempt.test.ts +138 -7
- package/src/runner/attempt.ts +603 -104
- package/src/runner/discover.test.ts +47 -0
- package/src/runner/discover.ts +36 -2
- package/src/runner/eval-source.test.ts +1 -27
- package/src/runner/feedback/agent.test.ts +504 -0
- package/src/runner/feedback/agent.ts +409 -0
- package/src/runner/feedback/ci.test.ts +562 -0
- package/src/runner/feedback/ci.ts +401 -0
- package/src/runner/feedback/coordinator.test.ts +317 -0
- package/src/runner/feedback/coordinator.ts +397 -0
- package/src/runner/feedback/failure.ts +40 -0
- package/src/runner/feedback/human.test.ts +616 -0
- package/src/runner/feedback/human.ts +535 -0
- package/src/runner/feedback/index.ts +66 -0
- package/src/runner/feedback/io.ts +78 -0
- package/src/runner/feedback/profile.test.ts +50 -0
- package/src/runner/feedback/profile.ts +58 -0
- package/src/runner/feedback/reducer.test.ts +395 -0
- package/src/runner/feedback/reducer.ts +260 -0
- package/src/runner/feedback/renderer.ts +82 -0
- package/src/runner/feedback/sink.ts +203 -0
- package/src/runner/feedback/testing.ts +106 -0
- package/src/runner/ledger.test.ts +230 -0
- package/src/runner/ledger.ts +329 -0
- package/src/runner/report.test.ts +128 -3
- package/src/runner/report.ts +33 -9
- package/src/runner/reporters/artifacts.ts +8 -2
- package/src/runner/reporters/braintrust.test.ts +8 -7
- package/src/runner/reporters/braintrust.ts +9 -2
- package/src/runner/reporters/index.ts +2 -2
- package/src/runner/reporters/json.test.ts +162 -0
- package/src/runner/reporters/json.ts +35 -8
- package/src/runner/reporters/shared.ts +1 -5
- package/src/runner/run.test.ts +760 -3
- package/src/runner/run.ts +242 -36
- package/src/runner/sandbox-prep.ts +3 -42
- package/src/runner/timing.ts +158 -0
- package/src/runner/types.ts +518 -22
- package/src/sandbox/checkpoint.test.ts +55 -0
- package/src/sandbox/checkpoint.ts +29 -8
- package/src/sandbox/cli-commands.ts +407 -0
- package/src/sandbox/docker.ts +115 -16
- package/src/sandbox/e2b-agent-template.test.ts +56 -0
- package/src/sandbox/e2b-agent-template.ts +94 -0
- package/src/sandbox/e2b.ts +74 -9
- package/src/sandbox/errors.ts +111 -4
- package/src/sandbox/index.ts +2 -0
- package/src/sandbox/io-retry.test.ts +58 -0
- package/src/sandbox/io-retry.ts +45 -0
- package/src/sandbox/keep-registry.test.ts +86 -0
- package/src/sandbox/keep-registry.ts +142 -0
- package/src/sandbox/keep.ts +178 -0
- package/src/sandbox/paths.test.ts +1 -0
- package/src/sandbox/paths.ts +19 -8
- package/src/sandbox/registry.ts +20 -3
- package/src/sandbox/resolve.ts +76 -11
- package/src/sandbox/retry.test.ts +70 -0
- package/src/sandbox/retry.ts +46 -4
- package/src/sandbox/types.ts +44 -6
- package/src/sandbox/vercel.ts +43 -20
- package/src/scoring/collector.ts +60 -17
- package/src/scoring/coverage.ts +95 -0
- package/src/scoring/diff.ts +81 -0
- package/src/scoring/display.test.ts +121 -0
- package/src/scoring/display.ts +133 -0
- package/src/scoring/evidence.test.ts +189 -0
- package/src/scoring/judge.test.ts +142 -0
- package/src/scoring/judge.ts +15 -18
- package/src/scoring/scoped.ts +217 -50
- package/src/scoring/types.ts +117 -20
- package/src/scoring/verdict.ts +16 -4
- package/src/shared/aggregate.ts +3 -2
- package/src/shared/types.ts +31 -0
- package/src/show/compose.ts +2 -2
- package/src/show/index.ts +21 -1
- package/src/show/render.ts +619 -104
- package/src/show/show.test.ts +235 -19
- package/src/tty-line.ts +8 -26
- package/src/util.test.ts +1 -0
- package/src/util.ts +41 -0
- package/src/view/app/components/AttemptModal.tsx +153 -2
- package/src/view/app/components/CodeView.tsx +32 -11
- package/src/view/app/components/CopyControls.tsx +2 -2
- package/src/view/app/i18n.ts +6 -0
- package/src/view/app/lib/attempt-route.test.ts +1 -0
- package/src/view/app/lib/verdict.ts +7 -9
- package/src/view/artifact-serving.test.ts +2 -1
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +17 -17
- package/src/view/data.test.ts +1 -0
- package/src/view/data.ts +11 -1
- package/src/view/index.ts +11 -0
- package/src/view/server.ts +2 -0
- package/src/view/styles.css +3 -0
- package/src/view/view-report.test.ts +6 -5
- package/src/runner/reporters/console.ts +0 -70
- package/src/runner/reporters/live.test.ts +0 -56
- package/src/runner/reporters/live.ts +0 -247
- package/src/runner/reporters/quiet.test.ts +0 -66
- package/src/runner/reporters/quiet.ts +0 -49
- package/src/runner/reporters/table.ts +0 -277
package/dist/scoring/types.d.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import type { Severity, SourceLoc } from "../shared/types.ts";
|
|
2
2
|
import type { DerivedFacts, StreamEvent, Usage } from "../o11y/types.ts";
|
|
3
|
-
|
|
3
|
+
import type { ResolvedCoverage } from "./coverage.ts";
|
|
4
|
+
export type { CoverageChannel, ResolvedCoverage, ResolvedCoverageChannel, ResolvedCoverageStatus, } from "./coverage.ts";
|
|
5
|
+
/** 值断言(expect 匹配器)。纯函数 score + 可链式改严重度 / 阈值 / optional。 */
|
|
4
6
|
export interface ValueAssertion {
|
|
5
7
|
readonly name: string;
|
|
6
8
|
readonly severity: Severity;
|
|
7
9
|
readonly threshold?: number;
|
|
10
|
+
/** `.optional()` 链过的标记:评不了只记 unavailable,不把 attempt 拖成 errored。 */
|
|
11
|
+
readonly isOptional?: boolean;
|
|
12
|
+
/** 期望条件的有界文本描述(如 `contains "Brooklyn"`),失败时进 AssertionResult.expected。 */
|
|
13
|
+
readonly expected?: string;
|
|
8
14
|
score(value: unknown): number | Promise<number>;
|
|
9
15
|
/** 转成硬门槛断言:未达阈值(省略 threshold 则按 score > 0 判定)整条 eval 判为 failed。返回新实例,不改原对象。 */
|
|
10
16
|
gate(threshold?: number): ValueAssertion;
|
|
@@ -13,34 +19,76 @@ export interface ValueAssertion {
|
|
|
13
19
|
* `--strict` 运行下,软阈值失败也会把整条 eval 的 verdict 计为 failed。返回新实例,不改原对象。
|
|
14
20
|
*/
|
|
15
21
|
atLeast(threshold: number): ValueAssertion;
|
|
22
|
+
/**
|
|
23
|
+
* 允许这条断言证据缺席:评不了时只记录 `outcome: "unavailable"`,不影响判定。
|
|
24
|
+
* 与 severity 正交(severity 说影不影响质量判定,optional 说证据允不允许缺席)。返回新实例,不改原对象。
|
|
25
|
+
*/
|
|
26
|
+
optional(): ValueAssertion;
|
|
16
27
|
}
|
|
17
|
-
/**
|
|
18
|
-
|
|
28
|
+
/**
|
|
29
|
+
* 断言记录的公共字段(见 docs/feature/scoring/architecture.md「断言记录」——字段契约的单点定义)。
|
|
30
|
+
*/
|
|
31
|
+
export interface AssertionBase {
|
|
32
|
+
/** 断言标题:t.group 内是该断言自己的摘要,组外是 matcher 摘要或 judge 问题;show/view 失败行的标题。 */
|
|
19
33
|
name: string;
|
|
34
|
+
/** 所属分组路径:外层在前的 t.group 标题数组;无分组省略。纯报告用,不影响判定。 */
|
|
35
|
+
groupPath?: string[];
|
|
20
36
|
severity: Severity;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
37
|
+
/** 作者用 .optional() 显式允许该断言缺席;只改变 unavailable 的折叠方式(见 Severity 与 Verdict),不改变 severity 语义。 */
|
|
38
|
+
optional?: true;
|
|
39
|
+
/** matcher / judge 摘要,如 `equals(4)`、`closedQA("…")`;与 name 分开,供 show/view 同时展示分组标题与检查方式。 */
|
|
40
|
+
detail?: string;
|
|
41
|
+
/** 断言在 eval 源码中的调用点,`--eval` 把结果标回源码行的锚。 */
|
|
42
|
+
loc?: SourceLoc;
|
|
24
43
|
}
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
44
|
+
/**
|
|
45
|
+
* 断言评估完的结果(进判定 / 报告)。判别键是 `outcome`——`unavailable` 是没有分数的独立态,
|
|
46
|
+
* 普通聚合代码按 `outcome` 分支就不可能把证据缺口算成零分。判定只消费
|
|
47
|
+
* `severity` / `outcome` / `optional` / `score` / `threshold`。
|
|
48
|
+
*/
|
|
49
|
+
export type AssertionResult = (AssertionBase & {
|
|
50
|
+
outcome: "passed" | "failed";
|
|
51
|
+
/** 归一化得分:值断言 0/1,judge 等打分断言 0..1。 */
|
|
30
52
|
score: number;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
/**
|
|
53
|
+
/** soft 断言的 .atLeast(x) 阈值;没有设阈值则省略。 */
|
|
54
|
+
threshold?: number;
|
|
55
|
+
/** 失败证据摘要:期望值的有界文本预览,供 show/view 直接展示。 */
|
|
56
|
+
expected?: string;
|
|
57
|
+
/** 失败证据摘要:实际值的有界文本预览。 */
|
|
58
|
+
received?: string;
|
|
59
|
+
/** 这条分数看着什么材料算出(judge 输入或被检查值预览);view 展开排查用,默认不展示。 */
|
|
34
60
|
evidence?: string;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
|
|
61
|
+
}) | (AssertionBase & {
|
|
62
|
+
outcome: "unavailable";
|
|
63
|
+
/** 机器可读原因,如 "judge-model-unresolved"、"coverage:actions=partial"。 */
|
|
64
|
+
reason: string;
|
|
65
|
+
});
|
|
66
|
+
/**
|
|
67
|
+
* 摘要面从完整 `AssertionResult[]` 选出的一条主失败断言。它只负责展示,不参与 verdict;
|
|
68
|
+
* `show @locator` / view Attempt 详情仍读取完整断言数组。字段保持结构化,使 CI 不必解析
|
|
69
|
+
* Human 的 `gate: …` 文案。
|
|
70
|
+
*/
|
|
71
|
+
export interface PrimaryAssertionSummary {
|
|
72
|
+
severity: Severity;
|
|
73
|
+
/** `groupPath.join(" > ")`,无 group 时回退到断言 name。 */
|
|
74
|
+
assertion: string;
|
|
75
|
+
/** `detail ?? name`;与 assertion 相同时省略,避免重复。 */
|
|
76
|
+
matcher?: string;
|
|
77
|
+
expected?: string;
|
|
78
|
+
received?: string;
|
|
79
|
+
score?: number;
|
|
80
|
+
threshold?: number;
|
|
81
|
+
/** unavailable 断言的结构化原因。 */
|
|
82
|
+
reason?: string;
|
|
83
|
+
/** 同类因果失败中除主失败外的条数。 */
|
|
84
|
+
additionalFailures: number;
|
|
39
85
|
}
|
|
40
86
|
/** eval 作者拿到的可链式句柄(t.judge.autoevals.closedQA(...).atLeast(0.7))。 */
|
|
41
87
|
export interface AssertionHandle {
|
|
42
88
|
atLeast(threshold: number): AssertionHandle;
|
|
43
89
|
gate(threshold?: number): AssertionHandle;
|
|
90
|
+
/** 允许这条断言证据缺席:unavailable 只保留在记录里,不影响判定(见 Severity 与 Verdict)。 */
|
|
91
|
+
optional(): AssertionHandle;
|
|
44
92
|
}
|
|
45
93
|
/** scoped / judge 断言在 final 评估时拿到的运行结果。 */
|
|
46
94
|
export interface ScoringContext {
|
|
@@ -50,6 +98,8 @@ export interface ScoringContext {
|
|
|
50
98
|
readonly scripts: Record<string, ScriptResult>;
|
|
51
99
|
readonly usage: Usage;
|
|
52
100
|
readonly status: "completed" | "failed" | "waiting";
|
|
101
|
+
/** 当前作用域(turn / session / attempt)解析后的证据覆盖;断言按它做三值折叠(见 scoped.ts)。 */
|
|
102
|
+
readonly coverage: ResolvedCoverage;
|
|
53
103
|
/** 读沙箱里某文件的最终内容(judge / file 断言用)。 */
|
|
54
104
|
readFile(path: string): Promise<string | undefined>;
|
|
55
105
|
}
|
|
@@ -57,9 +107,42 @@ export interface ScriptResult {
|
|
|
57
107
|
success: boolean;
|
|
58
108
|
output: string;
|
|
59
109
|
}
|
|
110
|
+
/** diff.json 的落盘形状:按时序的窗口数组(逐窗口 delta 序列,不做跨窗口压缩)。 */
|
|
111
|
+
export type DiffArtifact = DiffWindow[];
|
|
112
|
+
export interface DiffWindow {
|
|
113
|
+
/** send 窗口标签,与时间树 turn 节点、--execution 轮次同源(如 "s1/t2")。 */
|
|
114
|
+
window: string;
|
|
115
|
+
/** 该窗口内 agent 改动的文件;窗口内没有 workspace 变化时窗口仍落一条、changes 为空对象。 */
|
|
116
|
+
changes: Record<string, WindowChange>;
|
|
117
|
+
}
|
|
118
|
+
export interface WindowChange {
|
|
119
|
+
status: "added" | "modified" | "deleted";
|
|
120
|
+
/** 窗口开始时的内容;added 无此字段。 */
|
|
121
|
+
before?: string;
|
|
122
|
+
/** 窗口结束时的内容;deleted 无此字段。 */
|
|
123
|
+
after?: string;
|
|
124
|
+
/** 二进制文件不内联内容,只记字节数。 */
|
|
125
|
+
binary?: {
|
|
126
|
+
beforeBytes?: number;
|
|
127
|
+
afterBytes?: number;
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/** 读取面在窗口序列之上派生的文件级视图(派生物可随时重算,不落盘)。 */
|
|
131
|
+
export interface DiffFileSummary {
|
|
132
|
+
/** 净效果:首个触及窗口的起点 vs 最后触及窗口的终点;"none" = 动过但净无变化(创建又删除、改回原样)。 */
|
|
133
|
+
net: "added" | "modified" | "deleted" | "none";
|
|
134
|
+
/** 触及该文件的窗口标签,按时序。 */
|
|
135
|
+
windows: string[];
|
|
136
|
+
binary?: true;
|
|
137
|
+
}
|
|
138
|
+
/** agent 归因 diff 的消费视图:窗口序列(落盘事实)+ 派生的文件级摘要与终态读取。 */
|
|
60
139
|
export interface DiffData {
|
|
61
|
-
|
|
62
|
-
|
|
140
|
+
/** 落盘事实,原样。 */
|
|
141
|
+
windows: DiffWindow[];
|
|
142
|
+
/** 派生:每个被 agent 触及的文件一条。 */
|
|
143
|
+
files: Record<string, DiffFileSummary>;
|
|
144
|
+
/** 该文件最后一个触及窗口结束时的内容;净删除或从未触及返回 undefined。t.sandbox.diff.get 同一语义。 */
|
|
145
|
+
get(path: string): string | undefined;
|
|
63
146
|
}
|
|
64
147
|
export type Verdict = "passed" | "failed" | "errored" | "skipped";
|
|
65
148
|
export interface JudgeConfig {
|
|
@@ -22,6 +22,7 @@ export declare function experimentGroupOf(experimentId: string): string | undefi
|
|
|
22
22
|
export declare function evalPrefixPredicate(evals?: string | string[]): (id: string) => boolean;
|
|
23
23
|
/** 无 experimentId 时的兜底标签。 */
|
|
24
24
|
export declare function fallbackExperimentLabel(result: {
|
|
25
|
+
experimentId?: string;
|
|
25
26
|
experiment?: ExperimentRunInfo;
|
|
26
27
|
agent: string;
|
|
27
28
|
model?: string;
|
package/dist/shared/aggregate.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// report 聚合(report/aggregate.ts)与 view 榜单(view/app/lib/rows.ts)共用的聚合小工具。
|
|
2
2
|
// 实验标签推导、token/成本求和、verdict 排序各只有一份 —— 否则同一个实验在终端和网页上
|
|
3
3
|
// 会显示成两个名字 / 两组数。保持环境无关(纯函数,只 type import)。
|
|
4
4
|
/** 明细行排序:失败最靠前(failed > errored > skipped > passed 的紧急程度)。 */
|
|
@@ -47,8 +47,8 @@ export function evalPrefixPredicate(evals) {
|
|
|
47
47
|
}
|
|
48
48
|
/** 无 experimentId 时的兜底标签。 */
|
|
49
49
|
export function fallbackExperimentLabel(result) {
|
|
50
|
-
if (result.
|
|
51
|
-
return displayExperimentName(result.
|
|
50
|
+
if (result.experimentId)
|
|
51
|
+
return displayExperimentName(result.experimentId) ?? result.experimentId;
|
|
52
52
|
if (result.model)
|
|
53
53
|
return `${result.agent}/${result.model}`;
|
|
54
54
|
return result.agent || "ad hoc run";
|
package/dist/shared/types.d.ts
CHANGED
|
@@ -20,6 +20,34 @@ export interface SourceArtifact {
|
|
|
20
20
|
}
|
|
21
21
|
/** 通用清理闭包(setup 返回值 / teardown 的形状),异步同步皆可,统一在 finally 里执行。 */
|
|
22
22
|
export type Cleanup = () => Promise<void> | void;
|
|
23
|
+
/** `ScopedFeedback.progress` 的入参:此刻正在做什么(短命状态,可被后续更新覆盖)。 */
|
|
24
|
+
export interface ProgressUpdate {
|
|
25
|
+
message: string;
|
|
26
|
+
current?: number;
|
|
27
|
+
total?: number;
|
|
28
|
+
}
|
|
29
|
+
/** `ScopedFeedback.diagnostic` 的入参:运行结束后仍应保留的问题(永久事件)。 */
|
|
30
|
+
export interface DiagnosticInput {
|
|
31
|
+
code: string;
|
|
32
|
+
level: "warning" | "error";
|
|
33
|
+
message: string;
|
|
34
|
+
data?: Readonly<Record<string, JsonValue>>;
|
|
35
|
+
/** 并发 attempt 产生同一问题时的去重键;相同 key 折叠成一条并累计次数。 */
|
|
36
|
+
dedupeKey?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 作用域反馈 API(见 docs/feature/experiments/library.md「生命周期代码怎样向这次运行反馈」):
|
|
40
|
+
* sandbox provider、sandbox hook、eval 与 Agent Adapter 从 runner 注入的上下文获得同一套入口。
|
|
41
|
+
* - `progress` 是短命状态:Human profile 更新 active 行,Agent/CI 不逐条打印,不进最终结果;
|
|
42
|
+
* - `diagnostic` 是永久事件:进 Human/Agent/CI 的永久输出流并落进 attempt 的 diagnostics;
|
|
43
|
+
* 即使 level 为 "error" 也不自动改变 verdict(要 errored 抛异常,要 failed 用断言)。
|
|
44
|
+
* 两个方法都不接受 phase / scope / 颜色 / 输出流——runner 知道当前回调属于哪个生命周期阶段,
|
|
45
|
+
* 调用方不能冒充其它阶段。
|
|
46
|
+
*/
|
|
47
|
+
export interface ScopedFeedback {
|
|
48
|
+
progress(update: ProgressUpdate): void;
|
|
49
|
+
diagnostic(input: DiagnosticInput): void;
|
|
50
|
+
}
|
|
23
51
|
/**
|
|
24
52
|
* 可本地化文案:纯字符串,或按 locale 代码(如 "en"、"zh-CN")映射多语言。
|
|
25
53
|
* view 按当前界面语言挑一条,挑不到回退到 en / 第一条。
|
package/dist/tty-line.d.ts
CHANGED
|
@@ -1,6 +1,2 @@
|
|
|
1
|
-
/** live.ts 用来订阅"即将有一行独立诊断消息落地"。返回取消订阅函数。 */
|
|
2
|
-
export declare function onBeforeExternalTerminalWrite(fn: () => void): () => void;
|
|
3
|
-
/** 任何要绕开 Reporter 直接往终端打一行独立诊断消息的地方,写之前都先调用这个。 */
|
|
4
|
-
export declare function beforeExternalTerminalWrite(): void;
|
|
5
1
|
/** process.stderr.write 的替代:文本需自带换行(沿用现有 i18n 字符串的约定)。 */
|
|
6
2
|
export declare function writeStderrLine(text: string): void;
|
package/dist/util.d.ts
CHANGED
|
@@ -10,6 +10,29 @@ export declare function stripComments(code: string): string;
|
|
|
10
10
|
* `e instanceof Error ? e.message : String(e)`——那样用户永远看不出错误发生在哪一行。
|
|
11
11
|
*/
|
|
12
12
|
export declare function formatThrown(e: unknown): string;
|
|
13
|
+
/**
|
|
14
|
+
* 截到第一个换行为止。`formatThrown()` 优先带完整 `.stack`(含本地绝对文件路径的多行调用栈)
|
|
15
|
+
* 给需要按 file:line 定位问题的落盘产物(`EvalResult.error`、`niceeval show`);但机器消费的
|
|
16
|
+
* 单行 envelope(`FailureNotice.reason`、reporter 失败诊断的 `message`……)只要「一层可行动摘要」
|
|
17
|
+
* ——`Error.stack` 的第一行恒为 `name: message`,不含栈帧,直接满足这个要求。完整栈仍然原样
|
|
18
|
+
* 留在调用方各自的落盘字段里,这个函数只负责第二次、更短的那份表达,不是唯一出口。
|
|
19
|
+
*/
|
|
20
|
+
export declare function firstLine(text: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* 把 catch 到的 e 拆成 `AttemptError` 的三段:`message`(一层原因,Error.message,不带 name 前缀、
|
|
23
|
+
* 不拼 stack)、`stack`(完整多行栈,供 `niceeval show` 展开)、`cause`(沿 `e.cause` 取一层结构化
|
|
24
|
+
* 摘要 `{ name?, code?, message }`)。Node 的 `Error.stack` 不展开 cause 链,所以 cause 只能从
|
|
25
|
+
* `e.cause` 单独取。非 Error 值只给 message。
|
|
26
|
+
*/
|
|
27
|
+
export declare function describeError(e: unknown): {
|
|
28
|
+
message: string;
|
|
29
|
+
stack?: string;
|
|
30
|
+
cause?: {
|
|
31
|
+
name?: string;
|
|
32
|
+
code?: string;
|
|
33
|
+
message: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
13
36
|
/** 零填充到 4 位(数据集扇出的 id:sql/0000)。 */
|
|
14
37
|
export declare function pad4(n: number): string;
|
|
15
38
|
/**
|
|
@@ -37,6 +37,7 @@ export function webAgent(options: WebAgentOptions): Agent {
|
|
|
37
37
|
return defineAgent({
|
|
38
38
|
name: "web-agent",
|
|
39
39
|
async send(input, ctx) {
|
|
40
|
+
ctx.progress({ message: "等待 Web Agent" });
|
|
40
41
|
const response = await fetch(`${baseUrl}/api/turn`, {
|
|
41
42
|
method: "POST",
|
|
42
43
|
headers: {
|
|
@@ -54,6 +55,12 @@ export function webAgent(options: WebAgentOptions): Agent {
|
|
|
54
55
|
});
|
|
55
56
|
|
|
56
57
|
if (!response.ok) {
|
|
58
|
+
ctx.diagnostic({
|
|
59
|
+
code: "agent-http-error",
|
|
60
|
+
level: "error",
|
|
61
|
+
message: `Web Agent 返回 HTTP ${response.status}`,
|
|
62
|
+
data: { status: response.status },
|
|
63
|
+
});
|
|
57
64
|
return {
|
|
58
65
|
status: "failed",
|
|
59
66
|
events: [{ type: "error", message: `HTTP ${response.status}` }],
|
|
@@ -107,10 +114,12 @@ interface Agent {
|
|
|
107
114
|
}
|
|
108
115
|
```
|
|
109
116
|
|
|
117
|
+
`progress` 是运行中的短期状态,不落盘;`diagnostic` 是需要运行后回顾的有界 warning/error,会随 Attempt 保存。它们都不能指定 lifecycle phase,也不会自动改变 `Turn.status`。连接失败或响应无法解析时抛出异常;runner 会把它记录为 `errored`,并在终端给出可下钻的 locator。
|
|
118
|
+
|
|
110
119
|
`send` 是唯一要实现的函数。它的签名里只有三个类型,分开看。
|
|
111
120
|
|
|
112
121
|
<Note>
|
|
113
|
-
这里的 `setup` / `teardown` 是 Agent 自己"怎么连自己"的私事(装 CLI、写鉴权配置)。按实验变化的环境准备(装某个实验专属的二进制、预热、跨 attempt 保存状态)不写在 Agent 上,挂在 `sandbox` 字段那个 Sandbox spec 自己的 `.setup()` / `.teardown()` 链式方法上,见 [
|
|
122
|
+
这里的 `setup` / `teardown` 是 Agent 自己"怎么连自己"的私事(装 CLI、写鉴权配置)。按实验变化的环境准备(装某个实验专属的二进制、预热、跨 attempt 保存状态)不写在 Agent 上,挂在 `sandbox` 字段那个 Sandbox spec 自己的 `.setup()` / `.teardown()` 链式方法上,见 [沙箱 provider · 环境钩子](/zh/guides/sandbox-providers#环境钩子)。
|
|
114
123
|
</Note>
|
|
115
124
|
|
|
116
125
|
### 传入:`TurnInput`
|
|
@@ -182,10 +191,17 @@ Adapter 侧的对应义务:按 `requestId` 把裁决交回应用(不要按
|
|
|
182
191
|
|
|
183
192
|
```ts
|
|
184
193
|
interface AgentContext {
|
|
185
|
-
//
|
|
194
|
+
// 每轮都在的反馈与运行上下文
|
|
186
195
|
readonly signal: AbortSignal; // 运行器的超时与取消:透传给你发出的每个请求
|
|
187
196
|
readonly session: AgentSession; // 本条会话线的状态槽
|
|
188
|
-
|
|
197
|
+
progress(update: { message: string; current?: number; total?: number }): void;
|
|
198
|
+
diagnostic(input: {
|
|
199
|
+
code: string;
|
|
200
|
+
level: "warning" | "error";
|
|
201
|
+
message: string;
|
|
202
|
+
data?: Readonly<Record<string, JsonValue>>;
|
|
203
|
+
dedupeKey?: string;
|
|
204
|
+
}): void;
|
|
189
205
|
|
|
190
206
|
// 按接入档位出现的三个(见「接入分三个 Tier」)
|
|
191
207
|
readonly model?: string; // Tier 1 的模型对比:experiment.model 透传;应用接口收模型选择就转发
|
|
@@ -214,7 +230,9 @@ interface AgentSession {
|
|
|
214
230
|
}
|
|
215
231
|
```
|
|
216
232
|
|
|
217
|
-
|
|
233
|
+
Runner 为 Agent 的 `setup`、每次 `send` 和 `teardown` 分别绑定 lifecycle scope。Adapter 只报告当前回调内部的 progress/diagnostic,不能传 phase、颜色或输出流。`progress` 不落盘;diagnostic 会进入 Attempt 的 `result.json`。无法继续时抛错,由 runner 保存结构化 error 并把 Attempt 标为 `errored`。
|
|
234
|
+
|
|
235
|
+
`ctx` 里没有要你检查的标志位。三个档位字段都是"透传"语义:`model`、`flags` 由 experiment 声明、运行器原样递过来,Adapter 只负责随请求转发给应用,不解释它们的含义;`telemetry` 仅在配置了 OTel 接入时出现,`send` 里只需要把 `headers` spread 进请求头——接收端点在 `defineConfig` 里固定、由应用启动时指向,不从这里传,见[OTel 接入](/zh/guides/connect-otel)。`experimentId` 是路径推导出的稳定标识,典型用途是在 Sandbox 的环境钩子里按实验隔离跨 attempt 的状态(缓存目录名、沙箱快照 tag 按它分区),见 [沙箱 provider · 环境钩子](/zh/guides/sandbox-providers#环境钩子)。
|
|
218
236
|
|
|
219
237
|
`session` 是本条会话线的自有状态,[NiceEval](https://niceeval.com/) 对它只承诺一件事:**同一条会话线的每次 `send` 拿到同一个 `ctx.session`,新会话线(eval 的第一轮,或 `t.newSession()` 之后)拿到一个全新的。** 会话续接(`id`/`capture`、`history`)和 HITL 停轮现场(`hold`/`take`)的存取器都在它上面,"第一轮"就是新会话线的自然形态——`id` 是 `undefined`、`history.get()` 是空数组,没有要判断的分支;`state` 是这些存取器之外的逃生舱,框架从不往里写数据。
|
|
220
238
|
|
|
@@ -25,7 +25,7 @@ export default defineExperiment({
|
|
|
25
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
|
-
Experiment 是纯配置数据,没有 `setup` / `teardown` 这类生命周期字段。要按实验准备环境(装二进制、预热、跨 attempt 存取状态),挂在 `sandbox` 字段的 spec 上——`dockerSandbox()` 等工厂返回的对象可以链 `.setup()` / `.teardown()`,见 [
|
|
28
|
+
Experiment 是纯配置数据,没有 `setup` / `teardown` 这类生命周期字段。要按实验准备环境(装二进制、预热、跨 attempt 存取状态),挂在 `sandbox` 字段的 spec 上——`dockerSandbox()` 等工厂返回的对象可以链 `.setup()` / `.teardown()`,见 [沙箱 provider · 环境钩子](/zh/guides/sandbox-providers#环境钩子)。
|
|
29
29
|
|
|
30
30
|
## 矩阵对比
|
|
31
31
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "NiceEval 架构:evals、agents 与 sandboxes"
|
|
3
3
|
sidebarTitle: "概览"
|
|
4
|
-
description: "理解 NiceEval、Adapter 和 Sandbox
|
|
4
|
+
description: "理解 NiceEval、Adapter 和 Sandbox Provider 如何配合,用统一 API 评估任意 AI Agent。"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
[NiceEval](https://niceeval.com/) 的核心设计是把“评测逻辑”与“如何连接被测对象”分开。[NiceEval](https://niceeval.com/) 负责发现、调度、评分和报告;Adapter 负责调用被测系统;Sandbox
|
|
7
|
+
[NiceEval](https://niceeval.com/) 的核心设计是把“评测逻辑”与“如何连接被测对象”分开。[NiceEval](https://niceeval.com/) 负责发现、调度、评分和报告;Adapter 负责调用被测系统;Sandbox Provider 负责隔离文件系统。
|
|
8
8
|
|
|
9
9
|
## 四层架构
|
|
10
10
|
|
|
@@ -15,7 +15,7 @@ NiceEval
|
|
|
15
15
|
↓
|
|
16
16
|
Adapter
|
|
17
17
|
↓
|
|
18
|
-
Subject under test / Sandbox
|
|
18
|
+
Subject under test / Sandbox Provider
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## NiceEval 负责什么
|
|
@@ -53,12 +53,12 @@ Subject under test / Sandbox backend
|
|
|
53
53
|
|
|
54
54
|
<Tabs>
|
|
55
55
|
<Tab title="Docker">
|
|
56
|
-
|
|
56
|
+
本地容器 Provider,适合开发和 CI 中的 coding-agent eval。
|
|
57
57
|
</Tab>
|
|
58
58
|
<Tab title="Vercel Sandbox">
|
|
59
|
-
云端
|
|
59
|
+
云端 Sandbox Provider,适合更强隔离或更大的运行资源。
|
|
60
60
|
</Tab>
|
|
61
|
-
<Tab title="
|
|
61
|
+
<Tab title="第三方 Provider">
|
|
62
62
|
只要实现 `Sandbox` 接口,就可以接入其他沙箱服务。
|
|
63
63
|
</Tab>
|
|
64
64
|
</Tabs>
|
|
@@ -38,7 +38,7 @@ AI 通常按任务选择这些入口:
|
|
|
38
38
|
<Steps>
|
|
39
39
|
<Step title="运行实验">
|
|
40
40
|
```bash
|
|
41
|
-
npx niceeval exp local
|
|
41
|
+
npx niceeval exp local --output agent
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
先读取退出码:`0` 表示所有 Eval 通过;`1` 表示至少一个 Eval 失败或出错;`2` 表示 NiceEval 自身未能完成运行。退出码决定是否继续,控制台文本用于定位原因。
|
|
@@ -59,7 +59,7 @@ AI 通常按任务选择这些入口:
|
|
|
59
59
|
|
|
60
60
|
`--execution` 合并 AI 输出与 trace:标准事件流提供消息、thinking、tool call/result 和 Skill load;OTel 在能够关联时给同一节点补开始时间、耗时、父子关系和错误状态。没有 OTel 时步骤仍完整,只不显示时间。
|
|
61
61
|
|
|
62
|
-
不带证据 flag 时,`show @<id>` 是失败诊断首页。它先列出失败断言的 group、matcher、expected、received
|
|
62
|
+
不带证据 flag 时,`show @<id>` 是失败诊断首页。它先列出失败断言的 group、matcher、expected、received、原因和源码位置,再给执行、生命周期阶段耗时与文件变化摘要。AI 应该先读这一页;只有需要回答“为什么产生这个值”时,才继续打开对应证据。
|
|
63
63
|
|
|
64
64
|
```text
|
|
65
65
|
$ niceeval show @1k2m9qtr
|
|
@@ -89,20 +89,16 @@ AI 通常按任务选择这些入口:
|
|
|
89
89
|
source: evals/memory/swelancer-manager-proposals.eval.ts:40:11
|
|
90
90
|
|
|
91
91
|
execution: 3 events · 0 skill loads · 0 tool calls · 1 AI messages
|
|
92
|
-
timing:
|
|
93
|
-
agent run ▕████████████████████▏ 41.2s
|
|
94
|
-
├─ inference ▕█████░░░░░░░░░░░░░░░▏ 10.1s · "布鲁克林今天大约 24°C,晴。"
|
|
95
|
-
├─ inference ▕░░░░░█████████░░░░░░▏ 18.3s
|
|
96
|
-
└─ inference ▕░░░░░░░░░░░░░░██████▏ 12.4s
|
|
97
|
-
no tool calls
|
|
92
|
+
timing: eval.run 40.4s · scoring.evaluate 0.5s · teardown +0.2s
|
|
98
93
|
|
|
99
94
|
changes · diff unavailable
|
|
100
95
|
reason: this Attempt did not produce workspace file changes
|
|
101
96
|
|
|
102
|
-
full eval source: …/weather/brooklyn/a2/
|
|
97
|
+
full eval source: …/weather/brooklyn/a2/sources.json
|
|
103
98
|
available:
|
|
104
99
|
niceeval show @1k2m9qtr --eval
|
|
105
100
|
niceeval show @1k2m9qtr --execution
|
|
101
|
+
niceeval show @1k2m9qtr --timing
|
|
106
102
|
```
|
|
107
103
|
|
|
108
104
|
`--execution` 把 AI 消息、Skill load、工具调用和工具结果排成一棵执行树。它只展示 Agent 可理解的事件;没有关联到这些事件的 SDK / runtime span 不逐行输出,只报告省略数量并保留 `trace.json` 路径。下面的 Attempt 有 OTel,所以能关联的节点同时带相对时间与耗时:
|
|
@@ -175,9 +171,10 @@ AI 通常按任务选择这些入口:
|
|
|
175
171
|
|
|
176
172
|
| 要回答的问题 | 入口 | 输出必须包含 |
|
|
177
173
|
| --- | --- | --- |
|
|
178
|
-
| 快速判断一次 Attempt 发生了什么 | `niceeval show @<id>` | Eval
|
|
174
|
+
| 快速判断一次 Attempt 发生了什么 | `niceeval show @<id>` | Eval 断言、执行步骤、生命周期阶段耗时摘要、diff 摘要及各块可用性 |
|
|
179
175
|
| Eval 实际检查了什么,哪条 gate / soft 为什么通过或失败 | `niceeval show @<id> --eval` | 运行时 Eval 源码、源码哈希、断言所在行、严重度、分数与原因 |
|
|
180
|
-
| AI
|
|
176
|
+
| AI 做了什么、调用了什么 | `niceeval show @<id> --execution` | 消息、thinking、Skill load、工具调用与结果;有 OTel 时在同一节点附时间、父子关系和错误状态 |
|
|
177
|
+
| 整个 Attempt 的时间花在哪里 | `niceeval show @<id> --timing` | lifecycle → hook/turn → shell → OTel 的统一时间树;出错的 Attempt 标出已知的最深失败节点 |
|
|
181
178
|
| Sandbox 工作区文件变成什么 | `niceeval show @<id> --diff` | 文件摘要、增删行数、具体补丁和原始 diff 路径;无文件工作区时明确 unavailable |
|
|
182
179
|
</Step>
|
|
183
180
|
<Step title="提出假设并修改">
|
|
@@ -195,7 +192,7 @@ AI 通常按任务选择这些入口:
|
|
|
195
192
|
</Step>
|
|
196
193
|
<Step title="局部重跑并验证假设">
|
|
197
194
|
```bash
|
|
198
|
-
npx niceeval exp local weather/brooklyn --force
|
|
195
|
+
npx niceeval exp local weather/brooklyn --output agent --force
|
|
199
196
|
npx niceeval show weather/brooklyn
|
|
200
197
|
```
|
|
201
198
|
|
|
@@ -203,7 +200,7 @@ AI 通常按任务选择这些入口:
|
|
|
203
200
|
</Step>
|
|
204
201
|
<Step title="全量确认没有回归">
|
|
205
202
|
```bash
|
|
206
|
-
npx niceeval exp local --force
|
|
203
|
+
npx niceeval exp local --output agent --force
|
|
207
204
|
npx niceeval show
|
|
208
205
|
```
|
|
209
206
|
|
|
@@ -213,19 +210,22 @@ AI 通常按任务选择这些入口:
|
|
|
213
210
|
|
|
214
211
|
## AI 应该从输出里读什么
|
|
215
212
|
|
|
216
|
-
`
|
|
213
|
+
`--output agent` 运行中只向 stderr 追加低频 checkpoint(存活信号,不是结果数据源),结束时向 stdout 打印一个有界 handoff block——这才是 AI 应该解析的部分:
|
|
217
214
|
|
|
218
215
|
```text
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
216
|
+
NICEEVAL RESULT failed
|
|
217
|
+
summary: 14 passed, 1 failed, 0 errored (0 reused)
|
|
218
|
+
snapshots:
|
|
219
|
+
- .niceeval/local/2026-07-09T10-00-00-000Z-x1f2/
|
|
220
|
+
failures:
|
|
221
|
+
- @1k2m9qtr weather/brooklyn [local]
|
|
222
|
+
gate: tool was never called
|
|
223
|
+
next:
|
|
224
|
+
niceeval show @1k2m9qtr
|
|
225
|
+
niceeval show @1k2m9qtr --execution
|
|
226
226
|
```
|
|
227
227
|
|
|
228
|
-
AI
|
|
228
|
+
AI 应先从 `failures` 选中 Attempt locator,再按证据位执行 `next` 给出的 `niceeval show @<id>` 或对应证据 flag,不要解析运行期间 stderr 上低频追加的 checkpoint 行——那些只用于判断进程是否存活。失败条数超过上限(默认 5 条)时,handoff 只展开前几条并给出总数,完整清单读结果快照。需要机器读取时,结果快照是事实来源:
|
|
229
229
|
|
|
230
230
|
```text
|
|
231
231
|
.niceeval/<experiment>/<快照>/
|
|
@@ -238,7 +238,7 @@ AI 应先从失败项选中 Attempt locator,再按证据位执行 `niceeval sh
|
|
|
238
238
|
```
|
|
239
239
|
|
|
240
240
|
- `snapshot.json` 记录实验身份、运行配置、格式版本和时间。
|
|
241
|
-
- `result.json` 记录该 Attempt
|
|
241
|
+
- `result.json` 记录该 Attempt 的判定、断言、结构化错误、diagnostics 和用量;瞬时 progress 不落盘。
|
|
242
242
|
- `events.json` 是对话与工具调用事件,`trace.json` 是调用链,`diff.json` 是 Sandbox 文件变化。
|
|
243
243
|
- 某类证据不存在时,对应文件不会生成。先以 `show` 的提示为准,不要假设每个目录都有全部文件。
|
|
244
244
|
|
|
@@ -268,12 +268,14 @@ AI 应先从失败项选中 Attempt locator,再按证据位执行 `niceeval sh
|
|
|
268
268
|
|
|
269
269
|
```text
|
|
270
270
|
读取 node_modules/niceeval/INDEX.md,再按索引读取与任务有关的文档。
|
|
271
|
-
运行 npx niceeval exp local
|
|
271
|
+
运行 npx niceeval exp local --output agent,并根据退出码和失败 locator 决定下一步。
|
|
272
272
|
对每个失败的 Eval,从报告选择一个 Attempt locator;再运行 niceeval show @<id>
|
|
273
|
-
|
|
273
|
+
并按问题选择 --eval、--execution、--timing 或 --diff;--timing 从 lifecycle 展开 setup/teardown
|
|
274
|
+
hook、shell 命令、每轮 send 与可关联的 OTel model/tool,回答整个 Attempt 的时间花在哪里。
|
|
275
|
+
写出失败原因的假设,并判断应该修改被测程序、
|
|
274
276
|
Eval,还是实验环境。修改后用 --force 重跑对应 Eval,比较新的判定和证据。
|
|
275
277
|
同一问题连续三轮没有新证据或改善时停止并汇报,不要靠放宽断言碰绿。
|
|
276
|
-
全部局部失败清零后,用 npx niceeval exp local --force 全量验证;退出码 0 才完成。
|
|
278
|
+
全部局部失败清零后,用 npx niceeval exp local --output agent --force 全量验证;退出码 0 才完成。
|
|
277
279
|
```
|
|
278
280
|
|
|
279
281
|
真实 Agent 的运行可能产生费用。实验阶段可以加 `--budget <美元>` 限制本轮累计成本;预算只能限制单次命令,不能替代上面的停止条件。
|
|
@@ -18,6 +18,7 @@ export default defineEval({
|
|
|
18
18
|
reporters?: Reporter[];
|
|
19
19
|
timeoutMs?: number;
|
|
20
20
|
metadata?: Record<string, unknown>;
|
|
21
|
+
async setup(sandbox, ctx) { /* task fixture + progress/diagnostic */ },
|
|
21
22
|
async test(t) { /* interactions + assertions */ },
|
|
22
23
|
});
|
|
23
24
|
```
|
|
@@ -111,6 +112,38 @@ export default defineEval({
|
|
|
111
112
|
|
|
112
113
|
详见 [Fixtures](/zh/guides/fixtures)。
|
|
113
114
|
|
|
115
|
+
## 从 Eval 报告长步骤和诊断
|
|
116
|
+
|
|
117
|
+
`setup` 用于这条 Eval 的任务夹具。第二个参数绑定到 eval setup 阶段;`test(t)` 里的反馈绑定到 eval run 阶段:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
export default defineEval({
|
|
121
|
+
async setup(sandbox, ctx) {
|
|
122
|
+
ctx.progress({ message: "安装 fixture 依赖" });
|
|
123
|
+
await sandbox.runCommand("npm", ["install"]);
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
async test(t) {
|
|
127
|
+
t.progress({ message: "上传隐藏测试", current: 1, total: 2 });
|
|
128
|
+
await t.sandbox.uploadDirectory("../fixtures/project");
|
|
129
|
+
|
|
130
|
+
const preflight = await inspectFixture();
|
|
131
|
+
if (preflight.usedFallback) {
|
|
132
|
+
t.diagnostic({
|
|
133
|
+
code: "fixture-check-degraded",
|
|
134
|
+
level: "warning",
|
|
135
|
+
message: "Fixture 预检使用了备用检查器",
|
|
136
|
+
data: { checker: preflight.checker },
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
await t.send("完成任务");
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`progress` 只更新运行中的短期状态,不进入结果。`diagnostic` 会写进当前 Attempt 的 `result.json`,但不会代替断言或自动改变判定:业务结论仍用 `t.check` / `t.require` / gate;基础设施无法继续时抛出异常。
|
|
146
|
+
|
|
114
147
|
## 命名约定
|
|
115
148
|
|
|
116
149
|
<CardGroup cols={2}>
|
|
@@ -11,9 +11,15 @@ Evals 应该和测试一样进入 CI。它们能在 PR 阶段发现 agent 行为
|
|
|
11
11
|
默认情况下,只要存在失败的 gate,[NiceEval](https://niceeval.com/) 将以非零状态码退出。CI 中通常使用 `--strict`,让失败更明确。
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
npx niceeval exp ci
|
|
14
|
+
NICEEVAL_LANG=en npx niceeval exp ci \
|
|
15
|
+
--output ci \
|
|
16
|
+
--strict \
|
|
17
|
+
--json .niceeval/ci-summary.json \
|
|
18
|
+
--junit .niceeval/junit.xml
|
|
15
19
|
```
|
|
16
20
|
|
|
21
|
+
CI profile 不输出 ANSI、spinner 或动态表格。日志使用单一有序 stdout 流,只追加 start、低频 heartbeat、失败/错误、diagnostic 和最终 result;通过的 Attempt 不逐条打印。
|
|
22
|
+
|
|
17
23
|
## GitHub Actions 示例
|
|
18
24
|
|
|
19
25
|
```yaml
|
|
@@ -32,8 +38,12 @@ jobs:
|
|
|
32
38
|
node-version: 22
|
|
33
39
|
cache: npm
|
|
34
40
|
- run: npm ci
|
|
35
|
-
- run:
|
|
41
|
+
- run: >-
|
|
42
|
+
npx niceeval exp ci --output ci --strict
|
|
43
|
+
--json .niceeval/ci-summary.json
|
|
44
|
+
--junit .niceeval/junit.xml
|
|
36
45
|
env:
|
|
46
|
+
NICEEVAL_LANG: en
|
|
37
47
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
38
48
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
39
49
|
```
|
|
@@ -52,18 +62,19 @@ jobs:
|
|
|
52
62
|
</Step>
|
|
53
63
|
</Steps>
|
|
54
64
|
|
|
55
|
-
## JUnit
|
|
56
|
-
|
|
57
|
-
```ts
|
|
58
|
-
import { defineConfig } from "niceeval";
|
|
59
|
-
import { Console, JUnit } from "niceeval/reporters";
|
|
65
|
+
## JSON、JUnit 和结构化错误
|
|
60
66
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
```text
|
|
68
|
+
niceeval: start total=24 configs=3 concurrency=10 reused=18
|
|
69
|
+
niceeval: errored locator=@12h8m4k1 eval=fixtures/button experiment=ci/codex phase=sandbox.create reason="E2B sandbox allocation failed after 5 attempts"
|
|
70
|
+
niceeval: result=failed passed=23 failed=0 errored=1 reused=18 duration=128s
|
|
71
|
+
niceeval: json=.niceeval/ci-summary.json
|
|
72
|
+
niceeval: junit=.niceeval/junit.xml
|
|
64
73
|
```
|
|
65
74
|
|
|
66
|
-
|
|
75
|
+
退出码是第一层红绿信号;JSON、JUnit 和结果快照是完整机器接口,日志行只用于搜索和 annotation。`errored` 行带 locator、eval/experiment 身份、已知时的正式 phase,以及一层 `reason` 摘要。详细 cause、stack 和 diagnostics 保存在 Attempt 的 `result.json`,可在保留 artifact 后运行 `niceeval show @<locator>` 回顾。
|
|
76
|
+
|
|
77
|
+
CLI 显式要求的 JSON/JUnit 和默认 results artifact 都是 required 输出:写入失败必须让 job 判红,不能只留 warning 后退出 0。想把结果同时上报到 Braintrust 这类实验平台,见 [Reporter 上报](./reporters)。
|
|
67
78
|
|
|
68
79
|
## 只检查发现
|
|
69
80
|
|
|
@@ -80,7 +91,7 @@ npx niceeval list
|
|
|
80
91
|
## 控制并发
|
|
81
92
|
|
|
82
93
|
```bash
|
|
83
|
-
npx niceeval exp ci --max-concurrency 2
|
|
94
|
+
npx niceeval exp ci --output ci --max-concurrency 2
|
|
84
95
|
```
|
|
85
96
|
|
|
86
97
|
标准 GitHub-hosted runner 上,sandbox eval 并发不宜过高。远程 HTTP eval 可以按服务限流能力调高。
|