> Discover all available pages from the documentation index: https://mastra.zisheng.pro/fr/llms.txt # Scorers personnalisés Mastra fournit une fonction de fabrique `createScorer` unifiée qui permet de créer une logique d’évaluation personnalisée en utilisant, à chaque étape, soit des fonctions JavaScript, soit des objets de prompt basés sur un LLM. Cette flexibilité vous permet de choisir l’approche la mieux adaptée à chaque partie de votre pipeline d’évaluation. ## Le pipeline en quatre étapes Tous les scorers de Mastra suivent un pipeline d’évaluation cohérent en quatre étapes : 1. **preprocess** (facultative) : prépare ou transforme les données d’entrée et de sortie 2. **analyze** (facultative) : effectue l’analyse d’évaluation et recueille des informations 3. **generateScore** (obligatoire) : convertit l’analyse en score numérique 4. **generateReason** (facultative) : génère des explications lisibles par un humain Chaque étape peut utiliser soit des **fonctions**, soit des **objets de prompt** (évaluation basée sur un LLM), ce qui vous permet de combiner des algorithmes déterministes avec le jugement de l’IA selon vos besoins. ## Fonctions ou objets de prompt Les **fonctions** utilisent JavaScript pour appliquer une logique déterministe. Elles conviennent parfaitement aux cas suivants : - Évaluations algorithmiques reposant sur des critères clairs - Scénarios où les performances sont critiques - Intégration à des bibliothèques existantes - Résultats cohérents et reproductibles Les **objets de prompt** utilisent des LLM comme juges de l’évaluation. Ils conviennent parfaitement aux cas suivants : - Évaluations subjectives nécessitant un jugement comparable à celui d’un humain - Critères complexes difficiles à exprimer sous forme d’algorithme - Tâches de compréhension du langage naturel - Évaluation nuancée du contexte **Définition d’un « objet de prompt » :** au lieu d’une fonction, l’étape est un objet doté de `description` + `createPrompt` (ainsi que de `outputSchema` pour `preprocess`/`analyze`). Cet objet indique à Mastra d’exécuter le LLM juge pour cette étape et de stocker la sortie structurée dans `results.StepResult`. Vous pouvez combiner ces approches au sein d’un même scorer. Par exemple, vous pouvez utiliser une fonction pour prétraiter les données et un LLM pour en analyser la qualité. ## Initialiser un scorer Chaque scorer commence par la fonction de fabrique `createScorer`, qui nécessite un identifiant et une description, et accepte facultativement une spécification de type et une configuration du juge. ```typescript 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(...) ``` La configuration du juge n’est nécessaire que si vous prévoyez d’utiliser des objets de prompt dans au moins une étape. Chaque étape peut remplacer cette configuration par défaut par ses propres paramètres de juge. Si toutes les étapes reposent sur des fonctions, le juge n’est jamais appelé et ne produit aucune sortie. Pour obtenir la sortie du LLM, définissez au moins une étape sous forme d’objet de prompt et lisez le résultat de l’étape correspondante (par exemple, `results.analyzeStepResult`). ### Exemple minimal de juge (objet de prompt) Cet exemple utilise un objet de prompt dans `analyze` : le juge est donc exécuté et sa sortie structurée est disponible dans `results.analyzeStepResult`. ```typescript 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"] } ``` ### Type Agent pour l’évaluation d’un Agent Pour garantir la sûreté des types et la compatibilité avec la notation d’un Agent en direct comme avec celle des traces, utilisez `type: 'agent'` lorsque vous créez des scorers destinés à l’évaluation d’un Agent. Vous pouvez ainsi utiliser le même scorer pour un Agent et pour noter des traces : ```typescript 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 }) ``` ## Détail des étapes ### Étape preprocess (facultative) Prépare les données d’entrée et de sortie lorsque vous devez extraire des éléments précis, filtrer du contenu ou transformer des structures de données complexes. **Fonctions :** `({ run, results }) => any` ```typescript 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) }; }) ``` **Objets de prompt :** utilisez `description`, `outputSchema` et `createPrompt` pour structurer un prétraitement basé sur un LLM. ```typescript 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. ` }) ``` **Flux de données :** les résultats sont accessibles aux étapes suivantes dans `results.preprocessStepResult`. ### Étape analyze (facultative) Effectue l’analyse principale de l’évaluation et recueille les informations qui guideront la décision de notation. **Fonctions :** `({ run, results }) => any` ```typescript 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 }; }) ``` **Objets de prompt :** utilisez `description`, `outputSchema` et `createPrompt` pour effectuer une analyse basée sur un LLM. ```typescript 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). ` }) ``` **Flux de données :** les résultats sont accessibles aux étapes suivantes dans `results.analyzeStepResult`. ### Étape `generateScore` (obligatoire) Convertit les résultats de l’analyse en score numérique. Il s’agit de la seule étape obligatoire du pipeline. **Fonctions :** `({ run, results }) => number` ```typescript 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; }) ``` **Objets de prompt :** consultez la [référence de l’API `createScorer`](https://mastra.zisheng.pro/fr/reference/evals/create-scorer) pour savoir comment utiliser des objets de prompt avec generateScore, notamment la fonction `calculateScore` obligatoire. **Flux de données :** le score est accessible à generateReason par l’intermédiaire du paramètre `score`. ### Étape `generateReason` (facultative) Génère des explications lisibles par un humain concernant le score, utiles pour le débogage, la transparence ou les retours destinés aux utilisateurs. **Fonctions :** `({ run, results, score }) => string` ```typescript 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(', ')}`; } }) ``` **Objets de prompt :** utilisez `description` et `createPrompt` pour générer des explications à l’aide d’un LLM. ```typescript 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. ` }) ``` ## Filtrage des entrées Les conversations d’un Agent peuvent contenir des centaines de messages avec des appels de Tool et des parties de données, ainsi que des métadonnées système. La plupart des scorers n’ont besoin que d’une partie de ces données. L’option `prepareRun` transforme les données d’exécution avant le lancement du pipeline du scorer, ce qui réduit le bruit et permet aux scorers de rester concentrés sur les informations pertinentes. ### Filtrage déclaratif avec `filterRun()` L’utilitaire [`filterRun()`](https://mastra.zisheng.pro/fr/reference/evals/filter-run) crée une fonction `prepareRun` à partir d’options déclaratives : ```typescript 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 }) ``` Les options courantes comprennent : - `partTypes` : conserve uniquement les messages dont le type de partie correspond (par exemple, `'tool-invocation'`, `'text'`, `'reasoning'`) - `toolNames` : conserve uniquement les messages qui font intervenir certains Tools (par exemple, `['write_file', 'execute_command']`) - `maxRememberedMessages` : limite la taille de la fenêtre de contexte - `dropRequestContext`, `dropGroundTruth`, `dropExpectedTrajectory` : supprime les champs inutilisés Consultez la [référence de `filterRun()`](https://mastra.zisheng.pro/fr/reference/evals/filter-run) pour obtenir la liste complète des options. ### Fonctions `prepareRun` personnalisées Pour une logique qui n’est pas prise en charge par `filterRun()`, écrivez directement une fonction `prepareRun` : ```typescript 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 }) ``` La fonction `prepareRun` peut également être asynchrone. > **Les messages système sont toujours conservés:** `filterRun()` ne filtre jamais `systemMessages` ni `taggedSystemMessages`. Ces champs contiennent les instructions de l’Agent et constituent un contexte essentiel pour la notation. ## Exemple : créer un scorer personnalisé Dans Mastra, un scorer personnalisé utilise `createScorer` avec quatre composants principaux : 1. [**Configuration du juge**](#judge-configuration) 2. [**Étape d’analyse**](#analysis-step) 3. [**Génération du score**](#score-generation) 4. [**Génération de la justification**](#reason-generation) Ensemble, ces composants vous permettent de définir une logique d’évaluation personnalisée qui utilise des LLM comme juges. Consultez la [documentation de createScorer](https://mastra.zisheng.pro/fr/reference/evals/create-scorer) pour connaître l’ensemble de l’API et des options de configuration. ```typescript 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, }) }, }) ``` ### Configuration du juge Configure le modèle LLM et définit son rôle d’expert du domaine. ```typescript judge: { model: 'openai/gpt-5-mini', instructions: GLUTEN_INSTRUCTIONS, } ``` ### Étape d’analyse Définit la manière dont le LLM doit analyser l’entrée et la sortie structurée qu’il doit renvoyer. ```typescript .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 }); }, }) ``` L’étape d’analyse utilise un objet de prompt pour : - Fournir une description claire de la tâche d’analyse - Définir la structure de sortie attendue à l’aide d’un schéma JSON standard (à la fois un résultat booléen et une liste de sources de gluten) - Générer à l’exécution des prompts basés sur le contenu de l’entrée ### Génération du score Convertit l’analyse structurée du LLM en score numérique. ```typescript .generateScore(({ results }) => { return results.analyzeStepResult.isGlutenFree ? 1 : 0; }) ``` La fonction de génération du score utilise les résultats de l’analyse et applique une logique métier pour produire un score. Dans ce cas, le LLM détermine directement si la recette est sans gluten. Nous utilisons donc ce résultat booléen : 1 si la recette est sans gluten, 0 si elle en contient. ### Génération de la justification Fournit des explications lisibles par un humain concernant le score au moyen d’un autre appel au LLM. ```typescript .generateReason({ description: 'Generate a reason for the score', createPrompt: ({ results }) => { return generateReasonPrompt({ glutenSources: results.analyzeStepResult.glutenSources, isGlutenFree: results.analyzeStepResult.isGlutenFree, }); }, }) ``` L’étape de génération de la justification crée des explications qui aident les utilisateurs à comprendre pourquoi un score a été attribué. Elle utilise à la fois le résultat booléen et les sources précises de gluten détectées lors de l’étape d’analyse. ## Exemple avec un score élevé pour l’absence de gluten ```typescript 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) ``` ### Sortie avec un score élevé pour l’absence de gluten ```typescript { 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.' } ``` ## Exemple avec présence de gluten ```typescript 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) ``` ### Sortie avec présence de gluten ```typescript { 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.' } ``` ## Exemple avec un faible score pour l’absence de gluten ```typescript 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) ``` ### Sortie avec un faible score pour l’absence de gluten ```typescript { 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.' } ``` **Exemples et ressources :** - [Référence de l’API createScorer](https://mastra.zisheng.pro/fr/reference/evals/create-scorer) : documentation technique complète - [Code source des scorers intégrés](https://github.com/mastra-ai/mastra/tree/main/packages/evals/src/scorers) : implémentations réelles à consulter comme exemples