第 15 章 · Evals 评估体系
本章目标:理解 Scorers 评分器机制,学会使用内置评估指标、编写自定义 Scorer,并把评估接入 CI 流水线。
15.1 为什么传统测试不够用
传统测试有确定性的 pass/fail;而 AI 输出是非确定的——同一个问题两次回答措辞不同但都对。Mastra 用 Scorers 弥补这一空隙:给输出打 0~1 的数值分,量化"回答有多好"。
Scorer 三种实现路线:
- 模型评分(model-graded)——用 LLM 按标准打分;
- 规则评分(rule-based)——关键词/正则/结构校验;
- 统计评分(statistical)——相似度等数学指标。
15.2 安装与内置 Scorer
bash
npm install @mastra/evals@latesttypescript
// src/mastra/agents/evaluated-agent.ts
import { Agent } from '@mastra/core/agent'
import {
createAnswerRelevancyScorer,
createToxicityScorer,
} from '@mastra/evals/scorers/prebuilt'
export const evaluatedAgent = new Agent({
id: 'evaluated-agent',
name: 'Evaluated Agent',
instructions: '你是客服助手。',
model: 'openai/gpt-5-mini',
scorers: {
// 键为自定义名称,用于在报告中识别
relevancy: {
scorer: createAnswerRelevancyScorer({ model: 'openai/gpt-5-mini' }),
weight: 0.7, // 加权汇总
},
toxicity: {
scorer: createToxicityScorer({ model: 'openai/gpt-5-mini' }),
weight: 0.3,
},
},
})挂载后每次 generate() 都会异步评分,结果可在 Studio 与存储中查看。
15.3 自定义 Scorer
当内置指标不满足业务口径时:
typescript
// src/mastra/scorers/policy.ts
import { createScorer } from '@mastra/core/evals'
// 规则型:回答必须包含工单号格式 TK-XXXX
export const ticketFormatScorer = createScorer({
name: 'ticket-format',
description: '检查回复是否包含规范工单号',
})
.generateScore(({ run }) => {
const text = run.output?.text ?? ''
return /TK-\d{4,}/.test(text) ? 1 : 0
})
// 模型评分型:让 LLM 判断语气是否符合品牌要求
export const toneScorer = createScorer({
name: 'brand-tone',
description: '语气是否友好专业',
})
.generateScore(({ run }) => {
// 内部会调用配置的模型按描述打分
return judge(run.output?.text ?? '', '友好、专业、不使用命令式语气')
})15.4 脚本中独立运行 Scorer
不挂 Agent 也能对任意文本打分,适合批量回放历史数据:
typescript
// scripts/score-once.ts
import { ticketFormatScorer } from '../src/mastra/scorers/policy'
const result = await ticketFormatScorer.run({
input: { text: '您的工单号是 TK-20240801,请留意查收。' },
output: { text: '已收到,感谢反馈!' },
})
console.log(result.score) // 0 或 115.5 在 CI 中运行
15.4 在 CI 中运行
把评估当作回归测试:改提示词后自动跑一批用例,分数低于阈值则失败。
yaml
# .github/workflows/evals.yml
name: Agent Evals
on: [pull_request]
jobs:
eval:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22 }
- run: npm ci
- run: npm run evals # 内部跑评估脚本并断言平均分 >= 0.8
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}typescript
// scripts/evals.ts(npm run evals 入口)
const cases = [
{ input: '怎么退款?', mustInclude: ['7 天'] },
{ input: '发货多久?', mustInclude: ['48 小时'] },
]
let total = 0
for (const c of cases) {
const r = await evaluatedAgent.generate(c.input)
const hit = c.mustInclude.every((k) => r.text.includes(k))
total += hit ? 1 : 0
}
if (total / cases.length < 0.8) {
console.error('评估未达标'); process.exit(1)
}本章小结
- Scorers 用 0~1 分数量化非确定性输出质量;
- 内置指标来自
@mastra/evals/scorers/prebuilt,可加权组合挂在 Agent 上; - 自定义 Scorer 支持规则型与模型评分型两种写法;
- 把评估搬进 CI:提示词改动像代码一样有回归保护。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. Mastra 中 Scorer 返回的分数通常是什么范围?
2. 以下哪项属于 Mastra 提供的三类评分方法?
3. Agent 配置中 scorers.relevancy.weight: 0.7 的含义是?
4. 把评估接入 CI 后,什么情况会让流水线失败?
🛠️ 动手实践
- 给客服 Agent 同时挂 relevancy 与 toxicity 两个 Scorer,故意输入敏感话术看毒性分变化。
- 编写一个规则型自定义 Scorer:校验回答必须引用知识库来源编号。
- 搭建 GitHub Actions 评估流水线,人为降低一个阈值触发流水线失败。