niceeval 0.11.1 → 0.11.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.
Files changed (179) hide show
  1. package/INDEX.md +1 -0
  2. package/README.md +4 -2
  3. package/README.zh.md +4 -2
  4. package/dist/agents/types.d.ts +2 -2
  5. package/dist/o11y/types.d.ts +3 -3
  6. package/dist/report/assets/colors.d.ts +1 -1
  7. package/dist/report/assets/colors.js +1 -1
  8. package/dist/report/components/attempt-detail/AttemptSource.js +1 -2
  9. package/dist/report/components/attempt-detail/compute.d.ts +4 -4
  10. package/dist/report/components/attempt-detail/compute.js +6 -6
  11. package/dist/report/components/attempt-detail/faces.d.ts +1 -1
  12. package/dist/report/components/attempt-detail/faces.js +3 -3
  13. package/dist/report/components/entity-lists/AttemptList.d.ts +3 -3
  14. package/dist/report/components/entity-lists/AttemptList.js +3 -3
  15. package/dist/report/components/entity-lists/ExperimentList.js +2 -2
  16. package/dist/report/components/entity-lists/compute.js +1 -1
  17. package/dist/report/components/entity-lists/faces.js +1 -1
  18. package/dist/report/components/entity-lists/index.d.ts +1 -1
  19. package/dist/report/components/entity-lists/index.js +1 -1
  20. package/dist/report/components/metric-views/chart-math.d.ts +2 -2
  21. package/dist/report/components/metric-views/chart-math.js +2 -2
  22. package/dist/report/components/metric-views/compute.d.ts +2 -3
  23. package/dist/report/components/metric-views/compute.js +3 -4
  24. package/dist/report/components/shared.d.ts +1 -1
  25. package/dist/report/components/site-components/compute.d.ts +5 -5
  26. package/dist/report/components/site-components/compute.js +6 -6
  27. package/dist/report/components/site-components/index.d.ts +7 -7
  28. package/dist/report/components/site-components/index.js +7 -7
  29. package/dist/report/components/site-components/scope-warnings.d.ts +1 -1
  30. package/dist/report/components/site-components/scope-warnings.js +3 -3
  31. package/dist/report/components/site-components/snapshot-diagnostics.js +1 -1
  32. package/dist/report/components/summaries/compute.d.ts +1 -1
  33. package/dist/report/components/summaries/compute.js +2 -2
  34. package/dist/report/components/summaries/faces.js +1 -1
  35. package/dist/report/components/summaries/index.d.ts +1 -1
  36. package/dist/report/components/summaries/index.js +1 -1
  37. package/dist/report/index.js +1 -1
  38. package/dist/report/model/aggregate.d.ts +2 -2
  39. package/dist/report/model/aggregate.js +2 -2
  40. package/dist/report/model/format.d.ts +2 -2
  41. package/dist/report/model/format.js +2 -2
  42. package/dist/report/model/metrics.js +3 -3
  43. package/dist/report/model/types.d.ts +11 -12
  44. package/dist/results/locator.js +1 -1
  45. package/dist/results/select.d.ts +4 -4
  46. package/dist/results/select.js +5 -5
  47. package/dist/results/types.d.ts +8 -8
  48. package/dist/runner/types.d.ts +14 -14
  49. package/dist/sandbox/resolve.d.ts +1 -1
  50. package/dist/sandbox/types.d.ts +1 -1
  51. package/docs-site/zh/reference/builtin-agents.mdx +21 -0
  52. package/docs-site/zh/reference/define-agent.mdx +1 -1
  53. package/docs-site/zh/reference/report-components.mdx +156 -74
  54. package/docs-site/zh/tutorials/custom-reports.mdx +112 -22
  55. package/docs-site/zh/tutorials/sandbox-agent.mdx +3 -4
  56. package/docs-site/zh/tutorials/sandbox-providers.mdx +15 -15
  57. package/docs-site/zh/tutorials/theming.mdx +169 -0
  58. package/package.json +2 -1
  59. package/src/agents/ai-sdk.test.ts +1 -1
  60. package/src/agents/bub-install-spec.ts +26 -8
  61. package/src/agents/bub.ts +40 -15
  62. package/src/agents/builtin.ts +11 -1
  63. package/src/agents/coding-cli-versions.ts +67 -0
  64. package/src/agents/hermes.ts +235 -0
  65. package/src/agents/index.ts +4 -0
  66. package/src/agents/langgraph.test.ts +1 -1
  67. package/src/agents/manifest.ts +1 -1
  68. package/src/agents/openai-compat.test.ts +1 -1
  69. package/src/agents/openclaw.ts +103 -24
  70. package/src/agents/opencode.ts +183 -0
  71. package/src/agents/sdk-streams.test.ts +1 -1
  72. package/src/agents/shared.ts +13 -2
  73. package/src/agents/types.ts +2 -2
  74. package/src/context/session.test.ts +1 -1
  75. package/src/context/session.ts +1 -1
  76. package/src/o11y/cost.ts +1 -1
  77. package/src/o11y/parsers/bub.test.ts +1 -1
  78. package/src/o11y/parsers/bub.ts +1 -1
  79. package/src/o11y/parsers/codex.test.ts +1 -1
  80. package/src/o11y/parsers/codex.ts +1 -1
  81. package/src/o11y/parsers/hermes.ts +198 -0
  82. package/src/o11y/parsers/openclaw.ts +25 -3
  83. package/src/o11y/parsers/opencode.ts +295 -0
  84. package/src/o11y/types.ts +3 -3
  85. package/src/report/assets/colors.ts +1 -1
  86. package/src/report/assets/styles.css +17 -15
  87. package/src/report/components/attempt-detail/AttemptAssertions.tsx +1 -1
  88. package/src/report/components/attempt-detail/AttemptConversation.tsx +1 -1
  89. package/src/report/components/attempt-detail/AttemptDiagnostics.tsx +1 -1
  90. package/src/report/components/attempt-detail/AttemptDiff.tsx +1 -1
  91. package/src/report/components/attempt-detail/AttemptError.tsx +1 -1
  92. package/src/report/components/attempt-detail/AttemptFixPrompt.tsx +1 -1
  93. package/src/report/components/attempt-detail/AttemptSource.tsx +2 -3
  94. package/src/report/components/attempt-detail/AttemptSummary.tsx +1 -1
  95. package/src/report/components/attempt-detail/AttemptTimeline.tsx +1 -1
  96. package/src/report/components/attempt-detail/AttemptTrace.tsx +1 -1
  97. package/src/report/components/attempt-detail/UsageTable.tsx +1 -1
  98. package/src/report/components/attempt-detail/attempt-components.test.tsx +1 -1
  99. package/src/report/components/attempt-detail/compute.ts +6 -6
  100. package/src/report/components/attempt-detail/faces.ts +3 -3
  101. package/src/report/components/attempt-detail/index.tsx +1 -1
  102. package/src/report/components/entity-lists/AttemptList.tsx +3 -3
  103. package/src/report/components/entity-lists/ExperimentList.tsx +2 -2
  104. package/src/report/components/entity-lists/compute.ts +1 -1
  105. package/src/report/components/entity-lists/faces.ts +1 -1
  106. package/src/report/components/entity-lists/index.tsx +1 -1
  107. package/src/report/components/metric-views/DeltaTable.tsx +1 -2
  108. package/src/report/components/metric-views/StabilityMatrix.tsx +1 -1
  109. package/src/report/components/metric-views/chart-math.test.ts +1 -1
  110. package/src/report/components/metric-views/chart-math.ts +2 -2
  111. package/src/report/components/metric-views/compute.ts +3 -4
  112. package/src/report/components/shared.ts +1 -1
  113. package/src/report/components/site-components/CopyFixPrompt.tsx +1 -1
  114. package/src/report/components/site-components/HeroCard.tsx +1 -1
  115. package/src/report/components/site-components/PoweredBy.tsx +1 -1
  116. package/src/report/components/site-components/ScopeWarnings.tsx +1 -1
  117. package/src/report/components/site-components/SnapshotDiagnostics.tsx +1 -1
  118. package/src/report/components/site-components/TraceWaterfall.tsx +1 -1
  119. package/src/report/components/site-components/compute.ts +6 -6
  120. package/src/report/components/site-components/index.tsx +7 -7
  121. package/src/report/components/site-components/scope-warnings.ts +3 -3
  122. package/src/report/components/site-components/snapshot-diagnostics.ts +1 -1
  123. package/src/report/components/summaries/ScopeSummary.tsx +2 -2
  124. package/src/report/components/summaries/compute.ts +2 -2
  125. package/src/report/components/summaries/faces.ts +1 -1
  126. package/src/report/components/summaries/index.tsx +1 -1
  127. package/src/report/index.ts +1 -1
  128. package/src/report/model/aggregate.ts +2 -2
  129. package/src/report/model/format.ts +2 -2
  130. package/src/report/model/metrics.ts +3 -3
  131. package/src/report/model/types.ts +12 -13
  132. package/src/results/annotated-source.test.ts +1 -1
  133. package/src/results/attempt-evidence.test.ts +1 -1
  134. package/src/results/copy.ts +3 -3
  135. package/src/results/format.ts +3 -3
  136. package/src/results/host-equivalence.test.ts +6 -6
  137. package/src/results/index.ts +1 -1
  138. package/src/results/locator.test.ts +2 -2
  139. package/src/results/locator.ts +1 -1
  140. package/src/results/open.ts +2 -2
  141. package/src/results/publish.ts +1 -1
  142. package/src/results/results.test.ts +6 -323
  143. package/src/results/select.test.ts +438 -0
  144. package/src/results/select.ts +6 -6
  145. package/src/results/skipped-notice.ts +1 -1
  146. package/src/results/truncate.ts +2 -2
  147. package/src/results/types.ts +9 -9
  148. package/src/results/writer.ts +4 -4
  149. package/src/runner/attempt.test.ts +2 -2
  150. package/src/runner/attempt.ts +6 -6
  151. package/src/runner/eval-source.test.ts +1 -1
  152. package/src/runner/eval-source.ts +1 -1
  153. package/src/runner/reporters/artifacts.ts +1 -1
  154. package/src/runner/run.ts +2 -2
  155. package/src/runner/timing.ts +1 -1
  156. package/src/runner/types.ts +14 -14
  157. package/src/sandbox/docker-agent-image.ts +36 -0
  158. package/src/sandbox/e2b-agent-template.test.ts +6 -21
  159. package/src/sandbox/e2b-agent-template.ts +44 -11
  160. package/src/sandbox/index.ts +8 -0
  161. package/src/sandbox/official-baselines.test.ts +175 -0
  162. package/src/sandbox/resolve.ts +1 -1
  163. package/src/sandbox/types.ts +1 -1
  164. package/src/scoring/diff.ts +1 -1
  165. package/src/scoring/types.ts +1 -1
  166. package/src/shared/facts.ts +1 -1
  167. package/src/show/index.ts +1 -1
  168. package/src/show/render.ts +1 -1
  169. package/src/show/show.test.ts +3 -3
  170. package/src/view/app/App.tsx +3 -1
  171. package/src/view/client-dist/app.css +1 -1
  172. package/src/view/client-dist/app.js +1 -1
  173. package/src/view/data.test.ts +2 -2
  174. package/src/view/data.ts +1 -1
  175. package/src/view/site-base.test.ts +51 -0
  176. package/src/view/site.ts +16 -0
  177. package/src/view/styles.css +1 -2
  178. package/src/view/template.html +1 -0
  179. package/src/view/view-report.test.ts +1 -1
@@ -10,7 +10,7 @@ import type { AttemptLocator } from "../results/locator.ts";
10
10
  /**
11
11
  * 解析后运行配置的**穷尽可序列化投影**——记录这次运行实际生效的值,不是原始 `ExperimentDef`
12
12
  * (函数与 hooks 无法忠实落盘,存「原样」只能存谎)。`model` 与 `agent` 只在快照顶层存在,
13
- * 这里不复制(见 docs/feature/results/architecture.md「snapshot.json」)。
13
+ * 这里不复制(见 docs/feature/record/architecture.md「snapshot.json」)。
14
14
  */
15
15
  export interface ExperimentRunInfo {
16
16
  description?: string;
@@ -38,14 +38,14 @@ export interface SandboxRunInfo {
38
38
  fingerprint?: string;
39
39
  }
40
40
  /**
41
- * 一次 attempt 的生命周期词表——**全仓唯一一套**(见 docs/feature/results/architecture.md
41
+ * 一次 attempt 的生命周期词表——**全仓唯一一套**(见 docs/feature/record/architecture.md
42
42
  * 「result.json」)。计时(`phases[].name`)、错误归因(`error.phase`)、诊断归属
43
43
  * (`diagnostics[].phase`)、live 展示与 agent/ci envelope 的 `phase=` 都使用这同一个闭集,
44
44
  * 不存在第二套词表。phase 是 runner 对真实 lifecycle 的单方面投影,不是 adapter / sandbox
45
45
  * provider / 用户 hook 能直接设置的公共字段。
46
46
  */
47
47
  export type LifecyclePhase = "experiment.setup" | "experiment.teardown" | "sandbox.queue" | "sandbox.create" | "sandbox.setup" | "workspace.baseline" | "eval.setup" | "agent.setup" | "telemetry.configure" | "eval.run" | "agent.run" | "workspace.diff" | "scoring.evaluate" | "telemetry.collect" | "eval.teardown" | "agent.teardown" | "sandbox.teardown" | "sandbox.suspend" | "sandbox.stop";
48
- /** TimingNode 的种类(见 docs/feature/results/architecture.md「result.json」)。 */
48
+ /** TimingNode 的种类(见 docs/feature/record/architecture.md「result.json」)。 */
49
49
  export type TimingNodeKind = "hook" | "turn" | "command" | "provider" | "operation";
50
50
  /**
51
51
  * Runner 直接观察到的阶段内时间树节点;只供单 attempt 诊断,不做跨实验聚合。
@@ -77,7 +77,7 @@ export interface TimingNode {
77
77
  exitCode?: number;
78
78
  };
79
79
  }
80
- /** Runner 阶段计时,按执行顺序;只记录实际发生的阶段(见 docs/feature/results/architecture.md)。 */
80
+ /** Runner 阶段计时,按执行顺序;只记录实际发生的阶段(见 docs/feature/record/architecture.md)。 */
81
81
  export interface PhaseTiming {
82
82
  name: LifecyclePhase;
83
83
  /** 阶段耗时;失败阶段计到抛错或超时中断时。 */
@@ -88,7 +88,7 @@ export interface PhaseTiming {
88
88
  children?: TimingNode[];
89
89
  }
90
90
  /**
91
- * `commands.json` 的一条落盘记录(见 docs/feature/results/architecture.md「commandsjson」):
91
+ * `commands.json` 的一条落盘记录(见 docs/feature/record/architecture.md「commandsjson」):
92
92
  * 公开 `Sandbox.runCommand()` / `runShell()` 的最外层调用返回非零 `exitCode` 时,Runner 在
93
93
  * `CommandResult` 交还调用方**之前**登记的完整证据——Eval 后续即使只把 `.slice(-N)` 拼进
94
94
  * 异常消息,这份证据仍然完整。只记非零退出;成功命令的输出不进第二份 artifact,provider 内部
@@ -110,7 +110,7 @@ export interface FailedCommandEvidence {
110
110
  /** `commands.json` 的落盘形状。 */
111
111
  export type CommandsArtifact = FailedCommandEvidence[];
112
112
  /**
113
- * 使 attempt 无法正常完成的唯一致命执行错误(见 docs/feature/results/architecture.md 的
113
+ * 使 attempt 无法正常完成的唯一致命执行错误(见 docs/feature/record/architecture.md 的
114
114
  * `AttemptError`)。`message` 是人可读的一层原因(不拼整份 SDK response);完整 stack 单放
115
115
  * `stack`,`niceeval show @locator` 首页展开、终端即时反馈不整段打印。榜单只显示 `message`。
116
116
  */
@@ -131,7 +131,7 @@ export interface AttemptError {
131
131
  };
132
132
  }
133
133
  /**
134
- * 不一定改变 verdict、但运行后仍需回顾的有界诊断(见 docs/feature/results/architecture.md 的
134
+ * 不一定改变 verdict、但运行后仍需回顾的有界诊断(见 docs/feature/record/architecture.md 的
135
135
  * `DiagnosticRecord`)。`level` 表达消息严重度,不是 verdict 的别名 —— passed / failed / errored
136
136
  * 任一 verdict 都可以带 cleanup / teardown 诊断。与运行级的 `DiagnosticNotice` 不同,这条挂在单个
137
137
  * attempt 结果上、随 `result.json` 落盘。
@@ -173,7 +173,7 @@ export interface EvalResult {
173
173
  /**
174
174
  * 题型:`defineEval` → `"pass"`,`defineScoreEval` → `"points"`,定义期事实,与
175
175
  * `EvalDescriptor.scoring` 同源。省略等价于 `"pass"`——兼容此字段引入前写入的落盘与未声明它的
176
- * 第三方 harness(见 docs/feature/results/architecture.md「result.json」)。
176
+ * 第三方 harness(见 docs/feature/record/architecture.md「result.json」)。
177
177
  */
178
178
  scoring?: EvalScoring;
179
179
  /**
@@ -190,10 +190,10 @@ export interface EvalResult {
190
190
  /**
191
191
  * sandbox hook / agent setup·send·teardown 经 `ctx.fact()` 上报的运行事实(同 attempt 内
192
192
  * 后写覆盖先写)。中性环境观测,不参与 verdict / 评分 / 指纹。见
193
- * docs/feature/results/architecture.md#facts运行事实。
193
+ * docs/feature/record/architecture.md#facts运行事实。
194
194
  */
195
195
  facts?: Record<string, string | number | boolean>;
196
- /** Runner 阶段计时,按执行顺序;只记录实际发生的阶段(见 docs/feature/results/architecture.md)。 */
196
+ /** Runner 阶段计时,按执行顺序;只记录实际发生的阶段(见 docs/feature/record/architecture.md)。 */
197
197
  phases?: PhaseTiming[];
198
198
  skipReason?: string;
199
199
  events?: StreamEvent[];
@@ -228,7 +228,7 @@ export interface EvalResult {
228
228
  artifactBase?: string;
229
229
  /**
230
230
  * writer 实际写出的按需 artifact 词干列表(词表与全部横切属性单源在
231
- * docs/feature/results/architecture.md「证据 registry」,如 ["commands", "events", "sources"])。
231
+ * docs/feature/record/architecture.md「证据 registry」,如 ["commands", "events", "sources"])。
232
232
  * 省略等价于空列表;携带条目原样携带。读取面的懒加载语义(缺失返回 null)独立成立,
233
233
  * 本字段只服务「不 stat 磁盘就知道有什么」的消费方。
234
234
  */
@@ -237,7 +237,7 @@ export interface EvalResult {
237
237
  /** `snapshot.json` 的格式标记;把 niceeval 报告和其它工具的同名文件区分开。 */
238
238
  export declare const RESULTS_FORMAT = "niceeval.results";
239
239
  /**
240
- * 结果格式版本,只在破坏兼容读取时递增;读取器只认相同版本。见 docs/feature/results/architecture.md。
240
+ * 结果格式版本,只在破坏兼容读取时递增;读取器只认相同版本。见 docs/feature/record/architecture.md。
241
241
  * `5`(见 memory 的 attempt-locator-and-source-dedup 条目)= result.json 新增 `locator` 字段;
242
242
  * `sources.json` 从逐 attempt 内联全量内容改为「attempt 级引用 + 快照级 `sources/<sha256>.json`
243
243
  * 去重仓库」,`AttemptHandle.sources()` 的公开返回形状不变(仍是 `SourceArtifact[] | null`)。
@@ -255,7 +255,7 @@ export declare const RESULTS_FORMAT = "niceeval.results";
255
255
  * 旧版快照按格式规则整份判为不兼容并在扫描时列为占位条目,不迁移不降级。
256
256
  */
257
257
  export declare const RESULTS_SCHEMA_VERSION = 9;
258
- /** 一次 Invocation 的纯运行时内存聚合(reporter 契约用);落盘格式契约在 niceeval/results 的 SnapshotMeta / AttemptRecord,见 docs/feature/results/architecture.md。不携带顶层 `agent`/`model`——一次 Invocation 可能横跨多个 `(agent, model, flags)` 配置,塞一个顶层单值只能代表其中一份配置;需要时从 `results` 里逐条 `EvalResult.agent`/`.model` 去重派生。 */
258
+ /** 一次 Invocation 的纯运行时内存聚合(reporter 契约用);落盘格式契约在 niceeval/results 的 SnapshotMeta / AttemptRecord,见 docs/feature/record/architecture.md。不携带顶层 `agent`/`model`——一次 Invocation 可能横跨多个 `(agent, model, flags)` 配置,塞一个顶层单值只能代表其中一份配置;需要时从 `results` 里逐条 `EvalResult.agent`/`.model` 去重派生。 */
259
259
  export interface InvocationSummary {
260
260
  /** 项目名(来自 config.name),透传给 `niceeval view` 顶部 hero 显示。 */
261
261
  name?: LocalizedText;
@@ -474,7 +474,7 @@ export interface ExperimentHookContext extends ScopedFeedback {
474
474
  * 第三条反馈通道:上报整场实验的环境观测,与 `completedAt` 同批在快照封口补写进
475
475
  * `SnapshotMeta.facts`。key 匹配 `[a-z0-9._-]{1,64}`,value 是标量;同 key 后写覆盖先写,
476
476
  * 非法 key 或非标量 value 抛错。不影响判定,不参与 verdict / 评分 / 指纹。形状与归属语义见
477
- * docs/feature/results/architecture.md#facts运行事实。`niceeval exp --teardown` 的独立收尾
477
+ * docs/feature/record/architecture.md#facts运行事实。`niceeval exp --teardown` 的独立收尾
478
478
  * 路径不派发 attempt、不落任何 Snapshot,没有 `SnapshotMeta.facts` 可写——该路径下这个方法仍然
479
479
  * 校验入参(非法 key / 非标量 value 照样抛错),校验通过后丢弃写入(no-op:诚实优于
480
480
  * 静默——非法调用照样报错、不被这条路径悄悄吞掉,但也不假装有地方落盘),见 cli.ts 的
@@ -39,7 +39,7 @@ export declare function sandboxRecommendedConcurrency(opt: SandboxOption | undef
39
39
  /**
40
40
  * ExperimentRunInfo.sandbox 的投影:provider 名 + 公开参数(镜像/快照/模板/runtime)+ 配置指纹。
41
41
  * 参数只经这个投影落盘——token、凭据路径永不进来;defineSandbox 自定义 provider 未实现
42
- * `publicConfig()` 时只落 provider 名(见 docs/feature/results/architecture.md)。
42
+ * `publicConfig()` 时只落 provider 名(见 docs/feature/record/architecture.md)。
43
43
  */
44
44
  export declare function sandboxRunInfo(opt: SandboxOption | undefined): {
45
45
  provider: string;
@@ -70,7 +70,7 @@ export interface SandboxHookContext extends ScopedFeedback {
70
70
  * 第三条反馈通道:上报本次运行的中性环境观测,落进 `AttemptRecord.facts`。key 匹配
71
71
  * `[a-z0-9._-]{1,64}`,value 是标量;同 key 后写覆盖先写,非法 key 或非标量 value 抛错。
72
72
  * 不影响判定,不参与 verdict / 评分 / 指纹。形状与归属语义见
73
- * docs/feature/results/architecture.md#facts运行事实。
73
+ * docs/feature/record/architecture.md#facts运行事实。
74
74
  */
75
75
  fact(key: string, value: string | number | boolean): void;
76
76
  }
@@ -259,6 +259,27 @@ skills?: SkillSpec[];
259
259
  装进 Sandbox 的 Skill(本地目录/文件,或 repo + 可钉 ref + 可选启用集)。
260
260
  落在 `.agents/skills/<name>/`,并写一段发现指引进 AGENTS.md(bub 没有原生 Skill 加载机制)。
261
261
 
262
+ #### `version`
263
+
264
+ ```ts
265
+ version?: string;
266
+ ```
267
+
268
+ 装哪一版 Bub(PyPI 版本号,如 `"0.4.0"`)。省略时用 NiceEval 钉的默认版本;
269
+ 永远是确定版本,不装 latest —— 被测对象的版本要能从实验配置读出来。
270
+
271
+ #### `otelPlugin`
272
+
273
+ ```ts
274
+ otelPlugin?: string;
275
+ ```
276
+
277
+ OTel tape store 插件的 git 依赖(时间轨的来源)。省略时用 NiceEval 钉的默认 pin。
278
+
279
+ 插件与 Bub 的 tape 协议同代:默认 pin 从 `bub.tape` 取类型,要求 Bub ≥ 0.3.10;更早的
280
+ 插件 commit 按 republic 的类型校验,配 Bub ≤ 0.3.9。配错代不会安装失败,而是 span 全被拒、
281
+ 时间轨静默为空 —— 所以往回钉 `version` 时必须同批钉配套的插件 commit。
282
+
262
283
  #### `pythonPlugins`
263
284
 
264
285
  ```ts
@@ -331,7 +331,7 @@ fact(key: string, value: string | number | boolean): void;
331
331
  第三条反馈通道:上报本次运行的中性环境观测(如实际生效的 agent 配置、缓存命中状态)。
332
332
  落进 `AttemptRecord.facts` 成为一等观测量;不影响 Turn status 或 verdict,不参与
333
333
  verdict / 评分 / 指纹。key 匹配 `[a-z0-9._-]{1,64}`,value 是标量;同 key 后写覆盖先写,
334
- 非法 key 或非标量 value 抛错。形状与归属语义见 docs/feature/results/architecture.md#facts运行事实。
334
+ 非法 key 或非标量 value 抛错。形状与归属语义见 docs/feature/record/architecture.md#facts运行事实。
335
335
 
336
336
  #### `log`
337
337
 
@@ -20,13 +20,12 @@ description: "报告文件里能摆的全部官方双面组件:每个组件回
20
20
  | 实体列表 | 失败列表 | Failure list | `FailureList` | `AttemptList` 的成品过滤:只列 failed / errored,按开始时间倒序 |
21
21
  | 指标图形 | 指标表 | Metric table | `MetricTable` | 一个可配置行维度;每格是一个聚合指标值 |
22
22
  | 指标图形 | 指标矩阵 | Metric matrix | `MetricMatrix` | 两个可配置维度的交叉格;每格是一个聚合指标值 |
23
- | 指标图形 | 分组条形图 | Grouped bar chart | `MetricBars` | 两个可配置维度形成分组和系列;每根条是一个聚合指标值 |
24
23
  | 指标图形 | 成绩单 | Scoreboard | `Scoreboard` | 每行一个可配置维度值;按固定题集算总分和分科得分 |
25
- | 指标图形 | 指标散点图 | Metric scatter plot | `MetricScatter` | 每点一个可配置维度值,通常是 experiment;坐标是两个聚合指标值 |
26
- | 指标图形 | 指标趋势图 | Metric line chart | `MetricLine` | 每点一个 experiment;横轴是数值配置变量,纵轴是聚合指标值 |
27
- | 指标图形 | 成对差异表 | Paired delta table | `DeltaTable` | 每行一对 experiment 或结果快照;格内是指标值及差值 |
24
+ | 指标图形 | 成对差异表 | Paired delta table | `DeltaTable` | 每行一道评估用例、每组列一个条件;格内是各条件的值与对基准的差 |
25
+ | 指标图形 | 稳定性矩阵 | Stability matrix | `StabilityMatrix` | 每行一道评估用例、每列一个条件;格内是历史全部执行的判定计数 |
26
+ | 图表 | 图表 | Chart | `LineChart` / `BarChart` / `AreaChart` / `ScatterChart` / `ComposedChart` | 一个坐标系;`XAxis` / `YAxis` 定轴,`Line` / `Bar` / `Area` / `Scatter` 各画一组数据 |
28
27
 
29
- `Row`、`Col`、`Grid`、`Section`、`Stat`、`Text`、`Style`、`Table`、`Tabs` 和 `Tab` 是十个排版原语,不计算结果,因此不算一种分析图。它们的中文统称依次是行、列、网格、分节、摘要项、文本、样式、表格、标签页和标签;代码里始终使用 API 名。
28
+ `Row`、`Col`、`Grid`、`Section`、`Stat`、`Text`、`Markdown`、`Style`、`Table`、`Tabs` 和 `Tab` 是十一个排版原语,不计算结果,因此不算一种分析图。它们的中文统称依次是行、列、网格、分节、摘要项、文本、正文、样式、表格、标签页和标签;代码里始终使用 API 名。
30
29
 
31
30
  所有组件共守同一套契约,下面不再逐个重复:
32
31
 
@@ -42,12 +41,18 @@ description: "报告文件里能摆的全部官方双面组件:每个组件回
42
41
 
43
42
  ```tsx
44
43
  // 省略 input/data:用宿主注入的当前 Scope 自动取数
45
- <MetricTable rows="agent" columns={[endToEndPassRate, costUSD]} filter />
44
+ <MetricTable filter>
45
+ <Rows dimension="agent" sort={endToEndPassRate} />
46
+ <Column metric={endToEndPassRate} />
47
+ <Column metric={costUSD} />
48
+ </MetricTable>
46
49
 
47
50
  // 自己先算好数据再传:中间可以插入任意 JavaScript 加工
48
51
  <MetricTable data={await metricTableData(scope, { rows: "agent", columns: [endToEndPassRate, costUSD] })} filter />
49
52
  ```
50
53
 
54
+ 要取哪些数据写在子标签里:一行一个 `<Column>`,行维度一个 `<Rows>`,图表的轴和每组数据也各是一个子标签。要加一列就加一行 JSX,不用去改一个越来越长的选项数组;每个子标签还能带自己的设置(某一列的显示名、某条数据的误差线、某根柱的强调色),不用在外层再摆一张按名字对应的表。
55
+
51
56
  两种写法产出完全相同;同一个组件同时给出 `data` 和取数选项会报错,两者二选一。所有计算函数的第一个参数都是 Scope(或手工挑的结果快照数组);产出的数据都是普通可序列化 JSON,可以先存下来、传给别的进程,或者原样喂给对应组件的 `data` prop。`niceeval/report/react` 入口的同名组件只收 `data`,不做取数——那一层是纯 React 渲染,见[自定义报告](/zh/tutorials/custom-reports)。
52
57
 
53
58
  `evals` 是数据获取阶段唯一的过滤选项:评估用例 id 前缀,与 CLI 位置参数同语义,在聚合**之前**收窄题集。实体列表(`ExperimentList` / `EvalList` / `AttemptList`)不设这个选项——它们逐实体成行,取数后用普通数组 `.filter()` 收窄,效果和任何专门选项完全一样。
@@ -74,17 +79,41 @@ description: "报告文件里能摆的全部官方双面组件:每个组件回
74
79
  codex 78.0/100 40/50 38/50 │
75
80
  ```
76
81
 
82
+ ## 正文(`Markdown`)
83
+
84
+ 方法学、口径说明、脚注这类要成段写的字,用 `Markdown` 写,不用把每一段拆成一串 `Text`:
85
+
86
+ ```tsx
87
+ <Markdown>{`
88
+ ## 方法学
89
+
90
+ 每个配置跑 **3 轮**,取端到端通过率。成本按网关实测优先,估算兜底。
91
+
92
+ - 题集固定为 \`security/\` 下 12 题,未跑到的题按 0 分留在分母
93
+ - 超时的 Attempt 耗时记 \`null\`,不计入均值
94
+
95
+ 详细口径见[指标说明](https://example.com/metrics)。
96
+ `}</Markdown>
97
+ ```
98
+
99
+ 正文按 CommonMark 解析,另支持删除线、任务列表和自动链接。标题、列表、代码块、引用、强调、链接、图片在网页面是对应的 HTML 标签,在终端面是缩进、前缀和折行——两个面读同一棵解析结果,不是网页渲染一遍、终端再解析一遍。终端里代码块原样输出不折行(折行的代码不能复制执行),图片打成 `alt (链接)`,链接打成 `文字 (链接)`。
100
+
101
+ 三件事它不做:
102
+
103
+ - **不渲染正文里写的 HTML 标签**,一律转义成看得见的文字。报告是要发出去的静态站,正文里的字符串可能来自结果数据。
104
+ - **不解析 Markdown 表格**,遇到直接报错让你改用 `Table`。表格的列宽要按终端显示宽度算(中文记 2 列、放不下先折行再丢列并标注丢了几列),Markdown 表格绕开这套会在终端撕歪。
105
+ - **不把 `@1k2m9qrs` 变成链接**,正文里的 Attempt 定位符就是普通文字。要能点开的证据链接,用带定位符的组件。
106
+
107
+ 正文和标题一样支持多语言:传 `{ en: "…", "zh-CN": "…" }`,站点切语言时正文跟着切;只写一种语言就所有语言都显示那一种。内容从哪来是普通 JavaScript 的事——写在文件里、`readFile` 读一份 `METHODOLOGY.md`、或按数据拼出来都行。
108
+
109
+ 一个标记都不想解析时用 `Text`:一个负责有格式的散文,一个负责这段字原样打。
110
+
77
111
  ## 表格(`Table`)
78
112
 
79
113
  第六个排版原语:官方组件摆不出的表,用它摆。它不计算任何东西——列由你定,格子是你算好的显示值,它只负责把两个面都排整齐。
80
114
 
81
115
  ```tsx
82
116
  <Table
83
- columns={[
84
- { key: "eval", header: "题目" },
85
- { key: "pass", header: "通过率", align: "right" },
86
- { key: "cost", header: "成本", align: "right" },
87
- ]}
88
117
  rows={[
89
118
  {
90
119
  key: "记忆/写缓存",
@@ -97,7 +126,11 @@ description: "报告文件里能摆的全部官方双面组件:每个组件回
97
126
  cells: { eval: "浏览/表单填写", pass: null, cost: null },
98
127
  },
99
128
  ]}
100
- />
129
+ >
130
+ <Column key="eval" header="题目" />
131
+ <Column key="pass" header="通过率" align="right" />
132
+ <Column key="cost" header="成本" align="right" />
133
+ </Table>
101
134
  ```
102
135
 
103
136
  ```text
@@ -112,7 +145,7 @@ description: "报告文件里能摆的全部官方双面组件:每个组件回
112
145
 
113
146
  某一列的格子只想显示前几行时,给这一列加 `maxLines`:超出的行丢弃,最后一行按显示宽度收口成 `…`;表头不受这个限制。网页面不消费这个字段——格子的高度由你自己的样式决定。
114
147
 
115
- 指标表、指标矩阵、成绩单和成对差异表的终端面就建在 `Table` 上,所以你的表和官方的表用的是同一把尺子。表格之外的形态要自己排字符时,用[自定义报告](/zh/tutorials/custom-reports)「换形态」一节里那套文本排版函数。
148
+ 指标表、指标矩阵、成绩单、成对差异表和稳定性矩阵的终端面就建在 `Table` 上,所以你的表和官方的表用的是同一把尺子。表格之外的形态要自己排字符时,用[自定义报告](/zh/tutorials/custom-reports)「换形态」一节里那套文本排版函数。
116
149
 
117
150
  ## 摘要格(`Grid` / `Stat`)
118
151
 
@@ -144,7 +177,7 @@ description: "报告文件里能摆的全部官方双面组件:每个组件回
144
177
 
145
178
  ## 实验比较(`ExperimentComparison`)
146
179
 
147
- `niceeval show` / `view` 不传 `--report` 时使用的默认组合件。它把同一份 Scope 显式传给 `ScopeSummary`、成本 × 主指标的 `MetricScatter` 和 `ExperimentList` 三个组件,自己不产出数据,也不合并三者的结果:
180
+ `niceeval show` / `view` 不传 `--report` 时使用的默认组合件。它把同一份 Scope 显式传给 `ScopeSummary`、成本 × 主指标的散点图和 `ExperimentList` 三个组件,自己不产出数据,也不合并三者的结果:
148
181
 
149
182
  ```tsx
150
183
  <ExperimentComparison />
@@ -158,13 +191,17 @@ description: "报告文件里能摆的全部官方双面组件:每个组件回
158
191
 
159
192
  组卡使用 `Pass rate / 通过率`(计分制 Scope 换成 `Total score / 总分`,两类实验同时在场时两个都显示)、`Experiments / 实验`、`Evals / Eval`、`Attempts / Attempt`、`Eval results / Eval 结果`、`Total cost / 总成本` 这套字段标签,不在标签里重复“数”“次”或“计票”。时间显示为本地化到分钟的 `Last run / 最近运行` 或 `Run range / 运行范围`,不直接暴露 ISO 字符串;成本数据覆盖不全时写明“`63/72 次有成本数据`”,不显示没有上下文的 `63/72`。六项 KPI 在宽卡片保持同一行,空间不足时按三项或两项一组换行,避免总成本单独掉到下一行。
160
193
 
161
- 下面的 `MetricScatter`、`ScopeSummary` 和 `ExperimentList` 同样忠实消费调用方传入的数据,不推导隐藏范围。它等价于把三个组件按下面这样手工摆放——想自定义顺序或搭配其它组件时,照这个形状写(示例是通过制 Scope;计分制把 `y` 换成 `totalScore`):
194
+ 散点图、`ScopeSummary` 和 `ExperimentList` 同样忠实消费调用方传入的数据,不推导隐藏范围。`ExperimentComparison` 等价于把三个组件按下面这样手工摆放——想自定义顺序或搭配其它组件时,直接照这个形状写,不用去改 `ExperimentComparison`(示例是通过制 Scope;计分制把纵轴换成 `totalScore`):
162
195
 
163
196
  ```tsx
164
197
  export const MyComparison = defineComponent((_props, ctx) => (
165
198
  <Col>
166
199
  <ScopeSummary input={ctx.scope} />
167
- <MetricScatter input={ctx.scope} points="experiment" x={costUSD} y={endToEndPassRate} />
200
+ <ScatterChart input={ctx.scope}>
201
+ <XAxis metric={costUSD} />
202
+ <YAxis metric={endToEndPassRate} />
203
+ <Scatter points="experiment" x={costUSD} y={endToEndPassRate} />
204
+ </ScatterChart>
168
205
  <ExperimentList input={ctx.scope} filter />
169
206
  </Col>
170
207
  ));
@@ -198,7 +235,7 @@ const CompareSummary = defineComponent((_props, ctx) => (
198
235
 
199
236
  每项固定代表一个 experiment。主行显示 experiment id、agent、model、flags、评估用例判定构成、主指标(通过制实验是通过率,计分制实验是总分;两类同时在场时两列都显示,不适用的格子是 `—`)、Tokens、成本和耗时。默认按主指标从高到低排序;两类实验同时在场时改按 experiment id 字典序,两个指标列仍各自可点击排序。默认 `ExperimentComparison` 把当前 Scope 的全部条目交给它;组件本身不猜边界。
200
237
 
201
- 行标签默认缩成 experiment id 在当前列表里的最短唯一后缀——末段唯一就只显示末段,撞名的 id 各自向前多取一段直到能区分为止(与 `MetricScatter` 散点的点标签同一算法)。排序、过滤和折叠展开始终用完整 id,不受显示名影响。中文副行用“`8 个 Eval`”而不是“`8 道题`”。
238
+ 行标签默认缩成 experiment id 在当前列表里的最短唯一后缀——末段唯一就只显示末段,撞名的 id 各自向前多取一段直到能区分为止(与散点图的点标签同一算法)。排序、过滤和折叠展开始终用完整 id,不受显示名影响。agent 名的颜色按完整值取,同一页里图例、散点和这张表的同一个 agent 恒是同一种颜色。中文副行用“`8 个 Eval`”而不是“`8 道题`”。
202
239
 
203
240
  ```tsx
204
241
  <ExperimentList filter />
@@ -306,13 +343,13 @@ inspect: niceeval show @<id> [--source|--execution|--diff]
306
343
  一行一个维度值,一列一个指标,回答「谁整体更好」。行维度、指标列、排序全部可换,自定义指标(`defineMetric`)与内置指标同列。
307
344
 
308
345
  ```tsx
309
- <MetricTable
310
- rows="agent"
311
- columns={[endToEndPassRate, examScore, costUSD, durationMs]}
312
- sort={endToEndPassRate}
313
- evals="coding/"
314
- filter
315
- />
346
+ <MetricTable evals="coding/" filter>
347
+ <Rows dimension="agent" sort={endToEndPassRate} />
348
+ <Column metric={endToEndPassRate} />
349
+ <Column metric={examScore} />
350
+ <Column metric={costUSD} />
351
+ <Column metric={durationMs} />
352
+ </MetricTable>
316
353
  ```
317
354
 
318
355
  ```text
@@ -321,16 +358,20 @@ bub 87% 0.91 $0.42
321
358
  codex 80% 12/15 0.86 $0.51
322
359
  ```
323
360
 
324
- 两面都按 `sort` 预排,基准顺序一致——要固定换一种排序,改一行重跑。`12/15` 角标表示该格 15 个 attempt 里只有 12 个测得了这个指标。`sort` 必须是 `columns` 中同一个 Metric 实例且声明了 `better`,否则报错;省略时按行 key 字典序,避免为方向不明的指标猜顺序。`filter` 只给网页面加行过滤框,不改变数据或终端输出。
361
+ 两面都按 `sort` 预排,基准顺序一致——要固定换一种排序,改一行重跑。`12/15` 角标表示该格 15 个 attempt 里只有 12 个测得了这个指标。`<Rows sort>` 必须是本表某个 `<Column>` 的指标且声明了 `better`,否则报错;省略时按行 key 字典序,避免为方向不明的指标猜顺序。`filter` 只给网页面加行过滤框,不改变数据或终端输出。
325
362
 
326
- `rows: "experiment"` 时每行自动带 agent 与 model 列——结果里现成的元信息,不用配置。`MetricTable` 不展开实体层级:要看 experiment、评估用例或 Attempt 的固定诊断字段,用上面三个实体列表;要自由换维度和指标列,用指标表。
363
+ `<Rows dimension="experiment" />` 时每行自动带 agent 与 model 列——结果里现成的元信息,不用配置。`MetricTable` 不展开实体层级:要看 experiment、评估用例或 Attempt 的固定诊断字段,用上面三个实体列表;要自由换维度和指标列,用指标表。
327
364
 
328
365
  ## 指标矩阵(`MetricMatrix`)
329
366
 
330
367
  行 × 列两个维度、格子里一个指标,回答「哪道题谁挂了」。稀疏渲染:没有样本的格子空着,不编数。
331
368
 
332
369
  ```tsx
333
- <MetricMatrix rows="eval" columns="agent" cell={endToEndPassRate} />
370
+ <MetricMatrix>
371
+ <Rows dimension="eval" />
372
+ <Columns dimension="agent" />
373
+ <Cells metric={endToEndPassRate} />
374
+ </MetricMatrix>
334
375
  ```
335
376
 
336
377
  ```text
@@ -344,16 +385,16 @@ next: niceeval show geometry/area
344
385
 
345
386
  网页面点格子深链到该格的 attempt;终端面在表下印下钻命令。
346
387
 
347
- ## 分组条形图(`MetricBars`)
388
+ ## 分组条形图(`BarChart`)
348
389
 
349
- 同一份矩阵数据的另一种摆法:按组并排比大小,回答「每个科目上谁领先、差多少」。组维度一组条、系列维度一根条、条长是指标值——benchmark 发布图(Terminal-Bench、BrowseComp 各一组,每个 agent 一根柱)就是这个形状。
390
+ 同一批数据换成条形:按组并排比大小,回答「每个科目上谁领先、差多少」。横轴一组条、每条数据一根柱、柱高是指标值——benchmark 发布图(Terminal-Bench、BrowseComp 各一组,每个 agent 一根柱)就是这个形状。
350
391
 
351
392
  ```tsx
352
- <MetricBars
353
- rows="evalGroup" // 一组条 = 一个科目/benchmark
354
- columns="agent" // 一根条 = 一个 agent
355
- cell={endToEndPassRate}
356
- />
393
+ <BarChart>
394
+ <XAxis dimension="evalGroup" /> {/* 一组条 = 一个科目/benchmark */}
395
+ <YAxis metric={endToEndPassRate} />
396
+ <Bar metric={endToEndPassRate} by="agent" /> {/* 一根条 = 一个 agent */}
397
+ </BarChart>
357
398
  ```
358
399
 
359
400
  ```text
@@ -365,24 +406,51 @@ geometry
365
406
  codex —
366
407
  ```
367
408
 
368
- 网页面是竖向分组柱:柱顶标数值,系列颜色与其它组件的稳定配色一致,图例自动生成。终端面横向条形,字符宽度即刻度,同组内按值排序(方向随 `better`)。`better: "lower"` 的指标(成本、耗时)条形反向填充,短条恒为「好」。`MetricMatrix` 和 `MetricBars` 写同一份取数选项时,两个组件不会重复计算——只要 `input` 与选项相同,底层数据只算一次。
409
+ 网页面是竖向分组柱:柱顶要数值就加一个 `<LabelList position="top" />`,颜色与同页其它组件一致,图例自动生成。终端面横向条形,字符宽度即刻度,同组内按值排序(方向随 `better`)。`better: "lower"` 的指标(成本、耗时)条形反向填充,短条恒为「好」。同一页里矩阵和条形图用同一组维度时,底层数据只算一次。
410
+
411
+ ### benchmark 站首屏的那张排行榜
412
+
413
+ 换成横向、一行一名、按值排序,就是 benchmark 站首屏那块榜单:
414
+
415
+ ```tsx
416
+ <BarChart layout="vertical">
417
+ <XAxis metric={endToEndPassRate} orientation="top" />
418
+ <YAxis
419
+ dimension={["agent", label("memory")]} {/* 一行 = 一个 agent 线 × 一种记忆机制 */}
420
+ sort={endToEndPassRate} {/* 按值排名,方向随 better */}
421
+ limit={10} {/* 只留前十 */}
422
+ rest="其余" {/* 剩下的合成一行重新聚合 */}
423
+ />
424
+ <Bar metric={endToEndPassRate} colorBy={label("memory")}>
425
+ <LabelList position="right" /> {/* 行尾数值 */}
426
+ <ErrorBar kind="ci95" /> {/* 置信区间 */}
427
+ </Bar>
428
+ </BarChart>
429
+ ```
430
+
431
+ 三处值得单独说:
432
+
433
+ - **行身份可以是复合的。** `["agent", label("memory")]` 的一个取值是一行(`codex · mempal`),不是两行。
434
+ - **`rest` 是重新聚合,不是把截掉的几行平均。** 它回答的是「其余那些 attempt 合起来多少」,所以带自己的样本数和证据引用,也能下钻。
435
+ - **颜色可以说另一件事。** 行是「agent × 记忆机制」,而 `colorBy` 让颜色只表达记忆机制——同一种记忆机制在这张图、图例和页上任何按它取色的地方恒同色。要指定具体哪个值用哪个颜色(例如 baseline 恒中性),在[主题里钉色](/zh/tutorials/custom-reports#钉住某个值的颜色)。
436
+
437
+ 要在行首标 `#1` `#2`,用刻度的呈现定制:`tick` 回调收到的 `index` 就是排序后的名次。
369
438
 
370
439
  ## 成绩单(`Scoreboard`)
371
440
 
372
- 总分 + 分科小计,回答「这套题它能得几分」。逐题分值制:权重按评估用例 id 前缀配置,分母对所有被打分者恒定,没跑到的题挣 0 分并如实报 `missing`。
441
+ 总分 + 分科小计,回答「这套题它能得几分」。逐题分值制:题目和分科逐条写在子标签里,权重挂在题目或分科上,分母对所有被打分者恒定,没跑到的题挣 0 分并如实报 `missing`。
373
442
 
374
443
  ```tsx
375
- <Scoreboard
376
- rows="agent"
377
- questions={[
378
- "security/sql-injection",
379
- "security/path-traversal",
380
- "correctness/retry",
381
- ]}
382
- weights={{ "security/": 3, "correctness/": 2 }}
383
- fullMarks={100}
384
- score={examScore}
385
- />
444
+ <Scoreboard fullMarks={100} score={examScore}>
445
+ <Rows dimension="agent" />
446
+ <Subject name="security" weight={3}>
447
+ <Question id="security/sql-injection" />
448
+ <Question id="security/path-traversal" />
449
+ </Subject>
450
+ <Subject name="correctness" weight={2}>
451
+ <Question id="correctness/retry" />
452
+ </Subject>
453
+ </Scoreboard>
386
454
  ```
387
455
 
388
456
  ```text
@@ -391,20 +459,28 @@ bub 86.5/100 45/50 41.5/50
391
459
  codex 71.0/100 40/50 31/50 (1 missing)
392
460
  ```
393
461
 
394
- `questions` 是显式固定题集,不从已观测的 Attempt 并集猜——所有配置都没跑到的题仍然留在分母里按 0 分计。分数为 `null`(跑了但测不了)与完全没跑到的题分开计数(分别是 `unscorable` 和 `unrun`),成绩单能回答「这 0 分是没去考还是考了判不了」。`score` 默认是 `examScore`,每道题必须产出 `[0, 1]`;总分是 `fullMarks × earned / possible`。
462
+ 题集是你写下的 `<Question>` 列表,不从已观测的 Attempt 并集猜——所有配置都没跑到的题仍然留在分母里按 0 分计。分数为 `null`(跑了但测不了)与完全没跑到的题分开计数(分别是 `unscorable` 和 `unrun`),成绩单能回答「这 0 分是没去考还是考了判不了」。`score` 默认是 `examScore`,每道题必须产出 `[0, 1]`;总分是 `fullMarks × earned / possible`。题目多的时候用普通 `map` 展开 `<Question>`,不用手抄一长串。不想给分科起名时,把 `<Question>` 直接放在 `<Scoreboard>` 下,分科按评估用例 id 的父路径自动归。
395
463
 
396
- ## 指标散点图(`MetricScatter`)
464
+ ## 散点图(`ScatterChart`)
397
465
 
398
- 每个点一个配置、两个指标各占一轴,回答「又好又便宜的是谁」。`series` 把同 agent 不同档位的点连成线;`better` 驱动轴向——`lower` 的轴反向画,「好」的角落恒在右上。
466
+ 每个点一个配置、两个指标各占一轴,回答「又好又便宜的是谁」。`by` 把同 agent 不同档位的点归成一组;`better` 驱动轴向——`lower` 的轴反向画,「好」的角落恒在右上。
399
467
 
400
468
  ```tsx
401
- <MetricScatter points="experiment" series="agent" x={costUSD} y={endToEndPassRate} />
469
+ <ScatterChart>
470
+ <XAxis metric={costUSD} />
471
+ <YAxis metric={endToEndPassRate} />
472
+ <Scatter points="experiment" by="agent" x={costUSD} y={endToEndPassRate} />
473
+ </ScatterChart>
402
474
 
403
- // 同族变体连线:同 line 值的实验一色成线,connect 连出基线 → 变体的位移
404
- <MetricScatter points="experiment" series={label("line")} connect x={costUSD} y={endToEndPassRate} />
475
+ // 同族变体连线:同 line 值的实验一色成线,line 连出基线 → 变体的位移
476
+ <ScatterChart>
477
+ <XAxis metric={costUSD} />
478
+ <YAxis metric={endToEndPassRate} />
479
+ <Scatter points="experiment" by={label("line")} x={costUSD} y={endToEndPassRate} line />
480
+ </ScatterChart>
405
481
  ```
406
482
 
407
- `MetricScatter` 直接消费传入的 Scope,宿主渲染前替你算好数据;默认 `ExperimentComparison` 也把当前 Scope 原样交给它。要嵌预先算好的数据,改传 `data`。
483
+ 散点直接消费传入的 Scope,宿主渲染前替你算好数据;默认 `ExperimentComparison` 也把当前 Scope 原样交给它。要嵌预先算好的数据,改传 `data`。
408
484
 
409
485
  ```text
410
486
  pass ↑ (好 → 右上)
@@ -418,15 +494,20 @@ pass ↑ (好 → 右上)
418
494
  A bub-high B bub-medium C codex-high D codex-low
419
495
  ```
420
496
 
421
- 网页面点带悬停提示(值与 `samples/total`,禁用 JS 时退化为图内提示)、同系列连线、点击深链下钻。终端面用字母标点,图例列在图下;x 或 y 缺数据的点两个面都不画,注脚如实报「n 个点缺数据」;点太密排不下时降级为坐标表,不硬挤。画得出来的点是 0 个时,两个面都明说这两个指标没有可用数据,不留一片空白;1 个点也照常出图。维度槽也收自定义维度和 `flag()`(experiment 声明的变量),怎么选见[自定义报告](/zh/tutorials/custom-reports)的「换分组」一节。
497
+ 网页面点带悬停提示(值与 `samples/total`,禁用 JS 时退化为图内提示)、同系列连线、点击深链下钻。终端面用字母标点,图例列在图下;x 或 y 缺数据的点两个面都不画,注脚如实报「n 个点缺数据」;点太密排不下时降级为坐标表,不硬挤。画得出来的点是 0 个时,两个面都明说这两个指标没有可用数据,不留一片空白;1 个点也照常出图。`points` `by` 也收自定义维度和 `flag()`(experiment 声明的变量),怎么选见[自定义报告](/zh/tutorials/custom-reports)的「换分组」一节。想在同一张图上再叠一条平均线或一根柱,把 `<Line>` / `<Bar>` 和散点一起放进 `<ComposedChart>`。
422
498
 
423
- ## 指标趋势图(`MetricLine`)
499
+ ## 趋势图(`LineChart`)
424
500
 
425
- x 是有序变量、每个系列一条线,回答「变量拧大,分数怎么走」——并行 agent 数 × 模拟延迟 × 得分这类 scaling 图就是它。与 `MetricScatter` 的分工:散点图的两轴都是测出来的指标(找优势前沿),趋势图的 x 是你配置的变量(看趋势)。变量在 experiment 的 flags 里声明,报告用 `flag()` 或数值型 flag 助手直接引用,不从 experiment 命名里解析。
501
+ x 是有序变量、每组数据一条线,回答「变量拧大,分数怎么走」——并行 agent 数 × 模拟延迟 × 得分这类 scaling 图就是它。与散点图的分工:散点图的两轴都是测出来的指标(找优势前沿),趋势图的 x 是你配置的变量(看趋势)。变量在 experiment 的 flags 里声明,报告用 `flag()` 或数值型 flag 助手直接引用,不从 experiment 命名里解析。
426
502
 
427
503
  ```tsx
428
504
  const budget = numericFlag("budget", { label: "Token budget", unit: "tokens" });
429
- <MetricLine x={budget} series="agent" y={endToEndPassRate} />
505
+
506
+ <LineChart>
507
+ <XAxis numeric={budget} />
508
+ <YAxis metric={endToEndPassRate} />
509
+ <Line metric={endToEndPassRate} by="agent" />
510
+ </LineChart>
430
511
  ```
431
512
 
432
513
  ```text
@@ -444,26 +525,27 @@ pass ↑
444
525
  A 1 agents B 4 agents C 16 agents
445
526
  ```
446
527
 
447
- 每个点是一个 experiment 的聚合,与其它组件同一套指标引擎;同系列的点按 x 排序连线。网页面每个点可 hover、可深链下钻。终端面同系列共用一个字母、沿 x 排布,趋势肉眼可读;y 缺数据的点不画、注脚报数,点太密降级为坐标表。
528
+ 每个点是一个 experiment 的聚合,与其它组件同一套指标引擎;同一条线上的点按 x 排序连起来。网页面每个点可 hover、可深链下钻。终端面同系列共用一个字母、沿 x 排布,趋势肉眼可读;y 缺数据的点不画、注脚报数,点太密降级为坐标表。
448
529
 
449
530
  ## 成对差异表(`DeltaTable`)
450
531
 
451
- 每行一对配置、每列一个指标,格子里 A、B、Δ 三个值,回答「这个开关值不值」「这次修复翻转了什么」。涨跌好坏由 `better` 判定,任一侧缺数据 Δ 显示为缺,不硬算。
532
+ 每行一道评估用例、每组列一个条件,格子里是该条件的结果与对基准的差,回答「这个开关值不值」「这次修复翻转了什么」。涨跌好坏由 `better` 判定,任一侧缺数据时差值显示为缺,不硬算。
452
533
 
453
534
  ```tsx
454
- // 字面形态:逐对声明,label 自定义
455
- <DeltaTable
456
- by="experiment"
457
- pairs={[{ label: "memory", a: "compare/baseline", b: "compare/with-memory" }]}
458
- metrics={[endToEndPassRate, costUSD, durationMs]}
459
- />
460
-
461
- // 派生形态:按 flag 机械配对,加实验不用改报告
462
- <DeltaTable
463
- by="experiment"
464
- pairs={pairsByFlag("memory")}
465
- metrics={[endToEndPassRate, costUSD, durationMs]}
466
- />
535
+ // 逐个写下条件,其中一个是基准
536
+ <DeltaTable>
537
+ <Columns dimension="experiment">
538
+ <Condition value="compare/baseline" baseline />
539
+ <Condition value="compare/with-memory" />
540
+ </Columns>
541
+ </DeltaTable>
542
+
543
+ // 按 flag 机械导出条件,加实验不用改报告
544
+ <DeltaTable>
545
+ <Columns dimension="experiment">
546
+ <FlagConditions flag="memory" />
547
+ </Columns>
548
+ </DeltaTable>
467
549
  ```
468
550
 
469
551
  ```text
@@ -472,7 +554,7 @@ bub 87% → 93% +6% $0.42 → $0.45 +$0.03
472
554
  codex 80% → 80% ±0 $0.51 → — —
473
555
  ```
474
556
 
475
- 「这次 vs 上次」(同一配置的两个结果快照)也是它:`pairs` 的 `a` / `b` 除 experiment id 外也收快照键 `<experimentId> @ <startedAt>`——手挑的快照数组(比如某个实验的最新一次和上一次)配这种写法。`pairsByFlag(name)` 按一个 flag 机械导出全部 A/B 对:实验矩阵是「同配置开关某个 flag」时,配对关系本来就是 experiment 配置的推论,手抄 id 字面量等于把配置复写进报告,加实验后报告会静默缺行。
557
+ 「这次 vs 上次」(同一配置的两个结果快照)也是它:把 `<Columns dimension="snapshot">` 配上手挑的快照数组,两次运行各成一列。`<FlagConditions>` 按一个 flag 机械导出全部条件:实验矩阵是「同配置开关某个 flag」时,条件关系本来就是 experiment 配置的推论,手抄 id 字面量等于把配置复写进报告,加实验后报告会静默缺列。
476
558
 
477
559
  ## 官方组件之外
478
560