Skip to content

第 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@latest
typescript
// 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 或 1

15.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 后,什么情况会让流水线失败?

🛠️ 动手实践

  1. 给客服 Agent 同时挂 relevancy 与 toxicity 两个 Scorer,故意输入敏感话术看毒性分变化。
  2. 编写一个规则型自定义 Scorer:校验回答必须引用知识库来源编号。
  3. 搭建 GitHub Actions 评估流水线,人为降低一个阈值触发流水线失败。