人工智能AI AgentAgent 框架后端多智能体RAG工具调用Agent 记忆【免费下载链接】voltagentAI Agent Engineering Platform built on an Open Source TypeScript AI Agent Framework项目地址https://gitcode.com/gh_mirrors/vo/voltagent点击查看免费下载Experiments实验是 VoltAgent 评测体系的核心抽象它把测什么数据集、怎么测runner、如何衡量scorers、怎样算通过pass criteria封装成一个可复用的声明式定义既可以交给 CLI 一键执行也能嵌入 CI 流程作为回归门禁。本文以voltagent/evals包为主线完整讲解 Experiment 的配置字段、runner 写法、数据集五种来源、评分器与通过标准并结合 packages/evals/src/experiment 下的源码实现说明每一步在底层是如何被解析与执行的读完你就能独立写出可运行、可追踪、可判定成败的 Agent 评测实验。一、什么是 Experiment评测的核心抽象在 VoltAgent 中Experiment 定义了评测 Agent 的完整闭环用什么数据dataset、如何驱动被测 Agentrunner、如何打分scorers、达到什么标准才算通过passCriteria。它由createExperiment工厂函数创建返回一个带有kind: voltagent.experiment标记、配置被深度冻结Object.freeze的不可变定义对象——这意味着一个 Experiment 定义可以在多个运行之间安全复用不会因运行过程被意外改写。该约束直接体现在 create-experiment.ts 中id必须是非空字符串、runner必须是函数否则会抛出TypeError。最简单的实验定义形如import { createExperiment } from voltagent/evals; import { scorers } from voltagent/scorers; export default createExperiment({ id: customer-support-quality, label: Customer Support Quality, description: Evaluate customer support agent responses, // 按名称引用已注册的数据集 dataset: { name: support-qa-dataset, }, // 定义每个数据集条目上要执行的评测逻辑 runner: async ({ item, index, total }) { const input item.input; const expected item.expected; const response await myAgent.generateText(input); return { output: response.text, metadata: { processingTime: Date.now(), modelUsed: gpt-4o-mini, }, }; }, // 配置评分器 scorers: [ scorers.exactMatch, { scorer: scorers.levenshtein, threshold: 0.8, }, ], // 通过标准 passCriteria: { type: meanScore, min: 0.7, }, });二、Experiment 配置字段全解2.1 必填字段与常用字段对照 types.ts 中的ExperimentConfig接口完整的字段清单如下interface ExperimentConfig { // 唯一标识符必须是非空字符串 id: string; // 每个数据集条目上执行的评测函数必填 runner: ExperimentRunner; // 可选但推荐便于识别的名称与描述 label?: string; description?: string; // 数据集描述符name / id / items / resolve 四选一 dataset?: ExperimentDatasetDescriptor; // 评分器列表可为内置或自定义 LocalScorerDefinition scorers?: ReadonlyArrayExperimentScorerConfig; // 通过标准单个对象或对象数组 passCriteria?: ExperimentPassCriteriaInput; // 顶层标签用于运行时元数据会被去空白、去重并冻结 tags?: readonly string[]; // 与 VoltOps 上 Experiment 实体的绑定关系 experiment?: ExperimentBindingDescriptor; // 任意自定义元数据随运行结果一并返回 metadata?: Recordstring, unknown | null; // VoltOps 云端追踪配置 voltOps?: ExperimentVoltOpsOptions; }注意createExperiment会对配置做防御性归一化见 create-experiment.tstags会被trim、去空、去重并冻结数据集描述符、passCriteria、voltOps都会被克隆一份避免外部对象被后续逻辑修改。2.2 Runner 函数被评测的主体Runner 的类型签名如下见 types.tstype ExperimentRunner (context: ExperimentRunnerContext) PromiseExperimentRunnerReturn; interface ExperimentRunnerContext { item: ExperimentDatasetItem; // 当前数据集条目 index: number; // 条目序号从 0 开始 total?: number; // 总条目数已知时提供 signal?: AbortSignal; // 取消信号 voltOpsClient?: any; // 配置了 VoltOps 客户端时可用 runtime?: { runId?: string; // 本次运行的 ID startedAt?: number; // 运行开始时间戳 tags?: readonly string[]; // 实验 tags }; }Runner 的返回值有两种合法形态源码 normalizeRunnerResult 会自动归一化对象形态{ output, metadata?, traceIds? }metadata会附加到该条目的运行快照traceIds可关联追踪链路裸值形态直接返回任意值会被当作outputmetadata置为null。// 简单文本生成裸值形态 runner: async ({ item }) { const result await processInput(item.input); return { output: result, metadata: { confidence: 0.95 }, }; }; // 使用 expected 参与构造 prompt 的对象形态 runner: async ({ item }) { const prompt Question: ${item.input}\nExpected answer format: ${item.expected}; const result await generateResponse(prompt); return { output: result }; }; // 带错误处理失败时返回 null 输出并记录错误 runner: async ({ item, signal }) { try { const result await processWithTimeout(item.input, signal); return { output: result }; } catch (error) { return { output: null, metadata: { error: error.message, failed: true }, }; } }; // 访问运行时上下文 runner: async ({ item, index, total, runtime }) { console.log(Processing item ${index 1}/${total}); console.log(Run ID: ${runtime?.runId}); const result await process(item.input); return { output: result }; };底层执行时见 run-experiment.tsrunner 的抛错会被捕获进runnerSnapshot.error该条目状态记为error且不会再执行任何评分器。三、数据集配置五种数据来源3.1 引用已注册数据集按名称 / 按 ID// 按名称引用 dataset: { name: my-dataset } // 按 ID 引用可指定精确版本 dataset: { id: dataset-uuid, versionId: version-uuid } // 限制只取前 N 条 dataset: { name: large-dataset, limit: 100 }按名称引用依赖全局数据集注册表registerExperimentDataset()把数据集注册进globalThis上的注册表见 dataset.ts。若名称未注册resolveExperimentDataset会抛出Experiment dataset xxx is not registered错误。limit通过 limitAsyncIterable 对流做惰性截断不实际加载全部数据当服务端也返回 total 时total会被取min(total, limit)。3.2 内联条目与动态解析// 内联条目直接在定义中写数据 dataset: { items: [ { id: 1, input: { prompt: What is 22? }, expected: 4 }, { id: 2, input: { prompt: Capital of France? }, expected: Paris }, ], } // 动态解析器运行时拉取数据可用于分页 API、数据库查询等 dataset: { resolve: async ({ limit, signal }) { const items await fetchDatasetItems(limit); return { items, total: items.length, dataset: { name: Dynamic Dataset, description: Fetched at runtime, }, }; }, }resolve接收{ limit, signal }见 types.ts可以返回同步或异步的Iterable / AsyncIterable也可以返回带{ items, total, dataset }的流对象ensureAsyncIterable 会把普通可迭代对象统一包装成异步迭代器。此外当配置了 VoltOps 客户端、且 dataset 只有name/id而没有items/resolve时attachVoltOpsDatasetResolver 会自动注入一个从 VoltOps 远端拉取数据集的分页解析器每页默认 200 条见 voltops/dataset.ts。3.3 数据集条目结构interface ExperimentDatasetItem { id: string; // 唯一条目 ID label?: string; // 可选显示名 input: any; // 输入数据你自己的格式 expected?: any; // 期望输出可选 extra?: Recordstring, any; // 附加数据 metadata?: Recordstring, any; // 条目元数据 // 来自注册数据集时自动附加 datasetId?: string; datasetVersionId?: string; datasetName?: string; }源码中该接口还支持dataset?: ExperimentDatasetInfo内嵌数据集信息与raw?: unknown保留远端原始记录见 types.ts。评分器通过运行时 payload 可以访问item、input、expected、output、datasetId等全部字段normalizeExperimentScorerPayload 保证这些字段总是存在。四、Scorers 配置如何给输出打分4.1 三种配置形态import { scorers } from voltagent/scorers; scorers: [ // 形态一直接使用预置评分器exactMatch、levenshtein 等 scorers.exactMatch, // 形态二带 threshold 与自定义名称 { scorer: scorers.levenshtein, threshold: 0.9, name: String Similarity, }, // 形态三自定义评分器 元数据 { scorer: myCustomScorer, threshold: 0.7, metadata: { category: custom, version: 1.0.0 }, }, ];voltagent/scorers导出的scorers对象目前包含五个无需 LLM/API Key 的启发式评分器exactMatch、levenshtein、listContains、numericDiff、jsonDiff见 scorers/src/index.ts需要 LLM 评判时则使用createAnswerCorrectnessScorer、createAnswerRelevancyScorer、createContextPrecisionScorer、createContextRecallScorer、createContextRelevancyScorer、createToolCallAccuracyScorerCode、createModerationScorer等原生评分器同文件 L74-L124。4.2 底层归一化逻辑在 scorers.ts 中resolveExperimentScorers会把每条配置归一化为运行时 bundle每个评分器会得到一个稳定的id显式id优先其次name最后回退为scorer-N和namethreshold会被Number()归一化。定义级params、条目级params与buildPayload / buildParams会按定义参数 → 配置参数的顺序合并后传入评分器。每个条目的评分结果会带上threshold、thresholdPassed、reason字段见 run-experiment.ts其中thresholdPassed在score threshold时判定为 true。条目的最终状态判定规则evaluateItemStatus如下runner 或评分过程抛错 → 条目状态error任一评分器状态为error→ 条目状态error存在thresholdPassed false的评分器 → 条目状态failed没有配置评分器 → 条目直接passed否则 →passed。五、Pass Criteria定义成功条件通过标准支持两种类型可以单独使用也可以作为数组组合全部满足才算通过// 单一标准平均分 passCriteria: { type: meanScore, min: 0.8, label: Average Quality, scorerId: exact-match, // 可选限定某个评分器 } // 单一标准通过率 passCriteria: { type: passRate, min: 0.9, label: 90% Pass Rate, severity: error, // error 或 warn } // 组合标准所有条件都必须通过 passCriteria: [ { type: meanScore, min: 0.7, label: Overall Quality }, { type: passRate, min: 0.95, label: Consistency Check, scorerId: exact-match }, ];评估逻辑在 aggregator.ts 中实现meanScore取全局平均分未指定scorerId时或指定评分器的meanScorepassRate同理取全局或指定评分器的passRateactual min即通过否则不通过。severity字段保留在标准对象中见 types.ts可用于区分错误级别与警告级别的门槛。每个条目的结果会被流式聚合recordAggregatorResult实时统计successCount / failureCount / errorCount / skippedCount、全局平均分、每个评分器的meanScore / minScore / maxScore / passRate / threshold。六、VoltOps 集成与实验绑定6.1 VoltOps 云端追踪配置 VoltOps 后实验结果可以实时上云用于跨版本追踪、团队共享与回归对比voltOps: { client: voltOpsClient, // VoltOps 客户端实例 triggerSource: ci, // 来源标识默认 run-experiment autoCreateRun: true, // 自动创建评测 run autoCreateScorers: true, // 自动注册评分器 tags: [nightly, regression], // 过滤用标签 }triggerSource的默认值是run-experiment见 voltops/run.ts。底层由VoltOpsRunManager驱动运行前prepare()创建 runPOST runs.create每个条目完成后appendResults追加结果最后根据汇总状态complete或fail。终端状态的推断规则inferTerminalStatus是有 error →failed配置了通过标准 → 全部通过才succeeded有失败条目 →failed否则succeeded。值得注意的是即使createExperiment阶段没有显式传voltOps只要在runExperiment(experiment, { voltOpsClient })传入客户端同样会启用云端追踪见 run-experiment.ts。6.2 实验绑定Experiment Binding把本地实验绑定到 VoltOps 上的 Experiment 实体便于历史结果归组experiment: { name: production-quality-check, // VoltOps 实验名称 id: exp-uuid, // 或直接使用已有 ID autoCreate: true // 不存在时自动创建默认 true }createExperiment会把autoCreate的默认值归一化为truecreate-experiment.ts。运行时若提供了id则直接使用若只有name会调用客户端的resolveExperimentId按名称解析必要时携带autoCreate、tags、metadata自动创建解析结果记录在最终元数据voltOps.experiment中voltops/run.ts。七、运行实验CLI 与程序化两种方式7.1 通过 CLI把实验保存到文件默认导出然后交给 VoltAgent CLI// experiments/support-quality.ts import { createExperiment } from voltagent/evals; export default createExperiment({ id: support-quality, dataset: { name: support-dataset }, runner: async ({ item }) { // evaluation logic return { output: response }; }, });npm run volt eval run --experiment ./experiments/support-quality.ts7.2 程序化运行直接调用runExperiment可精细控制并发度与回调import { runExperiment } from voltagent/evals; import experiment from ./experiments/support-quality; const result await runExperiment(experiment, { concurrency: 5, // 5 个条目并行执行 onItemComplete: (event) { console.log(Completed item ${event.index}/${event.total}); console.log(Score: ${event.result.scores[0]?.score}); }, onComplete: (summary) { console.log(Experiment completed: ${summary.passed ? PASSED : FAILED}); console.log(Mean score: ${summary.meanScore}); }, });runExperiment支持RunExperimentOptionsconcurrency、signalAbortSignal 取消、voltOpsClient、onItem每个条目完成、onProgress进度事件见 run-experiment.ts。并发实现采用信号量式的任务池concurrency会被Math.max(1, Math.trunc(n) || 1)归一化当活跃任务数达到上限时用Promise.race等待最早完成的任务L128-L181。集成测试覆盖了三种典型场景run-experiment.spec.ts流式追加结果并成功完成 run、通过标准未达成时 run 标记为failed、以及通过scorerId精确评估指定评分器的通过率。八、完整示例评测一个客服 Agent下面是从项目代码风格整理出的端到端示例把上述所有要素串起来import { createExperiment } from voltagent/evals; import { scorers } from voltagent/scorers; import { Agent } from voltagent/core; import { openai } from ai-sdk/openai; const supportAgent new Agent({ name: Support Agent, instructions: You are a helpful customer support agent., model: openai(gpt-4o-mini), }); export default createExperiment({ id: support-agent-eval, label: Support Agent Evaluation, description: Evaluates support agent response quality, dataset: { name: support-qa-v2, limit: 100, // 只测前 100 条 }, runner: async ({ item, index, total }) { console.log(Processing ${index 1}/${total}); try { const response await supportAgent.generateText({ messages: [{ role: user, content: item.input.prompt }], }); return { output: response.text, metadata: { model: gpt-4o-mini, tokenUsage: response.usage }, }; } catch (error) { return { output: null, metadata: { error: error.message, failed: true }, }; } }, scorers: [ { scorer: scorers.exactMatch, threshold: 1.0 }, { scorer: scorers.levenshtein, threshold: 0.8, name: String Similarity }, ], passCriteria: [ { type: meanScore, min: 0.75, label: Overall Quality }, { type: passRate, min: 0.9, scorerId: exact-match, label: Exact Match Rate }, ], experiment: { name: support-agent-regression, autoCreate: true, }, voltOps: { autoCreateRun: true, tags: [regression, support], }, });九、结果结构如何读取运行汇总runExperiment返回ExperimentResult其中summary是最关键的聚合结果见 types.ts 与文档中的结构说明interface ExperimentSummary { totalCount: number; completedCount: number; successCount: number; // passed 条目数 failureCount: number; // failed 条目数 errorCount: number; // error 条目数 skippedCount: number; meanScore?: number | null; // 全局平均分 passRate?: number | null; // success / completed startedAt: number; completedAt?: number; durationMs?: number; scorers: Recordstring, ExperimentScorerAggregate; // 每个评分器的聚合 criteria: ExperimentPassCriteriaEvaluation[]; // 每个标准的判定 }聚合统计的细节见 aggregator.tsmeanScore是所有成功评分分数的算术平均passRate是successCount / completedCountscorers中每个评分器都带meanScore / minScore / maxScore / passRate / threshold。若启用了 VoltOpsresult.runId会被填充且每个条目的runner.output、scores、traceIds会被逐条上传便于在云端查看单条明细。十、最佳实践1. 使用描述性 IDid: gpt4-customer-support-accuracy-v2; // Good id: test1; // Bad描述性 ID 不仅便于区分实验也是 VoltOps 上按名称绑定与历史归档的基础。2. 优雅处理错误runner: async ({ item }) { try { const result await process(item.input); return { output: result }; } catch (error) { // 把错误信息返回给分析侧而不是让条目直接中断 return { output: null, metadata: { error: error.message, errorType: error.constructor.name }, }; } };底层会把 runner 抛错标记为条目error而返回错误元数据则视为一次有效输出两种策略适用于不同场景前者严苛、后者容忍。3. 添加有意义的元数据runner: async ({ item, runtime }) { const startTime Date.now(); const result await process(item.input); return { output: result, metadata: { processingTimeMs: Date.now() - startTime, runId: runtime?.runId, itemCategory: item.metadata?.category, }, }; };元数据会随条目快照与 VoltOps 上报是事后分析延迟、模型版本、错误类型的首要数据源。4. 合理设置并发度// 受速率限制的外部 API低并发 await runExperiment(experiment, { concurrency: 2 }); // 本地处理高并发 await runExperiment(experiment, { concurrency: 10 });5. 规范打标签voltOps: { tags: [model:gpt-4, version:2.1.0, type:regression, priority:high], }结构化标签key:value风格在 VoltOps 上可以高效过滤与对比不同模型、版本的评测结果。十一、相关资源继续深入时可以按需阅读评测体系的其余部分Datasets创建与管理数据集Building Custom Scorers构建领域特定评分器Prebuilt Scorers预置评分器清单CLI Reference命令行运行实验源码层面Experiment 的全部实现集中在 packages/evals/src/experiment创建、类型、运行、数据集注册解析、评分器归一化、聚合判定云端同步逻辑位于 packages/evals/src/voltops评分器实现与导出位于 packages/scorers/src集成测试可参考 run-experiment.spec.ts。赞分享人工智能AI AgentAgent 框架后端多智能体RAG工具调用Agent 记忆【免费下载链接】voltagentAI Agent Engineering Platform built on an Open Source TypeScript AI Agent Framework项目地址https://gitcode.com/gh_mirrors/vo/voltagent点击查看免费下载相关推荐VoltAgent 在线评估Live Evals实战在 Agent 上挂载启发式、LLM 评判与自定义评分器VoltAgent 在线评估Live Evals实战在 Agent 上挂载启发式、LLM 评判与自定义评分器 在 VoltAgent 中在线评估Liv人工智能AI AgentAgent 框架后端多智能体RAG工具调用Agent 记忆Agent 工作流AI 评测MCP 服务MCP Clients语音VoltAgent Evals 实验编排指南用 voltagent/evals 构建离线回归评测流水线VoltAgent Evals 实验编排指南用 voltagent/evals 构建离线回归评测流水线 VoltAgent 的 voltagent/eva人工智能AI AgentAgent 框架后端多智能体RAG工具调用Agent 记忆Agent 工作流AI 评测MCP 服务MCP Clients语音VoltAgent A2A Server 实战指南用 JSON-RPC 把 VoltAgent Agent 暴露给外部 AgentVoltAgent A2A Server 实战指南用 JSON RPC 把 VoltAgent Agent 暴露给外部 Agent 本篇技术指南围绕 vol人工智能AI AgentAgent 框架后端多智能体RAG工具调用Agent 记忆Agent 工作流AI 评测MCP 服务MCP Clients语音上一篇FlipIt 项目使用教程下一篇Void编辑器中的跨域请求问题分析与解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考