跳到主要内容

Gate 与 Verdict

Gate 与 Verdict 让 runEvals 能区分评估条件的严重程度。Gate 是必须获得 1.0 分的 Scorer,代表一旦未满足就会让运行失败的硬性要求。阈值是所跟踪指标可接受的最低分数。Verdict 会将结果汇总为 passedscoredfailed

何时使用 Gate 与 Verdict
何时使用 Gate 与 Verdict的直接链接

  • 在 CI 中强制执行硬性要求(例如“Agent 必须调用正确的 Tool”)
  • 使用最低阈值跟踪质量指标(例如“faithfulness 高于 0.7”)
  • 无需编写自定义断言逻辑,即可从 Evals 运行中获取单一 Verdict 信号(passedscoredfailed
  • 将“必须通过”的 Gate 与“最好达到”的跟踪指标分开

Quickstart
Quickstart的直接链接

src/evals/weather-eval.ts
import { runEvals } from '@mastra/core/evals'
import { checks } from '@mastra/evals/checks'
import { weatherAgent } from '../agents'
import { faithfulnessScorer } from '../scorers'

const result = await runEvals({
data: [{ input: 'What is the weather in Brooklyn?' }],
target: weatherAgent,

// Gates: must all score 1.0 or the run fails
gates: [checks.calledTool('get_weather'), checks.noToolErrors()],

// Scorers: tracked with optional thresholds
scorers: [
{ scorer: faithfulnessScorer, threshold: 0.7 },
checks.includes('Brooklyn'), // no threshold = tracked only
],
})

console.log(result.verdict) // 'passed' | 'scored' | 'failed'

Verdict 的工作原理
Verdict 的工作原理的直接链接

处理完所有数据项目后,会根据 Gate 和阈值计算 Verdict:

  • failed:至少有一个 Gate 在所有数据项目上的平均分低于 1.0
  • scored:所有 Gate 均已通过,但至少有一个带阈值的 Scorer 未达到阈值
  • passed:所有 Gate 均获得 1.0 分,并且所有阈值均已达到

如果未提供 Gate 或带阈值的 Scorer,Verdict 字段将省略,runEvals 的行为与之前完全相同。

Gate
Gate的直接链接

Gate 是通过 gates 字段传入的 Scorer。它们会在每个数据项目的常规 Scorer 之前运行。要通过 Gate,其在所有数据项目上的平均分必须达到 1.0。

src/evals/tool-gate.ts
import { runEvals } from '@mastra/core/evals'
import { checks } from '@mastra/evals/checks'

const result = await runEvals({
data: [{ input: 'What is the weather?' }],
target: weatherAgent,
gates: [checks.calledTool('get_weather')],
scorers: [qualityScorer],
})

// result.gateResults: [{ id: 'check-called-tool', passed: true, score: 1 }]

任何 Scorer 都可用作 Gate。Quick Checks 很适合此场景,因为它们返回二元的 1/0 分数。有关完整的参数和返回类型文档,请访问 runEvals() Reference

仅使用 Gate 的运行
仅使用 Gate 的运行的直接链接

如果至少提供了一个 Gate,scorers 就是可选的。这适用于只关心通过/失败 Gate、无需跟踪任何质量指标的确定性 CI 检查。

src/evals/gate-only.ts
import { runEvals } from '@mastra/core/evals'
import { checks } from '@mastra/evals/checks'

const result = await runEvals({
data: [{ input: 'What is the weather in Brooklyn?' }],
target: weatherAgent,
gates: [checks.calledTool('get_weather'), checks.noToolErrors()],
})

必须提供至少一个 Scorer 或 Gate;两者均未提供时,运行会抛出错误。

阈值
阈值的直接链接

将 Scorer 包装在 { scorer, threshold } 中,可以设置通过/失败边界。阈值会与该 Scorer 在所有数据项目上的平均分进行比较。

threshold 可以是:

  • 数值:表示下限(分数达到或超过该值即通过):{ scorer, threshold: 0.7 }
  • 包含 min 和/或 max 的对象:用于基于范围的检查:{ scorer, threshold: { max: 0.3 } }

对于高分代表不良结果的 Scorer(例如幻觉、毒性),请使用 max。当分数应处于特定范围内时,请使用 { min, max }

src/evals/threshold-example.ts
import { runEvals } from '@mastra/core/evals'

const result = await runEvals({
data: [{ input: 'Explain quantum computing' }],
target: myAgent,
scorers: [
{ scorer: faithfulnessScorer, threshold: 0.7 }, // min threshold (number shorthand)
{ scorer: hallucinationScorer, threshold: { max: 0.3 } }, // max threshold — high score = bad
{ scorer: verbosityScorer, threshold: { min: 0.3, max: 0.8 } }, // range threshold
toneScorer, // bare scorer, no threshold — tracked only
],
})

// result.thresholdResults:
// [
// { id: 'faithfulness', passed: true, averageScore: 0.85, threshold: 0.7 },
// { id: 'hallucination', passed: true, averageScore: 0.1, threshold: { max: 0.3 } },
// { id: 'verbosity', passed: false, averageScore: 0.9, threshold: { min: 0.3, max: 0.8 } },
// ]

不带阈值的裸 Scorer 仍会出现在 result.scores 中,但不会影响 Verdict。

在 CI 中使用 Verdict
在 CI 中使用 Verdict的直接链接

Verdict 为 CI Pipeline 提供一个信号:

src/evals/ci-check.ts
import { runEvals } from '@mastra/core/evals'
import { checks } from '@mastra/evals/checks'

const result = await runEvals({
data: testDataset,
target: myAgent,
gates: [checks.calledTool('search'), checks.noToolErrors()],
scorers: [{ scorer: faithfulnessScorer, threshold: 0.7 }],
})

if (result.verdict === 'failed') {
console.error(
'Gate failures:',
result.gateResults?.filter(g => !g.passed),
)
process.exit(1)
}

if (result.verdict === 'scored') {
console.warn(
'Threshold misses:',
result.thresholdResults?.filter(t => !t.passed),
)
}