niceeval 依赖已经装进项目、niceeval init 已经运行,你是从随包 INDEX.md 进入本页的。用用户的语言与用户交流。所有 API、字段和 CLI 行为以随包文档为准,不要凭训练记忆现编。
第 1 步:探索项目,再和用户确认
这一步决定后面整条路径。先自己读代码探明,把探到的结论列给用户核对,探不到的再提问——不要一上来就抛一串问题,也不要没探就假设。要探明的信息:-
这是个什么 Agent:读 README、
package.json依赖、路由和 agent loop 代码,判断它是用什么写的(AI SDK、LangGraph、OpenAI Agents SDK、Claude Agent SDK、自研 loop……),核心用例是什么(客服?SQL?编码任务?)。 -
前端和 Agent 怎么通信:HTTP 还是 gRPC / WebSocket?协议是标准的还是自己实现的——AI SDK UI Message Stream、OpenAI Responses / Chat Completions 这类标准协议,还是 SDK 原生事件流透传,还是用户自定义的 JSON/SSE 帧?这直接决定 Adapter 是用内置的(零映射)还是手写
send(要自己写事件映射)。 - 后端有没有接 OTel:搜有没有 OTel SDK 初始化、AI SDK telemetry、LangSmith / OpenLLMetry / OpenInference 这类埋点。已经有的话 Tier 2 几乎零成本。
-
用户自己有没有做 A/B Test / feature flag:应用里已有变体开关的话,Experiment 的
flags可以直接透传给它(Tier 3 的现成入口)。 -
Judge 用什么:语义评估(
t.judge.autoevals.*)要一个与被测 Agent 分离的 Judge 模型,走 OpenAI 兼容的/chat/completions协议。问用户手上有哪个服务的 key、想用什么 Judge 模型。缺 key 不会静默通过:Judge Assertion 会记为unavailable;它参与 Pass grading,或在 Score Eval 中配置了.score()/.orStop()时,Attempt 的 grading 不可用。没有 Judge 配置时,先只写精确 Match。 -
是不是 Agent 本体要进 Sandbox:被测对象是 coding agent CLI、或给 coding agent 写的 Skill/Plugin/Hook/MCP server(要在隔离 workspace 里改文件/跑命令)的话,必须走 Sandbox,不能像 HTTP 服务那样直接
send。 默认建议dockerSandbox({ source: { type: "image", image: "node:24-slim" } }),但要先跟用户确认——本机/CI 有没有 Docker、要不要 Vercel Sandbox 或其它远程 Provider。用户没有异议就默认走 Docker。Provider 声明在 Eval 或 Experiment 上,没有 CLI flag、项目级默认值或自动探测——见选择 Sandbox Provider。
- Tier 1(只接 send):应用一行不改,全套断言(文本、Judge、多轮、工具、HITL)都在这一档。
- Tier 2(send + OTel):应用把 OTel span 也发 NiceEval 一份,换
niceeval view的调用瀑布图。已有埋点(第 3 点探到的)就零改动。 - Tier 3(侵入改造 + flags):把应用内部变体暴露成
flags做 feature A/B。已有 A/B 开关(第 4 点探到的)就是现成入口。
第 2 步:配置 Judge
把第 1 步问到的 Judge 服务配上。Judge 走 OpenAI 兼容的/chat/completions 协议,在 niceeval.config.ts 里配:
baseUrl 必须显式写。 只配 key 不配 baseUrl,NiceEval 打的是官方端点,网关凭据会被发到 OpenAI 并回一句「Incorrect API key provided」——看起来像 key 过期,实际是端点选错了。
三个要提醒用户的点:
- 配错不会假装通过。 模型或 key 解析不到、网关拒绝鉴权、评估请求超时时,Judge Assertion 记为
unavailable并带上原因;读取面不会把缺失 measurement 伪装成 mismatch 或零分。 - 配完先验一次。 写一条带
judge: true的轻量 Pass Eval,调用turn.judge.autoevals.closedQA(...).atLeast(0.8)。跑完niceeval show <评估用例 id>,确认能看到 measurement、threshold、condition 与 Assertion 证据。 - 没有用户在场(自治接入)时不要直接跳过 Judge:先看环境里是否已有可用 key,例如
OPENAI_API_KEY或DEEPSEEK_API_KEY。有就用apiKeyEnv指向它并验证;没有时先写精确 Match,并在收尾说明 Judge 没有配置。 - Judge 模型要与被测 Agent 分离,避免同一个模型给自己的输出打分。配置按 Eval、Experiment、项目配置逐字段解析;三种 recipe 见 Judge。
第 3 步:写三件套
按第 1 步选中的方向读完对应文档后,依次写:- Adapter(
agents/*.ts或用户项目里约定的目录)——用defineAgent实现send,配置走工厂参数,不写死、不读process.env。契约见 Adapter,API 签名见 Adapter 参考,事件映射见事件参考。两个容易踩的点:端点/模式要选被测系统核心能力的入口,不是最容易跑通的入口——比如被测平台既有「纯 LLM 聊天」又有「连库执行」两种模式,接前者等于评了个底层模型代理,没评到产品本身。evidenceCoverage必须按实际映射如实声明——只把最终文本映射出来就不要用completeEvidenceCoverage,声明会影响断言完整性,虚报比保守更糟。 - Experiment(
experiments/*.ts)——引用上面的 Adapter,声明model、flags、attempts等。模型对比写两个实验文件,各自钉一个model。evals: (eval) => boolean决定各自本次运行哪些评估用例。路径只负责 id 和批量运行,报告消费当前 Sample 的物理结果与覆盖事实。 - 评估用例(
evals/*.eval.ts)——先探明这个应用是干嘛的,再写一条贴着它真实功能的评估用例。读它的 README、路由、工具定义或系统提示,找出它的核心用例(客服机器人就问一条真实的客服问题、SQL agent 就给一个真实的查询任务),拿这个用例做第一条评估用例的输入和断言。两类输入都不合格:「你好」这种和应用无关的占位输入,以及「你是什么/你能做什么」这种问被测系统它自己的元问题——那不是用户拿它干活的用例。形式上仍从最小写起:一句输入,t.succeeded()+ 一个针对预期回答的内容断言——但最小形式只是调通的脚手架,不是交付标准,收尾前还要满足两条:- 断言在被测系统胡编时要会变红。不要断言输入里本来就有的词(问「X 是什么」再断言回答含「X」,被测方复读题目就能通过)。断言预期回答独有的实质内容——具体事实、结构(
hasSections())、真实链接(includesUrl()),或用t.judge做语义判定。 - 至少一条负例。喂一个被测系统应该答不了的输入(不存在的表、检索不到的主题),断言它明确说查不到/做不到,而不是编造一个看似合理的结果——对连着真实数据/检索源的 agent,这是最值得先测的失败形态。 写法见编写评估用例,断言与题型见题型与断言,签名见 defineEval 参考。
- 断言在被测系统胡编时要会变红。不要断言输入里本来就有的词(问「X 是什么」再断言回答含「X」,被测方复读题目就能通过)。断言预期回答独有的实质内容——具体事实、结构(
ctx),见接入自己的 Agent。
架构上有两条硬规则,写 Adapter 时不要违反:
- 不做进程内直调。就算 agent runtime 和评估用例在同一个代码库里,Adapter 也要走 HTTP(或对应传输层),不要把
fetch换成直接import被测函数——原因见接入自己的 Agent 里「为什么不直调」。 - 评估用例侧不代管被测进程。不 spawn 应用、不另开端口。应用由用户自己按平时的方式启动(
pnpm dev之类),Adapter 连不上时报「先起应用」这类明确的错误,不要自己起服务。
第 4 步:跑通并验证
niceeval show 加 --source、--execution、--timing、--diff 完成运行、观察、修改和重跑。查看器用法见查看结果。没跑通分三类定位:fetch 直接抛错 → 应用没起来或 URL 不对。t.succeeded() 不过 → 应用回了非成功状态。只有内容断言不过 → 接入已经通了,调断言或调应用。
第 5 步:收尾,告诉用户做了什么
总结之前先过一遍收尾自检——任何一条不满足就回第 3 步补,不要在总结里含糊带过:- 评估用例的输入是被测系统的核心用例,不是问它自己的元问题或占位寒暄
- 每条内容断言在被测系统复读题目/胡编时会变红(断言的词不是输入里本来就有的)
- 至少有一条负例(应该答不了的输入,断言它明确说做不到)
- 有 key 时 Judge 已配置,且在
niceeval view里看到过 Judge 分数。没 key 时总结里说明了 - Experiment 里声明的
model/flags确实被 Adapter 消费(没有写了没人读的死配置,也没有编造被测系统不存在的 model 值)
niceeval exp <实验路径> 和 niceeval view 怎么跑、第一次运行的结果是什么样。不要在没被要求的情况下顺手重构用户已有代码,也不要在这几个文件之外新增抽象。
第 6 步:问用户要不要往深了接
总结完之后,把还能往深接的选项列给用户——每一项都说清能做什么、大概改多少代码、买到什么好处,让用户自己选,不要自作主张多做:
共同点也要讲给用户:这些全是给 Adapter 或应用加增量,已写的评估用例一行不用改。三档投入分别买到什么、什么时候值得升级,见接入等级。第 1 步探到应用已有 OTel 埋点的话,瀑布图那条要主动推荐——成本接近零。