niceeval 0.6.1 → 0.7.0

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 (378) hide show
  1. package/INDEX.md +23 -23
  2. package/README.zh.md +6 -6
  3. package/dist/agents/types.d.ts +69 -7
  4. package/dist/context/types.d.ts +32 -12
  5. package/dist/i18n/en.d.ts +54 -0
  6. package/dist/i18n/zh-CN.d.ts +57 -3
  7. package/dist/o11y/types.d.ts +16 -2
  8. package/dist/report/aggregate.d.ts +32 -24
  9. package/dist/report/aggregate.js +158 -50
  10. package/dist/report/built-in/index.d.ts +2 -0
  11. package/dist/report/built-in/index.js +8 -0
  12. package/dist/report/components.d.ts +93 -160
  13. package/dist/report/components.js +377 -114
  14. package/dist/report/compute.d.ts +87 -81
  15. package/dist/report/compute.js +597 -417
  16. package/dist/report/flag.d.ts +32 -6
  17. package/dist/report/flag.js +92 -4
  18. package/dist/report/format.d.ts +19 -11
  19. package/dist/report/format.js +30 -13
  20. package/dist/report/index.d.ts +16 -16
  21. package/dist/report/index.js +20 -21
  22. package/dist/report/load.js +3 -2
  23. package/dist/report/locale.d.ts +57 -33
  24. package/dist/report/locale.js +122 -56
  25. package/dist/report/metrics.d.ts +23 -4
  26. package/dist/report/metrics.js +110 -25
  27. package/dist/report/primitives.d.ts +48 -15
  28. package/dist/report/primitives.js +135 -26
  29. package/dist/report/react/AttemptList.d.ts +9 -7
  30. package/dist/report/react/AttemptList.js +17 -10
  31. package/dist/report/react/DeltaTable.js +19 -18
  32. package/dist/report/react/EvalList.d.ts +4 -4
  33. package/dist/report/react/EvalList.js +0 -0
  34. package/dist/report/react/ExperimentComparison.d.ts +10 -0
  35. package/dist/report/react/ExperimentComparison.js +12 -0
  36. package/dist/report/react/ExperimentList.d.ts +4 -3
  37. package/dist/report/react/ExperimentList.js +17 -18
  38. package/dist/report/react/MetricBars.js +5 -4
  39. package/dist/report/react/MetricLine.js +12 -5
  40. package/dist/report/react/MetricMatrix.js +1 -1
  41. package/dist/report/react/MetricScatter.js +59 -28
  42. package/dist/report/react/MetricTable.js +2 -12
  43. package/dist/report/react/ScopeSummary.d.ts +10 -0
  44. package/dist/report/react/ScopeSummary.js +17 -0
  45. package/dist/report/react/Scoreboard.js +6 -6
  46. package/dist/report/react/cell.js +2 -2
  47. package/dist/report/react/chart-math.d.ts +23 -6
  48. package/dist/report/react/chart-math.js +71 -19
  49. package/dist/report/react/fixtures.d.ts +5 -9
  50. package/dist/report/react/fixtures.js +110 -147
  51. package/dist/report/react/index.d.ts +15 -5
  52. package/dist/report/react/index.js +18 -7
  53. package/dist/report/report.d.ts +137 -16
  54. package/dist/report/report.js +259 -28
  55. package/dist/report/text/faces.d.ts +17 -19
  56. package/dist/report/text/faces.js +253 -184
  57. package/dist/report/text/plot.js +1 -1
  58. package/dist/report/text/table.js +38 -7
  59. package/dist/report/tree.d.ts +90 -40
  60. package/dist/report/tree.js +252 -94
  61. package/dist/report/types.d.ts +247 -284
  62. package/dist/report/types.js +4 -3
  63. package/dist/report/web.d.ts +21 -5
  64. package/dist/report/web.js +42 -16
  65. package/dist/results/select.d.ts +38 -16
  66. package/dist/results/select.js +73 -25
  67. package/dist/results/types.d.ts +49 -14
  68. package/dist/runner/feedback/sink.d.ts +110 -0
  69. package/dist/runner/types.d.ts +513 -22
  70. package/dist/sandbox/docker.d.ts +23 -2
  71. package/dist/sandbox/e2b.d.ts +15 -1
  72. package/dist/sandbox/errors.d.ts +30 -3
  73. package/dist/sandbox/io-retry.d.ts +17 -0
  74. package/dist/sandbox/registry.d.ts +2 -0
  75. package/dist/sandbox/resolve.d.ts +18 -5
  76. package/dist/sandbox/retry.d.ts +11 -1
  77. package/dist/sandbox/types.d.ts +39 -5
  78. package/dist/sandbox/vercel.d.ts +7 -1
  79. package/dist/scoring/coverage.d.ts +30 -0
  80. package/dist/scoring/display.d.ts +21 -0
  81. package/dist/scoring/display.js +120 -0
  82. package/dist/scoring/types.d.ts +103 -20
  83. package/dist/shared/aggregate.d.ts +4 -2
  84. package/dist/shared/aggregate.js +8 -7
  85. package/dist/shared/types.d.ts +28 -0
  86. package/dist/tty-line.d.ts +0 -4
  87. package/dist/util.d.ts +23 -0
  88. package/docs-site/zh/README.md +44 -0
  89. package/docs-site/zh/examples/ai-agent-application.mdx +63 -0
  90. package/docs-site/zh/examples/coding-agent-extensions.mdx +57 -0
  91. package/docs-site/zh/examples/index.mdx +50 -0
  92. package/docs-site/zh/{concepts → explanation}/adapter.mdx +31 -13
  93. package/docs-site/zh/{concepts → explanation}/assert.mdx +7 -7
  94. package/docs-site/zh/{concepts → explanation}/drive.mdx +8 -8
  95. package/docs-site/zh/{concepts → explanation}/evals.mdx +4 -4
  96. package/docs-site/zh/{concepts → explanation}/experiment.mdx +8 -8
  97. package/docs-site/zh/{concepts → explanation}/hitl.mdx +8 -8
  98. package/docs-site/zh/{concepts → explanation}/judge.mdx +5 -5
  99. package/docs-site/zh/{concepts → explanation}/overview.mdx +11 -11
  100. package/docs-site/zh/{guides → explanation}/runner.mdx +18 -8
  101. package/docs-site/zh/{concepts → explanation}/tier.mdx +6 -6
  102. package/docs-site/zh/{guides → how-to}/agent-feedback-loop.mdx +35 -33
  103. package/docs-site/zh/{guides → how-to}/authoring.mdx +35 -2
  104. package/docs-site/zh/{guides → how-to}/ci-integration.mdx +23 -12
  105. package/docs-site/zh/{guides → how-to}/connect-otel.mdx +6 -6
  106. package/docs-site/zh/{guides → how-to}/connect-your-agent.mdx +47 -21
  107. package/docs-site/zh/{guides → how-to}/custom-reports.mdx +34 -39
  108. package/docs-site/zh/{guides → how-to}/dataset-fanout.mdx +25 -3
  109. package/docs-site/zh/{guides → how-to}/experiments.mdx +12 -5
  110. package/docs-site/zh/how-to/publish-report.mdx +105 -0
  111. package/docs-site/zh/{guides → how-to}/reporters.mdx +2 -2
  112. package/docs-site/zh/{guides → how-to}/sandbox-agent.mdx +56 -7
  113. package/docs-site/zh/how-to/sandbox-providers.mdx +350 -0
  114. package/docs-site/zh/{guides → how-to}/scoring-guide.mdx +4 -4
  115. package/docs-site/zh/{guides → how-to}/viewing-results.mdx +82 -39
  116. package/docs-site/zh/{guides → how-to}/write-experiment.mdx +6 -4
  117. package/docs-site/zh/{guides → how-to}/write-send.mdx +30 -14
  118. package/docs-site/zh/index.mdx +24 -26
  119. package/docs-site/zh/introduction.mdx +8 -8
  120. package/docs-site/zh/reference/builtin-agents.mdx +32 -5
  121. package/docs-site/zh/reference/capabilities.mdx +8 -8
  122. package/docs-site/zh/reference/cli.mdx +40 -12
  123. package/docs-site/zh/reference/define-agent.mdx +58 -5
  124. package/docs-site/zh/reference/define-config.mdx +1 -1
  125. package/docs-site/zh/reference/define-eval.mdx +42 -9
  126. package/docs-site/zh/reference/events.mdx +3 -3
  127. package/docs-site/zh/reference/expect.mdx +26 -1
  128. package/docs-site/zh/{guides → reference}/official-adapters.mdx +32 -8
  129. package/docs-site/zh/{guides → reference}/report-components.mdx +45 -33
  130. package/docs-site/zh/{guides → reference}/results-data.mdx +21 -13
  131. package/docs-site/zh/troubleshooting/debug-sandbox.mdx +57 -0
  132. package/docs-site/zh/troubleshooting/debugging.mdx +212 -0
  133. package/docs-site/zh/{quickstart.mdx → tutorials/quickstart.mdx} +5 -17
  134. package/package.json +10 -2
  135. package/src/agents/ai-sdk-otel.test.ts +1 -0
  136. package/src/agents/ai-sdk.test.ts +3 -0
  137. package/src/agents/ai-sdk.ts +3 -0
  138. package/src/agents/bub-install-spec.test.ts +34 -0
  139. package/src/agents/bub-install-spec.ts +32 -0
  140. package/src/agents/bub.ts +31 -32
  141. package/src/agents/claude-code.test.ts +130 -9
  142. package/src/agents/claude-code.ts +76 -4
  143. package/src/agents/codex.test.ts +189 -40
  144. package/src/agents/codex.ts +155 -14
  145. package/src/agents/coding-cli-versions.test.ts +15 -0
  146. package/src/agents/coding-cli-versions.ts +3 -0
  147. package/src/agents/index.ts +13 -2
  148. package/src/agents/langgraph.test.ts +204 -0
  149. package/src/agents/langgraph.ts +495 -0
  150. package/src/agents/marketplace.ts +85 -0
  151. package/src/agents/native-config.test.ts +179 -0
  152. package/src/agents/native-config.ts +267 -0
  153. package/src/agents/openai-compat.test.ts +1 -0
  154. package/src/agents/openai-compat.ts +1 -1
  155. package/src/agents/openclaw.test.ts +31 -0
  156. package/src/agents/openclaw.ts +171 -0
  157. package/src/agents/plugin-config.test.ts +1 -0
  158. package/src/agents/sdk-streams.test.ts +79 -0
  159. package/src/agents/sdk-streams.ts +55 -10
  160. package/src/agents/skills.test.ts +1 -0
  161. package/src/agents/streaming.test.ts +3 -9
  162. package/src/agents/streaming.ts +2 -2
  163. package/src/agents/types.ts +71 -8
  164. package/src/agents/ui-message-stream.test.ts +3 -0
  165. package/src/cli.ts +446 -124
  166. package/src/context/context.test.ts +51 -12
  167. package/src/context/context.ts +162 -30
  168. package/src/context/session.test.ts +2 -1
  169. package/src/context/session.ts +115 -7
  170. package/src/context/types.ts +30 -12
  171. package/src/define.test.ts +13 -8
  172. package/src/define.ts +25 -4
  173. package/src/expect/index.ts +53 -23
  174. package/src/i18n/en.ts +81 -17
  175. package/src/i18n/zh-CN.ts +80 -17
  176. package/src/o11y/cost.test.ts +1 -0
  177. package/src/o11y/execution-tree.test.ts +1 -20
  178. package/src/o11y/otlp/mappers/claude-code.test.ts +1 -0
  179. package/src/o11y/otlp/parse.test.ts +1 -0
  180. package/src/o11y/otlp/turn-otel.test.ts +1 -0
  181. package/src/o11y/parsers/bub.test.ts +1 -0
  182. package/src/o11y/parsers/claude-code.test.ts +1 -34
  183. package/src/o11y/parsers/openclaw.test.ts +154 -0
  184. package/src/o11y/parsers/openclaw.ts +310 -0
  185. package/src/o11y/prices.json +746 -311
  186. package/src/o11y/tool-names.test.ts +1 -0
  187. package/src/o11y/types.ts +16 -2
  188. package/src/report/aggregate.ts +178 -61
  189. package/src/report/built-in/index.tsx +9 -0
  190. package/src/report/components.tsx +625 -279
  191. package/src/report/compute.ts +723 -491
  192. package/src/report/dual-render.test.tsx +741 -1024
  193. package/src/report/flag.ts +104 -12
  194. package/src/report/format.ts +32 -12
  195. package/src/report/index.ts +119 -46
  196. package/src/report/load.ts +3 -2
  197. package/src/report/locale.ts +136 -65
  198. package/src/report/metrics.ts +108 -25
  199. package/src/report/primitives.tsx +196 -45
  200. package/src/report/react/AttemptList.tsx +30 -43
  201. package/src/report/react/DeltaTable.tsx +63 -45
  202. package/src/report/react/EvalList.tsx +0 -0
  203. package/src/report/react/ExperimentComparison.tsx +73 -0
  204. package/src/report/react/ExperimentList.tsx +50 -32
  205. package/src/report/react/MetricBars.tsx +5 -4
  206. package/src/report/react/MetricLine.tsx +13 -8
  207. package/src/report/react/MetricMatrix.tsx +2 -2
  208. package/src/report/react/MetricScatter.tsx +86 -34
  209. package/src/report/react/MetricTable.tsx +4 -76
  210. package/src/report/react/ScopeSummary.tsx +86 -0
  211. package/src/report/react/Scoreboard.tsx +28 -10
  212. package/src/report/react/cell.tsx +2 -2
  213. package/src/report/react/chart-math.test.ts +85 -0
  214. package/src/report/react/chart-math.ts +101 -22
  215. package/src/report/react/enhance.js +89 -5
  216. package/src/report/react/fixtures.ts +114 -154
  217. package/src/report/react/index.tsx +24 -39
  218. package/src/report/react/render.test.tsx +138 -158
  219. package/src/report/react/styles.css +243 -82
  220. package/src/report/report.test.ts +779 -841
  221. package/src/report/report.ts +423 -41
  222. package/src/report/text/faces.ts +290 -193
  223. package/src/report/text/plot.ts +1 -1
  224. package/src/report/text/table.ts +44 -7
  225. package/src/report/tree.ts +362 -104
  226. package/src/report/types.ts +261 -271
  227. package/src/report/web.ts +63 -20
  228. package/src/results/annotated-source.test.ts +62 -9
  229. package/src/results/annotated-source.ts +64 -6
  230. package/src/results/attempt-evidence.test.ts +13 -11
  231. package/src/results/attempt-evidence.ts +20 -13
  232. package/src/results/attempt-source.ts +6 -3
  233. package/src/results/copy.ts +150 -60
  234. package/src/results/host-equivalence.test.ts +34 -20
  235. package/src/results/index.ts +12 -4
  236. package/src/results/locator.test.ts +1 -22
  237. package/src/results/open.ts +15 -5
  238. package/src/results/publish.ts +149 -0
  239. package/src/results/results.test.ts +89 -54
  240. package/src/results/select.ts +104 -34
  241. package/src/results/truncate.ts +90 -0
  242. package/src/results/types.ts +43 -14
  243. package/src/results/writer.ts +31 -13
  244. package/src/runner/attempt.test.ts +138 -7
  245. package/src/runner/attempt.ts +603 -104
  246. package/src/runner/discover.test.ts +47 -0
  247. package/src/runner/discover.ts +36 -2
  248. package/src/runner/eval-source.test.ts +1 -27
  249. package/src/runner/feedback/agent.test.ts +504 -0
  250. package/src/runner/feedback/agent.ts +409 -0
  251. package/src/runner/feedback/ci.test.ts +562 -0
  252. package/src/runner/feedback/ci.ts +401 -0
  253. package/src/runner/feedback/coordinator.test.ts +317 -0
  254. package/src/runner/feedback/coordinator.ts +397 -0
  255. package/src/runner/feedback/failure.ts +40 -0
  256. package/src/runner/feedback/human.test.ts +616 -0
  257. package/src/runner/feedback/human.ts +535 -0
  258. package/src/runner/feedback/index.ts +66 -0
  259. package/src/runner/feedback/io.ts +78 -0
  260. package/src/runner/feedback/profile.test.ts +50 -0
  261. package/src/runner/feedback/profile.ts +58 -0
  262. package/src/runner/feedback/reducer.test.ts +395 -0
  263. package/src/runner/feedback/reducer.ts +260 -0
  264. package/src/runner/feedback/renderer.ts +82 -0
  265. package/src/runner/feedback/sink.ts +203 -0
  266. package/src/runner/feedback/testing.ts +106 -0
  267. package/src/runner/ledger.test.ts +230 -0
  268. package/src/runner/ledger.ts +329 -0
  269. package/src/runner/report.test.ts +128 -3
  270. package/src/runner/report.ts +33 -9
  271. package/src/runner/reporters/artifacts.ts +8 -2
  272. package/src/runner/reporters/braintrust.test.ts +8 -7
  273. package/src/runner/reporters/braintrust.ts +9 -2
  274. package/src/runner/reporters/index.ts +2 -2
  275. package/src/runner/reporters/json.test.ts +162 -0
  276. package/src/runner/reporters/json.ts +35 -8
  277. package/src/runner/reporters/shared.ts +1 -5
  278. package/src/runner/run.test.ts +760 -3
  279. package/src/runner/run.ts +243 -37
  280. package/src/runner/sandbox-prep.ts +3 -42
  281. package/src/runner/timing.ts +158 -0
  282. package/src/runner/types.ts +518 -22
  283. package/src/sandbox/checkpoint.test.ts +55 -0
  284. package/src/sandbox/checkpoint.ts +29 -8
  285. package/src/sandbox/cli-commands.ts +407 -0
  286. package/src/sandbox/docker.ts +115 -16
  287. package/src/sandbox/e2b-agent-template.test.ts +56 -0
  288. package/src/sandbox/e2b-agent-template.ts +94 -0
  289. package/src/sandbox/e2b.ts +74 -9
  290. package/src/sandbox/errors.ts +111 -4
  291. package/src/sandbox/index.ts +2 -0
  292. package/src/sandbox/io-retry.test.ts +58 -0
  293. package/src/sandbox/io-retry.ts +45 -0
  294. package/src/sandbox/keep-registry.test.ts +86 -0
  295. package/src/sandbox/keep-registry.ts +142 -0
  296. package/src/sandbox/keep.ts +178 -0
  297. package/src/sandbox/paths.test.ts +1 -0
  298. package/src/sandbox/paths.ts +19 -8
  299. package/src/sandbox/registry.ts +20 -3
  300. package/src/sandbox/resolve.ts +76 -11
  301. package/src/sandbox/retry.test.ts +70 -0
  302. package/src/sandbox/retry.ts +46 -4
  303. package/src/sandbox/types.ts +44 -6
  304. package/src/sandbox/vercel.ts +43 -20
  305. package/src/scoring/collector.ts +60 -17
  306. package/src/scoring/coverage.ts +95 -0
  307. package/src/scoring/diff.ts +81 -0
  308. package/src/scoring/display.test.ts +121 -0
  309. package/src/scoring/display.ts +133 -0
  310. package/src/scoring/evidence.test.ts +189 -0
  311. package/src/scoring/judge.test.ts +142 -0
  312. package/src/scoring/judge.ts +15 -18
  313. package/src/scoring/scoped.ts +217 -50
  314. package/src/scoring/types.ts +117 -20
  315. package/src/scoring/verdict.ts +16 -4
  316. package/src/shared/aggregate.ts +8 -6
  317. package/src/shared/types.ts +31 -0
  318. package/src/show/compose.ts +50 -67
  319. package/src/show/index.ts +127 -56
  320. package/src/show/render.ts +662 -131
  321. package/src/show/report-host.test.ts +188 -0
  322. package/src/show/report-host.ts +375 -0
  323. package/src/show/show.test.ts +320 -54
  324. package/src/tty-line.ts +8 -26
  325. package/src/util.test.ts +1 -0
  326. package/src/util.ts +41 -0
  327. package/src/view/app/App.test.tsx +69 -0
  328. package/src/view/app/App.tsx +144 -48
  329. package/src/view/app/components/AttemptModal.tsx +423 -11
  330. package/src/view/app/components/CodeView.tsx +41 -14
  331. package/src/view/app/components/CopyControls.tsx +2 -2
  332. package/src/view/app/i18n.ts +37 -17
  333. package/src/view/app/lib/attempt-route.test.ts +1 -0
  334. package/src/view/app/lib/verdict.ts +7 -9
  335. package/src/view/app/main.tsx +13 -8
  336. package/src/view/app/pages/{RunsPage.tsx → AttemptsPage.tsx} +6 -6
  337. package/src/view/app/types.ts +4 -1
  338. package/src/view/artifact-serving.test.ts +2 -1
  339. package/src/view/client-dist/app.css +1 -1
  340. package/src/view/client-dist/app.js +17 -17
  341. package/src/view/data.test.ts +10 -3
  342. package/src/view/data.ts +155 -49
  343. package/src/view/index.ts +56 -41
  344. package/src/view/server.ts +37 -15
  345. package/src/view/shared/types.ts +34 -5
  346. package/src/view/styles.css +227 -0
  347. package/src/view/view-report.test.ts +167 -62
  348. package/dist/report/built-ins/experiment-comparison.d.ts +0 -1
  349. package/dist/report/built-ins/experiment-comparison.js +0 -13
  350. package/dist/report/built-ins/index.d.ts +0 -1
  351. package/dist/report/built-ins/index.js +0 -2
  352. package/dist/report/react/GroupSummary.d.ts +0 -8
  353. package/dist/report/react/GroupSummary.js +0 -8
  354. package/dist/report/react/RunOverview.d.ts +0 -8
  355. package/dist/report/react/RunOverview.js +0 -12
  356. package/docs-site/zh/example/ai-agent-application.mdx +0 -152
  357. package/docs-site/zh/example/claude-code-codex-plugin.mdx +0 -167
  358. package/docs-site/zh/example/claude-code-codex-skill.mdx +0 -152
  359. package/docs-site/zh/example/showcase.mdx +0 -39
  360. package/docs-site/zh/guides/publish-report.mdx +0 -91
  361. package/docs-site/zh/guides/sandbox-providers.mdx +0 -102
  362. package/src/report/built-in-user-parity.test.tsx +0 -640
  363. package/src/report/built-ins/experiment-comparison.tsx +0 -19
  364. package/src/report/built-ins/index.ts +0 -2
  365. package/src/report/react/GroupSummary.tsx +0 -66
  366. package/src/report/react/RunOverview.tsx +0 -109
  367. package/src/runner/reporters/console.ts +0 -70
  368. package/src/runner/reporters/live.test.ts +0 -56
  369. package/src/runner/reporters/live.ts +0 -247
  370. package/src/runner/reporters/quiet.test.ts +0 -66
  371. package/src/runner/reporters/quiet.ts +0 -49
  372. package/src/runner/reporters/table.ts +0 -277
  373. /package/docs-site/zh/{example/tier1-ai-sdk-v7.mdx → examples/integrations/ai-sdk-v7.mdx} +0 -0
  374. /package/docs-site/zh/{example/tier1-claude-sdk.mdx → examples/integrations/claude-sdk.mdx} +0 -0
  375. /package/docs-site/zh/{example/tier1-codex-sdk.mdx → examples/integrations/codex-sdk.mdx} +0 -0
  376. /package/docs-site/zh/{example/tier1-langgraph.mdx → examples/integrations/langgraph.mdx} +0 -0
  377. /package/docs-site/zh/{example/tier1-pi-sdk.mdx → examples/integrations/pi-sdk.mdx} +0 -0
  378. /package/docs-site/zh/{guides → how-to}/fixtures.mdx +0 -0
@@ -4,14 +4,15 @@ sidebarTitle: "报告组件"
4
4
  description: "报告文件里能摆的全部官方双面组件:每个组件回答什么问题、怎么调用、网页面长什么样、终端字符输出长什么样。"
5
5
  ---
6
6
 
7
- 报告文件里的每个组件都是双面的:网页面是 React 渲染,终端面是字符渲染,两面吃同一份算好的数据,走哪扇门由宿主决定(见[自定义报告](/zh/guides/custom-reports))。每个组件的数据算法挂在它自己身上,组件名后打个点就找到,算和画永远配对。本页逐个列出官方组件:它展示哪一层数据、在报告里怎么调用、终端输出长什么样。
7
+ 报告文件里的每个组件都是双面的:网页面是 React 渲染,终端面是字符渲染,两面吃同一份算好的数据,走哪扇门由宿主决定(见[自定义报告](/zh/how-to/custom-reports))。每个组件的数据算法挂在它自己身上,组件名后打个点就找到,算和画永远配对。本页逐个列出官方组件:它展示哪一层数据、在报告里怎么调用、终端输出长什么样。
8
8
 
9
9
  ## 名称与用途
10
10
 
11
- 英文术语描述组件形态,API 名是代码里的导出名。组件分三类:实体列表逐项展示 experiment、Eval 或 Attempt;汇总组件概括整批 Selection;指标图形把指定维度聚合成值。三个实体列表的 `.data(selection)` 都返回普通数组,报告作者先用 JavaScript `.filter()` 收窄,再把 `items` 交给组件。过滤条件不藏在组件里。中文正文首次提到时用“中文名(`API 名`)”,后续可以只写中文名或 `API 名`。
11
+ 英文术语描述组件形态,API 名是代码里的导出名。组件分四类:默认组合件按可比组组织完整比较;实体列表逐项展示 experiment、Eval 或 Attempt;汇总组件概括整批 Selection;指标图形把指定维度聚合成值。三个实体列表的 `.data(selection)` 都返回普通数组,报告作者先用 JavaScript `.filter()` 收窄,再把 `items` 交给组件。过滤条件不藏在组件里。中文正文首次提到时用“中文名(`API 名`)”,后续可以只写中文名或 `API 名`。
12
12
 
13
13
  | 分类 | 中文名 | English | API | 主展示单位 |
14
14
  | --- | --- | --- | --- | --- |
15
+ | 组合 | 实验组比较 | Experiment comparison | `ExperimentComparison` | 按 experiment 父目录分组;每组独立的摘要、散点与实验列表 |
15
16
  | 汇总 | 运行总览 | Run overview | `RunOverview` | 一批 Selection;汇总其中的 experiment、Eval 和 Attempt |
16
17
  | 汇总 | 组摘要 | Group summary | `GroupSummary` | 收窄后的一批 Selection;汇总一组 experiment 和 Eval |
17
18
  | 实体列表 | 实验列表 | Experiment list | `ExperimentList` | 每项一个 experiment;展开到该 experiment 的 Eval |
@@ -93,7 +94,19 @@ description: "报告文件里能摆的全部官方双面组件:每个组件回
93
94
 
94
95
  表比终端宽时先压最宽的左对齐列(按显示宽度折行),右对齐列不折行——数字折行读不了;压到下限仍放不下,就从右侧丢列,并在表下如实报丢了几列,不静默截断。
95
96
 
96
- 指标表、指标矩阵、成绩单和成对差异表的终端面就建在 `Table` 上,所以你的表和官方的表用的是同一把尺子。表格之外的形态要自己排字符时,用[自定义报告](/zh/guides/custom-reports)「换形态」一节里那套文本排版函数。
97
+ 指标表、指标矩阵、成绩单和成对差异表的终端面就建在 `Table` 上,所以你的表和官方的表用的是同一把尺子。表格之外的形态要自己排字符时,用[自定义报告](/zh/how-to/custom-reports)「换形态」一节里那套文本排版函数。
98
+
99
+ ## 实验组比较(`ExperimentComparison`)
100
+
101
+ `niceeval show` / `view` 不传 `--report` 时使用的默认组合件。它先按 experiment id 的完整父目录分组,再为每组分别计算组摘要、成本 × 端到端成功率散点和实验列表:
102
+
103
+ ```tsx
104
+ <ExperimentComparison data={await ExperimentComparison.data(selection)} />
105
+ ```
106
+
107
+ `compare/bub` 与 `compare/codex` 属于 `compare`,可以横向比较;`dev-e2b/bub` 属于另一个组,不会进入同一张图、同一条 series 或同一张表。顶层 experiment 没有父目录时,以自己的完整 id 形成单例组。网页面持有完整组索引并一次聚焦一组,切组不重新读取 Selection;终端面命中多组时只显示组索引与单组查看命令,命中单组时才展开详情。浏览器禁用 JS 时,每组仍以独立 `<details>` 保留完整内容。
108
+
109
+ 这个分区只属于默认组合件。下面的 `MetricScatter`、`MetricTable` 和 `ExperimentList` 都忠实消费调用方传入的数据;自定义报告把跨组 Selection 传给它们,就表示明确选择跨组分析。
97
110
 
98
111
  ## 运行总览(`RunOverview`)
99
112
 
@@ -126,13 +139,15 @@ Pass rate 73.3% · 2 experiments · 15 evals · failed 3 · errored 1 · $0.93
126
139
  latest 2026-07-09T10:00:00Z
127
140
  ```
128
141
 
129
- 通过率是 eval 级折叠计票口径:同一 eval 的多轮 attempt 先折成一个判定(任一轮通过则算通过,否则取最严重的),`passed / (passed + failed + errored)`,`skipped` 不进分母——与页头 `RunOverview` 那个两级聚合、带 partial credit `passRate` 是两码事,两者服务不同问题:`GroupSummary` 回答「这组题过了几道」,`RunOverview` 回答「整体质量几分」,不要互相替代。`evals` 按 `experimentId + eval id` 的完整身份键去重——组里两个 experiment 各自的同名 eval 算两道题,不会被误合并成一道。`errored` 为 0 时这一段省略,但 `verdicts.errored` 这个数据字段本身永远在,省略只发生在渲染层。`totalCostUSD` 是组内可测 attempt 成本求和,一个 attempt 都没报成本时是 `null`,两面都渲染缺数据而不是 `$0`。
142
+ 通过率是 eval 级折叠计票口径:同一 eval 的多轮 attempt 先折成一个判定(任一轮通过则算通过,否则取最严重的),`passed / (passed + failed + errored)`,`skipped` 不进分母。页头 `RunOverview` 使用 `endToEndPassRate` 的两级聚合:先算同一道题各 Attempt 的端到端成功率,再跨题平均。两者的聚合粒度不同,但都会让 errored 降低成功率。`GroupSummary` 回答「这组题最终过了几道」,`RunOverview` 回答「每次实际运行交付成功结果的比例」,不要互相替代。`evals` 按 `experimentId + eval id` 的完整身份键去重——组里两个 experiment 各自的同名 eval 算两道题,不会被误合并成一道。`errored` 为 0 时这一段省略,但 `verdicts.errored` 这个数据字段本身永远在,省略只发生在渲染层。`totalCostUSD` 是组内可测 attempt 成本求和,一个 attempt 都没报成本时是 `null`,两面都渲染缺数据而不是 `$0`。
130
143
 
131
144
  组摘要是普通的公开组件,`GroupSummary.data` 是普通的公开计算函数——想在自己的报告里按目录前缀分组、每组摆一块,按上面的写法收窄 Selection 再调它就行。
132
145
 
133
146
  ## 实验列表(`ExperimentList`)
134
147
 
135
- 每项固定代表一个 experiment。主行显示 experiment id、agent、model、flags、Eval 判定构成、通过率、Tokens、成本和耗时;展开后显示这个 experiment 的 Eval 列表。它不接受列配置——这是 experiment 的诊断视图,不是通用指标表。
148
+ 每项固定代表一个 experiment。主行显示 experiment id、agent、model、flags、Eval 判定构成、通过率、Tokens、成本和耗时;展开后显示这个 experiment 的 Eval 列表。Eval 父行显示折叠判定、Attempt 数、平均耗时和平均成本;下面每个 Attempt 再显示该轮自己的失败摘要。失败内容只出现一次,不会在 Eval 与唯一 Attempt 上重复。它不接受列配置——这是 experiment 的诊断视图,不是通用指标表。默认 `ExperimentComparison` 每次只把一个可比组的 items 交给它;组件本身不猜组边界。
149
+
150
+ 默认比较已经用组名作面板 / 段落标题,所以组内每行的 experiment 标签会去掉这层文件夹前缀,只显示 id 末段(和同组散点的点标签一致),不在每行重复文件夹名;完整 id 仍是排序、着色和身份的依据。独立使用 `ExperimentList`、不告诉它相对哪个组时,显示完整 id。
136
151
 
137
152
  ```tsx
138
153
  const experiments = await ExperimentList.data(selection);
@@ -142,22 +157,19 @@ const experiments = await ExperimentList.data(selection);
142
157
  />
143
158
  ```
144
159
 
145
- `niceeval show` 每个 experiment 输出一行汇总,每个 Eval 只占一行。Attempt ID 后的证据位告诉 AI 能读什么,不展开重复命令:
160
+ `niceeval show` 先输出 experiment 比较表,再按 experiment 展开 Eval / Attempt 父子表。Eval 父行给题级平均值,Attempt 子行给这一轮的失败摘要和 locator:
146
161
 
147
162
  ```text
148
- compare/bub-gpt-5.4 · bub · gpt-5.4
149
- pass 87% · 13 passed / 1 failed / 1 errored · 42 attempts · 4m 12s · $0.42
150
- ✓ algebra/quadratic @12f9k3aq✓ @141bm7cx✓ 18s avg · $0.02 avg
151
- ✗ weather/brooklyn @1k2m9qrs✗ @1nx4dpqr✗ @19vq2jex✗ gate calledTool("get_weather")
152
- ! fixtures/button @1c3h6twx! command timed out after 120s
153
-
154
- compare/codex-gpt-5.4 · codex · gpt-5.4
155
- pass 80% · 12 passed / 3 failed · 45 attempts · 5m 03s · $0.51
156
- algebra/quadratic @1d8p4kwz✓ 16s · $0.02
157
- algebra/matrix @1e2j7mxa✗ @1f5r9nab✗ @1g4w8pcd✗ gate equals("42")
158
- ✗ weather/brooklyn @1h6t3vbe✗ @1j9m2qfg✗ @1k7c5rxh✗ gate calledTool("get_weather")
159
-
160
- inspect: niceeval show @<id> [--eval|--execution|--diff]
163
+ Experiment Model Agent Avg duration E2E pass rate Result Tokens Est. cost
164
+ bub-gpt-5.4 gpt-5.4 bub 41.0s 50% 1 passed / 1 failed 42k $0.08
165
+
166
+ bub-gpt-5.4
167
+ Status Eval / Attempt Result Duration Cost
168
+ ✓ passed algebra/quadratic 18.0s avg $0.02 avg
169
+ ✓ └─ @12f9k3aq — 18.0s $0.02
170
+ failed weather/brooklyn 42.0s avg $0.04 avg
171
+ ✗ ├─ @1k2m9qrs calledTool("get_weather") · no calls 41.0s $0.04
172
+ └─ @1nx4dpqr calledTool("get_weather") · no calls 43.0s $0.04
161
173
  ```
162
174
 
163
175
  locator 由 `experimentId + snapshot.startedAt + evalId + attempt index` 的不可变身份确定,复制或发布结果后保持不变。宿主在当前结果根解析 locator;不存在或发生冲突时直接报错,不回退到“最新一次”。`@` 前缀让它与 Eval ID 前缀选择器无歧义。
@@ -166,7 +178,7 @@ locator 由 `experimentId + snapshot.startedAt + evalId + attempt index` 的不
166
178
 
167
179
  ## Eval 列表(`EvalList`)
168
180
 
169
- 每项固定代表一个 `experimentId + evalId`,因为同一个 Eval 跑在两个 experiment 上是两条不同结果。主行显示判定、Attempt 数、聚合分数、成本、耗时和失败原因;展开后显示这个 Eval 的 Attempt 列表。
181
+ 每项固定代表一个 `experimentId + evalId`,因为同一个 Eval 跑在两个 experiment 上是两条不同结果。主行显示判定、Attempt 数、聚合分数、平均成本和平均耗时;展开后显示这个 Eval 的 Attempt 列表,由每个 Attempt 行显示该轮自己的失败原因。Eval 主行不挑某一轮的失败原因冒充题级结论。
170
182
 
171
183
  ```tsx
172
184
  const evals = await EvalList.data(selection);
@@ -196,7 +208,7 @@ inspect: niceeval show @<id> [--eval|--execution|--diff]
196
208
 
197
209
  ## Attempt 列表(`AttemptList`)
198
210
 
199
- 每项固定代表一个 Attempt,显示 experiment、Eval、Attempt 序号、判定、耗时、成本、失败断言、errorJudge 评语和证据链接。它既能列失败证据,也能列通过样本,不把 verdict 过滤写死在组件名里。
211
+ 每项固定代表一个 Attempt,显示 experiment、Eval、Attempt 序号、判定、耗时、成本、失败断言、结构化 error 的一层摘要、Judge 评语和证据链接。diagnostics、cause 和 stack 留给 locator 下钻详情,避免比较列表被基础设施日志撑开。它既能列失败证据,也能列通过样本,不把 verdict 过滤写死在组件名里。
200
212
 
201
213
  ```tsx
202
214
  const attempts = await AttemptList.data(selection, { redact });
@@ -206,7 +218,7 @@ const attempts = await AttemptList.data(selection, { redact });
206
218
  />
207
219
  ```
208
220
 
209
- `niceeval show` 每项完整输出一个 Attempt,不再折叠到 Eval 汇总。这里已经是叶子层,只在末尾给一条与该 Attempt 可用证据对应的模板:
221
+ `niceeval show` 每项完整输出一个 Attempt,不折叠到 Eval 汇总。这里已经是叶子层,只在末尾给一条与该 Attempt 可用证据对应的模板:
210
222
 
211
223
  ```text
212
224
  ✗ @1k2m9qrs · weather/brooklyn · compare/bub-gpt-5.4 · 41s · $0.04
@@ -222,7 +234,7 @@ inspect: niceeval show @<id> [--eval|--execution|--diff]
222
234
  (3 more not shown · showing 20 of 23)
223
235
  ```
224
236
 
225
- 发布前要消毒 error、断言 detail 或 Judge 评语时,把 `redact` 交给 `.data()`;要展示哪些 Attempt,过滤返回的 `AttemptListItem[]`。`limit` 也由报告作者在数组上用 `.slice(0, 20)` 表达,截断时把原始数量交给组件的 `total`,组件据此显示“还有 n 项未展示”,不静默截断。
237
+ 要在页面显示前遮蔽 error message/cause/stack、diagnostic message/data、断言 detail 或 Judge 评语,把 `redact` 交给 `.data()`;稳定 code、lifecycle operation、experiment、Eval 和 locator 不改。它只影响这份组件数据,管不到发布目录里的 artifact 文件——发布数据集的消毒用 [`copySnapshots` 的 `redact` 选项](/zh/reference/results-data)。要展示哪些 Attempt,过滤返回的 `AttemptListItem[]`。`limit` 也由报告作者在数组上用 `.slice(0, 20)` 表达,截断时把原始数量交给组件的 `total`,组件据此显示“还有 n 项未展示”,不静默截断。
226
238
 
227
239
  ## 指标表(`MetricTable`)
228
240
 
@@ -231,8 +243,8 @@ inspect: niceeval show @<id> [--eval|--execution|--diff]
231
243
  ```tsx
232
244
  <MetricTable data={await MetricTable.data(selection, {
233
245
  rows: "agent",
234
- columns: [passRate, codeLines, costUSD],
235
- sort: passRate,
246
+ columns: [endToEndPassRate, codeLines, costUSD],
247
+ sort: endToEndPassRate,
236
248
  })} />
237
249
  ```
238
250
 
@@ -251,7 +263,7 @@ codex 80% 12/15 355 lines $0.51
251
263
  行 × 列两个维度、格子里一个指标,回答「哪道题谁挂了」。稀疏渲染:没有样本的格子空着,不编数。
252
264
 
253
265
  ```tsx
254
- <MetricMatrix data={await MetricMatrix.data(selection, { rows: "eval", columns: "agent", cell: passRate })} />
266
+ <MetricMatrix data={await MetricMatrix.data(selection, { rows: "eval", columns: "agent", cell: endToEndPassRate })} />
255
267
  ```
256
268
 
257
269
  ```text
@@ -273,7 +285,7 @@ next: niceeval show geometry/area
273
285
  <MetricBars data={await MetricBars.data(selection, {
274
286
  rows: "evalGroup", // 一组条 = 一个科目/benchmark
275
287
  columns: "agent", // 一根条 = 一个 agent
276
- cell: passRate,
288
+ cell: endToEndPassRate,
277
289
  })} />
278
290
  ```
279
291
 
@@ -317,11 +329,11 @@ codex 71.0/100 40/50 31/50 (1 missing)
317
329
  points="experiment" // 每个点 = 一个配置的聚合
318
330
  series="agent" // 同 agent 的档位连成线
319
331
  x={costUSD}
320
- y={passRate}
332
+ y={endToEndPassRate}
321
333
  />
322
334
  ```
323
335
 
324
- 直接把 `selection` 传给它,宿主渲染前替你算好数据——默认报告就是这么写的。要在自己已经跑起来的 React 应用里嵌这张图、或数据是预先算好的,改传 `data`:`<MetricScatter data={await MetricScatter.data(selection, { points: "experiment", series: "agent", x: costUSD, y: passRate })} />`;同时传 `data` 和 `selection`、或两者都不传,类型检查都会报错。
336
+ 直接把 `selection` 传给它,宿主渲染前替你算好数据。`MetricScatter` 不根据 experiment id 自动分组;默认 `ExperimentComparison` 会先收窄到一个可比组,再逐组调用它。要在自己已经跑起来的 React 应用里嵌这张图、或数据是预先算好的,改传 `data`:`<MetricScatter data={await MetricScatter.data(selection, { points: "experiment", series: "agent", x: costUSD, y: endToEndPassRate })} />`;同时传 `data` 和 `selection`、或两者都不传,类型检查都会报错。
325
337
 
326
338
  ```text
327
339
  pass ↑ (好 → 右上)
@@ -335,7 +347,7 @@ pass ↑ (好 → 右上)
335
347
  A bub-high B bub-medium C codex-high D codex-low
336
348
  ```
337
349
 
338
- 网页面点带悬停提示(值与 `samples/total`,禁用 JS 时退化为图内提示)、同系列连线、点击深链下钻。终端面用字母标点、图例列在图下;x 或 y 缺数据的点两个面都不画,注脚如实报「n 个点缺数据」;点太密排不下时降级为坐标表,不硬挤。画得出来的点是 0 个(x 或 y 全缺数据)时,两个面都明说这两个指标没有可用数据,不留一片空白;只有 1 个点时,明说「至少要两个实验才能比较」;2 个及以上才正常出图——组件从不因为点不够就整块消失,让你不知道图为什么没了。维度槽也收自定义维度(`{ name, of }`,从 attempt 已有数据算组名)和 `flag()`(experiment 声明的变量),怎么选见[自定义报告](/zh/guides/custom-reports)的「换分组」一节。
350
+ 网页面点带悬停提示(值与 `samples/total`,禁用 JS 时退化为图内提示)、同系列连线、点击深链下钻。终端面用字母标点、图例列在图下;x 或 y 缺数据的点两个面都不画,注脚如实报「n 个点缺数据」;点太密排不下时降级为坐标表,不硬挤。画得出来的点是 0 个(x 或 y 全缺数据)时,两个面都明说这两个指标没有可用数据,不留一片空白;1 个点也照常出图——组件从不因为点不够就整块消失,让你不知道图为什么没了。维度槽也收自定义维度(`{ name, of }`,从 attempt 已有数据算组名)和 `flag()`(experiment 声明的变量),怎么选见[自定义报告](/zh/how-to/custom-reports)的「换分组」一节。
339
351
 
340
352
  ## 指标趋势图(`MetricLine`)
341
353
 
@@ -345,7 +357,7 @@ x 是有序变量、每个系列一条线,回答「变量拧大,分数怎么
345
357
  <MetricLine data={await MetricLine.data(selection, {
346
358
  x: flag("latencyMs", { label: "Simulated latency", unit: "ms" }),
347
359
  series: flag("agents", { label: (v) => `${v} agents` }),
348
- y: passRate,
360
+ y: endToEndPassRate,
349
361
  })} />
350
362
  ```
351
363
 
@@ -376,7 +388,7 @@ A 1 agents B 4 agents C 16 agents
376
388
  { a: "compare/bub", b: "compare/bub--agents-md", label: "bub" },
377
389
  { a: "compare/codex", b: "compare/codex--agents-md", label: "codex" },
378
390
  ],
379
- metrics: [passRate, costUSD],
391
+ metrics: [endToEndPassRate, costUSD],
380
392
  })} />
381
393
  ```
382
394
 
@@ -388,4 +400,4 @@ codex 80% → 80% ±0 $0.51 → — —
388
400
 
389
401
  ## 官方组件之外
390
402
 
391
- 以上摆法都表达不了时,用 `defineComponent` 写自己的双面组件——网页怎么渲染、终端字符怎么排,两个面你都说了算,写法见[自定义报告](/zh/guides/custom-reports)的「换形态」一节。
403
+ 以上摆法都表达不了时,用 `defineComponent` 写自己的双面组件——网页怎么渲染、终端字符怎么排,两个面你都说了算,写法见[自定义报告](/zh/how-to/custom-reports)的「换形态」一节。
@@ -4,7 +4,7 @@ sidebarTitle: "结果数据 API"
4
4
  description: "报告积木脚下的数据层:openResults 把 .niceeval/ 的落盘 artifact parse 成「实验 → 结果快照 → eval → attempt」的类型化层次,createResultsWriter 把别家结果写成 NiceEval 格式,copySnapshots 负责发布瘦身。"
5
5
  ---
6
6
 
7
- [自定义报告](/zh/guides/custom-reports)的积木——指标、计算函数、双面组件——脚下还有一层:`niceeval/results`,落盘 artifact 的 parser。官方两扇门和全部报告积木读的都是这一层,没有私有数据通道;报告表达不了的口径,下到这层直接拿数据算。
7
+ [自定义报告](/zh/how-to/custom-reports)的积木——指标、计算函数、双面组件——脚下还有一层:`niceeval/results`,落盘 artifact 的 parser。官方两扇门和全部报告积木读的都是这一层,没有私有数据通道;报告表达不了的口径,下到这层直接拿数据算。
8
8
 
9
9
  什么时候下到这层:
10
10
 
@@ -68,7 +68,7 @@ const attempt = snap.evals[0].attempts[0];
68
68
 
69
69
  attempt.evalId; // "algebra/quadratic" —— 属于哪道题,不用绕 result
70
70
  attempt.experimentId; // 属于哪个实验
71
- attempt.result; // EvalResult:判定、断言、用量、成本、experiment 元数据
71
+ attempt.result; // EvalResult:判定、断言、结构化 error/diagnostics、用量、成本
72
72
  attempt.ref; // { snapshot, attempt }:证据引用,与 view 深链、报告格子的 refs 同一身份
73
73
  await attempt.events(); // StreamEvent[] | null
74
74
  await attempt.trace(); // TraceSpan[] | null
@@ -83,6 +83,8 @@ await attempt.sources(); // SourceArtifact[] | null
83
83
  - **读不了的落盘不静默。** 版本不兼容、目录损坏的快照进 `skipped` 并带原因;要不要展示由你定,但缺口永远被算出来。
84
84
  - **同进程内按句柄记忆化。** 两处都读同一个 `diff()` 不会把上百 MB 读两遍。
85
85
 
86
+ `attempt.result.error` 是让 Attempt 进入 `errored` 的唯一致命执行错误,包含稳定 `code`、人可读 `message`、发生错误的 lifecycle operation,以及可选的有限 cause/stack。`attempt.result.diagnostics` 可以与任意判定共存,保存运行仍可继续或收尾时发现的问题。瞬时 `progress` 不落盘;OTel trace 也不是错误存储的前提。
87
+
86
88
  ## 超大输出会被截断
87
89
 
88
90
  Agent 跑一条命令,输出可以大得离谱——一次递归 `grep` 扫进 `node_modules`,撞上压缩过的 JS 文件,单行就有几 MB。这种输出会同时进 `events.json` 和 `trace.json`,不管的话一个 attempt 就能占上百 MB。
@@ -106,7 +108,9 @@ for (const e of events ?? []) {
106
108
  }
107
109
  ```
108
110
 
109
- `diff.json` 不截断——里面每个文件都要完整才能用来核对 agent 改了什么。它是唯一可能拖到上百 MB artifact,所以发布时通常不带它(见下面的 `copySnapshots`)。
111
+ 这条上限管的是**单个字符串值**,不是整个 JSON 文件。一个文件可以有很多正常值;`diff.json` 和源码也不能截断,因为它们要保持完整语义。所以 `.niceeval/` 适合做本地事实根,不默认适合直接提交进 Git。发布前用下面的 `copySnapshots` artifact 选择和整文件大小检查。
112
+
113
+ 截断发生在持久化边界,不能替 agent runtime 限制发给模型的工具输出。如果 runtime 先把 50 MB 工具结果完整塞进模型请求并收到 413,NiceEval 仍会把 Attempt 记为 `errored`;这里只保证失败后的 events / trace 不再被同一段输出撑爆。
110
114
 
111
115
  ## 版本:谁写的、读不读得了
112
116
 
@@ -156,7 +160,7 @@ latest.warnings[0];
156
160
 
157
161
  字段供程序判断——CI 里「覆盖缩水就 fail」直接判 `covered < total`,不解析文本;`message` 是渲染好的英文句子,要展示就原样打。渲染与否在你,缺口永远被算出来。警告不止这一种:快照落后于 Selection 中最新的落盘进 `stale-snapshot`、选中的快照没收尾(进程中断)进 `unfinished-snapshot`,每种都带 `kind`、可判断的结构化字段和渲染好的 `message`。
158
162
 
159
- Selection 是[报告积木](/zh/guides/custom-reports)和下文 `copySnapshots` 的通用输入:收 `Selection` 时 warnings 随行(`RunOverview` 之类的组件会如实展示),手工挑的 `Snapshot[]` 数组照收。微调官方口径不用降级成裸数组:`latest.filter((s) => s.experimentId !== "compare/broken")` 返回新 Selection——快照被删减,warnings 修剪到幸存的实验,provenance 不丢。`filter` 只做删减;「换成该实验上一个完整快照」这类**替换式**重挑不是它的事,回到 `exp.snapshots` 自己拿——手工挑的数组没有挑选过程,自然没有 warnings 可带,也如实。
163
+ Selection 是[报告积木](/zh/how-to/custom-reports)和下文 `copySnapshots` 的通用输入:收 `Selection` 时 warnings 随行(`RunOverview` 之类的组件会如实展示),手工挑的 `Snapshot[]` 数组照收。微调官方口径不用降级成裸数组:`latest.filter((s) => s.experimentId !== "compare/broken")` 返回新 Selection——快照被删减,warnings 修剪到幸存的实验,provenance 不丢。`filter` 只做删减;「换成该实验上一个完整快照」这类**替换式**重挑不是它的事,回到 `exp.snapshots` 自己拿——手工挑的数组没有挑选过程,自然没有 warnings 可带,也如实。
160
164
 
161
165
  ## 一个真实脚本:分布不是折叠
162
166
 
@@ -180,9 +184,9 @@ for (const exp of results.experiments) {
180
184
  }
181
185
  ```
182
186
 
183
- 即使在这条最深的路径上也不碰磁盘布局:路径拼接、存在性、版本过滤、快照切分全被库消化。要把这份分布摆进报告页,用 [`defineComponent`](/zh/guides/custom-reports) 包一个双面组件即可。
187
+ 即使在这条最深的路径上也不碰磁盘布局:路径拼接、存在性、版本过滤、快照切分全被库消化。要把这份分布摆进报告页,用 [`defineComponent`](/zh/how-to/custom-reports) 包一个双面组件即可。
184
188
 
185
- 一条跨快照累计时的义务:`--resume` 会把上一轮已通过、fingerprint 匹配的结果携带合入新快照,同一个 attempt 因此可能存在于多份落盘。携带条目不是空壳:它带着原快照的 `startedAt`(身份锚)与 `artifactBase`(指向原快照 attempt 目录的相对路径),懒加载按候选顺序回退——先本快照的 attempt 目录,再 `artifactBase` 指向的原快照目录(原快照被清理后如实返回 `null`);`ref` 指向条目所在的落盘,即携带入的那份新快照。身份键 `(experimentId, evalId, attempt, startedAt)` 的四个字段都在数据上——前两个是 attempt 的直达字段,序号与 `startedAt` 在 `attempt.result` 上。reader 忠实反映这份重复;跨快照聚合前用 `dedupeAttempts` 按身份键去重,重复保留最新快照里的那份——报告积木的计算函数内置这条,自己写脚本时记得过一遍:
189
+ 一条跨快照累计时的义务:NiceEval 默认把上一轮已有确定判定(passed / failed)、且 eval 代码和配置没变的结果携带合入新快照(`--force` 全部重跑),同一个 attempt 因此可能存在于多份落盘。携带条目不是空壳:它带着原快照的 `startedAt`(身份锚)与 `artifactBase`(指向原快照 attempt 目录的相对路径),懒加载按候选顺序回退——先本快照的 attempt 目录,再 `artifactBase` 指向的原快照目录(原快照被清理后如实返回 `null`);`ref` 指向条目所在的落盘,即携带入的那份新快照。身份键 `(experimentId, evalId, attempt, startedAt)` 的四个字段都在数据上——前两个是 attempt 的直达字段,序号与 `startedAt` 在 `attempt.result` 上。reader 忠实反映这份重复;跨快照聚合前用 `dedupeAttempts` 按身份键去重,重复保留最新快照里的那份——报告积木的计算函数内置这条,自己写脚本时记得过一遍:
186
190
 
187
191
  ```typescript
188
192
  import { dedupeAttempts } from "niceeval/results";
@@ -210,7 +214,7 @@ const snap = await writer.snapshot({ // 建快照目录(独占创建,撞名换
210
214
  });
211
215
 
212
216
  for (const r of convertedResults) {
213
- await snap.writeAttempt(r.result, { // 写 result.json(判决权威落点,一次写成)+ 拆 artifact 文件
217
+ await snap.writeAttempt(r.result, { // 写 result.json(判定权威落点,一次写成)+ 拆 artifact 文件
214
218
  events: r.events, // 第二参 = artifact,都可选;缺哪样读取面就懒加载出 null
215
219
  diff: r.diff,
216
220
  }); // 拆 artifact 文件、算目录、回填引用,全在库内发生
@@ -219,7 +223,7 @@ for (const r of convertedResults) {
219
223
  await writer.finish(); // 给每个快照补 completedAt,没有任何收尾聚合
220
224
  ```
221
225
 
222
- `writer.snapshot()` 就是读取面「实验 → 快照」层次的镜像:转多个 experiment 就开多个快照目录,experimentId / agent / model / startedAt 这些快照级元数据在这里声明一次,不用塞进每条 attempt;可选的 `knownEvalIds`(该实验已知的 eval 并集)也在这里声明——它是残缺检测的分母,转换只覆盖部分题目时如实交代全集,下游的覆盖警告就能算出来(`copySnapshots` 发布时会自动补记这个字段,见下文)。转完的目录就是标准结果目录:`niceeval show` / `niceeval view` 直接能看,报告积木直接能算,不用抄格式文档;`producer` 会原样出现在读取面的 `snap.producer` 上。**每个文件恰好写入一次**是写入面的核心承诺:`snapshot.json` 开跑即写、收尾只补 `completedAt`;`result.json` 与 artifact 随 attempt 完成落盘。进程中断只丢未完成的 attempt,已完成的判决与 artifact 已经在盘上——真正「有 attempt 落盘却没有 `snapshot.json`」的极端情况才归 `skipped("incomplete")`,未收尾但元数据齐全的快照能正常读,只带一条警告。
226
+ `writer.snapshot()` 就是读取面「实验 → 快照」层次的镜像:转多个 experiment 就开多个快照目录,experimentId / agent / model / startedAt 这些快照级元数据在这里声明一次,不用塞进每条 attempt;可选的 `knownEvalIds`(该实验已知的 eval 并集)也在这里声明——它是残缺检测的分母,转换只覆盖部分题目时如实交代全集,下游的覆盖警告就能算出来(`copySnapshots` 发布时会自动补记这个字段,见下文)。转完的目录就是标准结果目录:`niceeval show` / `niceeval view` 直接能看,报告积木直接能算,不用抄格式文档;`producer` 会原样出现在读取面的 `snap.producer` 上。**每个文件恰好写入一次**是写入面的核心承诺:`snapshot.json` 开跑即写、收尾只补 `completedAt`;`result.json` 与 artifact 随 attempt 完成落盘。进程中断只丢未完成的 attempt,已完成的判定与 artifact 已经在盘上——真正「有 attempt 落盘却没有 `snapshot.json`」的极端情况才归 `skipped("incomplete")`,未收尾但元数据齐全的快照能正常读,只带一条警告。
223
227
 
224
228
  ## 发布:`copySnapshots`
225
229
 
@@ -230,22 +234,26 @@ import { openResults, copySnapshots } from "niceeval/results";
230
234
 
231
235
  const results = await openResults(".niceeval");
232
236
  await copySnapshots(results.latest(), "site-data/run", {
233
- artifacts: ["sources", "events", "trace", "o11y"], // diff 不截断、可达百 MB,发布时常见地不带;
234
- }); // events / trace 有 256 KiB 上限,带上是安全的;
237
+ artifacts: ["sources", "events", "trace", "o11y"], // diff 不截断,缺省也不带;
238
+ redact: (text) => text.replaceAll(/sk-[A-Za-z0-9]+/g, "[redacted]"),
239
+ }); // redact 必填:函数消毒,或 false 显式声明原文发布;
240
+ // 每个待发布文件还会经过 50 MiB 预检;
235
241
  // o11y 只有几 KB,报告用到 turns 这类
236
242
  // 读 o11y 的指标就把它带上,不然渲染成「—」
237
243
  ```
238
244
 
239
- 第一个参数收 `Selection` 或手工挑的 `Snapshot[]`——和报告积木同一个输入约定。`artifacts` 的合法值是 `"events" | "trace" | "o11y" | "diff" | "sources"`,缺省全带。目标目录已存在且非空时报错,不静默覆盖——发布脚本要幂等就自己先清目标目录。
245
+ 第一个参数收 `Selection` 或手工挑的 `Snapshot[]`——和报告积木同一个输入约定。`artifacts` 的合法值是 `"events" | "trace" | "o11y" | "agentSetup" | "diff" | "sources"`;缺省带除 `diff` 外的五类。目标目录已存在且非空时报错,不静默覆盖——发布脚本要幂等就自己先清目标目录。
246
+
247
+ 复制开始前,NiceEval 会规划全部目标文件并检查序列化后的大小。任一文件超过固定的 50 MiB,整次复制在创建目标目录前失败,错误会列出路径、实际大小和处理建议。你可以从 `artifacts` 排除那类证据;如果是旧版本留下的超大 events / trace,用当前版本重跑后再发布。这个检查既覆盖没有逐值截断的源码 / diff,也覆盖单值都正常但累计过大的 JSON,避免直到 `git push` 才撞上 Git host 的单文件限制。
240
248
 
241
- 复制不改 artifact 内容、不消毒;发布前给自由文本消毒用报告积木 `AttemptList.data` 的 `redact` 钩子。唯一随行补记的是挑选时的**覆盖事实**:`partial-coverage` 警告的分母是实验的历史并集,而发布目录没有历史——所以每个复制出的快照带上 `knownEvalIds`(复制时刻该实验已知的 eval 并集),reader 端把它并进 `exp.evalIds` 的计算(取本地历史与快照携带值的并集)。发布目录上重新 `openResults().latest()`,残缺警告被同一套机制重新算出来,不靠发布者转述。复制出的目录就是标准结果目录,`niceeval view --run <目录>` 直接能看;要让报告站随 push 自动更新,workflow 见[通过 CI 发布报告](/zh/guides/publish-report)。
249
+ 大小预检只决定整次复制成功或失败,不会从一个超大文件中间删内容。消毒不是可选项——`copySnapshots` 要求显式传 `redact`:给一个函数就改写复制出来的所有文件里的自由文本(events、trace、源码、diff、运行摘要都在内;id、事件类型这类标识字段不动),确定这批数据可以原文公开就传 `redact: false`,两个都不传会直接报错。注意报告积木 `AttemptList.data` 的 `redact` 只影响页面上显示的数据,管不到发布目录里的 artifact 文件;发布场景一律在 `copySnapshots` 这一步消毒。唯一随行补记的是挑选时的**覆盖事实**:`partial-coverage` 警告的分母是实验的历史并集,而发布目录没有历史——所以每个复制出的快照带上 `knownEvalIds`(复制时刻该实验已知的 eval 并集),reader 端把它并进 `exp.evalIds` 的计算(取本地历史与快照携带值的并集)。发布目录上重新 `openResults().latest()`,残缺警告被同一套机制重新算出来,不靠发布者转述。复制出的目录就是标准结果目录,`niceeval view --run <目录>` 直接能看;要让报告站随 push 自动更新,workflow 见[通过 CI 发布报告](/zh/how-to/publish-report)。
242
250
 
243
251
  ## 分层速览
244
252
 
245
253
  | 层 | 入口 | 回答 |
246
254
  | --- | --- | --- |
247
255
  | 官方两扇门 | `niceeval show` / `niceeval view` | 零代码看官方摆法 |
248
- | 报告积木 | [自定义报告](/zh/guides/custom-reports)、[报告组件](/zh/guides/report-components) | 自己的口径与摆法 |
256
+ | 报告积木 | [自定义报告](/zh/how-to/custom-reports)、[报告组件](/zh/reference/report-components) | 自己的口径与摆法 |
249
257
  | 结果数据 API(本页) | `niceeval/results` | 折叠表达不了的算法、接自己的系统、读写格式本身 |
250
258
 
251
259
  每层都建立在下一层之上,同一份落盘 artifact 是唯一事实来源——上层的派生物删了随时可重算。
@@ -0,0 +1,57 @@
1
+ ---
2
+ title: "保留沙箱现场排查问题"
3
+ sidebarTitle: "保留沙箱现场"
4
+ description: "用 --keep-sandbox 把失败 Attempt 的沙箱保留成可随时唤醒的现场,用 niceeval sandbox enter 进去手动排查,用 sandbox list / stop 查看和清理。"
5
+ ---
6
+
7
+ 沙箱默认在每个 Attempt 结束后销毁,排查依据是落盘的 artifact:`niceeval show` 能看到判定、断言、diff 和事件流。大多数问题到这里就够了——完整的排查路线见 [Debug 手册](/zh/troubleshooting/debugging)。
8
+
9
+ 但有些问题只能进活的环境里看:
10
+
11
+ - **环境起不来**——setup 阶段装依赖失败、agent CLI 启动不了。这时 agent 还没开始跑,事件流是空的,最快的办法是进沙箱手动重跑一遍安装命令。
12
+ - **改动落在 `git diff` 之外**——全局装了什么包、`$HOME` 下写了什么配置、`PATH` 实际是什么,artifact 里没有。
13
+ - **重跑太慢**——冷启动加安装要几分钟,想逐条验证猜测时,留着现场比每次重跑快得多。
14
+
15
+ ## 跑的时候保留现场
16
+
17
+ ```bash
18
+ npx niceeval exp local onboarding/tool-first --keep-sandbox # 等价 --keep-sandbox=failed
19
+ npx niceeval exp local onboarding/tool-first --keep-sandbox=all # 通过的也保留
20
+ ```
21
+
22
+ `--keep-sandbox` 是 `niceeval exp` 的运行参数,两档:`failed`(缺省值)保留判定为 `failed` 或 `errored` 的 Attempt(包括超时打断的);`all` 连通过的也保留——调 setup 钩子、核对通过环境的真实状态时用它,不用故意弄挂一条 eval。不带这个参数时全部销毁。
23
+
24
+ 运行结束后,摘要里会列出保留了哪些沙箱、怎么进去:
25
+
26
+ ```text
27
+ Kept sandboxes (1)
28
+ @1x7f3q9k onboarding/tool-first #1 errored docker · a3f9c2d1
29
+ enter: niceeval sandbox enter a3f9c2d1
30
+ Stop them with: niceeval sandbox stop --all
31
+ ```
32
+
33
+ 每行给三样东西:Attempt 定位符(用 `niceeval show @1x7f3q9k` 看落盘证据)、沙箱实例 id、进入现场的命令。保留下来的沙箱不会一直跑着烧资源——Docker 容器停在磁盘上,E2B 微 VM 暂停计费,Vercel 保存文件系统。`niceeval sandbox enter` 会先唤醒再进入,在 workdir 打开 shell;退出 shell 后现场自动回到休眠(想让它保持运行,加 `--leave-running`)。进去之后就是这次 Attempt 跑完时的环境,可以手动执行命令、翻文件、复现失败。
34
+
35
+ ## 查看和清理
36
+
37
+ 保留下来的沙箱逐条记录在 `.niceeval/sandboxes/` 里,用 `niceeval sandbox` 管理:
38
+
39
+ ```bash
40
+ niceeval sandbox list # 列出保留的沙箱和现场状态
41
+ niceeval sandbox enter a3f9c2d1 # 唤醒并进入;退出后自动回到休眠
42
+ niceeval sandbox stop a3f9c2d1 # 销毁指定沙箱(id 可以只写唯一前缀)
43
+ niceeval sandbox stop --all # 全部销毁
44
+ ```
45
+
46
+ `stop` 是幂等的:沙箱已经不在了(手动删过、云端过期)不算错误,只会把记录移掉并说明。如果 provider 销毁失败,命令会保留记录并返回错误,方便稍后重试,不会把仍活着的资源从列表里藏掉。忘了清也有提醒——下次运行开始时,如果还有上次保留的沙箱,会打一行提示。
47
+
48
+ ## 各 Provider 的差别
49
+
50
+ - **Docker**:保留 = 容器停在磁盘上(不占内存,重启 Docker 也还在),进入时自动启动。容器不会自己消失,是唯一需要主动清理的 provider。除了 `niceeval sandbox stop`,也可以用 `docker ps -a -f label=niceeval.keep-candidate=true` 直接核对。
51
+ - **E2B**:保留 = 暂停微 VM——文件和内存整体保存,暂停期间停止计费、无限期保留,进入时自动恢复。
52
+ - **Vercel Sandbox**:保留 = 停止微 VM——文件系统保存、之后可恢复,但内存状态不保留,唤醒后进程要重新启动;超过 provider 的保留期限后 `niceeval sandbox list` 标成 `expired`。
53
+ - **自定义 Provider**:`defineSandbox` 产出的 provider 不支持留存,因为事后的 `sandbox stop` 不加载用户配置,无法在新进程里安全找回自定义销毁函数。
54
+
55
+ ## 边界
56
+
57
+ 保留的沙箱只用来排查,不能续跑或重新评分;判定、断言、diff 这些结论仍以 artifact 为准。查看 artifact 的方法见[查看结果](/zh/how-to/viewing-results)。
@@ -0,0 +1,212 @@
1
+ ---
2
+ title: "排查失败与复盘历史运行"
3
+ sidebarTitle: "Debug 手册"
4
+ description: "一份按场景组织的排查手册:断言失败怎么定位、环境错误怎么进沙箱、agent 改了什么怎么看、旧的运行怎么翻出来复盘——每一步都有命令顺序和输出示例。"
5
+ ---
6
+
7
+ 跑完一次 `niceeval exp`,失败的 Attempt 都带一个 `@` 开头的定位符(如 `@1qrdcfq8`)。它出现在运行摘要、CI 日志和报告里,定位符本身不会过期——只要 `.niceeval/` 里对应的结果快照还在,今天的定位符下周还能用同一条命令打开同一次 Attempt。所有排查都从它开始。
8
+
9
+ 先说一条通用规则:NiceEval 自己的报错和警告都在消息末尾直接给出下一步。能用一条命令解决的,消息里就是替换好实验名的完整命令,复制执行即可(网页里还可以一键复制);可以不管的警告会写明「什么情况下可以忽略」。所以看到报错先读完最后一句;本手册处理的是消息之外还需要人工判断的场景——判定失败了怎么定位、环境错误怎么进现场。
10
+
11
+ ## 第一步永远是 `niceeval show @<定位符>`
12
+
13
+ 不带任何参数打开 Attempt,第一页就是为排查设计的:判定、失败的断言、耗时分布、改动概览,以及下一步可用的命令。
14
+
15
+ ```text
16
+ $ niceeval show @1qrdcfq8
17
+ @1qrdcfq8 · memory/swelancer-manager-proposals · dev-e2b/codex-e2b · failed
18
+ snapshot 2026-07-12T10:08:29.361Z · attempt 1 · 50.0s · 58.5k tokens · $0.05
19
+
20
+ assertions: 3 passed · 1 gate failed
21
+ eval source: evals/memory/swelancer-manager-proposals.eval.ts · sha256:ee33b9c4…
22
+
23
+ failures:
24
+ gate · Issue 15193: selected proposal matches the one maintainers accepted
25
+ assertion: equals(4)
26
+ expected: 4
27
+ received: 1
28
+ source: evals/memory/swelancer-manager-proposals.eval.ts:40:11
29
+
30
+ execution: 12 events · 0 skill loads · 7 tool calls · 4 AI messages
31
+ timing: sandbox.queue 0.2s · sandbox.create 5.6s · sandbox.setup 3.5s · agent.setup 12.1s ·
32
+ eval.run 26.3s · workspace.diff 0.3s · scoring.evaluate 1.4s · teardown +0.8s
33
+
34
+ changes: 2 files changed by agent · M manager_decisions.json · A notes/decision-log.md
35
+
36
+ available:
37
+ niceeval show @1qrdcfq8 --eval
38
+ niceeval show @1qrdcfq8 --execution
39
+ niceeval show @1qrdcfq8 --timing
40
+ niceeval show @1qrdcfq8 --diff
41
+ ```
42
+
43
+ 看这一页先回答一个问题:**是 agent 答错了(`failed`),还是环境根本没跑起来(`errored`)?** 两种情况的排查路线完全不同。
44
+
45
+ ## 场景一:断言失败(failed)——agent 跑完了,但结果不对
46
+
47
+ 排查顺序是「哪条断言挂了 → agent 当时做了什么 → 它到底改了什么」。
48
+
49
+ **1. 把断言放回源码。** `--eval` 显示运行时保存的那份 eval 源码(不是你工作区里可能已经改过的版本),失败的断言直接标在对应行上;`t.send(...)` 的调用行标出它产生的那一轮——轮标签(`s1/t1`,与 `--execution` / `--timing` 用同一套)、这轮成没成、花了多久:
50
+
51
+ ```text
52
+ $ niceeval show @1qrdcfq8 --eval
53
+ 21✓ await t.send("Review the proposals and record your decision…");
54
+ s1/t1 · completed · 22.4s
55
+ 38 for (const [issue, label] of Object.entries(expected)) {
56
+ 39 await t.group(`Issue ${issue}: selected proposal matches…`, async () => {
57
+ 40✗ t.check(Number(decisions[issue]?.selected_proposal_id), equals(label.selected_proposal_id));
58
+ gate · Issue 15193 · equals(4) · expected 4 · received 1
59
+ 41 });
60
+ 42 }
61
+ ```
62
+
63
+ **2. 看 agent 当时做了什么。** `--execution` 把这次 Attempt 的对话按时间线展开——用户消息、assistant 回复、每次工具调用的入参和结果:
64
+
65
+ ```text
66
+ $ niceeval show @1qrdcfq8 --execution
67
+ TURN s1/t1 · completed · 22.4s · 12.4k tok · $0.02
68
+ USER
69
+ Review the proposals and record your decision for each issue…
70
+
71
+ ASSISTANT
72
+ I'll inspect the task layout and the decision format first…
73
+
74
+ TOOL · command_execution +12.8s · 1.3s
75
+ input
76
+ /bin/bash -lc 'cat tasks/15193/proposals.md'
77
+ result · completed · exit 0
78
+ Proposal 1: …
79
+ ```
80
+
81
+ 对话按轮分段,每轮头行给出编号(`s1/t1`)、状态、耗时和用量——这个编号和 `--diff`、`--timing` 里的轮次标签是同一套,能互相对照。
82
+
83
+ **3. 看它到底改了什么。** `--diff` 只显示 **agent 自己改动的文件**——你上传的起始文件、跑完后写入的验证材料不会混在里面,所以列表里的每一行都真的是 agent 干的:
84
+
85
+ ```text
86
+ $ niceeval show @1qrdcfq8 --diff
87
+ 2 files changed by agent
88
+ M manager_decisions.json +6 -2 s1/t1, s1/t2
89
+ A notes/decision-log.md +18 s1/t2
90
+
91
+ single file: niceeval show @1qrdcfq8 --diff=manager_decisions.json
92
+ ```
93
+
94
+ 行尾的 `s1/t1` 表示这个文件是在第几轮对话里被改的,能和 `--execution` 的轮次对上。要看单个文件的逐行改动,用 `=` 连写文件路径:
95
+
96
+ ```text
97
+ $ niceeval show @1qrdcfq8 --diff=manager_decisions.json
98
+ M manager_decisions.json · changed in s1/t1, s1/t2
99
+ @@ -1,5 +1,7 @@
100
+ {
101
+ - "15193": { "selected_proposal_id": 1 },
102
+ + "15193": { "selected_proposal_id": 4 },
103
+ ```
104
+
105
+ 到这里通常能下结论:是任务描述有歧义、agent 理解错了,还是断言本身写得太死。
106
+
107
+ **要看文件本身,而不只是改动?** 落盘的证据刻意不保存整个工作区——`--diff` 只有 agent 改过的文件,agent 该写没写的文件、你上传的起始材料、setup 装出来的东西都不在里面。想看它们的实际内容,进活现场:重跑这一条 eval 加 `--keep-sandbox`(`failed` 的 Attempt 同样会保留,不只是环境错误),用下面场景二的方式进沙箱,workdir 里就是这次跑完时的完整文件树。
108
+
109
+ ## 场景二:环境错误(errored)——agent 根本没跑起来
110
+
111
+ `errored` 的第一页不列断言,而是列出错误发生在哪个阶段、什么原因:
112
+
113
+ ```text
114
+ $ niceeval show @12h8m4k1
115
+ @12h8m4k1 · memory/agent-029-use-cache · compare/claude-e2b · errored
116
+
117
+ error:
118
+ phase: sandbox.create
119
+ code: sandbox-rate-limit
120
+ message: E2B sandbox allocation failed after 5 attempts
121
+ cause: RateLimitError · too many concurrent sandboxes
122
+
123
+ execution: unavailable (attempt failed before telemetry was configured)
124
+ timing: sandbox.queue 1.2s · sandbox.create 2m 6s ✗ failed here
125
+ ```
126
+
127
+ `phase` 直接告诉你死在哪一步,而且决定了下一步走哪条路:
128
+
129
+ **`sandbox.create` 失败——沙箱根本没创建出来,没有现场可留。** 这类错误(配额、限流、凭据、镜像 / 模板不存在)在你自己的机器和账号侧排查:核对 API key 和配额、降低 `--max-concurrency`、确认镜像 / 模板名。示例里的 rate-limit 就属于这类,重跑加 `--keep-sandbox` 只会原地再死一次。
130
+
131
+ **`sandbox.setup` / `agent.setup` / `eval.run` 失败——沙箱活过,值得留现场。** 装依赖失败、agent CLI 起不来、跑到一半超时,这类问题事件流往往是空的,落盘证据帮不上忙,最快的办法是留住现场进去手动重跑一遍出错的命令:
132
+
133
+ ```bash
134
+ # 只重跑这一条 eval,失败时保留沙箱
135
+ npx niceeval exp compare memory/agent-029 --keep-sandbox
136
+ ```
137
+
138
+ ```text
139
+ Kept sandboxes (1)
140
+ @18c1m2qx memory/agent-029-use-cache #1 errored docker · a3f9c2d1
141
+ enter: niceeval sandbox enter a3f9c2d1
142
+ Stop them with: niceeval sandbox stop --all
143
+ ```
144
+
145
+ `niceeval sandbox enter a3f9c2d1` 会唤醒现场并在 workdir 打开 shell——手动执行安装命令看真实报错、翻 `$HOME` 下的配置、检查 `PATH`,这些都在 artifact 之外,只有活现场能回答;退出 shell 后现场自动回到休眠,不白烧资源。保留策略、各 provider 的差别见[保留沙箱现场](/zh/troubleshooting/debug-sandbox)。
146
+
147
+ ## 查看和清理留下的沙箱
148
+
149
+ 保留下来的沙箱不会一直烧资源:Docker 容器停驻在磁盘上,E2B 微 VM 暂停计费,进入时自动唤醒。用 `niceeval sandbox` 管理:
150
+
151
+ ```text
152
+ $ niceeval sandbox list
153
+ ID PROVIDER STATE FROM
154
+ a3f9c2d1 docker dormant memory/agent-029-use-cache #1 · errored · @18c1m2qx · 2026-07-14 15:02
155
+ enter: niceeval sandbox enter a3f9c2d1
156
+ 9f21c07b vercel expired onboarding/tool-first #2 · failed · @1x7f3q8a · 2026-07-14 14:31
157
+ expired 2026-07-14 14:36 — remove with: niceeval sandbox stop 9f21c07b
158
+ ```
159
+
160
+ `dormant` 是「睡着但随时能进」,`expired` 是「现场已经没了,只剩记录」。排查完记得清理:
161
+
162
+ ```bash
163
+ niceeval sandbox stop a3f9c2d1 # id 可以只写唯一前缀
164
+ niceeval sandbox stop --all
165
+ ```
166
+
167
+ ## 场景三:复盘旧的运行
168
+
169
+ 每次运行都会在 `.niceeval/<实验>/<时间戳>/` 下留一份完整的结果快照,判定、断言、事件流、diff 都在里面,**不会被下一次运行覆盖**。复盘有三个入口:
170
+
171
+ **用旧定位符直接打开。** 从上周的终端记录、CI 日志或报告里复制 `@` 定位符,`niceeval show @<定位符>` 照常工作,上面的 `--eval` / `--execution` / `--diff` 全部可用——包括那份运行时的 eval 源码,哪怕你后来把 eval 改了。
172
+
173
+ **按实验回看当前水位。** 不记得定位符时,从实验入手:
174
+
175
+ ```bash
176
+ niceeval show --experiment compare/bub # 这个实验每道题现在的判定
177
+ niceeval show memory/swelancer --experiment compare/bub # 收窄到某道题
178
+ ```
179
+
180
+ 列表里每道题、每次 Attempt 都带定位符,接着往深处钻就回到上面的场景一 / 场景二。
181
+
182
+ **在浏览器里翻。** 复盘一批失败、对比多次 Attempt 时,网页比终端顺手:
183
+
184
+ ```bash
185
+ niceeval view
186
+ ```
187
+
188
+ 首页是成本 × 通过率总览和实验对比表;每个 Attempt 的详情页有判定、断言、完整时间树、对话、trace 和 diff,还有「Copy fix prompt」按钮——把失败整理成一段可以直接交给 coding agent 的修复提示词。报告里的 Attempt 深链和 `show` 用同一套定位符。
189
+
190
+ **打开归档或别人发来的结果。** 结果目录是自包含的——从 CI 下载的、同事拷给你的、发布到静态站前生成的目录,都能直接指过去:
191
+
192
+ ```bash
193
+ niceeval show --run tmp/ci-artifacts/results
194
+ niceeval view --run site-data/run
195
+ ```
196
+
197
+ 一个注意点:如果本地清理过旧快照目录,之后的运行里「沿用上次结果」的条目会找不到原始证据(显示为缺失)。要长期归档某次运行,先用 [`copySnapshots`](/zh/reference/results-data) 复制出一份再删。
198
+
199
+ ## 速查:症状 → 命令
200
+
201
+ | 症状 | 命令顺序 |
202
+ |---|---|
203
+ | 断言挂了,不知道为什么 | `show @loc` → `show @loc --eval` |
204
+ | 想知道 agent 当时做了什么 | `show @loc --execution` |
205
+ | 想确认 agent 改了哪些文件 | `show @loc --diff` → `--diff=<path>` |
206
+ | 哪一步慢 / 超时死在哪 | `show @loc`(看 `timing:` 行)→ `show @loc --timing` |
207
+ | 沙箱创建就失败(配额 / 凭据 / 镜像) | `show @loc` 看 error 的 code 与 cause → 查账号配额、核对凭据、降 `--max-concurrency`(没有现场可留) |
208
+ | 装依赖失败、CLI 起不来、跑一半超时 | 重跑该 eval 加 `--keep-sandbox` → `niceeval sandbox enter <id>` |
209
+ | 想看文件实际内容(agent 没改的、起始材料、`$HOME`) | 重跑加 `--keep-sandbox`(failed 也留)→ `sandbox enter` 进 workdir 看 |
210
+ | 留了哪些沙箱、清理 | `sandbox list` → `sandbox stop <id>` / `--all` |
211
+ | 复盘上周那次失败 | 翻出旧定位符 → `show @loc`;不记得就 `show --experiment <实验>` |
212
+ | 一批失败一起看 | `niceeval view` → Attempt 详情 → Copy fix prompt |