跳到主要内容
非官方简体中文翻译,内容版权归 Mastra 所有,遵循 Apache-2.0 许可。

Vitest integration

@mastra/evals/vitest 模块将 runEvals 与 Vitest 集成,使评估像常规测试一样运行。当 eval 未通过时,流式断言会使测试失败,reporter 会在运行器输出中打印得分表。

要使用它,请安装 @mastra/evals 和 vitest(版本 3 或 4):

npm install @mastra/evals vitest

配置 Vitest
配置 Vitest的直接链接

在 vitest.config.ts 中注册 reporter 和匹配器:

vitest.config.ts
import { defineConfig } from 'vitest/config'
import { MastraEvalsReporter } from '@mastra/evals/vitest'

export default defineConfig({
test: {
reporters: ['default', new MastraEvalsReporter()],
setupFiles: ['@mastra/evals/vitest/setup'],
},
})

setup 文件会在 expect 上注册自定义匹配器。你也可以在自己的 setup 文件中调用来自 @mastra/evals/vitest 的 registerEvalMatchers()。

使用 expectEvals 对数据集进行断言
使用-expectevals-对数据集进行断言的直接链接

expectEvals 在常规 test() 中运行一次 runEvals 评估,并断言最低通过率。它接受与 runEvals 相同的配置:target agent 或 workflow、data 项,以及 scorers、gates 或 thresholds。Gates 会对每一项评分通过/失败,因此 toPass(0.8) 要求至少 80% 的项通过每个 gate。Scorer 阈值仍会比较各项的平均得分,并且无论通过率如何都必须通过:

tests/weather-agent.eval.test.ts
import { test } from 'vitest'
import { expectEvals } from '@mastra/evals/vitest'
import { checks } from '@mastra/evals/checks'
import { mastra } from '../src/mastra'

test('weather agent calls the tool and answers relevantly', { timeout: 60_000 }, async () => {
await expectEvals({
target: mastra.getAgent('weatherAgent'),
data: [{ input: "What's the weather in London?" }],
gates: [checks.calledTool('weatherTool'), checks.noToolErrors(), checks.includes('London')],
scorers: [{ scorer: mastra.getScorer('answerRelevancyScorer'), threshold: 0.7 }],
}).toPass(0.8)
})

不带参数的 toPass() 要求每一项都通过每个 gate。请始终 await 该断言:它会解析为完整的 RunEvalsResult 以便进一步检查,并将本次运行的得分附加到当前测试中,使 MastraEvalsReporter 显示它们。

基于 LLM 的 evals 比 Vitest 默认的 5 秒超时慢得多,因此请传入每测试的 timeout(或在 Vitest 配置中设置 testTimeout)。

使用 expectEval 进行矩阵测试
使用-expecteval-进行矩阵测试的直接链接

expectEval 是单项变体:data 是一个项而不是数组。将其与 test.for(或 test.each)组合,可以为每个数据项获得一个测试(以及一条 reporter 记录),而不是每个数据集一个聚合结果:

tests/weather-agent.matrix.eval.test.ts
import { test } from 'vitest'
import { expectEval } from '@mastra/evals/vitest'
import { checks } from '@mastra/evals/checks'
import { mastra } from '../src/mastra'

test.for([
{ input: "What's the weather in London?", groundTruth: 'London' },
{ input: "What's the weather in Tokyo?", groundTruth: 'Tokyo' },
{ input: "What's the weather in Sydney?", groundTruth: 'Sydney' },
])('weather agent: $input', { timeout: 60_000 }, async item => {
await expectEval({
target: mastra.getAgent('weatherAgent'),
data: item,
gates: [checks.calledTool('weatherTool'), checks.includes(item.groundTruth)],
}).toPass()
})

每个项独立通过或失败。单个回归会显示为一个失败的测试,而不是降低聚合通过率。

使用匹配器对结果进行断言
使用匹配器对结果进行断言的直接链接

如需更细粒度的控制,请在常规 test() 中直接调用 runEvals,并对结果使用自定义匹配器:

tests/weather-agent.matchers.eval.test.ts
import { test, expect } from 'vitest'
import { runEvals } from '@mastra/core/evals'
import { checks } from '@mastra/evals/checks'
import { mastra } from '../src/mastra'

test('weather agent quality', { timeout: 60_000 }, async () => {
const result = await runEvals({
target: mastra.getAgent('weatherAgent'),
data: [{ input: "What's the weather in London?" }],
scorers: [mastra.getScorer('answerRelevancyScorer')],
gates: [checks.noToolErrors()],
})

expect(result).toHaveVerdict('passed')
expect(result).toPassGates()
expect(result).toHaveScoreAbove('answer-relevancy-scorer', 0.7)
})

可用的匹配器:

  • toHaveVerdict(verdict):断言运行的 verdict("passed"、"scored" 或 "failed")。
  • toHaveScoreAbove(scorerName, min) / toHaveScoreBelow(scorerName, max):断言 scorer 的平均得分。分类 scorer 配置使用点路径,例如 "agent.my-scorer" 或 "steps.step-1.my-scorer"。
  • toPassGates():断言所有 gates 都通过。未配置 gates 时失败。
  • toPassThresholds():断言所有 scorer 阈值都通过。未配置 thresholds 时失败。

评估工作流
评估工作流的直接链接

工作流也是有效的目标。为 scorers 使用 WorkflowScorerConfig 对象,以对工作流输出、各个步骤或步骤执行轨迹进行评分,并在匹配器中使用点路径引用嵌套得分:

tests/weather-workflow.eval.test.ts
import { test, expect } from 'vitest'
import { runEvals } from '@mastra/core/evals'
import { mastra } from '../src/mastra'

test('weather workflow runs fetch-weather then plan-activities', { timeout: 60_000 }, async () => {
const result = await runEvals({
target: mastra.getWorkflow('weatherWorkflow'),
data: [
{
input: { city: 'London' },
expectedTrajectory: {
steps: [
{ stepType: 'workflow_step', name: 'fetch-weather' },
{ stepType: 'workflow_step', name: 'plan-activities' },
],
},
},
{
input: { city: 'Tokyo' },
expectedTrajectory: {
steps: [
{ stepType: 'workflow_step', name: 'fetch-weather' },
{ stepType: 'workflow_step', name: 'plan-activities' },
],
},
},
],
scorers: {
trajectory: [mastra.getScorer('trajectoryAccuracyScorer')],
},
})

expect(result).toHaveScoreAbove('trajectory.code-trajectory-accuracy-scorer', 0.9)
})

此工作流中的 plan-activities 步骤会在运行时调用 mastra.getAgent('weatherAgent'),这之所以可行,是因为目标是通过 mastra.getWorkflow() 解析的。完整的 WorkflowScorerConfig 结构请参阅 runEvals reference。

阅读 reporter 输出
阅读 reporter 输出的直接链接

MastraEvalsReporter 会在运行完成后为每个 eval 测试打印得分表:

Mastra Evals

✓ weather agent calls the tool and answers relevantly (1 item)
check-called-tool (gate) 1.0 ✓
check-no-tool-errors (gate) 1.0 ✓
check-includes (gate) 1.0 ✓
answer-relevancy-scorer (threshold: min 0.7) 0.9 ✓

Eval runs: 1 (1 passed)

每个条目显示运行的 verdict、gates、thresholds 以及每个 scorer 的平均得分。Reporter 读取 expectEval/expectEvals 附加到 task.meta.mastraEval 的元数据,因此它适用于并行测试文件和任何填充该字段的测试。

速率限制与并发
速率限制与并发的直接链接

Vitest 会并行运行测试文件,而 runEvals 接受自己的 concurrency 选项,因此总 LLM 流量会在两者之间相乘。如果遇到提供方速率限制,请降低 eval 选项中的 concurrency,或在 Vitest 配置中设置 fileParallelism: false。