> ## Documentation Index
> Fetch the complete documentation index at: https://niceeval.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 查看 NiceEval 结果并调试 agent 行为

> 用 niceeval show 在终端按 @<locator> 查看 Attempt 的评估用例源码、执行记录与 diff，或用 niceeval view 在网页中查看同一份证据。

每次运行后，[NiceEval](https://niceeval.com/) 都会把结构化结果写入该实验的**结果快照**目录 `.niceeval/<experiment>/<快照>/`。快照记录本次运行实际选择的 `selectedEvalIds`；`show` / `view` 直接比较当前 Scope 中的 experiments。

## 控制台输出

```text theme={null}
Plan: 45 attempts · 9 evals × 5 configs · concurrency 19
✗ @12h8m4k1 fixtures/button [compare/claude-e2b] errored · sandbox.create
    sandbox-rate-limit: E2B sandbox allocation failed after 5 attempts
    Inspect: niceeval show @12h8m4k1

niceeval exp compare                                      2m 14s
45 total · 6 reused · 19 running · 12 queued · 8 completed  $0.84

ACTIVE
● memory/agent-029-use-cache  compare/bub-e2b      1m 42s  running tests
● memory/agent-030-app-route compare/codex        1m 18s  editing src/app.ts
… 17 more active
```

Human profile 只原位更新当前总数和 active slots，不把历史帧推入 scrollback。失败、错误和去重后的 diagnostic 会保留并带 locator。结束块只包含摘要、失败 locator、查看命令和结果路径。完整的运行、读取、修复与重跑流程见 [Coding Agent 反馈闭环](/docs/zh/tutorials/agent-feedback-loop)。

## `.niceeval/<experiment>/<快照>/`

落盘单位是**结果快照**（一个 experiment 的一次运行）：实验目录在外层，快照目录（时间戳 + 随机后缀，独占创建）在实验目录下。典型结构：

```text theme={null}
.niceeval/
└─ compare_web-agent/                     # 实验目录:experimentId 清洗后的名字
   └─ 2026-07-09T10-00-00-000Z-x1f2/      # 快照目录:时间戳 + 随机后缀
      ├─ snapshot.json                    # 快照元数据(开始时写,收尾补 completedAt)
      └─ weather-tool/a0/                 # 单个 eval attempt 的目录
         ├─ result.json                   # 判定、断言、结构化错误/diagnostics、用量
         ├─ events.json
         ├─ sources.json
         ├─ trace.json
         ├─ o11y.json
         └─ diff.json
```

## `niceeval show`：终端下钻

`niceeval show` 的位置参数有两种形态：评估用例 ID 前缀选择评估用例范围，`@<locator>` 精确指定一次 Attempt。Flag 选择证据类型：

```bash theme={null}
niceeval show                       # 默认报告：比较当前 Scope 并下钻到 Attempt
niceeval show weather               # 前缀过滤：weather/* 下每个 eval 的判定
niceeval show weather/brooklyn      # 单个 eval：各 experiment 的 attempt 行(含 @<locator>)、默认 attempt 的断言明细
niceeval show @1k2m9qrs             # 精确到一次 Attempt：断言、执行、diff 与可用证据摘要
niceeval show @1k2m9qrs --source      # 该 Attempt 运行时保存的 Eval 源码,断言标回源码行
niceeval show @1k2m9qrs --execution # 该 Attempt 的消息、thinking、Skill 加载、工具调用,有 OTel 时补时间
niceeval show @1c3h6twx --timing    # 有界诊断时间树:phase、hook、operation、shell、turn、OTel 与收尾
niceeval show @1c3h6twx --timing=full # 同一棵时间树逐节点完整展开
niceeval show @1c3h6twx --diff      # sandbox 里的文件改动
niceeval show weather/brooklyn --history      # 跨 run 时间轴：抖动与回归拐点
```

`@<locator>` 是一次 Attempt 的持久、不透明身份，形如 `@1k2m9qrs`：由 `{experimentId, 快照时刻, evalId, attempt 序号}` 这个身份元组确定性派生，同一份落盘结果反复解析恒得到同一个值。它出现在 `niceeval show` 的分组明细里，直接复制粘贴就能定位到那一次运行。列表里的 locator 只打印 `@<id>`，不追加判定或证据能力缩写；打开 Attempt 首页后，`available` 会列出实际可用的证据命令。

Sandbox 创建、setup 或 teardown 错误不依赖 trace。`result.json` 保存错误的 `code`、`message`、生命周期阶段（`phase`）、有限 cause/stack 和 diagnostics；trace 只在存在时补调用关系与耗时。Attempt 在 cleanup、teardown 和 sandbox stop 结束后才封口写入，因此收尾 diagnostic 也能回顾。

同一个 Experiment 多次运行会留下多份结果。不带 `@<locator>` 的默认视图显示每个 Experiment × 评估用例最新的判定，并可能从同一个 Experiment 的多次运行中组合结果。按前缀只重跑部分评估用例时，其余评估用例的判定从更早的运行补齐。每份判定都带有生成时间，因此可以识别组合结果的来源。组合结果可能包含不同版本的被测代码，最终判定应以 `--force` 全量重跑为准。

单个评估用例视图里，多 experiment、多 Attempt 时默认展开最新一次失败的 Attempt；需要精确查看就复制该行的 `@<locator>`。`--exp agents/codex` 按 experiment id 路径收窄；`--results` 换结果根，`--snapshot` 只看某一次快照。

`niceeval view` 的每个视图都有 CLI 对应物：

| `niceeval view` 里的视图                      | 终端对应                                                                                                      |
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| 当前结果的分组比较报告                               | `niceeval show`                                                                                           |
| 该评估用例各 experiment 的判定行 + 默认 attempt 的断言明细 | `niceeval show <eval id>`                                                                                 |
| 单次 Attempt 摘要（断言、执行、diff 与证据可用性）          | `niceeval show @<locator>`                                                                                |
| 评估用例源码标注（断言标回源码行）                         | `niceeval show @<locator> --source`                                                                       |
| AI 对话、thinking、Skill 加载与工具调用（有 OTel 时补时间） | `niceeval show @<locator> --execution`                                                                    |
| 单次 Attempt 的阶段耗时分解                        | `niceeval show @<locator> --timing`；逐节点审计用 `--timing=full`                                                |
| 文件改动                                      | `niceeval show @<locator> --diff`                                                                         |
| 历史 run 列表                                 | `niceeval show <eval id> --history`；钉死某一次用 `view --snapshot <snapshot.json>` 或复制该行 `@<locator>` 直接 `show` |

### 默认分组比较报告

不带 flag 的 `niceeval show` 直接输出当前 Scope 的成本 × 端到端成功率图和实验列表。每个 experiment 的 eval 数与指标分母读取其快照 `selectedEvalIds`；未选择的评估用例不补成失败。

实验列表先给固定列汇总，再按 experiment → 评估用例 → Attempt 展开。locator 只保留 `@<id>`；完整断言和 evidence 留在 `niceeval show @<locator>`。只有一个 experiment 时散点仍显示单点。

### 单个评估用例

```text theme={null}
$ niceeval show weather/brooklyn
weather/brooklyn — 布鲁克林天气查询

compare/bub-gpt-5.4     ✓ passed   1 attempt    38.0s   $0.03   (2h ago)    @1xqur9kx
compare/codex-gpt-5.4   ✗ failed   3 attempts   41.2s   $0.12   (40s ago)   @1k2m9qrs   gate calledTool("get_weather")

attempt 3 · compare/codex-gpt-5.4 · failed · 41.2s · 12.3k tokens · $0.04
  ✗ gate calledTool("get_weather") — tool was never called
  ✓ gate succeeded()
  ✗ soft judge("回答基于实时数据") — 0.2/1: reply invents a temperature without any tool call

artifacts: .niceeval/compare_codex-gpt-5.4/2026-07-09T10-00-00-000Z-x1f2/weather/brooklyn/a2/
attempt locator: @1k2m9qrs
next: niceeval show @1k2m9qrs [--source|--execution]
```

每个 experiment 一行的紧凑索引末尾就带着代表 attempt 的 `@<locator>`（`compare/bub-gpt-5.4` 只有一次 attempt 就代表它自己，`compare/codex-gpt-5.4` 代表的是默认展开的那一次失败）；展开的明细块结尾再重复一次精确的 `attempt locator:`，两处给的是同一个值。想看别的 attempt（比如 `compare/codex-gpt-5.4` 更早失败的那次），去 `--history` 或 artifact 目录里找到它的 locator，`niceeval show @<locator>` 直接跳过去，不需要先回到这张列表。

### `@<locator>`：一次 Attempt 的摘要

不带证据 flag 的 `niceeval show @<locator>` 是失败诊断首页：除断言计票外，失败项直接列 group、matcher、expected、received、原因和源码位置；随后给出评估用例源码可用性、Agent 事件计数、OTel 计时可用性与工作区 diff 摘要。该页用于定位失败原因；需要追查 Agent 结果的来源时，再打开对应证据视图：

```text theme={null}
$ niceeval show @1k2m9qrs
@1k2m9qrs · weather/brooklyn · compare/codex-gpt-5.4 · failed
snapshot 2026-07-09T10:00:00.000Z · attempt 3 · 41.2s · 12.3k tokens · $0.04

assertions: 1 passed · 1 gate failed · 1 soft below target
eval source: evals/weather/brooklyn.eval.ts · sha256:a3fa4555…

failures:
  gate · Issue 15193: selected proposal matches the one maintainers accepted
    assertion: equals(4)
    expected: 4
    received: 1
    source: evals/memory/swelancer-manager-proposals.eval.ts:40:11

execution: 2 events · 0 skill loads · 0 tool calls · 1 AI messages
timing: eval.run 40.8s · scoring.evaluate 0.3s

changes: diff unavailable · no workspace diff was recorded for this attempt

artifacts: .niceeval/compare_codex-gpt-5.4/2026-07-09T10-00-00-000Z-x1f2/weather/brooklyn/a2/
available:
  niceeval show @1k2m9qrs --source
  niceeval show @1k2m9qrs --execution
  niceeval show @1k2m9qrs --timing
```

该视图只给摘要，不复现完整证据。完整源码、事件流和文件列表分别由对应的证据 flag 展开。`changes` 行会说明 diff 不可用的具体原因：此处的 `weather/brooklyn` 不是 Sandbox 评估用例，因此从未收集工作区改动；Sandbox 评估用例未修改文件时则显示「没有产生工作区文件改动」。

### `--source`、`--execution`、`--timing`、`--diff`：四个证据切面

四个证据 flag 既可以加在 `@<locator>` 后面（精确到一次 Attempt），也可以加在能唯一收窄到一个评估用例的前缀后面（挑同一套默认启发式选中的 attempt——最新一次失败，没有失败就挑最新一次，与单个评估用例详情块展开的是同一次）；前缀撞到不止一个评估用例时会报错，报错正文直接给出每个候选评估用例的 `@<locator>`，照抄一个继续即可。可以同时传多个证据 flag，一次输出全部要看的切面。

`--source` 是运行时保存的评估用例源码，按行标注每条断言、gate 失败与 soft 分数直接排在对应源码行下面，源码里没能对应到具体行的断言（没有位置信息，或位置指向另一个文件）单独成一段，永不丢弃：

```text theme={null}
$ niceeval show @1k2m9qrs --source
@1k2m9qrs · weather/brooklyn · compare/codex-gpt-5.4 · failed

eval source: evals/weather/brooklyn.eval.ts · sha256:a3fa4555…

1  export default defineEval({
2    id: "weather/brooklyn",
3    async test(t) {
4      const turn = await t.send("布鲁克林今天天气怎么样?");
5✗     turn.calledTool("get_weather");
   gate · tool was never called
6✓     turn.succeeded();
7✗     t.check(turn.message, judge("回答基于实时数据"));
   soft · 0.2/1 · reply invents a temperature without any tool call
8    },
9  });

assertions: 1 passed · 1 gate failed · 1 soft below target

full eval source: .niceeval/compare_codex-gpt-5.4/2026-07-09T10-00-00-000Z-x1f2/weather/brooklyn/a2/sources.json
```

它展示的是**这次 Attempt 运行当时保存的源码**，不是当前工作区里同名文件的最新内容——评估用例改过之后再看旧 Attempt，看到的仍是它跑的那一版。没有捕获到源码时如实说明「eval source unavailable for this attempt」，不伪造一份空文档。

`--execution` 把标准事件流（消息、thinking、Skill 加载、工具调用/结果、subagent、错误）排成一棵执行树；这次 Attempt 接入过 OTel 时，同一节点能关联到的 span 会补上相对时间与耗时。无法关联到 Agent 事件的 SDK / runtime span 不逐行混入执行记录，只在结尾报告省略数量并指向完整 `trace.json`。没接入 OTel 时骨架、顺序和内容不变，只去掉时间列并标 `timing unavailable`：

```text theme={null}
$ niceeval show @1k2m9qrs --execution
@1k2m9qrs · weather/brooklyn · compare/codex-gpt-5.4 · failed

USER  0.0s · 100ms
  布鲁克林今天天气怎么样?

ASSISTANT  0.2s · 41.0s
  布鲁克林今天大约 24°C,晴。

total 41.2s · 0 skill loads · 0 tool calls · 1 AI message
full events: .niceeval/compare_codex-gpt-5.4/2026-07-09T10-00-00-000Z-x1f2/weather/brooklyn/a2/events.json
full OTel trace: .niceeval/compare_codex-gpt-5.4/2026-07-09T10-00-00-000Z-x1f2/weather/brooklyn/a2/trace.json
```

这次 Attempt 只有两条消息、没有任何工具调用节点——这正是上面 `calledTool("get_weather")` 这条 gate 断言失败的原因，`--execution` 在这里直接印证了 `--source` 那份断言标注。有 Skill 加载和工具调用时,`--execution` 把它们按发生顺序一起排进同一棵树,工具调用拆成「调用」与「结果」两行读:

```text theme={null}
$ niceeval show @1c3h6twx --execution
@1c3h6twx · fixtures/button · compare/bub-gpt-5.4 · errored

USER  0.0s · 100ms
  给 Button 组件加一个 loading 态

ASSISTANT  0.2s · 100ms
  我先看看现有 Button 组件的实现。

SKILL · component-scaffold  0.3s · 400ms
  loaded

TOOL · write_file  0.8s · 1.8s
  input
    {"path":"src/components/Button.tsx"}
  result · completed
    {"bytesWritten":1820}

ASSISTANT  2.8s · 1.2s
  已经加上 loading 态并补了对应样式。

total 4.0s · 1 skill load · 1 tool call · 2 AI messages
full events: fixtures/button/a1/events.json
full OTel trace: fixtures/button/a1/trace.json
```

没有 OTel 接入时，同一棵树去掉时间列，节点内容原样保留：

```text theme={null}
$ niceeval show @1nx4dpqr --execution
@1nx4dpqr · weather/brooklyn · compare/codex-gpt-5.4 · failed

USER
  布鲁克林今天天气怎么样?

ASSISTANT
  布鲁克林今天大约 24°C,晴。

timing unavailable · OTel trace was not collected
full events: .niceeval/compare_codex-gpt-5.4/2026-07-09T10-00-00-000Z-x1f2/weather/brooklyn/a2/events.json
```

`--timing` 显示整个 Attempt 的阶段耗时。Runner 把 lifecycle、setup/teardown hook、批量工作的 operation、所有经 Sandbox API 发出的 shell 命令，以及每个 session/turn 的 `send` 墙钟时间记入 `result.json`。某轮有 OTel 时，再按 `traceId` 把 Agent、model 和 tool span 挂到该轮下面。没有 OTel 时，phase、hook、operation、shell 与 Turn 时间仍完整，只缺轮内细分。出错或超时的 Attempt 会在已知的最深节点用 `✗` 标出：

```text theme={null}
$ niceeval show @1c3h6twx --timing
@1c3h6twx · fixtures/button · compare/bub-gpt-5.4 · errored
total 2m 4s

sandbox.queue         0.3s
sandbox.create        8.2s
sandbox.setup        21.6s
  ├─ restoreCache        18.9s
  │  └─ shell · tar xzf … 18.8s
  └─ setup#2              2.7s
     └─ shell · pnpm config set … 2.7s
workspace.baseline    0.2s
  └─ shell · git init && git commit … 0.2s
agent.setup          41.5s
  ├─ shell · npm install -g @openai/codex… 39.8s
  └─ shell · write ~/.codex/config.toml      1.7s
eval.run             50.9s
  └─ turn s1/t1      50.9s ✗ agent-runtime-error
     └─ shell · codex exec … 50.7s
        ├─ agent · codex.exec 50.5s  OTel
        └─ model · chat       44.2s  OTel

teardown (not counted in total):
agent.teardown       0.4s
sandbox.teardown     3.1s
  └─ persistCache       3.1s
     └─ shell · tar czf … 3.0s
sandbox.stop         1.2s
```

收尾阶段不计入 Attempt 总耗时，单独分组列出——「判定早就出了、进程还在等收尾」这类问题看这一组。缩进表示包含关系，不表示子项可以相加：命令、turn 和 OTel span 都可能嵌套或并发。

裸 `--timing` 会完整列出 phase，并把 phase 下的细节控制在 80 个节点内。超过上限时，它优先保留失败路径、最慢节点与首尾时序，在省略位置显示节点数、未展示的失败数，并给出 `--timing=full`。小树中两种模式输出相同；`--timing=full` 不设节点上限，适合审计旧结果或把完整树重定向到文件。NiceEval 不自动启动 pager，因此管道、CI 和 coding agent 不会等待键盘输入。命令可能并发，省略行不会把子节点耗时相加。

Operation 名称由执行工作的组件在采集时写入。例如一次 Workspace diff 导出可以显示为 `export workspace diff · 1 window · 3,302 files`，并包含一条真实的批量 shell。展示层不会解析 `git show` 等命令文本来推测 `git show ×N` 分组。旧 artifact 记录大量调用时，默认模式提供有界摘要，`--timing=full` 可以逐条核对。`--timing` 会分别显示 Sandbox 创建、安装命令和轮内模型或工具耗时。旧结果或第三方结果没有阶段数据时，两种模式都会提示 `phase timing unavailable`。

`--diff` 默认给文件级摘要（落盘的 diff 只有改动后的全文，没有基线可比对增删行数，所以每个改动过的文件都标 `M`、后面跟它现在的行数，不区分新建与修改）；看单个文件的完整内容用 `--diff=<文件路径>`——路径必须用 `=` 连写，位置参数永远留给评估用例 id 前缀 / `@<locator>`：

```text theme={null}
$ niceeval show @1c3h6twx --diff
@1c3h6twx · fixtures/button · compare/bub-gpt-5.4 · errored

M   src/components/Button.css   18 lines
M   src/components/Button.tsx   42 lines

(2 files · full diff: fixtures/button/a1/diff.json)
$ niceeval show @1c3h6twx --diff=src/components/Button.tsx
```

三个切面对长内容都会截断，但截断永远如实标注剩余数量和原始 artifact 路径——输出对上下文窗口友好，事实源一字不少地留在盘上。

### 给 coding agent 的最短阅读路径

把下面这条闭环交给 coding agent 即可。每一步只在上一层不足以定位问题时继续，避免一开始就把完整 artifact 塞进上下文：

```text theme={null}
1. 运行 niceeval show，找到失败或出错的 eval，从紧凑索引里摘一个 @<locator>。
2. 运行 niceeval show <eval-id>，读取该题各 experiment 的判定、默认 attempt 的断言明细,
   以及每行末尾的 @<locator>。
3. 运行 niceeval show @<locator>，从摘要确认这次 Attempt 捕获的证据类型。
4. 按问题选择 --source、--execution、--timing 或 --diff；可以同时传多个证据 flag。
5. 输出被截断时，读取末尾标出的 sources.json、events.json、trace.json 或 diff.json 原始路径。
6. 修复后重跑该 eval，再用 niceeval show <eval-id> 验证新的 @<locator>；收工前用 --force 全量重跑确认整体结果。
```

`--source` 显示评估用例检查内容和断言结果，`--execution` 显示 Agent 消息与调用（可关联时附 OTel 时间），`--timing` 显示 Attempt 各阶段耗时，`--diff` 显示 Sandbox 文件改动。按诊断目标选择证据，不需要每次读取全部四种视图。

### 历史与抖动：`--history`

默认报告和单个评估用例视图显示最新判定。`--history` 用跨 run 时间轴显示稳定性和回归起点，每次真实执行一行：

```text theme={null}
$ niceeval show weather/brooklyn --history
compare/codex-gpt-5.4 · 5 runs · passed 2/5

  2026-07-09T10-00   ✗ failed   3 attempts   $0.04   gate calledTool("get_weather")
  2026-07-09T08-12   ✓ passed   1 attempt    $0.03
  2026-07-08T18-22   ✗ failed   3 attempts   $0.05   gate calledTool("get_weather")
  2026-07-08T11-05   ✓ passed   1 attempt    $0.03
  2026-07-07T16-40   ✗ failed   3 attempts   $0.05   gate calledTool("get_weather")
```

✓✗ 交替说明这个评估用例在抖，该修的是稳定性（被测程序或断言），反复重跑碰运气只会烧钱；连续绿转红的拐点就是回归引入的位置，用 `view --snapshot <snapshot.json>` 钉住拐点前后两次细看。时间轴只列真实执行——缓存携带的旧结果是判定的复印件，不占行，否则趋势会被复印件灌满假数据。不带评估用例 id 的 `niceeval show --history` 给每个 experiment 的 per-run 通过率序列，同一份趋势的榜单视角。

两次 run 的精确对比不提供专用 flag。使用 `DeltaTable` 组件编写自定义对比报告，再把文件传给 `--report`；终端和网页会渲染同一份报告。具体写法见[自定义报告](/docs/zh/tutorials/custom-reports)。

## `niceeval view`：在网页看证据

```bash theme={null}
npx niceeval view
```

这会打开本地结果查看器。它的首页报告和 `niceeval show` 选结果的规则一模一样：对每个 experiment 的每道评估用例，取时间上最新的那份判定，同一个 experiment 跨多次运行拼出来；收窄范围（评估用例 ID 前缀、`--exp`）时按同一条规则收窄。你可以浏览评估用例、查看 agent 的对话与工具调用、读 diff、检查断言结果。数据不会上传到外部服务。

<Tip>
  失败后立刻运行 `npx niceeval view`，可以直接打开刚刚那次运行的 artifacts。
</Tip>

`view` 的首页直接显示当前 Scope 的摘要、成本 × 端到端成功率散点图与实验表。传 `--report` 就换成自己的报告。Attempt 详情里的 transcript、时间树、trace 与 diff 始终可用。

网页版可以排序实验表、筛行、展开 experiment、悬停散点看数值；这些操作不改判定口径。

## 导出与静态托管

发布结果使用 `--out <目录>` 导出完整静态站。需要自定义页面时，使用报告组件编写报告，见[自定义报告](/docs/zh/tutorials/custom-reports)。

### 内置查看器整站：静态托管

```bash theme={null}
npx niceeval view --out site
```

NiceEval 写入 `<dir>/index.html`，并把查看器要读取的 artifact（`sources.json`、`events.json`、`trace.json`，有 `diff.json` 也带上）复制到 `<dir>/artifact/` 下。把整个目录交给任何静态托管（Vercel、GitHub Pages 或 `python3 -m http.server`），代码视图、transcript 和 trace 瀑布图都和本地 `niceeval view` 一致；表格排序、过滤和图表悬停也照常可用——所需脚本已内联进页面，浏览器禁用 JS 时页面仍完整可读，只是少这些浏览操作。

只想发布一部分结果时，把本地查看用的收窄直接交给导出：

```bash theme={null}
npx niceeval view --exp agents/codex --out site       # 只发布一个 experiment 路径范围
npx niceeval view weather --out site                # 只发布 weather 开头的 eval
```

出站的就是收窄到的：页面和 `artifact/` 证据都只含收窄后的范围，被滤掉实验的 transcript、源码和 trace 不会跟着出站。不收窄就是整站导出完整结果。

`--out` 只接受目录。没有单文件导出：代码视图、transcript 和 trace 依赖 artifact 文件，单个 HTML 装不下完整证据。要把结果发给别人，托管整站发链接即可。

零可读结果时 `view` 直接报错、非零退出——本地服务起不来，`--out` 不导出空页面。错误逐条列出被跳过的快照目录与原因；落盘 schemaVersion 与当前版本不兼容时，还给出能直接查看旧落盘的 `npx niceeval@<版本> view` 命令。

两点限制：

* `o11y.json` 不会被复制——报告数字已经算进页面，查看器不读取它。
* 用 `file://` 直接打开 `index.html` 时浏览器不允许 fetch artifact，代码视图会提示源码不可用。本地预览用 http 服务打开。

最短流程是在运行评估用例的机器上导出并直接部署产物目录。要让站点随 push 自动更新，先用 `copySnapshots` 生成经过单文件预算检查的发布结果根并提交到仓库，再让 CI 对该目录运行 `view --results <目录> --out`。Workflow 与托管平台配置见[通过 CI 发布报告](/docs/zh/tutorials/publish-report)。

## Artifact 说明

### `snapshot.json`

快照级元数据：实验身份（experimentId）、agent、model、开始与收尾时间、格式版本。不含任何逐 attempt 数据——通过数、失败数这类聚合不落盘，由读取面逐条推导。

### `result.json`

单个 Attempt 的权威记录：判定、断言、正式阶段耗时、结构化 error、去重后的 diagnostics、用量和成本。cleanup、teardown 与 Sandbox stop 完成后一次写成，之后不再改写。`progress` 的短期 message/current/total 不落盘。

### `events.json`

标准事件流，是工具调用、消息、命令和错误的底层事实来源。

### `diff.json`

Sandbox 评估用例中 agent 改动的文件 diff。

### `sources.json`

评估用例源码位置和断言位置，用于查看器把失败断言对应回代码。

### `trace.json`

OTLP trace 或标准化后的 span 数据。它是可选的执行关系和耗时证据，不是错误存储：Sandbox 创建（`sandbox.create`）可能早于 telemetry，teardown 可能晚于 trace collect。

### `o11y.json`

工具调用、命令、usage 和成本等观测摘要。

## Verdict 含义

<Tabs>
  <Tab title="passed">所有 gate 通过。</Tab>
  <Tab title="failed">至少一个 gate 失败，或 `--strict` 下 soft 断言低于阈值。</Tab>
  <Tab title="errored">环境、超时、adapter 或 agent runtime 出错。</Tab>
  <Tab title="skipped">评估用例被主动跳过。</Tab>
</Tabs>

Soft 断言的分数记录在 `assertions[].score` 里；非 `--strict` 模式下，它们不会产生额外的 verdict。

## 调试建议

* 榜单里失败/错误的题每条自带下钻命令：`niceeval show <eval id>` 先看断言明细，行尾的 `@<locator>` 可以直接精确下钻到某一次 Attempt。
* 检查评估用例内容和断言结果时使用 `--source`，断言标注会对应到源码行。
* 检查 Agent 消息和调用时使用 `--execution`；关联成功的 OTel 时间会显示在同一事件旁。
* coding-agent 失败时看 `--diff` 和 `--execution` 里的工具调用；两者结合能看出「改错了文件」还是「压根没调用该调用的工具」。
* Sandbox 评估用例运行缓慢或超时时，先看 `--timing`。该视图分别列出排队、Sandbox 启动、setup hook 中的 shell、Agent 安装命令、每轮 `send`、可关联的 OTel model/tool 以及收尾阶段耗时。
* 定位被测程序或评估用例缺陷并完成重跑的流程见 [Coding Agent 反馈闭环](/docs/zh/tutorials/agent-feedback-loop)。
