niceeval 0.2.1 → 0.4.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 (113) hide show
  1. package/README.md +34 -34
  2. package/README.zh.md +29 -15
  3. package/docs/README.md +2 -1
  4. package/docs/adapters/collection.md +1 -1
  5. package/docs/adapters/contract.md +6 -5
  6. package/docs/adapters/reference/claude-code-otel-telemetry.md +1 -1
  7. package/docs/architecture.md +20 -20
  8. package/docs/assertions.md +0 -1
  9. package/docs/capabilities-by-construction.md +2 -2
  10. package/docs/cli.md +2 -2
  11. package/docs/concepts.md +2 -2
  12. package/docs/e2e-ci.md +135 -98
  13. package/docs/eval-authoring.md +3 -3
  14. package/docs/experiments.md +7 -4
  15. package/docs/observability.md +9 -5
  16. package/docs/origin-integration.md +18 -18
  17. package/docs/references.md +1 -1
  18. package/docs/reports.md +551 -0
  19. package/docs/results-format.md +3 -3
  20. package/docs/results-lib.md +191 -0
  21. package/docs/runner.md +1 -1
  22. package/docs/sandbox.md +1 -1
  23. package/docs/source-map.md +22 -2
  24. package/docs/tier-sync.md +74 -58
  25. package/docs/view.md +41 -4
  26. package/package.json +29 -3
  27. package/src/agents/ai-sdk.ts +3 -0
  28. package/src/agents/claude-code.ts +18 -1
  29. package/src/agents/codex.ts +3 -2
  30. package/src/agents/index.ts +16 -0
  31. package/src/agents/openai-compat.test.ts +56 -0
  32. package/src/agents/openai-compat.ts +151 -0
  33. package/src/agents/types.ts +3 -1
  34. package/src/cli.ts +2 -1
  35. package/src/context/context.test.ts +78 -1
  36. package/src/context/context.ts +19 -11
  37. package/src/context/session.ts +2 -0
  38. package/src/context/types.ts +11 -5
  39. package/src/i18n/en.ts +8 -4
  40. package/src/i18n/zh-CN.ts +6 -3
  41. package/src/o11y/cost.test.ts +40 -0
  42. package/src/o11y/cost.ts +27 -5
  43. package/src/o11y/derive.ts +2 -2
  44. package/src/o11y/otlp/mappers/claude-code.test.ts +31 -0
  45. package/src/o11y/otlp/mappers/claude-code.ts +24 -0
  46. package/src/o11y/otlp/mappers/index.ts +1 -0
  47. package/src/o11y/otlp/parse.test.ts +127 -0
  48. package/src/o11y/prices.json +176 -119
  49. package/src/o11y/types.ts +2 -1
  50. package/src/report/aggregate.ts +215 -0
  51. package/src/report/compute.ts +405 -0
  52. package/src/report/format.ts +47 -0
  53. package/src/report/index.ts +40 -0
  54. package/src/report/metrics.ts +96 -0
  55. package/src/report/react/CaseList.tsx +70 -0
  56. package/src/report/react/DeltaTable.tsx +79 -0
  57. package/src/report/react/MetricMatrix.tsx +75 -0
  58. package/src/report/react/MetricScatter.tsx +217 -0
  59. package/src/report/react/MetricTable.tsx +71 -0
  60. package/src/report/react/RunOverview.tsx +87 -0
  61. package/src/report/react/Scoreboard.tsx +87 -0
  62. package/src/report/react/cell.tsx +49 -0
  63. package/src/report/react/colors.ts +42 -0
  64. package/src/report/react/data.ts +17 -0
  65. package/src/report/react/fixtures.ts +236 -0
  66. package/src/report/react/format.ts +34 -0
  67. package/src/report/react/index.tsx +34 -0
  68. package/src/report/react/render.test.tsx +303 -0
  69. package/src/report/react/styles.css +302 -0
  70. package/src/report/report.test.ts +667 -0
  71. package/src/report/types.ts +210 -0
  72. package/src/results/copy.ts +200 -0
  73. package/src/results/format.ts +75 -0
  74. package/src/results/index.ts +27 -0
  75. package/src/results/open.ts +207 -0
  76. package/src/results/results.test.ts +359 -0
  77. package/src/results/select.ts +101 -0
  78. package/src/results/types.ts +98 -0
  79. package/src/runner/attempt.ts +3 -1
  80. package/src/runner/report.test.ts +111 -0
  81. package/src/runner/report.ts +52 -1
  82. package/src/runner/reporters/artifacts.ts +1 -1
  83. package/src/runner/reporters/braintrust.test.ts +106 -0
  84. package/src/runner/reporters/braintrust.ts +197 -0
  85. package/src/runner/reporters/console.ts +3 -3
  86. package/src/runner/reporters/index.ts +3 -1
  87. package/src/runner/reporters/live.ts +17 -5
  88. package/src/runner/reporters/shared.ts +3 -0
  89. package/src/runner/reporters/table.ts +1 -0
  90. package/src/runner/run.ts +31 -18
  91. package/src/runner/types.ts +20 -1
  92. package/src/sandbox/types.ts +3 -3
  93. package/src/shared/format.ts +8 -2
  94. package/src/view/app/App.tsx +82 -17
  95. package/src/view/app/components/AttemptModal.tsx +9 -2
  96. package/src/view/app/components/CodeView.tsx +2 -2
  97. package/src/view/app/components/CostScoreChart.tsx +127 -0
  98. package/src/view/app/components/LazyArtifact.tsx +2 -1
  99. package/src/view/app/i18n.ts +13 -1
  100. package/src/view/app/lib/artifact-url.ts +6 -0
  101. package/src/view/app/lib/attempt-route.test.ts +89 -0
  102. package/src/view/app/lib/attempt-route.ts +52 -0
  103. package/src/view/app/lib/chart.ts +72 -0
  104. package/src/view/app/lib/outcome.ts +1 -1
  105. package/src/view/app/types.ts +4 -5
  106. package/src/view/client-dist/app.css +1 -1
  107. package/src/view/client-dist/app.js +18 -18
  108. package/src/view/index.ts +36 -7
  109. package/src/view/loader.ts +14 -7
  110. package/src/view/server.ts +7 -0
  111. package/src/view/shared/types.ts +14 -1
  112. package/src/view/styles.css +43 -0
  113. package/src/view/template.html +0 -1
package/README.md CHANGED
@@ -12,13 +12,17 @@
12
12
 
13
13
  </div>
14
14
 
15
- NiceEval is an agent-native eval tool inspired by [eve](https://eve.dev). It has an excellent DX design — anyone can get started and configured in about 10 minutes. It's also versatile: it can eval plugins, Hooks, and Skills written for Claude Code/Codex coding agents, and can directly eval your own AI agent application or framework (AI SDK, LangGraph, Pi, or any custom agent loop).
15
+ NiceEval is an Agent-Native eval tool inspired by [eve](https://eve.dev), built for the best possible DX.
16
16
 
17
- After the eval completes, it generates readable reports and lets you view agent behavior details. Convenient for debugging and optimization.
17
+ Thanks to its universal design, NiceEval can evaluate almost any agent application.
18
+ Whether you need to evaluate coding agent plugins, Hooks, and Skills written for Claude Code / Codex, or your own AI agent application — both plug in easily.
19
+
20
+ After the eval completes, it generates readable reports and lets you view agent behavior details. Convenient for debugging and understanding agent behavior.
18
21
 
19
22
  ## Why NiceEval when DeepEval, LangFuse, and BrainTrust already exist
20
23
 
21
- NiceEval is an AI-native eval tool. In tools built around Dataset/golden-style Input vs. Expected Output, that shape doesn't fit real agent evaluation well. NiceEval is built for evaluating agents at a finer grain — multi-turn conversations, multi-agent setups, tool calls, skill loading, and more.
24
+ NiceEval is an Agent-Native eval tool. The Dataset / golden pattern of building an Input and an Expected Output doesn't fit real agent evaluation.
25
+ Agents today need to be evaluated at a finer grain — multi-turn conversations, multi-agent collaboration, tool calls, skill loading — and NiceEval does this better.
22
26
 
23
27
  It also coexists with LangFuse and BrainTrust: use them for tracing, or upload eval results to both (in progress).
24
28
 
@@ -32,19 +36,19 @@ NiceEval supports two integration modes, depending on whether the agent under te
32
36
  evals/*.eval.ts
33
37
 
34
38
 
35
- ┌─────────────────────┐
36
- NiceEval
37
- └─────────────────────┘
39
+ ┌────────────┐
40
+ NiceEval
41
+ └────────────┘
38
42
 
39
43
  │ Agent adapter (official)
40
44
 
41
- ┌──────────────────────────────┐
42
- Docker Sandbox │
43
- ┌────────────────────────┐ │
44
- │ Codex / Claude Code / │ │
45
- │ apps needing isolation │ │
46
- └────────────────────────┘ │
47
- └──────────────────────────────┘
45
+ ┌────────────────────────────────┐
46
+ Docker Sandbox │
47
+ ┌────────────────────────┐ │
48
+ │ Codex / Claude Code │ │
49
+ │ apps needing isolation │ │
50
+ └────────────────────────┘ │
51
+ └────────────────────────────────┘
48
52
  ```
49
53
 
50
54
  **Mode 2: Direct — connect straight to your own AI Agent**
@@ -53,18 +57,16 @@ NiceEval supports two integration modes, depending on whether the agent under te
53
57
  evals/*.eval.ts
54
58
 
55
59
 
56
- ┌─────────────────────┐
57
- NiceEval
58
- └─────────────────────┘
60
+ ┌────────────┐
61
+ NiceEval
62
+ └────────────┘
59
63
 
60
64
  │ Agent adapter (official, or your own implementation)
61
65
 
62
- ┌──────────────────────────────┐
63
- your own AI Agent
64
- │ (AI SDK·LangGraph·Pi and
65
- │ other agent frameworks — │
66
- │ no Docker needed) │
67
- └──────────────────────────────┘
66
+ ┌──────────────────────────┐
67
+ your own AI Agent
68
+ │ (AI SDK·LangGraph·Pi)
69
+ └──────────────────────────┘
68
70
  ```
69
71
 
70
72
  - **NiceEval core** owns discovery, scheduling, scoring, reporting, and artifacts.
@@ -74,8 +76,6 @@ NiceEval supports two integration modes, depending on whether the agent under te
74
76
 
75
77
  ## Example
76
78
 
77
- Running an eval takes two files: the eval itself (what to check) and an experiment (which agent to run it against). The CLI won't run a bare eval id — the experiment in `niceeval exp <experiment> <eval prefix>` is what picks the system under test. Here's a real eval against a directly-connected web agent (full project in [`examples/zh/ai-sdk/`](examples/zh/ai-sdk/)), checking that the agent calls a tool for live weather questions and answers from the tool result instead of making it up:
78
-
79
79
  ```ts
80
80
  // evals/eval-tool-call.eval.ts
81
81
  import { defineEval } from "niceeval";
@@ -109,16 +109,15 @@ import { webAgent } from "./adapter"; // your agent adapter, pointed at the syst
109
109
 
110
110
  export default defineExperiment({
111
111
  agent: webAgent({ baseUrl: "http://127.0.0.1:5188" }),
112
+ model: "gpt-5.5"
112
113
  });
113
114
  ```
114
115
 
115
116
  ```sh
116
- npx niceeval exp local eval-tool-call // run only eval-tool-call under the local experiment
117
- npx niceeval view
117
+ pnpm exec niceeval exp local eval-tool-call // run only eval-tool-call under the local experiment
118
+ pnpm exec niceeval view // view eval results
118
119
  ```
119
120
 
120
- For coding agents that need an isolated workspace (Codex, Claude Code plugins/skills), see [`examples/zh/coding-agent-skill/`](examples/zh/coding-agent-skill/): evals there use `t.sandbox.uploadDirectory()` to seed the workspace, `t.fileChanged()` / `t.file()` to check what changed, and `t.sandbox.runCommand()` to run tests.
121
-
122
121
  ## Quick Start
123
122
 
124
123
  ```text
@@ -135,19 +134,20 @@ Start from the scenario that matches what you need to evaluate:
135
134
  ## Roadmap
136
135
  Official Adapters
137
136
  - [ ] Agent Software
138
- - [ ] Claude Code
139
- - [ ] Codex
140
- - [ ] Bub
137
+ - [x] Claude Code
138
+ - [x] Codex
139
+ - [x] Bub
141
140
  - [ ] OpenClaw
142
141
  - [ ] Hermess Agent
143
142
  - [ ] Alma
144
143
  - [ ] ...
145
144
 
146
145
  - [ ] Agent Frameworks
147
- - [ ] AI SDK
146
+ - [x] AI SDK
147
+ - [x] Claude SDK
148
+ - [x] Codex SDK
149
+ - [x] Pi Agent SDK
148
150
  - [ ] LangGraph
149
- - [ ] Claude SDK
150
- - [ ] Codex SDK
151
151
  - [ ] vm0
152
152
  - [ ] Cursor Agent SDK
153
153
 
package/README.zh.md CHANGED
@@ -12,12 +12,16 @@
12
12
 
13
13
  </div>
14
14
 
15
- NiceEval 是一个受 [eve](https://eve.dev) 启发的 agent-native eval 工具,追求足够好的开发体验:大多数项目可以在 10 分钟左右接入并配置。它的设计也足够通用——既可以用来 eval 给 Claude Code / Codex 写的 coding agent 插件、Hook 和 Skill,也可以直接 eval 你自己的 AI Agent 应用或框架(无论基于 AI SDK、LangGraph、Pi 还是自研 agent loop,都能轻松接入)。
15
+ NiceEval 是一个受 [eve](https://eve.dev) 启发的 Agent-Native eval 工具,追求极致的DX。
16
+
17
+ 通过通用式的设计,使 NiceEval 可以为几乎所有 Agent 应用进行评估。
18
+ 无论你是需要评估为 Claude Code / Codex 写的 coding agent 插件、Hook 和 Skill,还是可以评估你自己的 AI Agent 应用都能轻松接入。
16
19
 
17
20
  在 eval 完成之后可以生成易读的报告与查看 Agent 的行为细节。方便 Debug 与理解 Agent 行为。
18
21
 
19
22
  ## 为什么有了 DeepEval、LangFuse、BrainTrust 还需要 NiceEval
20
- NiceEval 是一个 AI-native 的评估工具。Dataset / golden 那一套「构建 Input 与 Expected Output」的模式,并不适合真实的 agent 评估;当 agent 需要在多轮对话、多 agent 协作、工具调用、Skill 加载等细粒度场景下被评估时,NiceEval 能做得更好。
23
+ NiceEval 是一个 Agent-Native 的评估工具。Dataset / golden 那一套「构建 Input 与 Expected Output」的模式,并不适合真实的 Agent 评估。
24
+ 现在 Agent 需要在多轮对话、多 agent 协作、工具调用、Skill 加载等细粒度场景下进行评估,NiceEval 能做得更好。
21
25
 
22
26
  同时,NiceEval 也能与 LangFuse、BrainTrust 共存:可以用它们做 tracing,或者把评估结果上传到两者(这部分功能还在开发中)。
23
27
 
@@ -40,8 +44,8 @@ NiceEval 支持两种接入方式,取决于被测 agent 是否需要隔离的
40
44
  ┌──────────────────────────────┐
41
45
  │ Docker Sandbox │
42
46
  │ ┌────────────────────────┐ │
43
- │ │ Codex / Claude Code / │ │
44
- │ │ 需要隔离文件系统的应用 │ │
47
+ │ │ Codex / Claude Code │ │
48
+ │ │ 需要隔离文件系统的应用 │ │
45
49
  │ └────────────────────────┘ │
46
50
  └──────────────────────────────┘
47
51
  ```
@@ -59,9 +63,8 @@ NiceEval 支持两种接入方式,取决于被测 agent 是否需要隔离的
59
63
  │ Agent 适配器(官方,或者自己实现)
60
64
 
61
65
  ┌──────────────────────────────┐
62
- │ 你自己的 AI Agent
63
- │ (AI SDK·LangGraph·Pi
64
- │ agent 框架,无需 Docker) │
66
+ │ 你自己的 AI Agent
67
+ │ (AI SDK·LangGraph·Pi )
65
68
  └──────────────────────────────┘
66
69
  ```
67
70
 
@@ -69,10 +72,19 @@ NiceEval 支持两种接入方式,取决于被测 agent 是否需要隔离的
69
72
  - **Agent 适配器** 是开放的边界:你来决定如何调用被测系统。
70
73
  - 需要文件系统隔离的 coding agent 走 **Docker Sandbox**;自有的 AI Agent 可以直连,无需 Docker。
71
74
 
75
+ ## 核心概念一览
72
76
 
73
- ## 示例
77
+ | 概念 | 一句话 |
78
+ |---|---|
79
+ | Eval | 一个测试用例:写在 `evals/*.eval.ts` 里,描述测什么。 |
80
+ | Experiment | 可签入的运行配置:决定连哪个 Adapter、什么 model、什么 flags。 |
81
+ | Adapter | 连接被测系统的适配层:实现一个 `send`,把返回翻译成标准事件流。 |
82
+ | Sandbox | 需要隔离工作区的 coding agent 才用得到;直连 Web Agent 不需要。 |
83
+ | Tier | 接入 Adapter 的三档投入:Tier 1 只接 send,Tier 2 加 OTel 换调用瀑布图,Tier 3 侵入改造做 feature A/B。 |
74
84
 
75
- 跑一条 eval 需要两个文件:eval 本身(测什么)和 experiment(跑哪个 agent)。CLI 不接受裸 eval id——`niceeval exp <experiment> <eval 前缀>` 里的 experiment 才是决定「连哪个被测对象」的地方。下面是一个直连 AI Agent 的真实场景(完整项目见 [`examples/zh/ai-sdk/`](examples/zh/ai-sdk/)),验证 agent 遇到实时天气问题时会调用工具、并基于工具结果作答,而不是凭空编造:
85
+ 完整术语表见[架构概览](https://niceeval.com/docs/zh/concepts/overview)
86
+
87
+ ## 示例
76
88
 
77
89
  ```ts
78
90
  // evals/eval-tool-call.eval.ts
@@ -107,6 +119,7 @@ import { webAgent } from "./adapter"; // 你自己写的 agent adapter,接被
107
119
 
108
120
  export default defineExperiment({
109
121
  agent: webAgent({ baseUrl: "http://127.0.0.1:5188" }),
122
+ model: "gpt-5.5"
110
123
  });
111
124
  ```
112
125
 
@@ -131,19 +144,20 @@ READ https://niceeval.com/INIT.md and install niceeval for this repo.
131
144
  ## Roadmap
132
145
  官方适配器
133
146
  - [ ] Agent 软件
134
- - [ ] Claude Code
135
- - [ ] Codex
136
- - [ ] Bub
147
+ - [x] Claude Code
148
+ - [x] Codex
149
+ - [x] Bub
137
150
  - [ ] OpenClaw
138
151
  - [ ] Hermess Agent
139
152
  - [ ] Alma
140
153
  - [ ] ...
141
154
 
142
155
  - [ ] Agent 框架
143
- - [ ] AI SDK
156
+ - [x] AI SDK
157
+ - [x] Claude SDK
158
+ - [x] Codex SDK
159
+ - [x] Pi Agent SDK
144
160
  - [ ] LangGraph
145
- - [ ] Claude SDK
146
- - [ ] Codex SDK
147
161
  - [ ] vm0
148
162
  - [ ] Cursor Agent SDK
149
163
 
package/docs/README.md CHANGED
@@ -110,7 +110,8 @@ npx niceeval exp local fixtures/button
110
110
  - **建立心智模型:** [Vision](vision.md)、[Concepts(术语表)](concepts.md)、[Architecture](architecture.md)。
111
111
  - **写 eval:** [Eval Authoring(编写 eval)](eval-authoring.md)、[Assertions(断言参考:作用域 + 来源)](assertions.md)、[Scoring(评分器)](scoring.md)。
112
112
  - **连 AI / 接 agent:** [Agents 与 Adapters(定位与导航)](adapters/README.md)、[Origin 应用接入手册(五个应用的接入记录)](origin-integration.md)、[Tier Sync(examples origin→tier 同步维护工具)](tier-sync.md)、[Adapter 契约(逐 API 适配义务)](adapters/contract.md)、[Adapter 写法(递进式写法)](adapters/authoring.md)、[Capabilities by Construction(能力由构造证明)](capabilities-by-construction.md)、[采集设计(三条外部路线对比)](adapters/collection.md)、[接入目标矩阵(12 个被测对象调研)](adapters/targets.md)、[Coding Agent Skills / Plugins DX](adapters/coding-agent-skills-plugins.md)、[Sandbox](sandbox.md)、[Observability(含 OTel trace 瀑布图)](observability.md)。
113
- - **设计提案(未实现):** [Multi-Agent(多 agent eval 的三种场景)](multi-agent.md)、[E2E CI(全链路 CI 测试方案)](e2e-ci.md)。
113
+ - **设计提案(未实现):** [Multi-Agent(多 agent eval 的三种场景)](multi-agent.md)、[Reports(自己搭报告页:React 组件 × 查询模型两档积木)](reports.md)、[Results Lib(结果数据的读写库,Reports 的数据层)](results-lib.md)。
114
+ - **框架自测:** [E2E CI(全链路 e2e 测试方案;`e2e/` 的 tier1 矩阵已落地,workflow 未建)](e2e-ci.md)。
114
115
  - **跑与看:** [Experiments(实验/运行矩阵)](experiments.md)、[Runner(执行引擎)](runner.md)、[CLI 参考](cli.md)、[Observability(可观测性)](observability.md)、[Results Format(结果保存格式)](results-format.md)、[View(本地查看器)](view.md)。
115
116
  - **背景调研:** [References(从其它项目学到什么)](references.md)、[agent-eval 是怎么做适配的](adapters/reference/agent-eval.md)、[OTel GenAI 等「行为怎么记」标准](adapters/reference/otel-genai.md)、[四个主流 agent loop 接入面调研](adapters/reference/agent-loop-apis.md)、[应用侧 OTel 埋点生态调研](adapters/reference/otel-instrumentation.md)、[eve 的协议机制](adapters/reference/eve-protocol.md)、[Claude Code 自带 OTel 遥测能不能提前拿结果](adapters/reference/claude-code-otel-telemetry.md)。
116
117
 
@@ -46,7 +46,7 @@
46
46
  | **usage** | assistant 行 `message.usage`(含 cache read) | 防御式多路径:`data/payload/item/turn/response.usage`,兼容 `input/output_tokens` 与 `prompt/completion_tokens` 两套命名 | `event(name=="run")` 的 `data.usage`(`prompt/completion_tokens`,**`cost` 直接有**) |
47
47
  | **session id(resume 用)** | transcript 首个 `sessionId` 字段(`shared.sessionIdFromClaudeTranscript`) | `thread.started.thread_id`(`shared.codexThreadId`) | tape 文件名含 session hash;adapter 自管 |
48
48
  | **实际模型** | transcript 行内有 | 网关场景要第二通道读 `~/.codex/sessions` 的 `turn_context.payload.model`(agent-eval 的做法;niceeval 未接,记为已知缺口) | tape 内 run 事件 |
49
- | **时间轨** | 稳定态无 OTel;有 beta 版原生 OTLP trace 导出(`CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1` 等一串 env,内容默认脱敏,详见 [reference/claude-code-otel-telemetry.md](reference/claude-code-otel-telemetry.md)) 目前仍用 transcript 时间戳合成 span | 原生 OTLP/JSON(`config.toml [otel]`,走 `tracing.configure`) | 原生 OTLP/protobuf(env-based `OTEL_*`,走 `tracing.env`) |
49
+ | **时间轨** | beta 版原生 OTLP trace 导出,已接进 adapter 的 `tracing.env`(`CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1` 等一串 env;span 只有结构与计时、内容默认脱敏,内容仍由 transcript 旁读补全,详见 [reference/claude-code-otel-telemetry.md](reference/claude-code-otel-telemetry.md)) | 原生 OTLP/JSON(`config.toml [otel]`,走 `tracing.configure`) | 原生 OTLP/protobuf(env-based `OTEL_*`,走 `tracing.env`) |
50
50
 
51
51
  remote agent(通道 0)不在表里——它没有"采集",字段来源就是你自己代码里的返回值,见下节示例。
52
52
 
@@ -72,10 +72,10 @@ interface AgentSession {
72
72
  | `t.respond()` / `t.parked()` 等 HITL | send 返回过 `"waiting"` + `input.requested` 事件——做到了就是有 |
73
73
  | `t.calledTool()` / `t.toolOrder()` 等正断言 | events 里有 `action.*` |
74
74
  | `t.notCalledTool()` / `t.usedNoTools()` 等负断言**可信** | 事件来自带完整性证明的官方转换器(SDK 原生事件流透传、`fromAiSdk` 的 `result.steps`、Responses 的 `output`);手写映射没有这份证明,负断言可信度按来源判断,不按作者的自觉 |
75
- | `t.sandbox`、`t.fileChanged()` 等 | `defineSandboxAgent` 构造(`kind: "sandbox"`) |
75
+ | `t.sandbox`、`t.sandbox.fileChanged()` 等 | `defineSandboxAgent` 构造(`kind: "sandbox"`) |
76
76
  | `EvalResult.trace`、`niceeval view` 瀑布图 | 配置了 OTel 接入(agent 的 `tracing` 块,或 remote agent + `defineConfig({ telemetry })`)——只画瀑布图,不影响任何断言,详见[观测性](../observability.md#otlp-traces--统一瀑布图) |
77
77
 
78
- 唯一仍然存在的运行时守卫是**沙箱能力**:`t.sandbox` / `t.file` / `t.fileChanged()` 等直接读沙箱文件系统,非沙箱型 agent(`kind !== "sandbox"`)调用这些方法会立即得到清晰报错(`src/context/context.ts` 的 `capabilityGuard`)——没有沙箱就没有东西可读,不报错会静默返回空结果。其余能力(多轮对话、工具观测……)不设运行时守卫:没接会话存取器的 agent 每轮各是新对话(不报错,只是断言看不到历史);没吐 `action.*` 事件的 agent 上正断言自然不命中;负断言的可信度靠事件来源判断,不靠拦截调用。
78
+ 唯一仍然存在的运行时守卫是**沙箱能力**:`t.sandbox` / `t.sandbox.file()` / `t.sandbox.fileChanged()` 等直接读沙箱文件系统,非沙箱型 agent(`kind !== "sandbox"`)调用这些方法会立即得到清晰报错(`src/context/context.ts` 的 `capabilityGuard`)——没有沙箱就没有东西可读,不报错会静默返回空结果。其余能力(多轮对话、工具观测……)不设运行时守卫:没接会话存取器的 agent 每轮各是新对话(不报错,只是断言看不到历史);没吐 `action.*` 事件的 agent 上正断言自然不命中;负断言的可信度靠事件来源判断,不靠拦截调用。
79
79
 
80
80
  ## 标准事件流
81
81
 
@@ -182,7 +182,7 @@ interface InputRequest {
182
182
 
183
183
  | 断言族 | 读什么 | adapter 义务 | 数据缺失时 |
184
184
  |---|---|---|---|
185
- | `succeeded()` / `parked()` / `turn.expectOk()` | `Turn.status` + 事件流(`parked` 看最后有意义事件是否 `input.requested`) | `status` 如实;HITL 停留吐 `input.requested` | `status` 恒 completed → `succeeded` **假通过(静默)** |
185
+ | `succeeded()` / `parked()` | `Turn.status` + 事件流(`parked` 看最后有意义事件是否 `input.requested`) | `status` 如实;HITL 停留吐 `input.requested` | `status` 恒 completed → `succeeded` **假通过(静默)** |
186
186
  | `messageIncludes()` / `t.reply` / `turn.message` | `message` 且 `role: "assistant"` 的事件文本 | 每段助手文本吐一条 `message`(工具结果**不是**助手消息,别混,见[参考笔记的踩坑记录](reference/agent-eval.md#claude-code-怎么转换)) | 断言 fail(响) |
187
187
  | `outputEquals()` / `outputMatches()` | `turn.data` | 结构化输出放 `data`,不要序列化塞进 `events` | fail(响) |
188
188
  | `calledTool()` / `toolOrder()` / `loadedSkill()` / `calledSubagent()` | 派生 `toolCalls` / `subagentCalls` | 每次调用吐 `called` + `result`(callId 配对);`name` 原始名 + `tool` 规范名;result `status` 如实 | fail(响) |
@@ -230,10 +230,11 @@ t.succeeded() ✓
230
230
  | 鉴权(API key / token / base url) | **agent 本地** —— 它怎么连自己,是私事 | 在定义里读 env / 闭包,不经 ctx |
231
231
  | CLI 细节(装什么包、参数形状、transcript 在哪) | **agent 本地** | 写死在 `send` / `setup` 里 |
232
232
  | **model** | **实验决定(留空)** | `ctx.model`(省略 → agent 原生默认) |
233
- | **feature flags**(webResearch、注入哪个 skill、effort…) | **实验决定** | `ctx.flags.*` —— agent `send` 与 eval 的 `t.flags` 都能读 |
233
+ | **reasoningEffort** | **实验决定(留空)** | `ctx.reasoningEffort`(省略 agent 原生默认) |
234
+ | **feature flags**(webResearch、注入哪个 skill…) | **实验决定** | `ctx.flags.*` —— agent 的 `send` 与 eval 的 `t.flags` 都能读 |
234
235
  | runs / earlyExit / evals / sandbox / budget | **实验决定** | 运行器据此调度 |
235
236
 
236
- 一句话:**agent 只配「怎么连我自己」,不配「跑哪个模型、开哪些开关」**;后者全留给 [experiment](../experiments.md),经 `ctx`(eval 里是 `t`)透传。这样同一个 agent 能被不同实验以不同 model / flags 复用,不必改 agent。
237
+ 一句话:**agent 只配「怎么连我自己」,不配「跑哪个模型、开哪些开关」**;后者全留给 [experiment](../experiments.md),经 `ctx`(eval 里是 `t`)透传。这样同一个 agent 能被不同实验以不同 model / reasoningEffort / flags 复用,不必改 agent。
237
238
 
238
239
  ## `ctx`(agent 侧)与 `t`(eval 侧):同一份东西,两个名字
239
240
 
@@ -79,7 +79,7 @@ OTEL_TRACES_EXPORT_INTERVAL=1000 # 拉到近实时,eval 场
79
79
 
80
80
  ## 跟现有采集矩阵的关系
81
81
 
82
- [采集设计](../collection.md#采集矩阵现状与-src-对齐)那张表里"时间轨"一行,claude-code 当前写的是"无原生 OTel → transcript 时间戳合成 span"——**这句话需要更新**:Claude Code 现在有原生 OTel trace 导出,只是处于 beta、默认关、内容默认脱敏。准确说法应该是"有 beta 版原生 OTLP trace 导出(需要一串 env 才能打开、且默认无内容),目前 niceeval 仍选择更省事、内容更全的 transcript 时间戳合成方案"。
82
+ **已落地(2026-07)**:下文"升级档"描述的接线已经实现——`claudeCodeAgent` 的 `tracing.env` 注入本页这组 env,与 bub/codex 一样默认开启(见 `src/agents/claude-code.ts`),[采集矩阵](../collection.md#采集矩阵现状与-src-对齐)的时间轨一行已同步。span 只有结构与计时(未开内容 flag),行为轨与断言仍走 transcript 旁读,与下文结论一致。
83
83
 
84
84
  ## 结论:这条路径值不值得接
85
85
 
@@ -7,26 +7,26 @@ niceeval 把一个评测过程拆成四段职责:**发现**要跑什么、**驱
7
7
  ## 系统总览
8
8
 
9
9
  ```text
10
- ┌──────────────────────────── core ────────────────────────────┐
11
- evals/ │
12
- └─ *.eval.ts ──────────►│ Discovery ──► Runner ──► Scoring ──► Outcome ──► Reporters
13
- (发现) (调度) (评分) (判决) (报告)
14
- │ 驱动 │ │
15
- └──────────────────┼──────────────────────────────────┼──────────┘
16
- │ ▼
17
- ▼ 对接口分发,不按名字分支 .niceeval/<run>/
18
- ┌──────────── Agent(自实现 Adapter)────────────┐
19
- 远程 adapter 沙箱 adapter
20
- (你的服务) ┌─────┴─────┐
21
- claude-code│
22
- codex / bub│
23
- └─────┬──────┘
24
-
25
- ┌── Sandbox ──┐
26
- docker
27
- │ vercel / 三方│
28
- └─────────────┘
29
- └────────────────────────────────────────────────┘
10
+ ┌─────────────────────────── core ───────────────────────────┐
11
+ evals/ │
12
+ └─ *.eval.ts ──────────►│ Discovery ──► Runner ──► Scoring ──► Outcome ──► Reporters
13
+ (发现) (调度) (评分) (判决) (报告)
14
+ │ 驱动 │ │
15
+ └──────────────┼──────────────────────────────────┼──────────┘
16
+ │ ▼
17
+ ▼ 对接口分发,不按名字分支 .niceeval/<run>/
18
+ ┌────── Agent(自实现 Adapter) ──────┐
19
+ 远程 adapter 沙箱 adapter
20
+ (你的服务) ┌───────────────┐
21
+ claude-code
22
+ codex / bub
23
+ └───────────────┘
24
+
25
+ ┌─── Sandbox ───┐
26
+ docker
27
+ │ vercel / 三方
28
+ └───────────────┘
29
+ └───────────────────────────────────┘
30
30
  ```
31
31
 
32
32
  四段职责是**单向数据流**:发现产出一批 `Eval`,运行器逐个对 Agent `send` 得到 `Turn`,评分器把 `Turn` 折叠成 `Assertion[]`,再把全部断言折叠成一个互斥的 `Outcome`;报告器消费 `Outcome` + 工件。没有反向耦合 —— 评分器不知道 agent 的 transport 是 HTTP 还是沙箱 CLI,它只看 `Turn`。
@@ -158,7 +158,6 @@ export default defineEval({
158
158
  | `t` | `check` / `require` / `skip` / `log` | 记录 eval run 级断言或控制流,不属于某个 session / turn |
159
159
  | `t` | `newSession()` | 只有 run 主上下文负责创建额外 session |
160
160
  | `t` | `sandbox.*` | 沙箱是 attempt / run 资源,不是某条会话或某一轮的资源 |
161
- | `turn` | `expectOk()` | 只对一次 `send` 的返回状态有意义 |
162
161
  | `turn` | `outputEquals(value)` / `outputMatches(schema)` | 只对这一轮的 `turn.data` 有意义 |
163
162
 
164
163
  ### 值级断言
@@ -8,7 +8,7 @@
8
8
 
9
9
  | 能力 | 证据 | 用户要写什么 |
10
10
  |---|---|---|
11
- | `t.sandbox`、`t.fileChanged()` 等文件系统断言 | `defineSandboxAgent` 构造(`kind: "sandbox"`) | 无——`defineAgent` 构造的 agent(`kind: "remote"`)调用这些方法会立即得到清晰报错,这是唯一仍需要运行时守卫的能力(`src/context/context.ts` 的 `capabilityGuard`) |
11
+ | `t.sandbox`、`t.sandbox.fileChanged()` 等文件系统断言 | `defineSandboxAgent` 构造(`kind: "sandbox"`) | 无——`defineAgent` 构造的 agent(`kind: "remote"`)调用这些方法会立即得到清晰报错,这是唯一仍需要运行时守卫的能力(`src/context/context.ts` 的 `capabilityGuard`) |
12
12
  | 多次 `t.send()`、`t.reply`、`t.newSession()` | `send` 里接了 `ctx.session` 的续接存取器(`history<TMsg>()` 或 `id` + `capture(id)`) | 无——没接就每轮各是新对话,不报错,只是断言看不到跨轮历史 |
13
13
  | `t.calledTool()` 等正断言 | `send` 返回的 events 里有 `action.*` | 无——有事件就能断,没有就是断言 fail(响,不静默) |
14
14
  | `t.notCalledTool()` / `usedNoTools()` 等负断言的**可信度** | 事件来源是否有完整性契约(SDK 原生事件流透传、`fromAiSdk` 的 `result.steps`、Responses 的 `output`) | 无——但手写映射时如果知道自己漏了工具层,应在 eval README 里如实说明这条 eval 的负断言不可信,而不是假装它和正断言一样可靠 |
@@ -38,7 +38,7 @@ export default defineAgent({
38
38
  ## 边界
39
39
 
40
40
  - **默认值问题已经不存在。** 提案担心的"`defineAgent` 默认给 `conversation: true, toolObservability: true`,教程第一步要写一行反悔"——这个问题连同整个 `capabilities` 字段一起消失了,不需要保留"opt-out 默认值"这个概念。
41
- - **`workspace` 对远程 agent 依然不放开。** 文件系统断言(`t.sandbox`、`t.fileChanged()`)只在 `kind: "sandbox"` 上解锁,远程 agent 即便自己改了文件,也没有 sandbox 提供的 diff 基线,这条边界和提案设想的一致。
41
+ - **`workspace` 对远程 agent 依然不放开。** 文件系统断言(`t.sandbox`、`t.sandbox.fileChanged()`)只在 `kind: "sandbox"` 上解锁,远程 agent 即便自己改了文件,也没有 sandbox 提供的 diff 基线,这条边界和提案设想的一致。
42
42
  - **`t.newSession()` 在没有接会话续接存取器时依然可以调用。** 新会话线的第一轮就是存取器的自然空态,不需要判断"这个 agent 支不支持多轮"。
43
43
 
44
44
  ## 相关阅读
package/docs/cli.md CHANGED
@@ -58,7 +58,7 @@ niceeval exp compare --no-early-exit # 关掉,要完整通过率分布
58
58
  niceeval exp compare --max-concurrency <n> # 并发上限
59
59
  niceeval exp compare --timeout <ms> # 单 eval 超时
60
60
  niceeval exp compare --force # 忽略指纹缓存,全量重跑
61
- niceeval exp compare --tag <tag> # 按标签过滤(可重复)
61
+ niceeval exp compare --tag <tag> # 按单个标签过滤
62
62
  ```
63
63
 
64
64
  ## 评分与退出
@@ -81,7 +81,7 @@ niceeval view # 起本地 web,自动打开浏览器,读
81
81
  niceeval view .niceeval/<run>/summary.json
82
82
  niceeval view --port 4317 # 固定端口;被占用时向后找可用端口
83
83
  niceeval view --no-open # 只启动服务并打印 URL(适合远端 shell / CI)
84
- niceeval view --out .niceeval/report.html # 导出静态 HTML,不启动服务
84
+ niceeval view --out site # 目录式静态导出,不启动服务(index.html + 查看器工件)
85
85
  niceeval clean # 清掉这些历史运行结果
86
86
  ```
87
87
 
package/docs/concepts.md CHANGED
@@ -40,7 +40,7 @@
40
40
 
41
41
  **Integration tier** / **接入 Tier** —— 按「Adapter 接到哪里、额外拿到什么观测数据」给接入方式分的三档(和下面的**模型档**是两回事,后者说的是给 agent 指定哪个模型)。**Tier 1(只接 send)**:应用代码一行不改——adapter 适配应用现有对外接口实现 `send`,靠手写事件映射或官方转换器拿到工具断言;应用接口本身暴露模型选择的话,**模型对比**类 [Experiment](#experiment) 也在这一档(`model` 经 `ctx.model` 透传)。**Tier 2(send + OTel)**:还是同一个 `send`,事件来源换成应用发给 niceeval 的 OTel span(应用已埋点则零代码,未埋点补一段通用 OTel 初始化)——买到的是观测质量的跃升:事件流免手写映射、负断言带完整性证明、trace 瀑布图。**Tier 3(侵入改造 + experiment flags)**:改应用内部代码,把内部可变点(prompt、工具集、feature flag)暴露成 experiment 可选的配置(经 `flags` → `ctx.flags` 透传),解锁**完整的 feature A/B test**——对照的不再只是模型,而是应用内部的功能变体。前两档都是**无侵入**的:应用按自己的方式启动,eval 侧不 spawn 应用进程、不另开端口,Adapter 只对着用户前端本来就在用的那个接口收发。三档递进不互斥,详见 [docs-site · Tier](../docs-site/zh/concepts/tier.mdx)。
42
42
 
43
- **Model tier** / **模型档** —— 给 agent 指定模型的标识(如 `opus`、`vendor/model?reasoningEffort=high`)。由 [Experiment](#experiment) 的 `model` 字段指定;省略则用 agent 原生默认,不经额外的策略层决定。
43
+ **Model tier** / **模型档** —— 给 agent 指定模型的标识(如 `opus`)。由 [Experiment](#experiment) 的 `model` 字段指定;省略则用 agent 原生默认,不经额外的策略层决定。推理努力程度(如 `low`/`medium`/`high`,取值由具体模型定义)是独立的 `reasoningEffort` 字段,经 `ctx.reasoningEffort`/`t.reasoningEffort` 透传,归属与 `model` 一致——都是实验决定、agent 留空。
44
44
 
45
45
  ## 数据集与发现
46
46
 
@@ -62,7 +62,7 @@
62
62
 
63
63
  **Session** —— 一条会话线。`t` 驱动主 session;`t.newSession()` 返回独立 session,用于并行或隔离的多会话测试。`session.*` 作用域断言只看这条 session 已经发生的事件;这些事件仍会汇入 `t.*` run 级断言。
64
64
 
65
- **Turn** —— `t.send()` 的一次返回值,对应这一轮的标准事件流片段。带 `message` / `data` / `toolCalls` / `status` / `usage` / `events` 等只读字段,以及 `expectOk()` 和一套与 `t` 同名的作用域断言(`turn.calledTool`/`turn.succeeded`/…),作用域收窄成只看这一轮,详见 [Assertions](assertions.md#作用域规则)。
65
+ **Turn** —— `t.send()` 的一次返回值,对应这一轮的标准事件流片段。带 `message` / `data` / `toolCalls` / `status` / `usage` / `events` 等只读字段,以及一套与 `t` 同名的作用域断言(`turn.calledTool`/`turn.succeeded`/…),作用域收窄成只看这一轮,详见 [Assertions](assertions.md#作用域规则)。
66
66
 
67
67
  **EarlyExit** / **早停** —— 一个 eval 取通过率时,先过一次即中止其余 attempt 的策略(可关)。
68
68