> Discover all available pages from the documentation index: https://mastra.zisheng.pro/llms.txt # 运行实验 **新增于:** `@mastra/core@1.4.0` 实验会让 Dataset 中的每个 item 通过一个目标(Agent、Workflow 或 Scorer),然后可以选择对输出进行评分。如果要评估 LLM judge 本身,请将 Scorer 用作目标。默认情况下,结果会持久保存到 Storage,因此你可以比较不同 prompt、模型或代码变更下的运行结果。 **面向 AI Agent:** 运行 `npx mastra api experiment run dataset_123 '{"name":"translation-baseline"}'` 可直接启动实验,无需打开 Studio 或编写临时脚本。请使用 `npx mastra api dataset list` 返回的 Dataset ID 替换示例 ID。该命令需要一个正在运行、配置了 Dataset Storage 并注册了实验目标的 Mastra Server;使用 `npx mastra dev` 启动本地 Server,或通过 `--url` 传入可访问 Server 的基础 URL。构造不同的 input 前,请运行 `npx mastra api experiment run --schema`;启动实验前,请先获得用户批准,因为实验可能会调用模型。使用 `npx skills add mastra-ai/skills --skill mastra` 安装 Mastra Skill,以获得完整的 API CLI 发现、目标选择、schema、身份验证和错误处理指南。 ## 基础实验 使用目标和 Scorer 调用 [`startExperiment()`](https://mastra.zisheng.pro/reference/datasets/startExperiment): ```typescript import { mastra } from '../index' const dataset = await mastra.datasets.get({ id: 'translation-dataset-id' }) const summary = await dataset.startExperiment({ name: 'gpt-5.1-baseline', targetType: 'agent', targetId: 'translation-agent', scorers: ['accuracy', 'fluency'], }) console.log(summary.status) // 'completed' | 'failed' console.log(summary.succeededCount) // number of items that ran successfully console.log(summary.failedCount) // number of items that failed ``` `startExperiment()` 会阻塞,直到所有 item 完成。如需触发后不等待的执行方式,请参阅[异步实验](#async-experiments)。 ## Studio 你也可以在 [Studio](https://mastra.zisheng.pro/docs/studio/overview) 中运行实验。添加 Dataset item 后,打开它并选择 **Run Experiment**,然后配置目标、Scorer 和选项。 实验运行后,**Experiments** 选项卡会显示该 Dataset 的所有运行(包括状态、数量和时间戳)。选择一个实验,可以查看每个 item 的结果、分数和执行 Trace。 在 **Experiments** 选项卡中选择 **Compare**,然后选择两个或更多实验,以并排比较其分数和结果。 ## 实验目标 可以将实验指向已注册的 Agent、Workflow 或 Scorer。 ### 已注册的 Agent 指向在 Mastra 实例上注册的 Agent: ```typescript const summary = await dataset.startExperiment({ name: 'agent-v2-eval', targetType: 'agent', targetId: 'translation-agent', scorers: ['accuracy'], }) ``` 每个 item 的 `input` 会直接传给 `agent.generate()`,因此必须是 `string`、`string[]` 或 `CoreMessage[]`。 #### 启用了 Memory 的 Agent 当目标 Agent 拥有自己的 Memory,且 request context 携带资源 ID(`MASTRA_RESOURCE_ID_KEY`,由身份验证 middleware、实验或 item 的 `requestContext`,或 Studio **Run Experiment** 表单设置)时,实验 runner 会为每个 item 注入新的 Memory 线程。Request context 中的资源 ID 表示“以此资源的身份运行”:每个 item 的对话都会作为该资源下的线程持久保存;重试的 item 每次尝试都会获得新线程,因此此前失败的尝试不会泄漏到重试上下文中。 注入的线程会带上标签,以便映射回运行:线程元数据包含 `experimentId`,以及 Dataset item 的 ID(形式为 `experimentItemId`)。系统不会为这些线程生成标题。 由于线程属于调用方的资源,资源范围的 Memory 功能会在运行期间同时读取和写入该资源的 state: - 资源范围的 working memory 更新会持久保存到该资源,运行中后续 item 可以看到此前 item 所做的更新。 - 资源范围的语义召回可以向实验提供该资源过去的对话,实验 transcript 也可在该资源之后的对话中被召回。 当你要根据真实用户积累的上下文评估 Agent 时,此功能很有用。如果不希望实验运行改动真实用户 state,请改用专用的评估资源 ID 运行实验。 以下情况会跳过线程注入: - 如果 request context 还设置了 `MASTRA_THREAD_ID_KEY`,runner 会原样使用该线程,因此每个 item(及重试)都会共享同一对话。 - 如果 Agent 没有 Memory,或 request context 没有资源 ID,则运行不使用 Memory,也不会持久保存任何内容。 ### 已注册的 Workflow 指向在 Mastra 实例上注册的 Workflow: ```typescript const summary = await dataset.startExperiment({ name: 'workflow-eval', targetType: 'workflow', targetId: 'translation-workflow', scorers: ['accuracy'], }) ``` Workflow 会接收每个 item 的 `input` 作为触发数据。 ### 已注册的 Scorer 指向一个 Scorer,以根据 ground truth 评估 LLM judge: ```typescript const summary = await dataset.startExperiment({ name: 'judge-accuracy-eval', targetType: 'scorer', targetId: 'accuracy', }) ``` Scorer 会接收每个 item 的 `input` 和 `groundTruth`。随着底层模型发生变化,基于 LLM 的 judge 可能随时间漂移,因此定期根据已知正确的标签重新对齐非常重要。Dataset 可提供稳定的基准,用于检测这种漂移。 ## 对结果评分 每个 item 的目标执行后,Scorer 会自动运行。传入 Scorer 实例或已注册的 Scorer ID: **Scorer ID**: ```typescript // Reference scorers registered on the Mastra instance const summary = await dataset.startExperiment({ name: 'with-registered-scorers', targetType: 'agent', targetId: 'translation-agent', scorers: ['accuracy', 'fluency'], }) ``` **Scorer 实例**: ```typescript import { createAnswerRelevancyScorer } from '@mastra/evals/scorers/prebuilt' const relevancy = createAnswerRelevancyScorer({ model: 'openai/gpt-5-mini' }) const summary = await dataset.startExperiment({ name: 'with-scorer-instances', targetType: 'agent', targetId: 'translation-agent', scorers: [relevancy], }) ``` 每个 item 的结果都包含各 Scorer 的分数: ```typescript for (const item of summary.results) { console.log(item.itemId, item.output) for (const score of item.scores) { console.log(` ${score.scorerName}: ${score.score} — ${score.reason}`) } } ``` 有关可用 Scorer 和自定义 Scorer 的详细信息,请访问 [Scorer 概览](https://mastra.zisheng.pro/docs/evals/overview)。 ## 控制每次运行的持久化 使用 `persistence` 跳过特定运行的 Storage 写入。可以分别禁用实验记录和分数记录: ```typescript const summary = await dataset.startExperiment({ targetType: 'agent', targetId: 'translation-agent', scorers: ['accuracy'], persistence: { experiments: 'none', scores: 'none', }, }) ``` 目标和 Scorer 仍会运行,`startExperiment()` 仍会在 `summary` 中返回 item 结果和分数。这些设置彼此独立。例如,只设置 `scores: 'none'`,可以持久保存实验及其 item 结果,而不创建分数记录。 省略的设置默认为 `'default'`,会保留标准 Storage 行为。此策略只控制由该次运行创建的实验记录和分数记录,不会禁用目标使用的 Storage,例如 Agent Memory、vector、Observability 或自定义 Tool Storage。 当 `startExperimentAsync()` 使用 `experiments: 'none'` 运行时,它不会持久保存实验记录、进度更新或 item 结果。分数持久化仍由 `persistence.scores` 单独控制。如果没有实验事件 observer,该运行会触发后不再跟踪,实验 API 也无法报告其完成还是失败。 当调用方需要返回的 summary 时,请使用同步的 `startExperiment()`。实验事件 observer 可以接收生命周期事件和最终 summary。 ## 观察实验事件 使用 `onEvent` 在实验运行期间接收带版本且可安全进行 JSON 序列化的生命周期事件。它适用于 `startExperiment()`、`startExperimentAsync()` 和 `runExperiment()`。 ```typescript import type { ExperimentEvent } from '@mastra/core/datasets' const events: ExperimentEvent[] = [] await dataset.startExperimentAsync({ task: async ({ input }) => processItem(input), persistence: { experiments: 'none' }, onEvent: async event => { events.push(event) await publishEvent(event) }, }) ``` Observer 会接收以下事件类型: - `experiment.run.started`:标识运行、目标、已解析的 Dataset 版本和 item 数量。 - `experiment.item.completed`:报告评分后已提交的 item 结果,包括分数、错误、重试次数、Tool mock 详情和稳定的 item 身份。 - `experiment.run.finished`:报告最终结果和 summary 计数器。 Mastra 会等待每次 observer 调用完成,然后再交付下一个事件。这种串行交付会施加背压,并确保事件的 `sequence` 值与交付顺序一致,同时 item 仍可并发执行。 如果 observer 抛出异常或拒绝,Mastra 会中止剩余运行,并使 `runExperiment()` 拒绝,同时返回 `id` 为 `EXPERIMENT_EVENT_OBSERVER_FAILED` 的 `MastraError`。它不会通过失败的 observer 发送最终事件。对于 `startExperimentAsync()`,分离的 observer 失败时,该方法已经返回。因此,如果调用方需要直接错误报告,请在 observer 内部处理交付失败。 Mastra 会等待 `experiment.run.finished` 事件,然后再持久保存最终实验状态。禁用实验持久化时,请将该事件视为权威的最终 signal,但不要将其用作 Storage 的写后读 signal。 导出的事件类型包括 `ExperimentEvent`、`ExperimentRunStartedEvent`、`ExperimentItemCompletedEvent` 和 `ExperimentRunFinishedEvent`。读取事件特有属性前,请使用可区分的 `type` 字段缩小事件类型范围。 ## Tool mock 当实验运行会调用具有副作用 Tool 的 Agent 时,请将静态 Tool mock 附加到各个 Dataset item,使运行具有确定性。在实验期间,被 mock 的 Tool 会返回其声明的输出,而不执行实际操作。默认情况下,item 上没有 mock 的 Tool 会实际运行。 Mock 位于 Dataset item 上,因此会随该行进行版本控制,并与测试用例一起转移。每个 mock 都声明 Tool 名称、预期参数以及要返回的输出: ```typescript await dataset.addItem({ input: 'What is the weather in Seattle?', toolMocks: [ { toolName: 'getWeather', args: { city: 'Seattle' }, output: { temperature: 60, conditions: 'rainy' }, }, ], }) ``` Tool mock 只支持 `agent` 目标。 ### 阻止未声明的 Tool 在实验上设置 `unmockedToolPolicy: 'deny'`,可阻止所有没有 mock 的 Tool 调用。当实际调用可能产生副作用时,此设置很有用: ```typescript const summary = await dataset.startExperiment({ targetType: 'agent', targetId: 'weather-agent', unmockedToolPolicy: 'deny', }) ``` 默认策略为 `'allow'`。可以在单个已存储或内联 item 上覆盖实验策略: ```typescript await dataset.addItem({ input: 'What is the weather in Seattle?', unmockedToolPolicy: 'allow', }) ``` Item 值优先于实验值。被拒绝的调用会在 Tool 执行前以 `TOOL_MOCK_NOT_DECLARED` 失败。该失败不会重试,也不会添加到 `liveCalls`。 ### 匹配与消耗 参数会严格匹配:忽略对象键顺序,但数组顺序有意义,并且不会进行类型强制转换。只有当 Agent 调用 Tool 时所用参数与 mock 的 `args` 深度相等,才会提供该 mock。 当 item 为同一 Tool 和参数声明多个 mock 时,会按顺序消耗:第一次调用获取第一个 mock,下一次调用获取第二个,依此类推。顺序按各 `(toolName, args)` 分组跟踪,不同参数之间互不影响。 ### 匹配模式 默认情况下,每个 mock 都会严格匹配其 `args`。设置 `matchArgs: 'ignore'` 可以只匹配 Tool 名称;系统不会比较 mock 的 `args`,无论 Agent 如何调用 Tool,都会提供该 Tool 下一个未消耗的 mock: ```typescript const subAgentMock = { toolName: 'agent-balanceAgent', args: { prompt: 'look up the balance for YJ' }, output: { text: "YJ's balance is $100." }, matchArgs: 'ignore', } ``` 当 Tool 参数不稳定或由模型生成时,此设置很有用。最常见的情况是模拟 **Subagent 的响应**:受委托的 Subagent 会作为 `agent-` Tool 暴露给父 Agent,其参数包括由 LLM 编写的 `prompt` 和运行时注入的字段。Mock `agent-` 会返回预设响应,而不是运行 Subagent 及其内部 Tool。从 Trace 创建 mock 时,Subagent 委托调用会自动使用 `matchArgs: 'ignore'` 派生。可以将其改为 `'strict'`,以固定确切参数。 ### 失败 Tool 调用违反 mock 配置时,该 item 会失败: - `TOOL_MOCK_MISMATCH`:调用 Tool 时使用的参数与任何 mock 都不匹配。 - `TOOL_MOCK_EXHAUSTED`:所有匹配的 mock 都已消耗。 - `TOOL_MOCK_NOT_DECLARED`:Tool 没有 mock,且有效的 `unmockedToolPolicy` 为 `'deny'`。 发生任何上述失败时,Agent 运行都会立即中止,因此模型无法继续调用任何其他 Tool,包括本应实际运行、未被 mock 且有副作用的 Tool。这些失败是确定性的,因此不会重试。已声明但从未使用的 mock 不会导致 item 失败,系统会将其报告为未消耗。 启用 mock 拦截时,Agent 的 Tool 会按顺序执行,因此重复的 `(toolName, args)` mock 会按 Provider 的调用顺序消耗。当 item 声明了 mock,或其有效 `unmockedToolPolicy` 为 `'deny'` 时,拦截会启用。 ### 诊断 每个 item 结果都带有 `toolMockReport`,说明该次运行如何处理 item 的 mock: ```typescript for (const item of summary.results) { const report = item.toolMockReport if (!report) continue console.log(report.served) // mocks matched and returned console.log(report.unconsumed) // mocks declared but never used console.log(report.liveCalls) // undeclared tools allowed to run live console.log(report.failure) // the first deterministic mock failure, if any } ``` 在 [Studio](https://mastra.zisheng.pro/docs/studio/overview) 中编辑 Dataset item,可以通过 JSON 数组编写 Tool mock;打开实验结果可以查看同一份报告。 ### 限制 - **被 mock 的调用没有 Tool span。** 被 mock 的调用会在 Tool 执行前返回输出,因此不会创建 Tool span。由已存储 Trace 支持的轨迹 Scorer 可能因此无法看到被 mock 的 Tool 调用。回退到 Agent 消息输出的轨迹提取仍能看到这些调用,因此轨迹评分可能会因 Observability 配置而异。 - **Storage 支持。** LibSQL、PostgreSQL、MongoDB 和 Spanner 适配器会持久保存 Tool mock 和 Tool mock 报告。MySQL 适配器不支持这些内容,并会拒绝携带 Tool mock 或 Tool mock 报告的写入。所有 Dataset Storage 适配器都会持久保存 `unmockedToolPolicy`。 ## 异步实验 `startExperiment()` 会阻塞,直到每个 item 都完成。对于长时间运行的 Dataset,请使用 [`startExperimentAsync()`](https://mastra.zisheng.pro/reference/datasets/startExperimentAsync) 在后台启动实验: ```typescript const { experimentId, status } = await dataset.startExperimentAsync({ name: 'large-dataset-run', targetType: 'agent', targetId: 'translation-agent', scorers: ['accuracy'], }) console.log(experimentId) // UUID console.log(status) // 'pending' ``` 使用 [`getExperiment()`](https://mastra.zisheng.pro/reference/datasets/getExperiment) 轮询完成状态: ```typescript let experiment = await dataset.getExperiment({ experimentId }) while (experiment.status === 'pending' || experiment.status === 'running') { await new Promise(resolve => setTimeout(resolve, 5000)) experiment = await dataset.getExperiment({ experimentId }) } console.log(experiment.status) // 'completed' | 'failed' ``` ## 配置选项 ### 并发 控制并行运行的 item 数量(默认值:5): ```typescript const summary = await dataset.startExperiment({ targetType: 'agent', targetId: 'translation-agent', maxConcurrency: 10, }) ``` ### 超时和重试 设置每个 item 的超时(以毫秒为单位)和重试次数: ```typescript const summary = await dataset.startExperiment({ targetType: 'agent', targetId: 'translation-agent', itemTimeout: 30_000, // 30 seconds per item maxRetries: 2, // retry failed items up to 2 times }) ``` 重试使用指数退避。中止错误绝不会重试。 ### 中止实验 传入 `AbortSignal` 可取消正在运行的实验: ```typescript const controller = new AbortController() // Cancel after 60 seconds setTimeout(() => controller.abort(), 60_000) const summary = await dataset.startExperiment({ targetType: 'agent', targetId: 'translation-agent', signal: controller.signal, }) ``` 其余 item 会在 summary 中标记为已跳过。 ### 固定 Dataset 版本 针对 Dataset 的特定快照运行: ```typescript const summary = await dataset.startExperiment({ targetType: 'agent', targetId: 'translation-agent', version: 3, // use items from dataset version 3 }) ``` ## 查看结果 ### 列出实验 ```typescript const { experiments, pagination } = await dataset.listExperiments({ page: 0, perPage: 10, }) for (const exp of experiments) { console.log(`${exp.name} — ${exp.status} (${exp.succeededCount}/${exp.totalItems})`) } ``` ### 实验详情 ```typescript const experiment = await dataset.getExperiment({ experimentId: 'exp-abc-123', }) console.log(experiment.status) console.log(experiment.startedAt) console.log(experiment.completedAt) ``` > **📹 观看:** 观看 [Mastra Dataset 和实验 Workflow](https://www.youtube.com/watch?v=R6pjAdGhxhQ),了解 Dataset 和实验如何帮助提高可靠性。 ### Item 级结果 ```typescript const { results, pagination } = await dataset.listExperimentResults({ experimentId: 'exp-abc-123', page: 0, perPage: 50, }) for (const result of results) { console.log(result.itemId, result.output, result.error) } ``` ## 理解 summary `startExperiment()` 返回包含计数和每个 item 结果的 `ExperimentSummary`: - 当实验已完成但部分 item 失败时,`completedWithErrors` 为 `true`。 - 通过 `signal` 取消的 item 会计入 `skippedCount`。 有关完整的参数和返回类型文档,请访问 [`startExperiment` Reference](https://mastra.zisheng.pro/reference/datasets/startExperiment)。 ## 相关内容 - [Datasets 概览](https://mastra.zisheng.pro/docs/datasets/overview) - [Scorer 概览](https://mastra.zisheng.pro/docs/evals/overview) - [`startExperiment` Reference](https://mastra.zisheng.pro/reference/datasets/startExperiment) - [`listExperimentResults` Reference](https://mastra.zisheng.pro/reference/datasets/listExperimentResults)