niceeval 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -12
- package/README.zh.md +8 -9
- package/docs-site/.mintignore +13 -0
- package/docs-site/AGENTS.md +46 -0
- package/docs-site/concepts/adapter.mdx +287 -0
- package/docs-site/concepts/assert.mdx +243 -0
- package/docs-site/concepts/drive.mdx +118 -0
- package/docs-site/concepts/evals.mdx +108 -0
- package/docs-site/concepts/experiment.mdx +37 -0
- package/docs-site/concepts/hitl.mdx +83 -0
- package/docs-site/concepts/judge.mdx +129 -0
- package/docs-site/concepts/overview.mdx +98 -0
- package/docs-site/concepts/tier.mdx +42 -0
- package/docs-site/docs-ref/00-index.md +23 -0
- package/docs-site/docs-ref/01-page-types.md +43 -0
- package/docs-site/docs-ref/03-style-rules.md +45 -0
- package/docs-site/docs-ref/04-code-examples.md +37 -0
- package/docs-site/docs-ref/05-dx-failure-paths.md +45 -0
- package/docs-site/docs-ref/06-checklists.md +53 -0
- package/docs-site/docs.json +256 -0
- package/docs-site/example/ai-agent-application.mdx +148 -0
- package/docs-site/example/claude-code-codex-plugin.mdx +162 -0
- package/docs-site/example/claude-code-codex-skill.mdx +147 -0
- package/docs-site/example/showcase.mdx +39 -0
- package/docs-site/example/tier1-ai-sdk-v7.mdx +136 -0
- package/docs-site/example/tier1-claude-sdk.mdx +119 -0
- package/docs-site/example/tier1-codex-sdk.mdx +111 -0
- package/docs-site/example/tier1-langgraph.mdx +119 -0
- package/docs-site/example/tier1-pi-sdk.mdx +119 -0
- package/docs-site/favicon.svg +5 -0
- package/docs-site/github-diff.css +135 -0
- package/docs-site/github-diff.js +11 -0
- package/docs-site/guides/authoring.mdx +129 -0
- package/docs-site/guides/ci-integration.mdx +97 -0
- package/docs-site/guides/connect-otel.mdx +209 -0
- package/docs-site/guides/connect-your-agent.mdx +221 -0
- package/docs-site/guides/dataset-fanout.mdx +98 -0
- package/docs-site/guides/experiments.mdx +71 -0
- package/docs-site/guides/fixtures.mdx +147 -0
- package/docs-site/guides/reporters.mdx +113 -0
- package/docs-site/guides/runner.mdx +79 -0
- package/docs-site/guides/sandbox-agent.mdx +138 -0
- package/docs-site/guides/sandbox-backends.mdx +64 -0
- package/docs-site/guides/scoring-guide.mdx +92 -0
- package/docs-site/guides/viewing-results.mdx +195 -0
- package/docs-site/guides/write-experiment.mdx +81 -0
- package/docs-site/guides/write-send.mdx +347 -0
- package/docs-site/images/adapter-tiers-zh.svg +60 -0
- package/docs-site/images/agent-turn-roundtrip-en.svg +62 -0
- package/docs-site/images/agent-turn-roundtrip-zh.svg +77 -0
- package/docs-site/images/hitl-handshake-zh.svg +89 -0
- package/docs-site/images/logo.svg +6 -0
- package/docs-site/index.mdx +181 -0
- package/docs-site/introduction.mdx +141 -0
- package/docs-site/quickstart.mdx +136 -0
- package/docs-site/reference/builtin-agents.mdx +161 -0
- package/docs-site/reference/capabilities.mdx +76 -0
- package/docs-site/reference/cli.mdx +120 -0
- package/docs-site/reference/define-agent.mdx +168 -0
- package/docs-site/reference/define-config.mdx +41 -0
- package/docs-site/reference/define-eval.mdx +160 -0
- package/docs-site/reference/events.mdx +131 -0
- package/docs-site/reference/expect.mdx +112 -0
- package/docs-site/tracker.js +8 -0
- package/docs-site/zh/concepts/adapter.mdx +286 -0
- package/docs-site/zh/concepts/assert.mdx +243 -0
- package/docs-site/zh/concepts/drive.mdx +118 -0
- package/docs-site/zh/concepts/evals.mdx +124 -0
- package/docs-site/zh/concepts/experiment.mdx +37 -0
- package/docs-site/zh/concepts/hitl.mdx +83 -0
- package/docs-site/zh/concepts/judge.mdx +130 -0
- package/docs-site/zh/concepts/overview.mdx +109 -0
- package/docs-site/zh/concepts/tier.mdx +44 -0
- package/docs-site/zh/example/ai-agent-application.mdx +152 -0
- package/docs-site/zh/example/claude-code-codex-plugin.mdx +163 -0
- package/docs-site/zh/example/claude-code-codex-skill.mdx +147 -0
- package/docs-site/zh/example/showcase.mdx +39 -0
- package/docs-site/zh/example/tier1-ai-sdk-v7.mdx +140 -0
- package/docs-site/zh/example/tier1-claude-sdk.mdx +122 -0
- package/docs-site/zh/example/tier1-codex-sdk.mdx +117 -0
- package/docs-site/zh/example/tier1-langgraph.mdx +123 -0
- package/docs-site/zh/example/tier1-pi-sdk.mdx +122 -0
- package/docs-site/zh/guides/agent-feedback-loop.mdx +79 -0
- package/docs-site/zh/guides/authoring.mdx +129 -0
- package/docs-site/zh/guides/ci-integration.mdx +97 -0
- package/docs-site/zh/guides/connect-otel.mdx +208 -0
- package/docs-site/zh/guides/connect-your-agent.mdx +226 -0
- package/docs-site/zh/guides/dataset-fanout.mdx +83 -0
- package/docs-site/zh/guides/experiments.mdx +72 -0
- package/docs-site/zh/guides/fixtures.mdx +147 -0
- package/docs-site/zh/guides/official-adapters.mdx +132 -0
- package/docs-site/zh/guides/reporters.mdx +113 -0
- package/docs-site/zh/guides/runner.mdx +82 -0
- package/docs-site/zh/guides/sandbox-agent.mdx +139 -0
- package/docs-site/zh/guides/sandbox-backends.mdx +75 -0
- package/docs-site/zh/guides/scoring-guide.mdx +92 -0
- package/docs-site/zh/guides/viewing-results.mdx +195 -0
- package/docs-site/zh/guides/write-experiment.mdx +101 -0
- package/docs-site/zh/guides/write-send.mdx +353 -0
- package/docs-site/zh/index.mdx +180 -0
- package/docs-site/zh/introduction.mdx +140 -0
- package/docs-site/zh/quickstart.mdx +137 -0
- package/docs-site/zh/reference/builtin-agents.mdx +364 -0
- package/docs-site/zh/reference/capabilities.mdx +76 -0
- package/docs-site/zh/reference/cli.mdx +140 -0
- package/docs-site/zh/reference/define-agent.mdx +461 -0
- package/docs-site/zh/reference/define-config.mdx +107 -0
- package/docs-site/zh/reference/define-eval.mdx +674 -0
- package/docs-site/zh/reference/events.mdx +252 -0
- package/docs-site/zh/reference/expect.mdx +208 -0
- package/package.json +3 -2
- package/src/agents/bub.ts +15 -5
- package/src/agents/claude-code.ts +8 -5
- package/src/agents/codex.ts +9 -5
- package/src/agents/sdk-streams.test.ts +4 -4
- package/src/agents/sdk-streams.ts +14 -9
- package/src/agents/types.ts +40 -1
- package/src/agents/ui-message-stream.ts +1 -0
- package/src/cli.ts +36 -7
- package/src/context/types.ts +140 -0
- package/src/expect/index.ts +15 -8
- package/src/i18n/en.ts +19 -6
- package/src/i18n/zh-CN.ts +19 -6
- package/src/o11y/parsers/bub.test.ts +71 -0
- package/src/o11y/parsers/bub.ts +5 -0
- package/src/o11y/types.ts +27 -2
- package/src/runner/reporters/artifacts.ts +11 -3
- package/src/runner/reporters/live.ts +45 -8
- package/src/runner/run.ts +71 -21
- package/src/runner/types.ts +45 -2
- package/src/sandbox/types.ts +18 -0
- package/src/scoring/types.ts +5 -0
- package/src/shared/types.ts +3 -0
- package/src/util.test.ts +26 -1
- package/src/util.ts +16 -0
- package/src/view/app/App.tsx +2 -2
- package/src/view/app/components/AttemptModal.tsx +2 -0
- package/src/view/app/components/CopyControls.tsx +87 -28
- package/src/view/app/i18n.ts +3 -3
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +18 -18
- package/docs/README.md +0 -120
- package/docs/adapters/README.md +0 -60
- package/docs/adapters/authoring.md +0 -179
- package/docs/adapters/coding-agent-skills-plugins.md +0 -324
- package/docs/adapters/collection.md +0 -128
- package/docs/adapters/contract.md +0 -264
- package/docs/adapters/reference/agent-eval.md +0 -215
- package/docs/adapters/reference/agent-loop-apis.md +0 -69
- package/docs/adapters/reference/claude-code-otel-telemetry.md +0 -100
- package/docs/adapters/reference/eve-protocol.md +0 -127
- package/docs/adapters/reference/otel-genai.md +0 -107
- package/docs/adapters/reference/otel-instrumentation.md +0 -65
- package/docs/adapters/targets.md +0 -99
- package/docs/architecture.md +0 -140
- package/docs/assertions.md +0 -387
- package/docs/capabilities-by-construction.md +0 -48
- package/docs/cli.md +0 -173
- package/docs/concepts.md +0 -106
- package/docs/e2e-ci.md +0 -332
- package/docs/eval-authoring.md +0 -319
- package/docs/experiments.md +0 -157
- package/docs/getting-started.md +0 -224
- package/docs/multi-agent.md +0 -145
- package/docs/observability.md +0 -337
- package/docs/origin-integration.md +0 -195
- package/docs/references.md +0 -35
- package/docs/reports.md +0 -551
- package/docs/results-format.md +0 -228
- package/docs/results-lib.md +0 -191
- package/docs/runner.md +0 -104
- package/docs/sandbox.md +0 -276
- package/docs/scoring.md +0 -119
- package/docs/source-map.md +0 -126
- package/docs/tier-sync.md +0 -193
- package/docs/view.md +0 -194
- package/docs/vision.md +0 -88
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "接入你的 Agent"
|
|
3
|
+
sidebarTitle: "接入 Agent"
|
|
4
|
+
description: "接入的全景:写一个 adapter、配一个 experiment、跑通第一条 eval;配置和参数怎么在 experiment 里声明、在 Adapter 里消费。事件流、多轮、HITL、tracing 是增量,各有专章。"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
把一个被测对象接进 [NiceEval](https://niceeval.com/),就是给它写一个 **adapter**:`defineAgent` 包住一个 `send` 函数——接一次输入,驱动你的 agent,返回这一轮结果。接入的全部工作就这一个文件。
|
|
8
|
+
|
|
9
|
+
本文只回答两个问题:**这个文件和它的两样配套(experiment、eval)怎么跑通**,以及**参数怎么从 experiment 流到 Adapter、再流到你的应用**。事件流、多轮、HITL、tracing 都是之后的可选增量,各有专章,文末给了一张地图。
|
|
10
|
+
|
|
11
|
+
## 先选路:你的被测对象是什么?
|
|
12
|
+
|
|
13
|
+
<CardGroup cols={3}>
|
|
14
|
+
<Card title="AI SDK 应用" icon="bolt" href="/zh/reference/builtin-agents">
|
|
15
|
+
用 Vercel AI SDK(`useChat` 后端)写的应用:内置 `uiMessageStreamAgent` 无侵入接它的 HTTP 接口,零映射、含 HITL,有完整的 before/after 示例。不用读本文。
|
|
16
|
+
</Card>
|
|
17
|
+
<Card title="Coding agent CLI" icon="terminal" href="/zh/guides/sandbox-agent">
|
|
18
|
+
评 claude-code / codex / bub 这类改文件的 coding agent:用内置 sandbox agent。
|
|
19
|
+
</Card>
|
|
20
|
+
<Card title="其它 AI Agent" icon="plug">
|
|
21
|
+
自研 agent loop、LangGraph / OpenAI Agents SDK 应用、已部署 agent:手写 send(本文 + [写 send](/zh/guides/write-send))。应用已埋 OTel 的话顺手接上,`niceeval view` 多一张调用瀑布图([OTel 接入](/zh/guides/connect-otel))。
|
|
22
|
+
</Card>
|
|
23
|
+
</CardGroup>
|
|
24
|
+
|
|
25
|
+
## 跑通最小接入
|
|
26
|
+
|
|
27
|
+
假设项目已经 `npx niceeval init` 过(有 `niceeval.config.ts` 和 `evals/` 目录)。三个文件各管一件事:**adapter 连被测系统,experiment 定评谁怎么跑,eval 写交互和断言。**
|
|
28
|
+
|
|
29
|
+
**1. 写 adapter。** 最小接入只填 `status` 和 `events`:你的 agent 说了什么,放一条 `message` 事件。
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
// agents/my-bot.ts
|
|
33
|
+
import { defineAgent } from "niceeval/adapter";
|
|
34
|
+
|
|
35
|
+
export default defineAgent({
|
|
36
|
+
name: "my-bot",
|
|
37
|
+
async send(input, ctx) {
|
|
38
|
+
// 示例地址:换成你自己 agent 的真实端点(HTTP、CLI、SDK 都行,只要 send 里能拿到回复文本)
|
|
39
|
+
const r = await fetch("http://localhost:3000/chat", {
|
|
40
|
+
method: "POST",
|
|
41
|
+
headers: { "content-type": "application/json" },
|
|
42
|
+
body: JSON.stringify({ message: input.text, model: ctx.model }), // ← experiment.model 经 ctx.model 到这里
|
|
43
|
+
signal: ctx.signal,
|
|
44
|
+
});
|
|
45
|
+
const body = await r.json();
|
|
46
|
+
return {
|
|
47
|
+
status: r.ok ? "completed" : "failed",
|
|
48
|
+
events: [{ type: "message", role: "assistant", text: body.reply }],
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
URL 先写死没关系——怎么把它从 experiment 传进来,见下文[「参数怎么进」](#参数怎么进:experiment-声明,adapter-消费)。`send` 的完整契约(`TurnInput` / `AgentContext` / `Turn` 各字段)见 [Adapter](/zh/concepts/adapter)。
|
|
55
|
+
|
|
56
|
+
就算 agent runtime 和 eval 在同一个代码库里,也照样走 HTTP,不要把 `fetch` 换成进程内的函数直调:
|
|
57
|
+
|
|
58
|
+
- **直调评的不是用户走的那条链路。** HTTP 层、序列化、中间件、流式传输全被绕过,eval 通过不代表线上行为正确。
|
|
59
|
+
- **进程不隔离,结果不可复现。** 被测代码的崩溃、全局状态、内存泄漏会连累运行器;并发跑多条 eval 时共享状态互相污染。
|
|
60
|
+
- **超时与取消会失效。** `ctx.signal` 挂在请求上才能真正掐掉一轮跑飞的 agent;进程内的函数调用停不下来。
|
|
61
|
+
- **adapter 会被焊死在这个代码库里。** 走 HTTP 的 adapter 换个 `baseUrl` 就能对着本地 / 预发 / 生产跑(见下文两个 experiment 文件的写法);直调做不到。
|
|
62
|
+
|
|
63
|
+
起一个本地 dev server 的成本只是一条命令,上面这些换不来。
|
|
64
|
+
|
|
65
|
+
**2. 注册进一个 experiment:**
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
// experiments/my-bot.ts
|
|
69
|
+
import { defineExperiment } from "niceeval";
|
|
70
|
+
import myBot from "../agents/my-bot.ts";
|
|
71
|
+
|
|
72
|
+
export default defineExperiment({
|
|
73
|
+
description: "my-bot 基线",
|
|
74
|
+
agent: myBot,
|
|
75
|
+
model: "gpt-4o",
|
|
76
|
+
runs: 1,
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**3. 写第一条 eval,然后跑:**
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
// evals/refund-policy.eval.ts
|
|
84
|
+
import { defineEval } from "niceeval";
|
|
85
|
+
import { includes } from "niceeval/expect";
|
|
86
|
+
|
|
87
|
+
export default defineEval({
|
|
88
|
+
description: "退款政策问答",
|
|
89
|
+
async test(t) {
|
|
90
|
+
await t.send("你们的退款政策是什么?");
|
|
91
|
+
t.succeeded();
|
|
92
|
+
t.check(t.reply, includes("30 天"));
|
|
93
|
+
t.judge.autoevals.closedQA("回答是否说明了退款期限?").atLeast(0.7);
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npx niceeval exp my-bot # 跑这个 experiment 下的全部 eval
|
|
100
|
+
npx niceeval exp my-bot refund # 只跑 ID 以 refund 开头的
|
|
101
|
+
npx niceeval view # 本地查看器里看结果
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**跑通的样子**:终端里每条 eval 一行判决,末尾汇总 `N passed, N failed`;`npx niceeval view` 里能看到每条 eval 逐轮的输入、事件和评分明细。
|
|
105
|
+
|
|
106
|
+
没跑通时,按报错的位置分三类排查:
|
|
107
|
+
|
|
108
|
+
- **`fetch` 直接抛错**(连接被拒等):应用没起来,或 `send` 里的 URL 不对——先用 `curl` 对那个接口发一次同样的请求确认。
|
|
109
|
+
- **`t.succeeded()` 没过、本轮判决是 failed**:请求发出去了但应用回了非 2xx。在 `send` 里用 `ctx.log()` 打印响应体,`niceeval view` 的本轮日志里能看到。
|
|
110
|
+
- **只有内容断言没过**:接入本身已经通了——在 `view` 里对照 `t.reply` 的实际值,调断言或调应用。
|
|
111
|
+
|
|
112
|
+
到这里接入已经完成:文本断言和 judge 评分都能用了。想要更多断言(工具、多轮、审批流),见文末的增量地图。
|
|
113
|
+
|
|
114
|
+
## 参数怎么进:experiment 声明,Adapter 消费
|
|
115
|
+
|
|
116
|
+
配置归属只有两条通道,分清它们,接入就不会乱:
|
|
117
|
+
|
|
118
|
+
1. **静态配置走 Adapter 工厂。** URL、鉴权、协议细节这类"这个环境长什么样"的配置,作为工厂参数写在 experiment 文件里——`defineExperiment` 的 `agent` 字段放的是**已经配置好的实例**。
|
|
119
|
+
2. **每轮动态值走 `ctx`。** experiment 声明的 `model`、`flags`,运行器每轮经 `ctx` 原样递给 `send`;Adapter 不解释它们的含义,只随请求转发给应用。
|
|
120
|
+
|
|
121
|
+
两条通道拼在一起,就是把第一步的 my-bot 从「写死 URL 的实例」改成「收配置的工厂」:default export 换成一个返回 `defineAgent(...)` 的函数,`send` 里写死的地方换成读工厂参数;experiment 声明的 `model`、`flags` 每轮从 `ctx` 到达,`send` 里随请求转发:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
// agents/my-bot.ts —— 工厂:静态配置进闭包
|
|
125
|
+
import { defineAgent } from "niceeval/adapter";
|
|
126
|
+
import type { Agent } from "niceeval/adapter";
|
|
127
|
+
|
|
128
|
+
export function myBot(options: { baseUrl: string }): Agent {
|
|
129
|
+
return defineAgent({
|
|
130
|
+
name: "my-bot",
|
|
131
|
+
async send(input, ctx) {
|
|
132
|
+
const r = await fetch(`${options.baseUrl}/chat`, { // ← 工厂配置:连哪
|
|
133
|
+
method: "POST",
|
|
134
|
+
headers: { "content-type": "application/json" },
|
|
135
|
+
body: JSON.stringify({
|
|
136
|
+
message: input.text,
|
|
137
|
+
model: ctx.model, // ← experiment.model:应用接口收模型选择就转发
|
|
138
|
+
flags: ctx.flags, // ← experiment.flags:原样交给应用,应用按 flag 切换
|
|
139
|
+
}),
|
|
140
|
+
signal: ctx.signal, // ← 运行器的超时与取消:挂到每个请求上
|
|
141
|
+
});
|
|
142
|
+
const body = await r.json();
|
|
143
|
+
return {
|
|
144
|
+
status: r.ok ? "completed" : "failed",
|
|
145
|
+
events: [{ type: "message", role: "assistant", text: body.reply }],
|
|
146
|
+
};
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
鉴权 header、协议开关这类同属静态配置,一样加进 `options`。experiment 侧对应两处小改:具名导入工厂,`agent` 字段从「引用实例」变成「调用工厂」:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
// experiments/my-bot.ts —— 参数的声明处
|
|
156
|
+
import { defineExperiment } from "niceeval";
|
|
157
|
+
import { myBot } from "../agents/my-bot.ts";
|
|
158
|
+
|
|
159
|
+
export default defineExperiment({
|
|
160
|
+
description: "my-bot 基线",
|
|
161
|
+
agent: myBot({ baseUrl: "http://localhost:3000" }), // ← 静态配置:实例化时传
|
|
162
|
+
model: "gpt-5.4", // ← 动态值:经 ctx.model 到 send
|
|
163
|
+
flags: { promptVariant: "concise" }, // ← 动态值:经 ctx.flags 到 send
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`ctx` 上每轮可能用到的字段和消费方式:
|
|
168
|
+
|
|
169
|
+
| `ctx` 字段 | 谁给的 | Adapter 怎么消费 |
|
|
170
|
+
|---|---|---|
|
|
171
|
+
| `signal` | 运行器(超时与取消) | 挂到发出的每个请求上 |
|
|
172
|
+
| `model` | experiment 的 `model` | 应用接口收模型选择就随请求转发;不收就忽略 |
|
|
173
|
+
| `flags` | experiment 的 `flags` | 原样转发(请求体、header 都行),应用按 flag 切换变体 |
|
|
174
|
+
| `telemetry` | 配置了 OTel 接入时出现 | 只碰 `headers`:每轮新的 W3C `traceparent`,spread 进请求头。接收端点每次运行都一样,在 `defineConfig` 里固定、应用启动时指向它,不从 send 传——见 [OTel 接入](/zh/guides/connect-otel) |
|
|
175
|
+
| `session` | 运行器(每条会话线一份) | 会话续接与 HITL 停轮现场的存取器都在它上面:`history()`、`id` / `capture()`、`hold()` / `take()`,见[写 send](/zh/guides/write-send) |
|
|
176
|
+
| `log(msg)` | 运行器 | 写进本轮日志,`niceeval view` 里可见 |
|
|
177
|
+
|
|
178
|
+
同一个 agent 对着本地和生产分别跑,就是两个 experiment 文件、两份工厂参数:
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
// experiments/local.ts
|
|
182
|
+
export default defineExperiment({
|
|
183
|
+
agent: myBot({ baseUrl: "http://localhost:3000" }),
|
|
184
|
+
model: "gpt-5.4",
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// experiments/prod.ts
|
|
188
|
+
export default defineExperiment({
|
|
189
|
+
agent: myBot({ baseUrl: "https://api.example.com" }),
|
|
190
|
+
model: "gpt-5.4",
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
npx niceeval exp local
|
|
196
|
+
npx niceeval exp prod
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
不要把 URL 放进 CLI 位置参数——experiment 名之后的位置参数只用于过滤 eval ID。experiment 的完整字段(`runs`、`budget`、并发、`sandbox`)见[写实验](/zh/guides/write-experiment)。
|
|
200
|
+
|
|
201
|
+
## 之后的增量:一张地图
|
|
202
|
+
|
|
203
|
+
最小接入之后,每给 adapter 加一个增量就解锁一组断言,已写的 eval 不用改:
|
|
204
|
+
|
|
205
|
+
| 想解锁什么 | 给 adapter 加什么 | 去哪看 |
|
|
206
|
+
|---|---|---|
|
|
207
|
+
| 工具断言(`calledTool` / `toolOrder` / 负断言) | 把应用返回映射成标准事件流 | [写 send](/zh/guides/write-send)、[事件流参考](/zh/reference/events) |
|
|
208
|
+
| 多轮对话、`t.newSession()` 隔离 | 接上 `ctx.session`:`history()` 或 `id` + `capture()` | [写 send](/zh/guides/write-send) |
|
|
209
|
+
| 审批流(HITL,人工介入) | 停轮返回 `waiting` + `input.requested`,回答轮续跑 | [HITL](/zh/concepts/hitl) |
|
|
210
|
+
| `niceeval view` 的调用瀑布图 | 应用把 OTel span 发给 NiceEval(不影响断言) | [OTel 接入](/zh/guides/connect-otel) |
|
|
211
|
+
| feature A/B 对比 | 应用把变体暴露成 `flags` 可切换的配置 | [Tier](/zh/concepts/tier)、[写实验](/zh/guides/write-experiment) |
|
|
212
|
+
|
|
213
|
+
这些增量落在哪一档、各档买到什么,见 [Tier](/zh/concepts/tier)。
|
|
214
|
+
|
|
215
|
+
## 参考实现
|
|
216
|
+
|
|
217
|
+
完整的可跑参考:[`examples/zh/tier1`](https://github.com/CorrectRoadH/niceeval/tree/main/examples/zh/tier1) 下五个无侵入接入示例(ai-sdk-v7、claude-sdk、codex-sdk、pi-sdk、langgraph),覆盖事件流断言、多轮隔离、HITL 批准 / 拒绝、trace 瀑布图。手写 `send` 时真正要写的只有 transport 和映射表——会话续接、HITL 暂停恢复的状态槽都在 `ctx.session` 上,逐帧驱动是官方件,见[内置 Agent 能力](/zh/reference/builtin-agents)。
|
|
218
|
+
|
|
219
|
+
## 相关阅读
|
|
220
|
+
|
|
221
|
+
- [官方适配器一览](/zh/guides/official-adapters) —— 被测对象能直接用官方 Adapter 时,Sandbox / 非 Sandbox 各有哪些、配置项怎么写。
|
|
222
|
+
- [写 send](/zh/guides/write-send) —— 手写 Adapter 的完整教程:七步递进,从发一条消息到 HITL、OTel、flags。
|
|
223
|
+
- [Adapter](/zh/concepts/adapter) —— `send` 的契约:`TurnInput` / `AgentContext` / `Turn` 逐字段。
|
|
224
|
+
- [OTel 接入](/zh/guides/connect-otel) —— 把应用的 span 也发给 [NiceEval](https://niceeval.com/),换 `niceeval view` 的调用瀑布图。
|
|
225
|
+
- [Tier](/zh/concepts/tier) —— 三档接入各投入什么、买到什么。
|
|
226
|
+
- [写实验](/zh/guides/write-experiment) —— `defineExperiment` 的完整字段。
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "数据驱动测试(dataset fan-out):用多份数据运行同一套 eval"
|
|
3
|
+
sidebarTitle: "数据驱动测试"
|
|
4
|
+
description: "从 .eval.ts 文件导出数组,将一套 eval 逻辑展开为多个 case。用 loadYaml 或 loadJson 读取外部数据集,并获得稳定 ID。"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
数据驱动测试(dataset fan-out)适合大量结构相同、输入不同的测试。例如 SQL 生成、意图分类、检索问答和工具选择。
|
|
8
|
+
|
|
9
|
+
## 工作原理
|
|
10
|
+
|
|
11
|
+
一个 `.eval.ts` 文件默认导出数组:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { defineEval } from "niceeval";
|
|
15
|
+
import { equals } from "niceeval/expect";
|
|
16
|
+
|
|
17
|
+
const rows = [
|
|
18
|
+
{ task: "Count users", prompt: "Count all users", sql: "SELECT COUNT(*) FROM users;" },
|
|
19
|
+
{ task: "Recent orders", prompt: "Find recent orders", sql: "SELECT * FROM orders ORDER BY created_at DESC LIMIT 10;" },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
export default rows.map((row) =>
|
|
23
|
+
defineEval({
|
|
24
|
+
description: row.task,
|
|
25
|
+
async test(t) {
|
|
26
|
+
await t.send(row.prompt);
|
|
27
|
+
t.check(t.reply, equals(row.sql));
|
|
28
|
+
},
|
|
29
|
+
}),
|
|
30
|
+
);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 生成 ID
|
|
34
|
+
|
|
35
|
+
如果文件是 `evals/sql.eval.ts`,生成的 ID 是:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
sql/0000
|
|
39
|
+
sql/0001
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
序号零填充,便于稳定引用和过滤。
|
|
43
|
+
|
|
44
|
+
## 从 YAML / JSON 加载
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { loadYaml } from "niceeval/loaders";
|
|
48
|
+
|
|
49
|
+
const doc = await loadYaml("evals/data/sql-cases.yaml");
|
|
50
|
+
const rows = doc.cases as { task: string; prompt: string; sql: string }[];
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```yaml
|
|
54
|
+
cases:
|
|
55
|
+
- task: Count users
|
|
56
|
+
prompt: Count all rows in the users table
|
|
57
|
+
sql: SELECT COUNT(*) FROM users;
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 过滤数据集 eval
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# 运行整个数据集
|
|
64
|
+
npx niceeval exp local sql
|
|
65
|
+
|
|
66
|
+
# 只运行第一个 case
|
|
67
|
+
npx niceeval exp local sql/0000
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 数据集 vs 独立文件
|
|
71
|
+
|
|
72
|
+
<Tabs>
|
|
73
|
+
<Tab title="使用数据集">
|
|
74
|
+
case 结构完全一致,主要差异是输入和预期输出。
|
|
75
|
+
</Tab>
|
|
76
|
+
<Tab title="使用独立文件">
|
|
77
|
+
每个 case 有不同流程、不同断言或需要不同 agent 配置。
|
|
78
|
+
</Tab>
|
|
79
|
+
</Tabs>
|
|
80
|
+
|
|
81
|
+
<Tip>
|
|
82
|
+
数据集适合“横向覆盖”,独立 eval 文件适合“行为复杂”的场景。
|
|
83
|
+
</Tip>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "实验矩阵:用运行矩阵比较 agents 和 models"
|
|
3
|
+
sidebarTitle: "实验矩阵"
|
|
4
|
+
description: "使用 NiceEval experiments 让同一批 eval 横跨多个 agents、models 和 feature flags,比较 pass rate、成本和延迟。"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Experiment 用于比较多组运行配置。典型问题包括:Claude Code 和 Codex 谁在同一批 coding-agent 任务上 pass rate 更高?某个 prompt 改动是否降低成本?不同模型的延迟和质量如何取舍?
|
|
8
|
+
|
|
9
|
+
## 基本形状
|
|
10
|
+
|
|
11
|
+
**一个实验文件 = 一个配置**(一个 agent × 一个 model)。`model` 是单个字符串,不接受数组。要跨模型 / 跨 agent 对比,就在一个**实验组文件夹**里写多个文件,各钉对照轴之外的一切:
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
experiments/
|
|
15
|
+
compare-models/ # 实验组
|
|
16
|
+
gpt-5.4.ts # 一格:钉 model = gpt-5.4
|
|
17
|
+
deepseek-v4-pro.ts # 一格:钉 model = deepseek-v4-pro
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// experiments/compare-models/gpt-5.4.ts
|
|
22
|
+
import { defineExperiment } from "niceeval";
|
|
23
|
+
import { webAgent } from "../../adapter/adapter.ts";
|
|
24
|
+
|
|
25
|
+
export default defineExperiment({
|
|
26
|
+
description: "gpt-5.4: 对比模型",
|
|
27
|
+
agent: webAgent({ baseUrl: "http://127.0.0.1:5188" }),
|
|
28
|
+
model: "gpt-5.4", // 单个字符串;另一个模型就复制一份文件改这一行
|
|
29
|
+
runs: 2,
|
|
30
|
+
earlyExit: true,
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx niceeval exp compare-models # 把同组各 model 并排出报告
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
这样每个配置独立成文件:可命名、可 diff、可单独 review,"这一组就是对照"在文件结构上就讲清楚了。
|
|
39
|
+
|
|
40
|
+
`defineExperiment` 的字段怎么写、`flags` 怎么透传到 adapter,见[写实验](/zh/guides/write-experiment)。
|
|
41
|
+
|
|
42
|
+
## 适合比较什么
|
|
43
|
+
|
|
44
|
+
- 不同 Adapters。
|
|
45
|
+
- 不同模型(Tier 1 接入即可:应用接口暴露模型选择,`model` 经 `ctx.model` 透传)。
|
|
46
|
+
- 不同 prompts 或 feature flags(要求 Tier 3 接入:变体在应用内部,需要应用把它暴露成 experiment 可选的配置,经 `flags` → `ctx.flags` 透传)。
|
|
47
|
+
- 不同 sandbox 后端。
|
|
48
|
+
- 同一任务的 pass@N。
|
|
49
|
+
|
|
50
|
+
Tier 1 / Tier 2 / Tier 3 的定义见 [Tier](/zh/concepts/tier)。
|
|
51
|
+
|
|
52
|
+
## 查看结果
|
|
53
|
+
|
|
54
|
+
Experiment 输出通常按 `(agent, model, eval)` 维度展示:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
api-validation claude-code+zod-skill pass@3 = 3/3 (100%) mean 34s
|
|
58
|
+
api-validation claude-code pass@3 = 1/3 (33%) mean 41s
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
除了 pass rate,还应该看平均耗时、token、成本和失败类型。
|
|
62
|
+
|
|
63
|
+
## 设计 experiment 的建议
|
|
64
|
+
|
|
65
|
+
- 保持 eval 集合稳定,避免比较时混入新变量。
|
|
66
|
+
- 每个 cell 跑多个 attempts,尤其是非确定性 coding agent。
|
|
67
|
+
- 把预算和并发写清楚。
|
|
68
|
+
- 对“失败原因”做归类,不只看总分。
|
|
69
|
+
|
|
70
|
+
## 与普通运行的关系
|
|
71
|
+
|
|
72
|
+
`npx niceeval exp <实验>` 关注“这一批 eval 在某个配置下是否通过”;实验组关注“多组配置谁更好”。两者使用同一套 eval、Adapter、scoring 和 artifacts。
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "沙箱 Fixture:用任务评估 coding agents"
|
|
3
|
+
sidebarTitle: "Fixture"
|
|
4
|
+
description: "用 .eval.ts 给 coding agent 准备隔离 workspace、发送真实任务,并用 sandbox 文件、命令、diff 和 judge 验证结果。"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
评估 coding agent 时,单纯检查一段回复不够。你通常要给它一个真实项目,让它读写文件、运行命令、提交改动,然后检查产物是否正确。[NiceEval](https://niceeval.com/) 的当前推荐写法是:用普通 `.eval.ts` 文件显式准备 sandbox workspace、发送任务并断言结果。
|
|
8
|
+
|
|
9
|
+
可运行参考见 [`examples/zh/coding-agent-skill`](https://github.com/CorrectRoadH/niceeval/tree/main/examples/zh/coding-agent-skill)。
|
|
10
|
+
|
|
11
|
+
## 推荐目录结构
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
evals/
|
|
15
|
+
├─ api-validation.eval.ts
|
|
16
|
+
├─ config-schema.eval.ts
|
|
17
|
+
└─ ponytail-safe-path.eval.ts
|
|
18
|
+
|
|
19
|
+
workspaces/
|
|
20
|
+
└─ ts-starter/
|
|
21
|
+
├─ package.json
|
|
22
|
+
├─ tsconfig.json
|
|
23
|
+
└─ src/
|
|
24
|
+
|
|
25
|
+
skills/
|
|
26
|
+
└─ zod.md
|
|
27
|
+
|
|
28
|
+
experiments/
|
|
29
|
+
├─ baseline.ts
|
|
30
|
+
└─ with-skill.ts
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`workspaces/` 放 agent 可见的初始项目。`.eval.ts` 控制什么时候上传 workspace、发什么 prompt、检查什么结果。`experiments/` 决定用哪个 agent、哪个模型、是否注入 Skill 或插件。
|
|
34
|
+
|
|
35
|
+
## 准备 workspace
|
|
36
|
+
|
|
37
|
+
在 eval 里把初始项目上传到 sandbox:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { defineEval } from "niceeval";
|
|
41
|
+
|
|
42
|
+
export default defineEval({
|
|
43
|
+
description: "给 Express 路由添加请求体验证",
|
|
44
|
+
async test(t) {
|
|
45
|
+
await t.sandbox.uploadDirectory("../workspaces/ts-starter");
|
|
46
|
+
|
|
47
|
+
await t.send("在 src/routes/users.ts 里实现 POST /users 路由。").then((turn) => turn.succeeded());
|
|
48
|
+
|
|
49
|
+
t.sandbox.fileChanged("src/routes/users.ts");
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
也可以用 `t.sandbox.writeFiles()` 在单个 eval 里补充种子文件,适合小任务或安全探针。
|
|
55
|
+
|
|
56
|
+
## 写任务 prompt
|
|
57
|
+
|
|
58
|
+
Prompt 应该像真实工单,描述目标和约束,但不要泄露验证答案。
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
await t
|
|
62
|
+
.send(
|
|
63
|
+
`在 src/config/env.ts 里实现环境变量校验。
|
|
64
|
+
要求:
|
|
65
|
+
- 用 Zod 定义 EnvSchema
|
|
66
|
+
- PORT 转成 number
|
|
67
|
+
- DATABASE_URL 必须是合法 URL
|
|
68
|
+
- JWT_SECRET 最少 32 个字符
|
|
69
|
+
- 导出 env 对象和 Env 类型`,
|
|
70
|
+
)
|
|
71
|
+
.then((turn) => turn.succeeded());
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
把“是否防路径穿越”“是否复用现有工具”这类隐式质量要求留给验证阶段,才能测出 Skill 或插件是否真的帮助 agent 主动补齐上下文。
|
|
75
|
+
|
|
76
|
+
## 验证文件和代码
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { includes, excludes } from "niceeval/expect";
|
|
80
|
+
|
|
81
|
+
const src = await t.sandbox.readSourceFiles({ extensions: ["ts"] });
|
|
82
|
+
const config = src.fileMatching(/env/);
|
|
83
|
+
const code = config?.content ?? "";
|
|
84
|
+
|
|
85
|
+
t.check(code, includes(/z\.object\s*\(/));
|
|
86
|
+
t.check(code, includes(/EnvSchema\.parse\s*\(\s*process\.env/));
|
|
87
|
+
t.check(code, excludes(/process\.env\.\w+\s*\?\?/));
|
|
88
|
+
t.sandbox.fileChanged("src/config/env.ts");
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`t.sandbox.fileChanged()` 是作用域断言:它在运行结束后用 sandbox diff 判断目标文件是否真的被修改。
|
|
92
|
+
|
|
93
|
+
## 运行项目测试或探针脚本
|
|
94
|
+
|
|
95
|
+
对 coding-agent 任务,真实命令通常比文本匹配更可靠:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
await t.sandbox.writeFiles({
|
|
99
|
+
"_test_traversal.py": [
|
|
100
|
+
"import sys, os",
|
|
101
|
+
"sys.path.insert(0, '.')",
|
|
102
|
+
"from uploads import safe_upload_path",
|
|
103
|
+
"base = '/var/uploads'",
|
|
104
|
+
"try:",
|
|
105
|
+
" p = safe_upload_path(base, '../../etc/passwd')",
|
|
106
|
+
" assert os.path.commonpath([base, os.path.abspath(p)]) == base",
|
|
107
|
+
"except (ValueError, PermissionError, AssertionError):",
|
|
108
|
+
" pass",
|
|
109
|
+
"print('ok')",
|
|
110
|
+
].join("\n"),
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const result = await t.sandbox.runCommand("python3", ["_test_traversal.py"]);
|
|
114
|
+
t.check(result.stdout.trim(), includes(/ok/));
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
也可以运行项目自己的 test/lint/build 脚本:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
const result = await t.sandbox.runCommand("npm", ["test"]);
|
|
121
|
+
t.check(result.exitCode, includes(0));
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## 用 experiment 做对照组
|
|
125
|
+
|
|
126
|
+
不要把 agent 名、插件名或 URL 放进位置参数。experiment 决定“对着哪个 agent、怎么跑”;CLI 的 eval 位置参数只过滤 eval ID。
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
pnpm exec niceeval exp compare
|
|
130
|
+
pnpm exec niceeval exp compare api-validation
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
典型 A/B:
|
|
134
|
+
|
|
135
|
+
- `experiments/baseline.ts`:裸 agent。
|
|
136
|
+
- `experiments/with-skill.ts`:同一 agent + setup hook 注入 `CLAUDE.md`。
|
|
137
|
+
- 两组使用同一批 eval、同一模型、同一 runs 和预算。
|
|
138
|
+
|
|
139
|
+
## 何时用 sandbox fixture
|
|
140
|
+
|
|
141
|
+
- 需要 agent 修改真实文件。
|
|
142
|
+
- 需要运行项目测试、构建或 lint。
|
|
143
|
+
- 需要比较 diff。
|
|
144
|
+
- 需要评估 Claude Code、Codex 这类 coding agent。
|
|
145
|
+
- 需要比较 Skill / 插件 / Hook 是否改善真实任务。
|
|
146
|
+
|
|
147
|
+
如果只是评 direct agent 应用,用普通 direct adapter 会更轻。
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "官方适配器一览"
|
|
3
|
+
sidebarTitle: "官方适配器"
|
|
4
|
+
description: "NiceEval 内置的 Sandbox 和非 Sandbox 适配器分别是什么、怎么鉴权,Sandbox 型里怎么装 MCP server、Skill 和插件。"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
[NiceEval](https://niceeval.com/) 随包带几个官方 Adapter(`niceeval/adapter` 导出的工厂函数),按被测对象要不要隔离工作区分两类:**Sandbox 型**(`claude-code` / `codex` / `bub`)在 Docker 或云端沙箱里跑 coding-agent CLI,能装 MCP server、Skill、Python 插件;**非 Sandbox 型**无侵入连一个已经在跑的 HTTP 服务,或者帮你手写 adapter 时省掉事件流映射。这篇按类型和具体 Adapter 分节,重点是每个 Adapter 的配置项——怎么选、怎么跑通第一条 eval,见[接入你的 Agent](/zh/guides/connect-your-agent)。
|
|
8
|
+
|
|
9
|
+
## Sandbox 适配器
|
|
10
|
+
|
|
11
|
+
三个内置 Sandbox agent 都用 `defineSandboxAgent` 构造,鉴权走环境变量(可用工厂参数覆盖),并且都支持在沙箱 `setup` 阶段装扩展。怎么运行内置 Sandbox agent、目录结构和自定义 Sandbox adapter,见 [Sandbox Agent](/zh/guides/sandbox-agent);这里只讲每个 Adapter 能装什么、配置项怎么写。
|
|
12
|
+
|
|
13
|
+
### claude-code
|
|
14
|
+
|
|
15
|
+
- **鉴权**:`ANTHROPIC_API_KEY`(工厂参数 `apiKey` 可覆盖),可选 `ANTHROPIC_BASE_URL`(工厂参数 `baseUrl`)。
|
|
16
|
+
- **装 MCP server**:`mcpServers` 配置项,`setup` 阶段写进沙箱里用户级的 `~/.claude.json`(顶层 `mcpServers` 字段)。
|
|
17
|
+
- **装 Skill**:`skills: ["org/repo"]`(GitHub 仓库),`setup` 阶段跑 `npx skills add <org/repo> -y -a claude-code`(`-y -a` 避免无 tty 的沙箱里卡在交互式的 agent 选择框),结果写进沙箱工作区的 `skills-lock.json`,claude CLI 启动时自动读取(原生 `Skill` 工具,可用 `t.calledTool("Skill", { input: { skill } })` 断言,不是 `t.loadedSkill()`——那是给 `load_skill` 这个不同协议约定的语法糖)。
|
|
18
|
+
- **tracing**:claude CLI 的 beta 原生遥测(`CLAUDE_CODE_ENHANCED_TELEMETRY_BETA`),span 只有结构和计时,细节见 [OTel 接入](/zh/guides/connect-otel)。
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { defineExperiment } from "niceeval";
|
|
22
|
+
import { claudeCodeAgent } from "niceeval/adapter";
|
|
23
|
+
import { dockerSandbox } from "niceeval/sandbox";
|
|
24
|
+
|
|
25
|
+
export default defineExperiment({
|
|
26
|
+
agent: claudeCodeAgent({
|
|
27
|
+
mcpServers: [
|
|
28
|
+
{ name: "browser", command: "npx", args: ["-y", "@anthropic/mcp-browser"] },
|
|
29
|
+
],
|
|
30
|
+
skills: ["Effect-TS/skills"],
|
|
31
|
+
}),
|
|
32
|
+
model: "claude-sonnet-4-6",
|
|
33
|
+
sandbox: dockerSandbox(),
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### codex
|
|
38
|
+
|
|
39
|
+
- **鉴权**:`CODEX_API_KEY`(工厂参数 `apiKey` 可覆盖,不是 `OPENAI_API_KEY`),可选 `CODEX_BASE_URL`(工厂参数 `baseUrl`)接 OpenAI 兼容代理。
|
|
40
|
+
- **装 MCP server**:`mcpServers` 配置项,`setup` 阶段追加进 `~/.codex/config.toml` 的 `[mcp_servers.<name>]` 段。
|
|
41
|
+
|
|
42
|
+
<Warning>
|
|
43
|
+
是复数 `mcp_servers`:单数 `[mcp_server.x]` 会被 codex CLI 静默忽略,MCP 压根挂不上,且不会报错——自查用 `codex mcp list`。
|
|
44
|
+
</Warning>
|
|
45
|
+
|
|
46
|
+
- **装 Skill**:`skills: ["org/repo"]`,和 claude-code 一样走 `npx skills add <org/repo> -y -a codex`。codex 没有 claude-code 那种原生 Skill 工具——装的技能落在 skills 包的通用目录(`.agents/skills/<name>`),codex CLI 不会主动去读,要在 prompt 里明说"检查仓库里有没有相关的 skill/guide 文件",agent 才会用 shell 命令读进上下文;断言"用没用到"只能看它是否真的执行过读那个文件的命令,没有工具调用可以直接认。
|
|
47
|
+
- **tracing**:内置,通过 `config.toml` 的 `[otel.trace_exporter.otlp-http]` 段配置,协议 `http/json`。
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { defineExperiment } from "niceeval";
|
|
51
|
+
import { codexAgent } from "niceeval/adapter";
|
|
52
|
+
import { dockerSandbox } from "niceeval/sandbox";
|
|
53
|
+
|
|
54
|
+
export default defineExperiment({
|
|
55
|
+
agent: codexAgent({
|
|
56
|
+
mcpServers: [
|
|
57
|
+
{ name: "browser", command: "npx", args: ["-y", "@anthropic/mcp-browser"] },
|
|
58
|
+
],
|
|
59
|
+
skills: ["Effect-TS/skills"],
|
|
60
|
+
}),
|
|
61
|
+
model: "gpt-5.4",
|
|
62
|
+
sandbox: dockerSandbox(),
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### bub
|
|
67
|
+
|
|
68
|
+
- **鉴权**:`BUB_API_KEY` + `BUB_API_BASE`(OpenAI 兼容代理),工厂参数 `apiKey` / `apiBase` 可覆盖。
|
|
69
|
+
- **装插件**:`pythonPlugins: string[]`(pip 包名或 git URL),`setup` 阶段以 `uv tool install --with <pkg>` 追加进 bub 的 Python 工具环境。
|
|
70
|
+
- bub 目前不支持 `mcpServers` / `skills` 配置项——没有对应的包装。
|
|
71
|
+
- **安装方式**:走 `uv tool install`(PyPI 包,不是 npm 包),首次安装会建 checkpoint 缓存加速后续沙箱。
|
|
72
|
+
- **tracing**:内置,通过环境变量注入,协议 `http/protobuf`。
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { defineExperiment } from "niceeval";
|
|
76
|
+
import { bubAgent } from "niceeval/adapter";
|
|
77
|
+
import { dockerSandbox } from "niceeval/sandbox";
|
|
78
|
+
|
|
79
|
+
export default defineExperiment({
|
|
80
|
+
agent: bubAgent({
|
|
81
|
+
pythonPlugins: ["bub-plugin-memory"],
|
|
82
|
+
}),
|
|
83
|
+
model: "gpt-5.4",
|
|
84
|
+
sandbox: dockerSandbox(),
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 三个 Sandbox 适配器对比
|
|
89
|
+
|
|
90
|
+
| | claude-code | codex | bub |
|
|
91
|
+
|---|---|---|---|
|
|
92
|
+
| 鉴权环境变量 | `ANTHROPIC_API_KEY` | `CODEX_API_KEY` | `BUB_API_KEY` + `BUB_API_BASE` |
|
|
93
|
+
| MCP server | ✅ `mcpServers` | ✅ `mcpServers` | ❌ |
|
|
94
|
+
| Skill | ✅ `skills`(GitHub `org/repo`) | ✅ `skills`(GitHub `org/repo`) | ❌ |
|
|
95
|
+
| 插件 | — | — | ✅ `pythonPlugins`(pip 包 / git URL) |
|
|
96
|
+
| tracing | ✅(beta,仅结构与计时) | ✅ | ✅ |
|
|
97
|
+
| 安装方式 | npm 全局包 | npm 全局包 | `uv tool install`(PyPI) |
|
|
98
|
+
|
|
99
|
+
## 非 Sandbox 适配器
|
|
100
|
+
|
|
101
|
+
被测对象不需要隔离工作区——已经部署的 HTTP 服务、自研 agent loop——用非 Sandbox 适配器无侵入接入,不需要 Docker。
|
|
102
|
+
|
|
103
|
+
- **`uiMessageStreamAgent`**:内置,无侵入连 AI SDK `useChat` 后端的 HTTP 端点,零映射(含 HITL)。
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import { uiMessageStreamAgent } from "niceeval/adapter";
|
|
107
|
+
|
|
108
|
+
export default uiMessageStreamAgent({
|
|
109
|
+
name: "my-assistant",
|
|
110
|
+
url: "http://localhost:3000/api/chat",
|
|
111
|
+
body: (ctx) => ({ model: ctx.model }),
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
- **SDK 事件流转换器**(`fromClaudeSdkMessages` / `fromPiAgentEvents` / `fromCodexThreadEvents`):手写 adapter 连一个跑着 Claude Agent SDK / pi-agent-core / Codex SDK 的服务时,原生帧到标准事件的映射官方已经做好,只需要补传输粘合,配合 `driveFrameStream` 逐帧驱动。
|
|
116
|
+
- **`fromAiSdk`**:AI SDK `generateText` / `streamText` 结果到标准事件流的转换器,写自己的 HTTP web agent adapter 时用。
|
|
117
|
+
|
|
118
|
+
这几个适配器的完整参数、能力表和示例代码,见[内置 Agent 能力参考](/zh/reference/builtin-agents)。
|
|
119
|
+
|
|
120
|
+
## 怎么选
|
|
121
|
+
|
|
122
|
+
- 被测对象是必须在真实文件系统里改代码、跑命令的 coding agent:`claude-code` / `codex` / `bub`,见上文 Sandbox 适配器分节选配置。
|
|
123
|
+
- 被测对象是 AI SDK(`useChat` 后端)应用:`uiMessageStreamAgent`,零映射且含 HITL。
|
|
124
|
+
- 被测对象是其它已部署的 agent 系统(HTTP / gRPC):非 Sandbox,手写 adapter,官方 SDK 转换器能覆盖大半映射工作。
|
|
125
|
+
|
|
126
|
+
## 相关阅读
|
|
127
|
+
|
|
128
|
+
- [接入你的 Agent](/zh/guides/connect-your-agent) — 全景:experiment 怎么配、eval 怎么写。
|
|
129
|
+
- [Sandbox Agent](/zh/guides/sandbox-agent) — 怎么运行内置 Sandbox agent,以及怎么写自己的。
|
|
130
|
+
- [内置 Agent 能力参考](/zh/reference/builtin-agents) — 每个适配器逐能力盘点、非 Sandbox 适配器的完整代码示例。
|
|
131
|
+
- [OTel 接入](/zh/guides/connect-otel) — 把 span 发给 [NiceEval](https://niceeval.com/),换 `niceeval view` 的调用瀑布图。
|
|
132
|
+
- [defineAgent 参考](/zh/reference/define-agent) — `defineAgent` / `defineSandboxAgent` 完整参数。
|