Tâches en arrière-plan
Ajouté dans : @mastra/core@1.29.0
Les tâches en arrière-plan permettent à un agent de lancer un appel d’outil de longue durée sans bloquer la boucle agentique. L’outil renvoie immédiatement un accusé de réception, le LLM poursuit sa réponse et la tâche s’exécute jusqu’à son terme en arrière-plan. Lorsqu’elle se termine, son résultat est écrit dans la mémoire. Si vous utilisez stream() avec l’option untilIdle, l’agent est automatiquement invoqué de nouveau afin que le résultat soit traité au cours du même appel.
Quand utiliser les tâches en arrière-planLien direct vers Quand utiliser les tâches en arrière-plan
Utilisez les tâches en arrière-plan lorsqu’un appel d’outil risque de durer assez longtemps pour que l’utilisateur ne doive pas attendre sa fin avant de voir une réponse. Cas courants :
- Délégations à des sous-agents qui effectuent eux-mêmes des recherches ou des travaux de rédaction en plusieurs étapes.
- Appels d’outils qui sollicitent des services externes lents, des files d’attente ou des traitements portant sur de grands volumes de données.
- Workflows déclenchés par un appel d’outil dont l’exécution peut prendre plusieurs minutes.
Pour les appels d’outils qui renvoient rapidement un résultat, une exécution au premier plan avec agent.stream() et agent.generate() est plus simple.
Les tâches en arrière-plan nécessitent un système de stockage configuré sur l’instance Mastra. Les tâches sont conservées afin de survivre aux redémarrages du processus.
Démarrage rapideLien direct vers Démarrage rapide
Les tâches en arrière-plan sont désactivées par défaut. Activez-les en définissant backgroundTasks.enabled sur l’instance Mastra :
import { Mastra } from '@mastra/core'
import { LibSQLStore } from '@mastra/libsql'
export const mastra = new Mastra({
storage: new LibSQLStore({ id: 'storage', url: 'file:mastra.db' }),
backgroundTasks: {
enabled: true,
globalConcurrency: 10,
perAgentConcurrency: 5,
backpressure: 'queue',
defaultTimeoutMs: 300_000,
},
})
La liste complète des options figure dans la référence de configuration de backgroundTasks.
Exécuter un outil en arrière-planLien direct vers Exécuter un outil en arrière-plan
L’activation du gestionnaire ne lance à elle seule aucune exécution en arrière-plan, car tous les outils s’exécutent par défaut au premier plan. L’activation des outils s’effectue à l’un des deux niveaux suivants :
- Configuration au niveau de l’outil : l’outil se déclare lui-même compatible avec l’exécution en arrière-plan.
- Configuration au niveau de l’agent : l’agent déclare lesquels de ses outils sont compatibles avec l’exécution en arrière-plan.
Une fois l’outil activé, le LLM peut ajouter un champ _background aux arguments de l’outil afin de remplacer facultativement la configuration résolue pour un appel donné : délai d’expiration, nouvelles tentatives ou retour de l’appel au premier plan.
Au niveau de l’outilLien direct vers Au niveau de l’outil
Définissez background.enabled: true dans la définition de l’outil. Les outils activés à ce niveau s’exécutent en arrière-plan chaque fois qu’ils sont appelés par un agent dont le gestionnaire est activé.
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'
export const researchTool = createTool({
id: 'research',
description: 'Run a long research job',
inputSchema: z.object({ topic: z.string() }),
background: {
enabled: true,
timeoutMs: 600_000,
maxRetries: 1,
},
execute: async ({ topic }) => {
// Run the research job for topic
},
})
Au niveau de l’agentLien direct vers Au niveau de l’agent
Utilisez backgroundTasks.tools sur l’agent pour activer certains outils, remplacer le délai d’expiration de chaque outil ou, à l’inverse, exécuter en arrière-plan tous les outils compatibles. Utilisez disabled: true pour désactiver entièrement le lancement en arrière-plan pour cet agent.
import { Agent } from '@mastra/core/agent'
export const researcher = new Agent({
id: 'researcher',
instructions: 'You research topics and answer questions.',
model: 'openai/gpt-5.6-sol',
tools: { researchTool, summarizeTool },
backgroundTasks: {
tools: {
researchTool: { enabled: true, timeoutMs: 600_000 },
summarizeTool: false,
},
},
})
Définissez tools: 'all' pour activer tous les outils de l’agent.
Remplacement par le LLM pour chaque appelLien direct vers Remplacement par le LLM pour chaque appel
Lorsqu’un outil est enregistré auprès d’un agent dont les tâches en arrière-plan sont activées, le modèle peut inclure un champ _background dans les arguments de l’outil afin de remplacer la configuration résolue pour cet appel précis. Le modèle n’inclut que les éléments qu’il souhaite remplacer ; tous les champs de _background sont facultatifs. Ce remplacement est retiré des arguments avant l’exécution de l’outil.
{
"topic": "solana",
"_background": { "enabled": true, "timeoutMs": 900_000 }
}
Le remplacement _background est un modificateur applicable aux outils que le développeur a déjà activés au niveau de l’outil ou de l’agent ; il ne constitue pas une activation autonome. Si un outil n’a pas été activé, la valeur _background.enabled: true fournie par le modèle est ignorée et l’outil s’exécute au premier plan. Cela évite que des outils déterministes, réservés au premier plan — calculatrices, recherches ou validateurs de schéma — soient discrètement lancés comme tâches.
Ordre de résolutionLien direct vers Ordre de résolution
Lorsqu’un appel d’outil est lancé, la configuration d’arrière-plan résolue est calculée selon l’ordre de priorité suivant :
- Entrée
backgroundTasks.toolsde l’outil au niveau de l’agent. - Configuration
backgroundau niveau de l’outil. - Remplacement
_background.enableddu LLM, utilisé uniquement pour activer le lancement en arrière-plan lorsque l’outil a été activé à l’un des niveaux précédents. - Valeurs par défaut du gestionnaire (
defaultTimeoutMs,defaultRetries).
Si l’agent possède backgroundTasks.disabled: true, chaque appel d’outil s’exécute de manière synchrone, quels que soient les niveaux précédents.
Fragments de diffusion liés aux tâches en arrière-planLien direct vers Fragments de diffusion liés aux tâches en arrière-plan
Lorsqu’un appel d’outil est lancé comme tâche en arrière-plan, deux flux peuvent exposer ses événements de cycle de vie : le propre flux de l’agent et le flux SSE de backgroundTaskManager.stream(). Chaque flux couvre un ensemble distinct de types de fragments :
| Type de fragment | Moment de l’émission | Émis par |
|---|---|---|
background-task-started | La tâche a été placée dans la file d’attente et un taskId lui a été attribué. | Flux de l’agent |
background-task-running | Un processus de travail a pris en charge la tâche et commencé son exécution. | Flux du gestionnaire |
background-task-progress | Indique le nombre de tâches en arrière-plan en cours d’exécution. | Flux de l’agent |
background-task-output | Fragment de sortie diffusé par la fonction execute de la tâche. | Flux du gestionnaire |
background-task-completed | La tâche s’est terminée correctement. La valeur payload.result correspond au résultat final de l’outil. | Flux du gestionnaire |
background-task-failed | La tâche a levé une erreur ou dépassé son délai d’expiration. | Flux du gestionnaire |
background-task-cancelled | La tâche a été annulée avant son achèvement. | Flux du gestionnaire |
background-task-suspended | L’outil a appelé suspend() depuis sa fonction execute. | Flux du gestionnaire |
background-task-resumed | Une tâche suspendue a été reprise au moyen de manager.resume(taskId, resumeData). | Flux du gestionnaire |
À lui seul, agent.stream().fullStream n’émet que les fragments de la boucle de l’agent (background-task-started, background-task-progress). agent.stream() avec untilIdle: true émet ces deux mêmes fragments, s’abonne en outre au système pub-sub du gestionnaire pour la portée mémoire de l’exécution et achemine les sept fragments du gestionnaire (background-task-running, background-task-output, background-task-completed, background-task-failed, background-task-cancelled, background-task-suspended, background-task-resumed) vers le même fullStream.
backgroundTaskManager.stream() n’émet que les sept fragments du gestionnaire.
La structure complète des charges utiles est décrite dans la référence des fragments de tâches en arrière-plan.
Maintenir le flux de l’agent ouvert avec untilIdleLien direct vers keep-the-agent-stream-open-with-untilidle
agent.stream() renvoie dès que le LLM émet une réponse finale, même si une tâche en arrière-plan est toujours en cours. Transmettez untilIdle: true pour maintenir le flux ouvert jusqu’à ce que toutes les tâches en arrière-plan lancées soient terminées et que le LLM ait pu réagir au résultat :
const stream = await agent.stream('Research solana for me', {
memory: { thread: 't1', resource: 'u1' },
untilIdle: true,
})
for await (const chunk of stream.fullStream) {
// chunks from the initial turn AND any continuation turns triggered by
// background task completions flow through here
}
Lorsqu’une tâche en arrière-plan se termine, son résultat est injecté dans la mémoire de l’agent et stream() réintègre la boucle agentique afin que le LLM puisse y réagir. Le flux se ferme lorsqu’aucune tâche n’est en cours et qu’aucun achèvement n’est en attente.
Pour personnaliser le délai d’inactivité, transmettez un objet plutôt que true. Le minuteur ne s’exécute que lorsque l’enveloppe se trouve entre deux tours ; un premier jeton lent ne fermera donc pas le flux. La valeur par défaut est de 5 minutes :
const stream = await agent.stream('Research solana for me', {
memory: { thread: 't1', resource: 'u1' },
untilIdle: { maxIdleMs: 30_000 },
})
Consultez la référence complète de Agent.stream().
Propriétés agrégéesLien direct vers Propriétés agrégées
stream() avec untilIdle renvoie un MastraModelOutput semblable à celui d’un appel stream() ordinaire, mais seul fullStream couvre le tour initial et toutes les continuations automatiques. Les propriétés agrégées (text, toolCalls, toolResults, finishReason, messageList, getFullOutput()) sont toujours résolues à partir du tampon interne du premier tour. Si vous avez besoin d’une vue agrégée couvrant les continuations, consommez vous-même fullStream et cumulez les données.
Sous-agents en arrière-planLien direct vers Sous-agents en arrière-plan
En interne, les invocations de sous-agents sont lancées sous forme d’appels d’outils ; la même configuration d’arrière-plan s’applique donc. La méthode recommandée consiste à activer chaque sous-agent sur le superviseur : elle est plus claire et permet d’ajuster timeoutMs pour chaque sous-agent depuis un emplacement unique.
import { Agent } from '@mastra/core/agent'
const supervisor = new Agent({
id: 'supervisor',
instructions: 'Coordinate research and writing using the available agents.',
model: 'openai/gpt-5.6-sol',
agents: { researchAgent, writingAgent },
backgroundTasks: {
tools: {
researchAgent: { enabled: true, timeoutMs: 900_000 },
writingAgent: { enabled: true, timeoutMs: 900_000 },
},
},
})
const stream = await supervisor.stream('Research AI in education and write an article', {
memory: { thread: 't1', resource: 'u1' },
untilIdle: true,
})
Héritage depuis le sous-agentLien direct vers Héritage depuis le sous-agent
Si un sous-agent ne figure pas dans backgroundTasks.tools du superviseur, mais possède ses propres outils compatibles avec l’exécution en arrière-plan — au moyen de background.enabled: true au niveau de l’outil ou de sa propre entrée backgroundTasks.tools — l’infrastructure logicielle lance malgré tout l’invocation complète du sous-agent comme tâche en arrière-plan. Le superviseur hérite de l’intention du sous-agent : le sous-agent devient lui-même la tâche en arrière-plan, tandis que ses outils internes s’exécutent au premier plan dans sa boucle.
La configuration d’arrière-plan utilisée pour ce lancement hérité, par exemple waitTimeoutMs, provient de la propre configuration backgroundTasks du sous-agent.
const researchAgent = new Agent({
id: 'research-agent',
description: 'Gathers factual information.',
model: 'openai/gpt-5-mini',
tools: { deepResearchTool },
backgroundTasks: {
tools: {
deepResearchTool: { enabled: true, timeoutMs: 600_000 },
},
waitTimeoutMs: 900_000,
},
})
Lorsque ce researchAgent reçoit une délégation d’un superviseur qui ne possède aucune configuration de tâche en arrière-plan pour researchAgent, le superviseur lance tout de même l’invocation complète de researchAgent comme tâche en arrière-plan. deepResearchTool s’exécute alors au premier plan au sein de cette invocation, au lieu de lancer sa propre tâche en arrière-plan imbriquée.
Utilisez cette méthode lorsque vous souhaitez qu’un sous-agent adopte un comportement cohérent en arrière-plan, quel que soit le superviseur qui l’invoque. Utilisez l’activation côté superviseur décrite précédemment lorsque vous souhaitez ajuster de façon centralisée le comportement en arrière-plan pour chaque superviseur.
Suspendre et reprendreLien direct vers Suspendre et reprendre
Une tâche en arrière-plan peut se mettre en pause en cours d’exécution et attendre un signal externe avant de poursuivre. Cette possibilité est utile pour les validations humaines, les webhooks ou tout processus dont l’étape suivante dépend de données reçues ultérieurement.
Un outil appelle suspend(data) depuis sa fonction execute, ce qui :
- conserve
status: 'suspended'et la charge utiledatadans l’enregistrement de la tâche ; - enregistre l’instantané du workflow afin que l’exécution survive aux redémarrages du processus ;
- émet un fragment
background-task-suspendeddans le flux du gestionnaire ; - libère l’emplacement d’exécution simultanée afin que d’autres tâches puissent s’exécuter.
Reprenez la tâche avec mastra.backgroundTaskManager.resume(taskId, resumeData). Les données resumeData sont transmises aux options execute de l’outil lors de l’exécution reprise, et la tâche repasse à l’état running.
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'
export const reviewTool = createTool({
id: 'review',
description: 'Submit a draft for human review.',
inputSchema: z.object({ draft: z.string() }),
outputSchema: z.object({ approvedBy: z.string(), edits: z.string().optional() }),
background: { enabled: true },
execute: async ({ draft }, context) => {
const { suspend, resumeData } = context.agent
if (!resumeData) {
await suspend?.({ awaiting: 'approval', draft })
return { approvedBy: '', edits: undefined }
}
const { reviewer, edits } = resumeData as { reviewer: string; edits?: string }
return { approvedBy: reviewer, edits }
},
})
Lors de la première invocation de execute, resumeData === undefined est constaté et suspend est appelé. Après la reprise de la tâche, l’environnement d’exécution redémarre l’outil avec resumeData renseigné. La condition if est alors fausse et l’outil renvoie son résultat réel.
Pour reprendre la tâche à la réception d’une validation :
await mastra.backgroundTaskManager?.resume(taskId, {
reviewer: 'alice@example.com',
edits: 'Reworded paragraph 3.',
})
Comportement de la boucle de l’agentLien direct vers Comportement de la boucle de l’agent
Lorsqu’une tâche est suspendue au milieu d’un stream() avec untilIdle, l’enveloppe la considère comme terminée pour l’itération en cours et se ferme. Pour relancer immédiatement l’agent dès que la charge utile de reprise est disponible, appelez agent.resumeStream(resumeData, { runId, toolCallId, memory, untilIdle: true }) : la tâche en arrière-plan reprise s’exécute jusqu’à son terme, son résultat est ajouté à la liste des messages et l’agent effectue un tour de suivi, le tout sur la même connexion SSE. Si vous préférez piloter la reprise hors bande, appelez directement mastra.backgroundTaskManager.resume(taskId, resumeData) ; le résultat sera tout de même écrit dans le fil afin d’être récupéré au prochain tour de l’utilisateur.
Réenregistrer l’exécuteur lors de la repriseLien direct vers Réenregistrer l’exécuteur lors de la reprise
Le gestionnaire conserve les exécuteurs d’outils dans la mémoire du processus. Si le processus redémarre alors qu’une tâche est suspendue, la fermeture de l’exécuteur disparaît ; l’appelant de resume() doit donc d’abord le réenregistrer au moyen de manager.registerTaskContext(taskId, ...). Cette opération n’est pas nécessaire pour les tâches lancées et reprises dans le même processus.
Annuler une tâche suspendueLien direct vers Annuler une tâche suspendue
manager.cancel(taskId) fonctionne sur les tâches suspendues de la même manière que sur celles en cours d’exécution. La ligne passe à l’état cancelled et l’instantané du workflow est supprimé. Un événement task.cancelled est ensuite émis.
Fonctions de rappel du cycle de vieLien direct vers Fonctions de rappel du cycle de vie
Chaque niveau peut enregistrer des fonctions de rappel pour les états finaux. Elles ne se remplacent pas mutuellement et les points d’extension de réussite ou d’échec sont déclenchés selon le résultat :
background.onComplete/onFailedau niveau de l’outil : portée limitée à un seul outil.backgroundTasks.onTaskComplete/onTaskFailedau niveau de l’agent : portée limitée à toutes les tâches lancées par cet agent.onTaskComplete/onTaskFailedau niveau du gestionnaire : portée globale.
export const mastra = new Mastra({
storage,
backgroundTasks: {
enabled: true,
onTaskComplete: task => {
logger.info('Background task complete', { taskId: task.id, toolName: task.toolName })
},
onTaskFailed: task => {
logger.error('Background task failed', { taskId: task.id, error: task.error })
},
},
})
Diffusion en continuLien direct vers Diffusion en continu
S’abonner à tous les événements de tâcheLien direct vers S’abonner à tous les événements de tâche
L’appel de stream() sans filtre renvoie un flux contenant tous les événements de tâche du système. Lors de la connexion, le flux émet un instantané de toutes les tâches en cours d’exécution, puis transmet les événements en direct à mesure qu’ils surviennent.
const bgManager = mastra.backgroundTaskManager
if (!bgManager) throw new Error('Background tasks are not enabled')
const controller = new AbortController()
const stream = bgManager.stream({ abortSignal: controller.signal })
for await (const chunk of stream) {
switch (chunk.type) {
case 'background-task-running':
console.log('started', chunk.payload.taskId, chunk.payload.toolName)
break
case 'background-task-completed':
console.log('done', chunk.payload.taskId, chunk.payload.result)
break
case 'background-task-failed':
console.error('failed', chunk.payload.taskId, chunk.payload.error)
break
}
}
Le flux reste ouvert jusqu’au déclenchement de l’AbortSignal de l’appelant. Transmettez toujours un abortSignal afin de pouvoir vous déconnecter proprement.
Filtrer le fluxLien direct vers Filtrer le flux
Transmettez toute combinaison d’options de filtrage pour restreindre les événements reçus. Les filtres s’appliquent à la fois à l’instantané initial et à l’abonnement aux événements en direct.
const stream = bgManager.stream({
agentId: 'researcher',
threadId: 't1',
resourceId: 'u1',
abortSignal: controller.signal,
})
| Filtre | Description |
|---|---|
agentId | Uniquement les événements des tâches lancées par cet agent |
runId | Uniquement les événements de cette exécution précise de l’agent |
threadId | Uniquement les événements des tâches associées à ce fil de mémoire |
resourceId | Uniquement les événements des tâches associées à cette ressource |
taskId | Uniquement les événements d’une seule tâche |
abortSignal | Ferme le flux lorsque le signal est interrompu |
Consulter directement l’état d’une tâcheLien direct vers Consulter directement l’état d’une tâche
Pour des consultations ponctuelles plutôt qu’un flux en direct, utilisez getTask et listTasks :
const task = await mastra.backgroundTaskManager?.getTask(taskId)
const { tasks, total } = await mastra.backgroundTaskManager?.listTasks({
status: 'running',
agentId: 'researcher',
})
Ces méthodes lisent les données depuis le stockage plutôt que depuis le flux pub-sub ; elles conviennent donc aux listes paginées et aux vues détaillées.