Skip to main content
这一页是 Coding Agent 把 NiceEval 接入一个项目的执行步骤。前提:niceeval 依赖已经装进项目、niceeval init 已经运行,你是从随包 INDEX.md 进入本页的。用用户的语言与用户交流。所有 API、字段和 CLI 行为以随包文档为准,不要凭训练记忆现编。

第 1 步:探索项目,再和用户确认

这一步决定后面整条路径。先自己读代码探明,把探到的结论列给用户核对,探不到的再提问——不要一上来就抛一串问题,也不要没探就假设。要探明的信息:
  1. 这是个什么 Agent:读 README、package.json 依赖、路由和 agent loop 代码,判断它是用什么写的(AI SDK、LangGraph、OpenAI Agents SDK、Claude Agent SDK、自研 loop……),核心用例是什么(客服?SQL?编码任务?)。
  2. 前端和 Agent 怎么通信:HTTP 还是 gRPC / WebSocket?协议是标准的还是自己实现的——AI SDK UI Message Stream、OpenAI Responses / Chat Completions 这类标准协议,还是 SDK 原生事件流透传,还是用户自定义的 JSON/SSE 帧?这直接决定 Adapter 是用内置的(零映射)还是手写 send(要自己写事件映射)。
  3. 后端有没有接 OTel:搜有没有 OTel SDK 初始化、AI SDK telemetry、LangSmith / OpenLLMetry / OpenInference 这类埋点。已经有的话 Tier 2 几乎零成本。
  4. 用户自己有没有做 A/B Test / feature flag:应用里已有变体开关的话,Experiment 的 flags 可以直接透传给它(Tier 3 的现成入口)。
  5. 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。
  6. 是不是 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 点探到的)就是现成入口。
默认推荐先 Tier 1 跑通,再升 Tier 2——尤其当第 3 点探到应用已有 OTel 时,明确告诉用户「升 Tier 2 只是把 span 多发一份,成本接近零」。Tier 3 只在用户明确要做变体对比时提。 按探明的形态挑对应文档,不要在没读的情况下直接开始写 Adapter:

第 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_KEYDEEPSEEK_API_KEY。有就用 apiKeyEnv 指向它并验证;没有时先写精确 Match,并在收尾说明 Judge 没有配置。
  • Judge 模型要与被测 Agent 分离,避免同一个模型给自己的输出打分。配置按 Eval、Experiment、项目配置逐字段解析;三种 recipe 见 Judge

第 3 步:写三件套

按第 1 步选中的方向读完对应文档后,依次写:
  1. Adapteragents/*.ts 或用户项目里约定的目录)——用 defineAgent 实现 send,配置走工厂参数,不写死、不读 process.env。契约见 Adapter,API 签名见 Adapter 参考,事件映射见事件参考。两个容易踩的点:端点/模式要选被测系统核心能力的入口,不是最容易跑通的入口——比如被测平台既有「纯 LLM 聊天」又有「连库执行」两种模式,接前者等于评了个底层模型代理,没评到产品本身。evidenceCoverage 必须按实际映射如实声明——只把最终文本映射出来就不要用 completeEvidenceCoverage,声明会影响断言完整性,虚报比保守更糟。
  2. Experimentexperiments/*.ts)——引用上面的 Adapter,声明 modelflagsattempts 等。模型对比写两个实验文件,各自钉一个 modelevals: (eval) => boolean 决定各自本次运行哪些评估用例。路径只负责 id 和批量运行,报告消费当前 Sample 的物理结果与覆盖事实。
  3. 评估用例evals/*.eval.ts)——先探明这个应用是干嘛的,再写一条贴着它真实功能的评估用例。读它的 README、路由、工具定义或系统提示,找出它的核心用例(客服机器人就问一条真实的客服问题、SQL agent 就给一个真实的查询任务),拿这个用例做第一条评估用例的输入和断言。两类输入都不合格:「你好」这种和应用无关的占位输入,以及「你是什么/你能做什么」这种问被测系统它自己的元问题——那不是用户拿它干活的用例。形式上仍从最小写起:一句输入,t.succeeded() + 一个针对预期回答的内容断言——但最小形式只是调通的脚手架,不是交付标准,收尾前还要满足两条:
    • 断言在被测系统胡编时要会变红。不要断言输入里本来就有的词(问「X 是什么」再断言回答含「X」,被测方复读题目就能通过)。断言预期回答独有的实质内容——具体事实、结构(hasSections())、真实链接(includesUrl()),或用 t.judge 做语义判定。
    • 至少一条负例。喂一个被测系统应该答不了的输入(不存在的表、检索不到的主题),断言它明确说查不到/做不到,而不是编造一个看似合理的结果——对连着真实数据/检索源的 agent,这是最值得先测的失败形态。 写法见编写评估用例,断言与题型见题型与断言,签名见 defineEval 参考
参数怎么从 Experiment 流到 Adapter、静态配置和每轮动态值怎么分(工厂参数 vs ctx),见接入自己的 Agent 架构上有两条硬规则,写 Adapter 时不要违反:
  • 不做进程内直调。就算 agent runtime 和评估用例在同一个代码库里,Adapter 也要走 HTTP(或对应传输层),不要把 fetch 换成直接 import 被测函数——原因见接入自己的 Agent 里「为什么不直调」。
  • 评估用例侧不代管被测进程。不 spawn 应用、不另开端口。应用由用户自己按平时的方式启动(pnpm dev 之类),Adapter 连不上时报「先起应用」这类明确的错误,不要自己起服务。

第 4 步:跑通并验证

Coding Agent 反馈闭环niceeval show--source--execution--timing--diff 完成运行、观察、修改和重跑。查看器用法见查看结果。没跑通分三类定位:fetch 直接抛错 → 应用没起来或 URL 不对。t.succeeded() 不过 → 应用回了非成功状态。只有内容断言不过 → 接入已经通了,调断言或调应用。

第 5 步:收尾,告诉用户做了什么

总结之前先过一遍收尾自检——任何一条不满足就回第 3 步补,不要在总结里含糊带过
  • 评估用例的输入是被测系统的核心用例,不是问它自己的元问题或占位寒暄
  • 每条内容断言在被测系统复读题目/胡编时会变红(断言的词不是输入里本来就有的)
  • 至少有一条负例(应该答不了的输入,断言它明确说做不到)
  • 有 key 时 Judge 已配置,且在 niceeval view 里看到过 Judge 分数。没 key 时总结里说明了
  • Experiment 里声明的 model / flags 确实被 Adapter 消费(没有写了没人读的死配置,也没有编造被测系统不存在的 model 值)
跑通之后先总结,再谈下一步。总结要说清:接了什么被测对象、生成了哪几个文件(Adapter / Experiment / 评估用例各在哪)、niceeval exp <实验路径>niceeval view 怎么跑、第一次运行的结果是什么样。不要在没被要求的情况下顺手重构用户已有代码,也不要在这几个文件之外新增抽象。

第 6 步:问用户要不要往深了接

总结完之后,把还能往深接的选项列给用户——每一项都说清能做什么、大概改多少代码、买到什么好处,让用户自己选,不要自作主张多做: 共同点也要讲给用户:这些全是给 Adapter 或应用加增量,已写的评估用例一行不用改。三档投入分别买到什么、什么时候值得升级,见接入等级。第 1 步探到应用已有 OTel 埋点的话,瀑布图那条要主动推荐——成本接近零。