Mastra 开发(07):Mastra 的评测体系

QuibblerAgentQuibblerAgent 2026-11-11 约 20 分钟 4 次阅读

《评测驱动开发(EDD)》系列用 14 篇把评测从"感觉"做成了"工程":数据集、断言、judge、轨迹、CI 门禁、线上监控,一层层砌完整条流水线。换到 TypeScript 阵营自然会问——Mastra 作为 TS 优先的 Agent 框架,把这些环节内置了多少?开箱能用的部分长什么样?

先给结论:Mastra evals 的内置部分是"组件级快评"——一批现成的 LLM judge 评分器、挂到 Agent 与工作流步骤上的随行打分、外加 Studio 里的一块评测界面;数据集工程与 CI 硬门禁仍在框架之外,要回到 EDD 的方法论里解决。本文按"定位 → 指标清单 → Agent 打分 → 流程门禁 → vitest 集成 → 与 EDD 分工 → Studio 界面 → 总结"组织,代码基于 2026 年 10 月官方文档逐一核实过的 scorer 接口。


1、内置评测的定位

判断一个框架"内置了多少评测",先看它把评测拆成了哪些件活。EDD 系列第 8 篇把评测分成组件级与端到端两层:组件级回答"这个输出好不好",端到端回答"整个任务成没成"。Mastra 内置的 evals 明确站在前者——每个 scorer 只对一次运行(run)的输入输出打一个分,不管业务链路通不通。

这套接口在 2026 年经历了一次大改版。2025 年刚引入时(见官方博客的引入文章)还是 AnswerRelevancyMetric 一类的 Metric 类,手动调 measure();如今官方 evals 总览的口径已全面换成 scorer 工厂函数 + runEvals + Quick Checks 三件套,指标本体、批量运行器、确定性断言各归其位。老教程里 new AnswerRelevancyMetric(model) 的写法已经过时,照抄跑不通。

运行形态是"随行打分":scorer 挂到 Agent 上之后,每次响应完成会异步补一次判分,不阻塞返回;分数连同 judge 给出的理由落进 mastra_scorers 表,Studio 里可以直接翻。这决定了它的定位——框架替你把"judge 怎么调、结果存哪、界面怎么看"包圆了,但评测集怎么建、多少分算过线、CI 里怎么卡,仍然是你的事。放到第 2 篇的语境里:generate 给你答案,scorer 补一句"这个答案值几分",答案与质量分在同一套 storage 里关联着,排障时能对上号。

2、内置指标清单

指标分三路:LLM judge 类、确定性类、Quick Checks。前两路都在 prebuilt scorer 里,按官方的内置 scorer 清单整理如下:

类别 scorer 判什么
准确与可靠 answer-relevancy 回答是否切题,越高越好
准确与可靠 faithfulness 是否忠实于给定上下文,需 context
准确与可靠 hallucination 事实矛盾与无依据断言,需 context
准确与可靠 completeness 该说的信息是否说全
准确与可靠 answer-similarity 与标准答案的语义相似度,需参考答案
准确与可靠 prompt-alignment 指令的意图、完整度与格式遵从度
准确与可靠 tool-call-accuracy 工具选择是否正确,需期望的工具调用
准确与可靠 trajectory-accuracy 工具与步骤序列是否符合预期轨迹
上下文质量 context-precision RAG 检索排序质量,早命中加分
上下文质量 context-relevance 检索内容相关度与缺失检测
输出质量 tone-consistency 语气正式度与风格一致性
输出质量 toxicity、bias 有害内容与偏见倾向
输出质量 keyword-coverage 关键术语覆盖度
确定性 content-similarity、textual-difference 字符级相似度,不调 LLM

注意:hallucination、toxicity、bias 三个是分数越低越好,其余都是越高越好。写断言前先看清方向,别把 0.9 的幻觉分当喜事。

再往下是 Quick Checks:checks.includes、checks.matches、checks.calledTool、checks.noToolErrors 等十余个确定性微断言,不调 LLM、微秒级出分,输出 0/1。几个细节:checks.equals 默认大小写不敏感;checks.similarity 用 Dice 系数算 0~1 相似度,配个阈值就能转成两值判定;checks.toolOrder、checks.maxToolCalls 则管调用顺序与次数上限。这一路对应 EDD 第 4 篇的"能断言的先断言"——字符串、正则、工具调用形态这些可精确判定的维度,根本轮不到 judge 出场。

顺带说一句:网上常被猜的"代笔检测/文风指纹"类指标并不存在,名单里全是主流质量维度。多轮对话有专门的 multi-turn-judge,按你给的一段自然语言标准逐轮打分,算是清单里最灵活的一个。

3、给 Agent 输出打分

用法从"构造 Agent 时挂 scorer"开始。judge 模型在 scorer 工厂里指定,走与 Agent 相同的模型字符串规则;判分本质是分类任务,配个便宜模型就够,不必与主模型同档:

import { Agent } from '@mastra/core/agent'
import {
  createAnswerRelevancyScorer,
  createHallucinationScorer,
} from '@mastra/evals/scorers/prebuilt'

export const judgedAgent = new Agent({
  id: 'judged-agent',
  model: 'anthropic/claude-sonnet-5-5',
  instructions: '你是严谨的助手。',
  scorers: {
    relevancy: {
      scorer: createAnswerRelevancyScorer({ model: 'openai/gpt-6-luna' }),
      sampling: { type: 'ratio', rate: 1 },   // 每次响应都评
    },
    hallucination: {
      scorer: createHallucinationScorer({ model: 'openai/gpt-6-luna' }),
      sampling: { type: 'ratio', rate: 0.5 }, // 一半采样
    },
  },
})

四个点值得展开。其一,sampling.rate 控制打分覆盖面,1 是全量、0.5 是一半;采样不是掷骰子,按 trace ID 确定性抽取——同 rate 的 scorer 永远选中同一批样本,两轮分数才可比。其二,判分异步进行,不增加响应延迟,代价是分数晚到一步。其三,分数要落库得给 Mastra 实例配 storage(如 LibSQLStore),并把 scorer 注册到实例的 scorers 选项,否则跑了但不持久化。其四,filter 能按请求上下文圈定打分资格,比如只对企业版租户生效。

judge 模型怎么选?EDD 第 5 篇的结论直接适用:judge 与被评模型解耦,避开自评偏好;判分本质是结构化分类,便宜的小模型反而稳定,官方示例统一用 openai/gpt-6-luna 一档。要抠成本也有现成组合——Quick Checks 全量跑、LLM scorer 低采样跑,两路的成本模型完全不同。

不想常驻挂载、只想手动评一条,scorer 实例本身带 run 方法(各 scorer 的入参细节见幻觉 scorer 参考):

const scored = await createHallucinationScorer({
  model: 'openai/gpt-6-luna',
}).run({
  input: [{ role: 'user', content: '总结这份检索结果' }],
  output: { text: '根据资料,该功能于 2026 年 3 月上线……' },
})
console.log(scored.score)   // 0~1,越低越好
console.log(scored.reason)  // judge 给出的判分理由

输入是消息数组,输出是运行结果对象,run 返回分数与理由。要定制自己的 judge 也不难:createScorer({ id, judge }) 链上 .analyze() 与 .generateScore(),用 Zod 约束中间产物,详见自定义 scorer 文档。

4、Workflow 步骤门禁

Agent 是"整段输出一个分",工作流还能把评测下沉到步骤。createStep 同样收 scorers 配置,中间产物照样被打分:

import { createWorkflow, createStep } from '@mastra/core/workflows'
import { z } from 'zod'
import { toneScorer } from '../scorers/tone.ts' // 自定义 scorer,见第 3 节

const draftStep = createStep({
  id: 'draft-step',
  inputSchema: z.object({ topic: z.string() }),
  outputSchema: z.object({ content: z.string() }),
  scorers: {
    tone: { scorer: toneScorer(), sampling: { type: 'ratio', rate: 1 } },
  },
  execute: async ({ inputData }) => ({
    content: await draft(inputData.topic), // 草稿生成,略
  }),
})

export const blogWorkflow = createWorkflow({
  id: 'blog-workflow',
  inputSchema: z.object({ topic: z.string() }),
  outputSchema: z.object({ content: z.string() }),
}).then(draftStep).commit()

步骤级打分的价值在于质量门禁前移:草稿步骤的语气分连续走低,你看到的是"第 2 步坏了",而不是"最终输出不对劲"再倒查。官方还提供 gates/verdicts 一类阈值判定 API,把分数转成通过与否的结论;但边界要说清——这仍是框架内的观测与软门禁,"改完不过线就不许合并"的硬卡口属于 CI,Mastra 没有替你做。

5、vitest 集成测试

官方文档没有专门的 vitest 集成页,也不需要:scorer 与 runEvals 都是普通异步函数,vitest 直接调。runEvals 收一批输入、一个目标 Agent、一组 scorer,返回按 scorer id 归位的分数:

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,
  scorers: [checks.calledTool('get_weather'), checks.noToolErrors()],
})
console.log(result.scores)
// { 'check-called-tool': 1, 'check-no-tool-errors': 1 }

data 就是一个最小数据集——EDD 第 3 篇说的"冒烟集",在 TS 里先写成常量数组就够用。套进 vitest,给第 2 篇的 timeAgent 配一组评测:

import { describe, it, expect } from 'vitest'
import { runEvals } from '@mastra/core/evals'
import { checks } from '@mastra/evals/checks'
import { createHallucinationScorer } from '@mastra/evals/scorers/prebuilt'
import { timeAgent } from '../src/mastra/agents/index.ts'

describe('timeAgent 评测', () => {
  it('调对工具且无幻觉', async () => {
    const { scores } = await runEvals({
      data: [{ input: '现在几点了?' }],
      target: timeAgent,
      scorers: [
        checks.calledTool('get-current-time'),
        checks.noToolErrors(),
        createHallucinationScorer({ model: 'openai/gpt-6-luna' }),
      ],
    })
    expect(scores['check-called-tool']).toBe(1)
    expect(scores['check-no-tool-errors']).toBe(1)
    expect(scores['hallucination']).toBeLessThanOrEqual(0.2)
  }, 60_000)
})

两类断言要区别对待。checks.* 微秒级、完全可复现,放进每次提交都跑的常规用例;LLM judge 有轮次波动(EDD 第 5 篇讲透的漂移问题),要给长超时、留宽松阈值,必要时用环境变量分流——比如 describe.skipIf(!process.env.RUN_LLM_EVALS) 包一层,日常提交只跑 checks,夜间流水线才带上 judge。这个测试文件本身就是 EDD 第 10 篇"评测进 CI"的最小 TS 版本。

6、与 EDD 方法论分工

把两边对齐成一张表,谁管什么一目了然:

环节 Mastra 内置 EDD 系列方法论
judge 质量维度 17 个 prebuilt scorer,构造时指定 judge 模型 第 5、6 篇:rubric 设计与漂移治理
确定性断言 11 个 Quick Checks 第 4 篇:精确断言优先
轨迹与工具调用 tool-call-accuracy、trajectory-accuracy 第 7 篇:轨迹评测
线上随行打分 sampling 按 trace 采样、分数落库 第 11 篇:线上监控闭环
数据集管理 无,Studio Evaluate 面板仅起步 第 3 篇:冒烟集到黄金集
CI 门禁 无原生卡口,gates/verdicts 偏软 第 10 篇:CI 硬门禁

一句话记法:Mastra 管"单次输出打分"的零件,EDD 管"分数驱动迭代"的体系。框架替你省掉的是 judge 的样板代码与结果存储;评测集的版本化、阈值的校准、红队与人工反馈,框架不碰。至于 LangSmith、Langfuse 这类外部观测平台,接的是 OpenTelemetry 导出的 trace;mastra_scorers 的分数在自家 storage 里,两边各管一段,并不冲突。

7、Studio 评测界面

第 2 篇用过的调试台 pnpm dev 里就有评测的位置。操作路径:左侧进 Observability 区,先在 Scorers 列表确认 scorer 已注册;随后是分页的打分结果,点开任意一条详情,分数、judge 理由、输入输出、以及 judge 实际使用的 prompt 全部可见——最后一样尤其值钱,rubric 调优时不用猜裁判到底读了什么。

Playground 侧还有 Agent Evaluate 标签:选数据集与已注册的 scorer 跑一轮实验,得到逐条 pass/fail 与版本标签,也可以对历史 trace/span 补打分。它不是完整的数据集管理台——没有版本 diff、没有审批流——但"改完 instructions 立刻跑同一组输入对比分数"这个高频动作,不用写代码就能完成。

8、总结

Mastra 的 evals 把评测里三件最脏的活——judge 调用、结果落库、查看界面——做成了框架内置:17 个 prebuilt scorer 覆盖切题度、幻觉、忠实度、语气、毒性等主流维度,11 个 Quick Checks 兜住确定性断言,sampling 提供线上轻量采样,Studio 里能直接看到 judge 的判分理由。而数据集工程、CI 硬门禁、阈值校准仍在框架之外,正是 EDD 系列 14 篇的地盘。

练习:回到第 2 篇的 timeAgent,挂一个 hallucination scorer(工具返回天然就是判分上下文),rate 设 1 连跑 10 次,在 Studio 观察分数分布;再把阈值写进 vitest 断言,从 0.2 起调。体会一次"调阈值"到底在调什么——不是拍一个数字,而是看完分布再画线。对于刚起步的 TS 团队,建议从 Quick Checks 加一个幻觉 scorer 入手,EDD 的数据集与门禁方法论随后补上。

相关推荐

获取系统SDK版本、判断手机ROM
精选Code

获取系统SDK版本、判断手机ROM

Build获取系统SDK版本Android中部分API的使用,需要在特定的SDK版本之后才能使用,因此在兼容老版本SDK的时候,经常需要判断API的版本。各种Android版本的对应关系参考《Android各版本对应的SDK版本》判断手机ROM有时候需要判断手机系统的ROM,检测ROM是MIUI、EMUI还是Flyme,可以使用getprop命令,去系统build.prop文件(关于build.p

4.2k
AndroidStudio中各种中文乱码问题
精选Code

AndroidStudio中各种中文乱码问题

AndroidStudio中各种中文乱码问题1、编译Java错误信息乱码在出现这个Annotation processors must be explicitly declared now...问题的时候,正好也发现AndroidStudio Build Output错误信息都乱码。经常遇到各种问题,习惯了,现在遇到的问题,都是以后的答案。 1.1、修改项目build.gradle(无效)在整个p

2.8k
Multi-Agent 详解:多智能体协作架构与实践
精选Agent

Multi-Agent 详解:多智能体协作架构与实践

一个智能体配上一套工具,已经能完成相当多任务。但让它同时“当产品经理、写后端、跑测试、做代码评审”,很快就会露馅:上下文里塞满了互相干扰的指令,十几件工具的清单来回切换,任务做到一半忘了开头。解法看起来很自然——像人类团队一样分工:让多个智能体各司其职,由一个协调者调度,这就是 Multi-Agent(多智能体)系统。它也是 2026 年智能体工程里讨论度最高、误用率也最高的架构。本文按“架构模式…

122