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

Custom scorers

使用 createScorer 构建自定义评估逻辑。每个步骤都可以使用 JavaScript 函数或基于 LLM 的提示词对象,具体取决于你要评估的内容。

四步流水线
四步流水线的直接链接

Mastra 中的所有 scorer 都遵循一致的四步评估流水线:

  1. preprocess(可选):准备或转换输入/输出数据
  2. analyze(可选):执行评估分析并收集洞察
  3. generateScore(必需):将分析转换为数值得分
  4. generateReason(可选):生成人类可读的解释

在单个 scorer 中组合函数和提示词对象,可以对某些步骤使用确定性检查,对其他步骤使用基于 LLM 的评估。

函数与提示词对象
函数与提示词对象的直接链接

函数使用 JavaScript 实现确定性逻辑。它们适用于:

  • 具有明确标准的算法评估
  • 性能关键场景
  • 与现有库集成
  • 一致、可复现的结果

提示词对象使用 LLM 作为 judge 进行评估。它们非常适合:

  • 需要类人判断的主观评估
  • 难以用算法编码的复杂标准
  • 自然语言理解任务
  • 细微的上下文评估

“提示词对象”的含义: 步骤不是函数,而是一个包含 description + createPrompt 的对象(对于 preprocess/analyze 还包括 outputSchema)。该对象告诉 Mastra 为该步骤运行 judge LLM,并将结构化输出存储在 results.<step>StepResult 中。

你可以在单个 scorer 中混合使用这些方法——例如,使用函数预处理数据,使用 LLM 分析质量。

初始化 scorer
初始化 scorer的直接链接

每个 scorer 都从 createScorer 工厂函数开始,它需要 id 和 description,并可选接受类型规范和 judge 配置。

import { createScorer } from '@mastra/core/evals';

const glutenCheckerScorer = createScorer({
id: 'gluten-checker',
description: 'Check if recipes contain gluten ingredients',
judge: { // Optional: for prompt object steps
model: 'openai/gpt-5.6-sol',
instructions: 'You are a Chef that identifies if recipes contain gluten.'
}
})
// Chain step methods here
.preprocess(...)
.analyze(...)
.generateScore(...)
.generateReason(...)

仅当计划在任何步骤中使用提示词对象时才需要 judge 配置。各个步骤可以用自己的 judge 设置覆盖此默认配置。

如果所有步骤都基于函数,则永远不会调用 judge,也不会有 judge 输出。要查看 LLM 输出,请至少将一个步骤定义为提示词对象,并读取相应的步骤结果(例如 results.analyzeStepResult)。

最小 judge 示例(提示词对象)
最小 judge 示例(提示词对象)的直接链接

此示例在 analyze 中使用提示词对象,因此 judge 会运行,其结构化输出可通过 results.analyzeStepResult 获取。

import { createScorer } from '@mastra/core/evals'
import { z } from 'zod'

const quoteSourcesScorer = createScorer({
id: 'quote-sources',
description: 'Check if the response includes sources',
judge: {
model: 'openai/gpt-5-mini',
instructions: 'You are a strict evaluator.',
},
})
.analyze({
description: 'Detect whether sources are present',
outputSchema: z.object({
hasSources: z.boolean(),
sources: z.array(z.string()),
}),
createPrompt: ({ run }) => `
Does the response contain sources? Extract them as a list.

Response:
${run.output}
`,
})
.generateScore(({ results }) => (results.analyzeStepResult.hasSources ? 1 : 0))

// Run the scorer and inspect judge output
const result = await quoteSourcesScorer.run({
input: 'What is the capital of France?',
output: 'Paris is the capital of France [1]. Source: [1] Wikipedia',
})

console.log(result.score) // 1
console.log(result.analyzeStepResult) // { hasSources: true, sources: ["Wikipedia"] }

用于 Agent 评估的 Agent 类型
用于 Agent 评估的 Agent 类型的直接链接

为了类型安全并兼容实时 agent 评分和 trace 评分,在创建用于 agent 评估的 scorer 时请使用 type: 'agent'。同一个 scorer 可以评估 agent 并对 trace 评分:

const myScorer = createScorer({
type: 'agent', // Automatically handles agent input/output types
}).generateScore(({ run, results }) => {
// run.output is automatically typed as ScorerRunOutputForAgent
// run.input is automatically typed as ScorerRunInputForAgent
})

分步详解
分步详解的直接链接

preprocess 步骤(可选)
preprocess 步骤(可选)的直接链接

当你需要提取特定元素或过滤内容,或者转换复杂数据结构时,用于准备输入/输出数据。

函数: ({ run, results }) => any

const glutenCheckerScorer = createScorer(...)
.preprocess(({ run }) => {
// Extract and clean recipe text
const recipeText = run.output.text.toLowerCase();
const wordCount = recipeText.split(' ').length;

return {
recipeText,
wordCount,
hasCommonGlutenWords: /flour|wheat|bread|pasta/.test(recipeText)
};
})

提示词对象: 使用 description、outputSchema 和 createPrompt 构建基于 LLM 的预处理。

const glutenCheckerScorer = createScorer(...)
.preprocess({
description: 'Extract ingredients from the recipe',
outputSchema: z.object({
ingredients: z.array(z.string()),
cookingMethods: z.array(z.string())
}),
createPrompt: ({ run }) => `
Extract all ingredients and cooking methods from this recipe:
${run.output.text}

Return JSON with ingredients and cookingMethods arrays.
`
})

数据流: 后续步骤可通过 results.preprocessStepResult 使用结果

analyze 步骤(可选)
analyze 步骤(可选)的直接链接

执行核心评估分析,收集用于指导评分决策的洞察。

函数: ({ run, results }) => any

const glutenCheckerScorer = createScorer({...})
.preprocess(...)
.analyze(({ run, results }) => {
const { recipeText, hasCommonGlutenWords } = results.preprocessStepResult;

// Simple gluten detection algorithm
const glutenKeywords = ['wheat', 'flour', 'barley', 'rye', 'bread'];
const foundGlutenWords = glutenKeywords.filter(word =>
recipeText.includes(word)
);

return {
isGlutenFree: foundGlutenWords.length === 0,
detectedGlutenSources: foundGlutenWords,
confidence: hasCommonGlutenWords ? 0.9 : 0.7
};
})

提示词对象: 使用 description、outputSchema 和 createPrompt 进行基于 LLM 的分析。

const glutenCheckerScorer = createScorer({...})
.preprocess(...)
.analyze({
description: 'Analyze recipe for gluten content',
outputSchema: z.object({
isGlutenFree: z.boolean(),
glutenSources: z.array(z.string()),
confidence: z.number().min(0).max(1)
}),
createPrompt: ({ run, results }) => `
Analyze this recipe for gluten content:
"${results.preprocessStepResult.recipeText}"

Look for wheat, barley, rye, and hidden sources like soy sauce.
Return JSON with isGlutenFree, glutenSources array, and confidence (0-1).
`
})

数据流: 后续步骤可通过 results.analyzeStepResult 使用结果

generateScore 步骤(必需)
generatescore-步骤必需的直接链接

将分析结果转换为数值得分。这是流水线中唯一必需的步骤。

函数: ({ run, results }) => number

const glutenCheckerScorer = createScorer({...})
.preprocess(...)
.analyze(...)
.generateScore(({ results }) => {
const { isGlutenFree, confidence } = results.analyzeStepResult;

// Return 1 for gluten-free, 0 for contains gluten
// Weight by confidence level
return isGlutenFree ? confidence : 0;
})

提示词对象: 关于将提示词对象与 generateScore 配合使用(包括必需的 calculateScore 函数)的详细信息,请参阅 createScorer API 参考。

数据流: 得分会作为 score 参数提供给 generateReason

generateReason 步骤(可选)
generatereason-步骤可选的直接链接

生成人类可读的得分解释,适用于调试、透明度或用户反馈。

函数: ({ run, results, score }) => string

const glutenCheckerScorer = createScorer({...})
.preprocess(...)
.analyze(...)
.generateScore(...)
.generateReason(({ results, score }) => {
const { isGlutenFree, glutenSources } = results.analyzeStepResult;

if (isGlutenFree) {
return `Score: ${score}. This recipe is gluten-free with no harmful ingredients detected.`;
} else {
return `Score: ${score}. Contains gluten from: ${glutenSources.join(', ')}`;
}
})

提示词对象: 使用 description 和 createPrompt 生成 LLM 解释。

const glutenCheckerScorer = createScorer({...})
.preprocess(...)
.analyze(...)
.generateScore(...)
.generateReason({
description: 'Explain the gluten assessment',
createPrompt: ({ results, score }) => `
Explain why this recipe received a score of ${score}.
Analysis: ${JSON.stringify(results.analyzeStepResult)}

Provide a clear explanation for someone with dietary restrictions.
`
})

输入过滤
输入过滤的直接链接

Agent 对话可能包含数百条消息,其工具调用和数据部分伴随系统元数据。大多数 scorer 只需要其中一部分。在 scorer 流水线执行之前,prepareRun 选项会转换运行数据,以减少噪声并让每个 scorer 保持聚焦。

使用 filterRun() 进行声明式过滤
使用-filterrun-进行声明式过滤的直接链接

filterRun() 工具根据声明式选项创建 prepareRun 函数:

src/mastra/scorers/tool-scorer.ts
import { createScorer, filterRun } from '@mastra/core/evals'

const toolScorer = createScorer({
id: 'tool-quality',
description: 'Evaluates tool usage quality',
type: 'agent',
prepareRun: filterRun({
partTypes: ['tool-invocation', 'text'],
maxRememberedMessages: 20,
}),
}).generateScore(({ run }) => {
// run.input.rememberedMessages has only tool and text messages, max 20
return 1
})

常见选项包括:

  • partTypes:仅保留包含匹配部分类型的消息(例如 'tool-invocation'、'text'、'reasoning')
  • toolNames:仅保留涉及特定工具的消息(例如 ['write_file', 'execute_command'])
  • maxRememberedMessages:限制上下文窗口大小
  • dropRequestContext、dropGroundTruth、dropExpectedTrajectory:移除未使用的字段

完整选项列表请参阅 filterRun() 参考。

自定义 prepareRun 函数
自定义-preparerun-函数的直接链接

对于 filterRun() 未覆盖的逻辑,请直接编写 prepareRun 函数:

import { createScorer } from '@mastra/core/evals'

const customScorer = createScorer({
id: 'recent-output',
description: 'Scores only the last response',
type: 'agent',
prepareRun: run => ({
...run,
output: run.output.slice(-1), // Keep only the last message
requestContext: undefined,
}),
}).generateScore(({ run }) => {
return run.output.length > 0 ? 1 : 0
})

prepareRun 函数也可以是异步的。

系统消息始终保留

filterRun() 永远不会过滤 systemMessages 或 taggedSystemMessages。它们包含 agent 指令,是评分的关键上下文。

跳过无法评分运行
跳过无法评分运行的直接链接

某些 scorer 只适用于一部分运行。评判退款处理得如何的 scorer,对于没有请求退款的运行无话可说。

从函数步骤返回 notScorable() 以声明该运行没有可评估的内容。剩余步骤会被跳过,该运行不会纳入 scorer 的聚合结果:

src/mastra/scorers/refund-judge.ts
import { createScorer, notScorable } from '@mastra/core/evals'
import { extractToolCalls } from '@mastra/evals/scorers/utils'

export const refundJudge = createScorer({
id: 'refund-judge',
description: 'Judges how well refund requests were handled',
type: 'agent',
judge: {
model: 'openai/gpt-5-mini',
instructions: 'You are a strict QA reviewer for customer-support refund handling.',
},
})
.preprocess(({ run }) => {
const { tools } = extractToolCalls(run.output)
return tools.includes('refundCustomer')
? { tools }
: notScorable('refundCustomer was not called')
})
.generateScore({
description: 'Score the refund handling from 0 to 1',
createPrompt: ({ run }) =>
`Rate this refund handling from 0 to 1:\n${JSON.stringify(run.output)}`,
})

请将检查放在 preprocess 中,以便它在任何 judge 步骤之前运行。notScorable() 与资格过滤器不同:过滤器根据请求上下文决定 scorer 是否运行,而 notScorable() 允许 scorer 先检查运行的输入和输出。

关于结果结构,以及实时评分、runEvals() 和实验如何处理被跳过的运行,请参阅 notScorable() 参考。

示例:创建自定义 scorer
示例:创建自定义 scorer的直接链接

Mastra 中的自定义 scorer 使用 createScorer,包含四个核心组件:

  1. Judge 配置
  2. 分析步骤
  3. 得分生成
  4. 原因生成

这些组件共同让你使用 LLM 作为 judge 来定义自定义评估逻辑。完整 API 和配置选项请参阅 createScorer。

src/mastra/scorers/gluten-checker.ts
import { createScorer } from '@mastra/core/evals'
import { z } from 'zod'

export const GLUTEN_INSTRUCTIONS = `You are a Chef that identifies if recipes contain gluten.`

export const generateGlutenPrompt = ({
output,
}: {
output: string
}) => `Check if this recipe is gluten-free.

Check for:
- Wheat
- Barley
- Rye
- Common sources like flour, pasta, bread

Example with gluten:
"Mix flour and water to make dough"
Response: {
"isGlutenFree": false,
"glutenSources": ["flour"]
}

Example gluten-free:
"Mix rice, beans, and vegetables"
Response: {
"isGlutenFree": true,
"glutenSources": []
}

Recipe to analyze:
${output}

Return your response in this format:
{
"isGlutenFree": boolean,
"glutenSources": ["list ingredients containing gluten"]
}`

export const generateReasonPrompt = ({
isGlutenFree,
glutenSources,
}: {
isGlutenFree: boolean
glutenSources: string[]
}) => `Explain why this recipe is${isGlutenFree ? '' : ' not'} gluten-free.

${glutenSources.length > 0 ? `Sources of gluten: ${glutenSources.join(', ')}` : 'No gluten-containing ingredients found'}

Return your response in this format:
"This recipe is [gluten-free/contains gluten] because [explanation]"`

export const glutenCheckerScorer = createScorer({
id: 'gluten-checker',
description: 'Check if the output contains any gluten',
judge: {
model: 'openai/gpt-5-mini',
instructions: GLUTEN_INSTRUCTIONS,
},
})
.analyze({
description: 'Analyze the output for gluten',
outputSchema: z.object({
isGlutenFree: z.boolean(),
glutenSources: z.array(z.string()),
}),
createPrompt: ({ run }) => {
const { output } = run
return generateGlutenPrompt({ output: output.text })
},
})
.generateScore(({ results }) => {
return results.analyzeStepResult.isGlutenFree ? 1 : 0
})
.generateReason({
description: 'Generate a reason for the score',
createPrompt: ({ results }) => {
return generateReasonPrompt({
glutenSources: results.analyzeStepResult.glutenSources,
isGlutenFree: results.analyzeStepResult.isGlutenFree,
})
},
})

Judge 配置
Judge 配置的直接链接

设置 LLM 模型,并定义其作为领域专家的角色。

judge: {
model: 'openai/gpt-5-mini',
instructions: GLUTEN_INSTRUCTIONS,
}

分析步骤
分析步骤的直接链接

定义 LLM 应如何分析输入,以及应返回什么结构化输出。

.analyze({
description: 'Analyze the output for gluten',
outputSchema: z.object({
isGlutenFree: z.boolean(),
glutenSources: z.array(z.string()),
}),
createPrompt: ({ run }) => {
const { output } = run;
return generateGlutenPrompt({ output: output.text });
},
})

分析步骤使用提示词对象来:

  • 提供分析任务的清晰描述
  • 使用 Standard JSON Schema 定义预期输出结构(布尔结果和 gluten 来源列表)
  • 根据输入内容生成运行时提示词

得分生成
得分生成的直接链接

将 LLM 的结构化分析转换为数值得分。

.generateScore(({ results }) => {
return results.analyzeStepResult.isGlutenFree ? 1 : 0;
})

得分生成函数接收分析结果并应用业务逻辑来生成得分。在此例中,LLM 直接判断食谱是否无麸质,因此我们使用该布尔结果:无麸质为 1,含麸质为 0。

原因生成
原因生成的直接链接

通过另一次 LLM 调用为得分提供人类可读的解释。

.generateReason({
description: 'Generate a reason for the score',
createPrompt: ({ results }) => {
return generateReasonPrompt({
glutenSources: results.analyzeStepResult.glutenSources,
isGlutenFree: results.analyzeStepResult.isGlutenFree,
});
},
})

原因生成步骤会创建解释,帮助用户理解为何给出某个得分;它同时使用布尔结果和分析步骤识别出的具体 gluten 来源。

高无麸质示例
高无麸质示例的直接链接

src/example-high-gluten-free.ts
const result = await glutenCheckerScorer.run({
input: [{ role: 'user', content: 'Mix rice, beans, and vegetables' }],
output: { text: 'Mix rice, beans, and vegetables' },
})

console.log('Score:', result.score)
console.log('Gluten sources:', result.analyzeStepResult.glutenSources)
console.log('Reason:', result.reason)

高无麸质输出
高无麸质输出的直接链接

{
score: 1,
analyzeStepResult: {
isGlutenFree: true,
glutenSources: []
},
reason: 'This recipe is gluten-free because rice, beans, and vegetables are naturally gluten-free ingredients that are safe for people with celiac disease.'
}

部分麸质示例
部分麸质示例的直接链接

src/example-partial-gluten.ts
const result = await glutenCheckerScorer.run({
input: [{ role: 'user', content: 'Mix flour and water to make dough' }],
output: { text: 'Mix flour and water to make dough' },
})

console.log('Score:', result.score)
console.log('Gluten sources:', result.analyzeStepResult.glutenSources)
console.log('Reason:', result.reason)

部分麸质输出
部分麸质输出的直接链接

{
score: 0,
analyzeStepResult: {
isGlutenFree: false,
glutenSources: ['flour']
},
reason: 'This recipe is not gluten-free because it contains flour. Regular flour is made from wheat and contains gluten, making it unsafe for people with celiac disease or gluten sensitivity.'
}

低无麸质示例
低无麸质示例的直接链接

src/example-low-gluten-free.ts
const result = await glutenCheckerScorer.run({
input: [{ role: 'user', content: 'Add soy sauce and noodles' }],
output: { text: 'Add soy sauce and noodles' },
})

console.log('Score:', result.score)
console.log('Gluten sources:', result.analyzeStepResult.glutenSources)
console.log('Reason:', result.reason)

低无麸质输出
低无麸质输出的直接链接

{
score: 0,
analyzeStepResult: {
isGlutenFree: false,
glutenSources: ['soy sauce', 'noodles']
},
reason: 'This recipe is not gluten-free because it contains soy sauce, noodles. Regular soy sauce contains wheat and most noodles are made from wheat flour, both of which contain gluten and are unsafe for people with gluten sensitivity.'
}

示例与资源: