Framework Mastra AI : Construire des agents et workflows intelligents en TypeScript

AI Bot
Par AI Bot ·

Chargement du lecteur de synthèse vocale...

Construisez des agents IA qui agissent vraiment. Mastra est le framework TypeScript-first de l'équipe derrière Gatsby — il vous offre agents, workflows, outils, RAG et évaluations dans une seule stack sans code de liaison. Dans ce tutoriel, vous construirez un assistant de recherche qui cherche sur le web, résume ses trouvailles et rédige des rapports structurés.

Ce que vous allez apprendre

À la fin de ce tutoriel, vous serez capable de :

  • Configurer un projet Mastra de zéro avec TypeScript
  • Créer des agents IA avec des instructions, une configuration de modèle et un accès aux outils
  • Construire des outils personnalisés avec validation des entrées via les schémas Zod
  • Concevoir des workflows multi-étapes sous forme de graphes orientés (DAGs)
  • Implémenter un pipeline RAG basique pour des agents contextuels
  • Tester et évaluer les sorties des agents
  • Déployer votre application Mastra en production

Prérequis

Avant de commencer, assurez-vous d'avoir :

  • Node.js 20+ installé (node --version)
  • Une expérience en TypeScript (types, génériques, async/await)
  • Une clé API OpenAI (ou Anthropic, Google, etc.)
  • Une compréhension basique des LLMs et du prompt engineering
  • Un éditeur de code — VS Code ou Cursor recommandé

Qu'est-ce que Mastra ?

Mastra est un framework TypeScript open-source pour construire des applications alimentées par l'IA. Créé par l'équipe derrière Gatsby (soutenu par Y Combinator), il propose une approche tout-en-un pour construire des applications agentiques avec :

  • Agents — des entités autonomes alimentées par des LLMs qui raisonnent et utilisent des outils
  • Outils (Tools) — des fonctions TypeScript typées que l'agent peut appeler
  • Workflows — des graphes orientés pour les processus multi-étapes
  • RAG — génération augmentée par récupération avec des vector stores
  • Évaluations (Evals) — évaluer la qualité des sorties des agents
  • Mémoire (Memory) — historique des conversations et mémoire sémantique

Contrairement à d'autres frameworks qui nécessitent de la configuration YAML ou du code Python de liaison, Mastra est du TypeScript pur — vous définissez tout en code avec une sécurité de typage complète.


Étape 1 : Créer un nouveau projet Mastra

La façon la plus rapide de démarrer est avec le CLI officiel :

npm create mastra@latest

Suivez les invites :

  • Nom du projet : research-assistant
  • Composants : Sélectionnez Agents, Tools et Workflows
  • Fournisseur de modèle par défaut : OpenAI (ou votre fournisseur préféré)
  • Inclure le code d'exemple : Oui

Cela génère une structure de projet complète :

research-assistant/
├── src/
│   └── mastra/
│       ├── agents/
│       │   └── index.ts
│       ├── tools/
│       │   └── index.ts
│       ├── workflows/
│       │   └── index.ts
│       └── index.ts
├── .env
├── package.json
└── tsconfig.json

Maintenant, installez les dépendances et configurez votre environnement :

cd research-assistant
npm install

Créez votre fichier .env :

OPENAI_API_KEY=sk-your-key-here

Étape 2 : Comprendre le point d'entrée Mastra

La configuration principale se trouve dans src/mastra/index.ts. C'est ici que vous enregistrez les agents, outils et workflows :

import { Mastra } from "@mastra/core";
import { researchAgent } from "./agents";
import { searchTool, summarizeTool } from "./tools";
import { researchWorkflow } from "./workflows";
 
export const mastra = new Mastra({
  agents: { researchAgent },
  workflows: { researchWorkflow },
});

L'instance Mastra est l'orchestrateur central. Elle connecte tous vos composants et expose automatiquement une API REST typée.


Étape 3 : Construire votre premier agent

Un agent est une entité alimentée par un LLM avec des instructions, une configuration de modèle et un accès aux outils. Créons un assistant de recherche.

Modifiez src/mastra/agents/index.ts :

import { Agent } from "@mastra/core/agent";
import { openai } from "@ai-sdk/openai";
import { searchTool, summarizeTool, reportTool } from "../tools";
 
export const researchAgent = new Agent({
  name: "Research Assistant",
  instructions: `You are a thorough research assistant. When given a topic:
    1. Search for relevant, up-to-date information
    2. Summarize key findings into concise points
    3. Generate a structured report with sections and citations
 
    Always verify information from multiple sources when possible.
    Be factual and cite your sources. If unsure, say so.`,
  model: openai("gpt-4o"),
  tools: {
    searchTool,
    summarizeTool,
    reportTool,
  },
});

Propriétés clés de l'agent

PropriétéDescription
nameNom d'affichage de l'agent
instructionsPrompt système guidant le comportement de l'agent
modelLLM à utiliser (supporte OpenAI, Anthropic, Google, etc.)
toolsObjet des outils que l'agent peut invoquer

L'agent raisonnera automatiquement sur les outils à utiliser en fonction de l'entrée de l'utilisateur et des descriptions des outils.


Étape 4 : Créer des outils personnalisés

Les outils sont des fonctions TypeScript qui étendent les capacités de votre agent. Chaque outil a une description (pour que le LLM sache quand l'utiliser), un schéma d'entrée (validé avec Zod) et une fonction d'exécution.

Modifiez src/mastra/tools/index.ts :

import { createTool } from "@mastra/core/tools";
import { z } from "zod";
 
// Outil 1 : Recherche web
export const searchTool = createTool({
  id: "web-search",
  description: "Search the web for information on a given query. Returns relevant results with titles, snippets, and URLs.",
  inputSchema: z.object({
    query: z.string().describe("The search query"),
    maxResults: z.number().default(5).describe("Maximum number of results to return"),
  }),
  execute: async ({ context }) => {
    const { query, maxResults } = context;
 
    // En production, utilisez une vraie API de recherche (Serper, Tavily, etc.)
    const response = await fetch(
      `https://api.tavily.com/search`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          api_key: process.env.TAVILY_API_KEY,
          query,
          max_results: maxResults,
        }),
      }
    );
 
    const data = await response.json();
 
    return {
      results: data.results.map((r: any) => ({
        title: r.title,
        url: r.url,
        snippet: r.content,
      })),
    };
  },
});
 
// Outil 2 : Résumer le texte
export const summarizeTool = createTool({
  id: "summarize-text",
  description: "Summarize a long piece of text into key bullet points.",
  inputSchema: z.object({
    text: z.string().describe("The text to summarize"),
    maxPoints: z.number().default(5).describe("Maximum number of bullet points"),
  }),
  execute: async ({ context }) => {
    const { text, maxPoints } = context;
 
    // Résumé extractif simple — en production, utilisez un appel LLM
    const sentences = text.split(". ").filter((s) => s.length > 20);
    const summary = sentences.slice(0, maxPoints).map((s) => s.trim());
 
    return { summary };
  },
});
 
// Outil 3 : Générer un rapport
export const reportTool = createTool({
  id: "generate-report",
  description: "Generate a structured markdown report from research findings.",
  inputSchema: z.object({
    title: z.string().describe("Report title"),
    sections: z.array(
      z.object({
        heading: z.string(),
        content: z.string(),
        sources: z.array(z.string()).optional(),
      })
    ).describe("Report sections with headings, content, and optional source URLs"),
  }),
  execute: async ({ context }) => {
    const { title, sections } = context;
 
    let markdown = `# ${title}\n\n`;
    markdown += `*Generated on ${new Date().toISOString().split("T")[0]}*\n\n`;
 
    for (const section of sections) {
      markdown += `## ${section.heading}\n\n`;
      markdown += `${section.content}\n\n`;
      if (section.sources?.length) {
        markdown += `**Sources:**\n`;
        for (const source of section.sources) {
          markdown += `- ${source}\n`;
        }
        markdown += "\n";
      }
    }
 
    return { report: markdown };
  },
});

Bonnes pratiques de conception d'outils

  1. Écrivez des descriptions claires — le LLM les lit pour décider quand utiliser chaque outil
  2. Utilisez Zod .describe() sur chaque champ — cela indique au modèle ce que signifie chaque paramètre
  3. Gardez les outils focalisés — un outil, une responsabilité
  4. Gérez les erreurs avec grâce — retournez des messages d'erreur que l'agent peut interpréter
  5. Ajoutez des valeurs par défaut raisonnables — utilisez .default() pour les paramètres optionnels

Étape 5 : Concevoir un workflow multi-étapes

Les workflows vous permettent de définir des pipelines déterministes sous forme de graphes orientés. Contrairement aux décisions des agents (non-déterministes), les workflows garantissent l'ordre d'exécution.

Créez src/mastra/workflows/index.ts :

import { Workflow, Step } from "@mastra/core/workflows";
import { z } from "zod";
import { researchAgent } from "../agents";
 
// Étape 1 : Rechercher un sujet
const researchStep = new Step({
  id: "research",
  inputSchema: z.object({
    topic: z.string(),
    depth: z.enum(["shallow", "deep"]).default("deep"),
  }),
  execute: async ({ context }) => {
    const agent = researchAgent;
    const response = await agent.generate(
      `Research the following topic thoroughly: ${context.topic}.
       Depth level: ${context.depth}.
       Use the search tool to find current information.`
    );
    return { findings: response.text };
  },
});
 
// Étape 2 : Extraire les insights clés
const analyzeStep = new Step({
  id: "analyze",
  inputSchema: z.object({
    findings: z.string(),
  }),
  execute: async ({ context }) => {
    const agent = researchAgent;
    const response = await agent.generate(
      `Analyze these research findings and extract 5-7 key insights:
       ${context.findings}
 
       Format as a JSON array of objects with "insight" and "confidence" fields.`
    );
    return { insights: response.text };
  },
});
 
// Étape 3 : Générer le rapport final
const reportStep = new Step({
  id: "report",
  inputSchema: z.object({
    topic: z.string(),
    insights: z.string(),
  }),
  execute: async ({ context }) => {
    const agent = researchAgent;
    const response = await agent.generate(
      `Create a comprehensive research report on "${context.topic}"
       using these insights: ${context.insights}.
       Use the report tool to generate a structured markdown document.`
    );
    return { report: response.text };
  },
});
 
// Construire le graphe du workflow
export const researchWorkflow = new Workflow({
  name: "research-pipeline",
  triggerSchema: z.object({
    topic: z.string().describe("The topic to research"),
    depth: z.enum(["shallow", "deep"]).default("deep"),
  }),
});
 
researchWorkflow
  .step(researchStep)
  .then(analyzeStep)
  .then(reportStep)
  .commit();

Fonctionnalités des workflows

  • Flux de données typé — la sortie d'une étape alimente la suivante avec une inférence TypeScript complète
  • Exécution parallèle — utilisez .parallel() pour exécuter des étapes indépendantes simultanément
  • Branchement conditionnel — ajoutez .if() / .else() pour des chemins dynamiques
  • Gestion des erreurs — chaque étape peut définir une logique de retry et des fallbacks
  • Suspension — les workflows peuvent se mettre en pause et attendre une intervention humaine

Étape 6 : Exécuter votre agent

Testons l'agent de manière interactive. Créez un script de test src/test.ts :

import { mastra } from "./mastra";
 
async function main() {
  // Récupérer l'agent
  const agent = mastra.getAgent("researchAgent");
 
  // Génération de texte simple
  const response = await agent.generate(
    "What are the latest developments in quantum computing in 2026?"
  );
  console.log(response.text);
 
  // Réponse en streaming
  const stream = await agent.stream(
    "Compare the top 3 TypeScript AI frameworks in 2026"
  );
  for await (const chunk of stream.textStream) {
    process.stdout.write(chunk);
  }
}
 
main().catch(console.error);

Exécutez-le :

npx tsx src/test.ts

Vous devriez voir l'agent raisonner sur votre requête, appeler les outils selon les besoins et produire une réponse structurée.


Étape 7 : Exécuter le workflow

Testez le pipeline de recherche complet :

import { mastra } from "./mastra";
 
async function runWorkflow() {
  const workflow = mastra.getWorkflow("researchWorkflow");
 
  const result = await workflow.execute({
    triggerData: {
      topic: "The state of WebAssembly in 2026",
      depth: "deep",
    },
  });
 
  // Accéder aux résultats de chaque étape
  console.log("Research:", result.results.research);
  console.log("Analysis:", result.results.analyze);
  console.log("Report:", result.results.report);
}
 
runWorkflow().catch(console.error);

Étape 8 : Ajouter RAG pour des agents contextuels

RAG (Retrieval-Augmented Generation) permet à votre agent de répondre aux questions en se basant sur vos propres documents. Mastra s'intègre avec des vector stores comme pgvector, Pinecone et Qdrant.

Installez les dépendances RAG :

npm install @mastra/rag @ai-sdk/openai

Créez un pipeline RAG :

import { embed, embedMany } from "ai";
import { openai } from "@ai-sdk/openai";
import { PgVector } from "@mastra/rag";
 
// 1. Initialiser le vector store
const vectorStore = new PgVector({
  connectionString: process.env.DATABASE_URL!,
});
 
// 2. Créer des embeddings à partir de vos documents
const documents = [
  "Mastra is a TypeScript framework for building AI agents...",
  "Workflows in Mastra are directed acyclic graphs...",
  "Tools extend agent capabilities with custom functions...",
];
 
const { embeddings } = await embedMany({
  model: openai.embedding("text-embedding-3-small"),
  values: documents,
});
 
// 3. Stocker les embeddings dans la base de données vectorielle
await vectorStore.upsert({
  indexName: "docs",
  vectors: embeddings.map((embedding, i) => ({
    id: `doc-${i}`,
    values: embedding,
    metadata: { text: documents[i] },
  })),
});
 
// 4. Interroger les documents similaires
const { embedding: queryEmbedding } = await embed({
  model: openai.embedding("text-embedding-3-small"),
  value: "How do Mastra workflows work?",
});
 
const results = await vectorStore.query({
  indexName: "docs",
  queryVector: queryEmbedding,
  topK: 3,
});

Puis créez un outil RAG pour votre agent :

import { createTool } from "@mastra/core/tools";
import { z } from "zod";
import { embed } from "ai";
import { openai } from "@ai-sdk/openai";
 
export const ragSearchTool = createTool({
  id: "search-knowledge-base",
  description: "Search the internal knowledge base for relevant information.",
  inputSchema: z.object({
    query: z.string().describe("What to search for in the knowledge base"),
  }),
  execute: async ({ context }) => {
    const { embedding } = await embed({
      model: openai.embedding("text-embedding-3-small"),
      value: context.query,
    });
 
    const results = await vectorStore.query({
      indexName: "docs",
      queryVector: embedding,
      topK: 5,
    });
 
    return {
      documents: results.map((r) => ({
        text: r.metadata?.text,
        score: r.score,
      })),
    };
  },
});

Étape 9 : Ajouter la mémoire de l'agent

La mémoire donne du contexte à votre agent à travers les conversations. Mastra supporte à la fois l'historique des conversations et la mémoire sémantique :

import { Agent } from "@mastra/core/agent";
import { Memory } from "@mastra/memory";
import { openai } from "@ai-sdk/openai";
 
const memory = new Memory({
  // Stocke les messages de conversation récents
  options: {
    lastMessages: 20,
    semanticRecall: {
      topK: 3,
      messageRange: 50,
    },
  },
});
 
export const assistantAgent = new Agent({
  name: "Assistant",
  instructions: "You are a helpful assistant with memory of past conversations.",
  model: openai("gpt-4o"),
  memory,
});

Lors de l'utilisation de la mémoire, passez un threadId pour maintenir le contexte de la conversation :

// Premier message
const res1 = await assistantAgent.generate("My name is Sarah", {
  threadId: "user-123",
});
 
// Message ultérieur — l'agent se souvient
const res2 = await assistantAgent.generate("What's my name?", {
  threadId: "user-123",
});
// Sortie : "Your name is Sarah"

Étape 10 : Évaluer la qualité de l'agent

Mastra inclut des outils d'évaluation intégrés pour mesurer la qualité des sorties de l'agent :

import { Agent } from "@mastra/core/agent";
import { openai } from "@ai-sdk/openai";
 
const agent = new Agent({
  name: "Evaluated Agent",
  instructions: "Answer questions accurately and concisely.",
  model: openai("gpt-4o"),
  evals: {
    completeness: {
      type: "model-graded",
      prompt: "Rate if the response fully addresses the question (0-1):",
    },
    conciseness: {
      type: "model-graded",
      prompt: "Rate the conciseness of the response (0-1):",
    },
    hallucination: {
      type: "model-graded",
      prompt: "Rate if the response contains made-up information (0=hallucinated, 1=factual):",
    },
  },
});

Étape 11 : Déployer avec le serveur Mastra

Mastra génère automatiquement une API REST pour vos agents et workflows. Lancez le serveur de développement :

npx mastra dev

Cela expose des endpoints comme :

POST /api/agents/researchAgent/generate
POST /api/agents/researchAgent/stream
POST /api/workflows/researchWorkflow/execute

Pour la production, construisez et déployez :

npx mastra build
npx mastra deploy

Mastra supporte le déploiement sur :

  • Serveurs Node.js (Express, Fastify)
  • Cloudflare Workers
  • Vercel / Netlify serverless
  • Conteneurs Docker

Utiliser plusieurs fournisseurs de modèles

Mastra fonctionne avec tout fournisseur de modèle compatible AI SDK. Vous pouvez mixer et combiner :

import { openai } from "@ai-sdk/openai";
import { anthropic } from "@ai-sdk/anthropic";
import { google } from "@ai-sdk/google";
 
// Utiliser différents modèles pour différents agents
const fastAgent = new Agent({
  name: "Fast Responder",
  instructions: "Quick, concise answers.",
  model: openai("gpt-4o-mini"),
  tools: { searchTool },
});
 
const deepAgent = new Agent({
  name: "Deep Analyst",
  instructions: "Thorough, detailed analysis.",
  model: anthropic("claude-sonnet-4-20250514"),
  tools: { searchTool, summarizeTool },
});
 
const visionAgent = new Agent({
  name: "Image Analyzer",
  instructions: "Analyze and describe images.",
  model: google("gemini-2.0-flash"),
  tools: {},
});

Bonnes pratiques de structure de projet

Pour les applications de production, organisez votre code ainsi :

src/mastra/
├── agents/
│   ├── research-agent.ts
│   ├── writing-agent.ts
│   └── index.ts          # Réexporte tous les agents
├── tools/
│   ├── search.ts
│   ├── summarize.ts
│   ├── report.ts
│   └── index.ts          # Réexporte tous les outils
├── workflows/
│   ├── research-pipeline.ts
│   ├── content-pipeline.ts
│   └── index.ts          # Réexporte tous les workflows
└── index.ts              # Instance Mastra

Dépannage

Problèmes courants

Erreurs "Tool not found" : Assurez-vous que les outils sont passés à l'objet tools de l'agent, pas simplement importés. L'agent n'a accès qu'aux outils explicitement listés dans sa configuration.

Les étapes du workflow ne reçoivent pas les données : Vérifiez que le schéma de sortie d'une étape correspond au schéma d'entrée de la suivante. Utilisez .parse() de Zod pour déboguer les incompatibilités de types.

La mémoire ne persiste pas : Assurez-vous de passer le même threadId à travers les messages liés. Chaque fil maintient son propre historique de conversation.

Limites de débit avec les fournisseurs de modèles : Ajoutez une logique de retry à vos outils et envisagez d'utiliser différents modèles pour différentes tâches (par exemple, GPT-4o-mini pour les appels d'outils simples, Claude pour l'analyse approfondie).


Prochaines étapes

Maintenant que vous avez une application Mastra fonctionnelle, envisagez d'explorer :

  • Documentation Mastra — référence API complète et guides
  • Systèmes multi-agents — créez des agents qui délèguent des tâches à d'autres agents
  • Intégrations personnalisées — connectez-vous à des bases de données, APIs et services tiers
  • Observabilité en production — ajoutez du tracing avec OpenTelemetry pour le débogage
  • Agents vocaux — ajoutez des capacités de transcription et de synthèse vocale

Conclusion

Mastra apporte structure et sécurité de typage au développement d'applications IA. Au lieu de connecter des outils disparates avec du code de liaison, vous obtenez un framework unifié où agents, outils, workflows, RAG et évaluations fonctionnent ensemble de manière transparente en TypeScript.

Les points clés de ce tutoriel :

  1. Les agents sont l'unité centrale — ils raisonnent sur les objectifs et utilisent les outils
  2. Les outils étendent les capacités des agents avec des fonctions validées et typées
  3. Les workflows fournissent des pipelines déterministes multi-étapes
  4. Le RAG ancre les réponses des agents dans vos propres données
  5. La mémoire maintient le contexte à travers les conversations
  6. Les évaluations garantissent que la qualité des sorties reste élevée

Avec Mastra, vous pouvez passer du prototype à une application IA prête pour la production sans quitter l'écosystème TypeScript. Commencez simplement avec un seul agent et un outil, puis montez en puissance vers des workflows multi-agents à mesure que vos besoins grandissent.


Vous voulez lire plus de tutoriels? Découvrez notre dernier tutoriel sur Construire une application web full-stack avec SolidStart : guide pratique complet.

Discutez de votre projet avec nous

Nous sommes ici pour vous aider avec vos besoins en développement Web. Planifiez un appel pour discuter de votre projet et comment nous pouvons vous aider.

Trouvons les meilleures solutions pour vos besoins.

Articles connexes

Construire un Agent IA Autonome avec Agentic RAG et Next.js

Apprenez a construire un agent IA qui decide de maniere autonome quand et comment recuperer des informations depuis des bases de donnees vectorielles. Un guide pratique complet avec Vercel AI SDK et Next.js, accompagne d'exemples executables.

30 min read·