Aller au contenu principal

ToolSearchProcessor

ToolSearchProcessor est un input processor qui permet la découverte et le chargement d’outils définis à l’exécution. Au lieu de fournir tous les outils à l’Agent dès le départ, il lui donne deux méta-outils (search_tools et load_tool) qui lui permettent de trouver et de charger des outils à la demande. Cela réduit l’utilisation de tokens de contexte avec de grandes bibliothèques d’outils.

Exemple d’utilisation
Lien direct vers Exemple d’utilisation

import { ToolSearchProcessor } from '@mastra/core/processors'

const toolSearch = new ToolSearchProcessor({
tools: {
createIssue: githubTools.createIssue,
sendEmail: emailTools.send,
getWeather: weatherTools.forecast,
// ... many more tools
},
search: {
topK: 5,
minScore: 0.1,
},
})

Paramètres du constructeur
Lien direct vers Paramètres du constructeur

options:

ToolSearchProcessorOptions
Options de configuration du processeur de recherche d’outils
ToolSearchProcessorOptions

tools:

Record<string, Tool>
Tous les outils pouvant être recherchés et chargés dynamiquement. Ces outils ne sont pas immédiatement disponibles pour l’Agent : ils doivent être découverts par recherche et chargés à la demande.

includeResolvedTools?:

boolean
Rend également recherchables les outils que l’Agent a résolus pour cette requête (outils MCP nécessitant les identifiants de l’appelant ou tout élément renvoyé par une fonction tools dynamique), et ne les inclut pas dans le prompt avant que l’Agent ne les charge. Les méta-outils ne sont jamais masqués. Les outils résolus par requête sont indexés séparément pour chaque requête ; chacune recherche et charge donc ses propres instances d’outils.

search?:

{ topK?: number; minScore?: number; autoLoad?: boolean }
Configuration du comportement de recherche.

search.topK?:

number
Nombre maximal d’outils à renvoyer dans les résultats de recherche.

search.minScore?:

number
Score de pertinence minimal (0-1) pour inclure un outil dans les résultats de recherche.

search.autoLoad?:

boolean
Lorsque sa valeur est true, les outils renvoyés par search_tools sont activés immédiatement dans le cadre de la recherche. Le méta-outil load_tool n’est pas exposé, ce qui réduit le flux en deux étapes de recherche puis chargement à une seule étape de recherche. Les outils découverts deviennent disponibles au tour suivant. Gardez une valeur topK faible, car chaque correspondance est activée.

storage?:

'in-memory' | 'context'
Emplacement de l’état des outils chargés. 'in-memory' (par défaut) suit les outils chargés dans une map en mémoire par thread, avec nettoyage TTL (voir ttl) ; l’état est perdu au redémarrage et les requêtes anonymes partagent une entrée 'default'. 'context' déduit l’état chargé des messages de conversation : un outil reste chargé tant qu’un résultat search_tools/load_tool qui le nomme reste dans les messages ; ce mode résiste aux redémarrages, ne nécessite aucune mémoire et décharge automatiquement l’outil lorsque ce résultat n’est plus présent dans les messages. Le magasin 'context' est opt-in.

ttl?:

number
Durée de vie de l’état de thread en mémoire, en millisecondes. S’applique uniquement au stockage par défaut, le magasin 'in-memory' ; après cette durée d’inactivité, l’état du thread est nettoyé. Définissez-la sur 0 pour désactiver le nettoyage. Ignorée par le magasin 'context'.

filter?:

(args: ToolSearchFilterArgs) => boolean | Promise<boolean>
Hook facultatif tenant compte de la requête, permettant de masquer des outils des résultats de recherche, de bloquer le chargement d’outils ou de masquer les outils déjà chargés pour la requête actuelle.

Renvoie
Lien direct vers Renvoie

id:

string
Identifiant du Processor défini sur 'tool-search'

name:

string
Nom d’affichage du Processor défini sur 'Tool Search Processor'

processInputStep:

(args: ProcessInputStepArgs) => Promise<ProcessInputStepResult>
Traite chaque étape pour injecter les méta-outils de recherche/chargement et les outils précédemment chargés dans l’ensemble d’outils de l’Agent.

Méthodes
Lien direct vers Méthodes

Inspection de l’état (magasin 'in-memory' hérité)
Lien direct vers state-inspection-legacy-in-memory-store

Ces méthodes fonctionnent uniquement avec le magasin 'in-memory' par défaut. Elles n’ont aucun effet pour le magasin 'context', dont l’état réside dans les messages de conversation plutôt que dans une map du processus.

clearState(threadId)
Lien direct vers clearstatethreadid

Efface l’état des outils chargés pour un seul thread.

processor.clearState('thread-123')

clearAllState()
Lien direct vers clearallstate

Efface l’état des outils chargés pour tous les threads.

processor.clearAllState()

getStateStats()
Lien direct vers getstatestats

Renvoie le nombre de threads suivis et l’heure d’accès la plus ancienne, pour déboguer la croissance de la mémoire.

const { threadCount, oldestAccessTime } = processor.getStateStats()

Renvoie : { threadCount: number; oldestAccessTime: number | null }

cleanupNow()
Lien direct vers cleanupnow

Exécute immédiatement le nettoyage TTL au lieu d’attendre le balayage planifié.

const cleaned = processor.cleanupNow()

Renvoie : number, le nombre de threads nettoyés.

Filtrage tenant compte de la requête
Lien direct vers Filtrage tenant compte de la requête

Utilisez filter pour appliquer une politique propre à la requête aux outils définis à l’exécution. Le hook reçoit l’ID de l’outil résolu sous la forme de toolName, l’outil, le contexte de la requête et la phase. toolName est l’ID renvoyé par search_tools, qui peut différer de la clé utilisée dans l’objet tools.

import { ToolSearchProcessor } from '@mastra/core/processors'

const toolSearch = new ToolSearchProcessor({
tools: allTools,
filter: ({ toolName, requestContext, phase }) => {
const plan = requestContext?.get('plan')

if (phase === 'search') {
return true
}

return plan === 'pro' || !toolName.startsWith('premium_')
},
})

La valeur phase indique où le filtre est appliqué :

  • search : filtre les résultats renvoyés par search_tools.
  • load : empêche load_tool de charger des outils non autorisés.
  • active : masque les outils déjà chargés de la requête actuelle s’ils ne sont plus autorisés.

Si le hook lève une erreur ou rejette, ToolSearchProcessor considère l’outil comme non autorisé pour cette requête. Le hook peut s’exécuter pour chaque candidat correspondant de la recherche ; gardez donc les contrôles de politique asynchrones légers ou mis en cache. Le méta-outil search_tools est toujours disponible. load_tool est disponible sauf si search.autoLoad est activé. Les outils transmis directement via l’Agent ou processInputStep restent disponibles, sauf si vous les filtrez en dehors de ToolSearchProcessor ou activez includeResolvedTools.

Recherche d’outils résolus par requête
Lien direct vers Recherche d’outils résolus par requête

L’option tools est fixée à la construction ; vous ne pouvez donc pas répertorier les outils qui n’existent que par requête (outils MCP nécessitant les identifiants de l’appelant ou tout élément renvoyé par une fonction tools dynamique). Par défaut, ces outils contournent la recherche et occupent de l’espace dans le prompt à chaque tour.

Définissez includeResolvedTools: true pour les indexer pour la requête et ne pas les inclure dans le prompt avant que l’Agent ne les charge :

src/mastra/agents/mcp-agent.ts
import { Agent } from '@mastra/core/agent'
import { ToolSearchProcessor } from '@mastra/core/processors'

const toolSearch = new ToolSearchProcessor({
tools: staticTools,
includeResolvedTools: true,
})

const agent = new Agent({
id: 'mcp-agent',
name: 'mcp-agent',
instructions: 'Search for a tool when you need a capability you do not have.',
model: 'openai/gpt-5.6-sol',
// Resolved per request, then searchable alongside staticTools
tools: async ({ requestContext }) => mcpClient.getTools(requestContext.get('userToken')),
inputProcessors: [toolSearch],
})

Chaque requête est indexée séparément ; un outil chargé par un appelant n’est donc jamais résolu vers l’instance du même nom d’outil d’un autre appelant.

Cette option s’applique à tous les outils résolus pour la requête, y compris les outils de mémoire, de workspace, de skill et de navigateur. Seuls les méta-outils search_tools et load_tool restent dans le prompt ; tout outil sur lequel l’Agent s’appuie implicitement doit donc être trouvé par recherche avant de pouvoir être appelé.

Exemple d’utilisation étendu
Lien direct vers Exemple d’utilisation étendu

src/mastra/agents/dynamic-tools-agent.ts
import { Agent } from '@mastra/core/agent'
import { ToolSearchProcessor } from '@mastra/core/processors'

// Tools from various integrations
import { githubTools } from './tools/github'
import { slackTools } from './tools/slack'
import { dbTools } from './tools/database'

const toolSearch = new ToolSearchProcessor({
tools: {
...githubTools, // createIssue, listPRs, mergePR, ...
...slackTools, // sendMessage, createChannel, ...
...dbTools, // query, insert, update, ...
},
search: {
topK: 5,
minScore: 0.1,
},
})

const agent = new Agent({
id: 'dynamic-tools-agent',
name: 'dynamic-tools-agent',
instructions:
'You are a helpful assistant with access to many tools. Use search_tools to find relevant tools, then load_tool to make them available.',
model: 'openai/gpt-5.6-sol',
inputProcessors: [toolSearch],
})

Le flux de travail de l’Agent est le suivant :

  1. L’Agent reçoit un message utilisateur
  2. L’Agent appelle search_tools avec des mots-clés (par exemple, "github issue")
  3. L’Agent examine les résultats et appelle load_tool avec le nom de l’outil
  4. L’outil chargé devient disponible au tour suivant
  5. L’Agent utilise normalement l’outil chargé

Découverte en une étape avec autoLoad
Lien direct vers single-step-discovery-with-autoload

Définissez search.autoLoad sur true pour ignorer l’étape de chargement distincte. Les outils renvoyés par search_tools sont activés immédiatement et le méta-outil load_tool n’est pas exposé. Cela supprime un tour de modèle par découverte, réduit l’utilisation de tokens et la latence, et fonctionne de la même manière avec tous les Providers.

const toolSearch = new ToolSearchProcessor({
tools: allTools,
search: {
topK: 3,
autoLoad: true,
},
})

Avec autoLoad, le flux de travail devient :

  1. L’Agent reçoit un message utilisateur
  2. L’Agent appelle search_tools avec des mots-clés
  3. Les outils correspondants sont activés automatiquement et deviennent disponibles au tour suivant
  4. L’Agent utilise normalement l’outil

Chaque correspondance est activée ; gardez donc topK faible (par exemple, 3) afin d’éviter d’ajouter des outils dont l’Agent n’a pas besoin. Les outils activés sont ajoutés après les outils existants, ce qui maintient stable le préfixe du prompt mis en cache pour les Providers qui prennent en charge la mise en cache des prompts.

Stockage des outils chargés
Lien direct vers Stockage des outils chargés

L’option storage contrôle l’emplacement où l’ensemble des outils chargés est suivi. La valeur par défaut est 'in-memory'. Le magasin 'context' est opt-in.

'in-memory' (par défaut)
Lien direct vers in-memory-default

Les outils chargés sont suivis dans une map en mémoire par thread, avec un nettoyage fondé sur TTL contrôlé par l’option ttl (une heure par défaut). Il s’agit du comportement initial :

  • Ne nécessite aucune configuration de mémoire.
  • L’état est perdu lors du redémarrage du processus.
  • Les requêtes sans ID de thread partagent une seule entrée 'default'.

Utilisez clearState, clearAllState, getStateStats et cleanupNow pour inspecter ou réinitialiser ce magasin.

'context'
Lien direct vers context

L’état chargé est déduit des messages de conversation : un outil est chargé tant qu’un résultat search_tools ou load_tool qui le nomme reste dans les messages. Ce mode :

  • Ne nécessite aucune configuration de mémoire.
  • Résiste aux redémarrages : l’enregistrement durable est l’historique des messages persisté.
  • Décharge automatiquement un outil lorsque ce résultat n’est plus présent dans les messages.
import { ToolSearchProcessor } from '@mastra/core/processors'

const toolSearch = new ToolSearchProcessor({
tools: allTools,
storage: 'context',
})

Le chargement d’outils est favorable au cache dans les deux modes : les chargements se font uniquement par ajout, de sorte que le préfixe du prompt mis en cache reste stable pour les Providers qui prennent en charge la mise en cache des prompts.

Le déchargement d’un outil modifie les définitions d’outils envoyées au modèle, ce qui décale le préfixe mis en cache et fait que le tour suivant effectue une écriture de cache plutôt qu’une lecture réussie. En mode 'in-memory', cela se produit lorsque l’état d’un thread est évincé par ttl. En mode 'context', cela se produit lorsqu’un résultat de découverte de l’outil n’est plus présent dans les messages (par exemple, lorsque les messages les plus anciens sont tronqués). L’outil est déchargé et le modèle doit le rechercher à nouveau avant de le réutiliser. C’est le comportement attendu : supprimer un outil inutilisé échange une écriture de cache contre un préfixe plus petit lors des tours suivants.

Combinaison avec d’autres Processors
Lien direct vers Combinaison avec d’autres Processors

import { Agent } from '@mastra/core/agent'
import { ToolSearchProcessor, TokenLimiter } from '@mastra/core/processors'

const agent = new Agent({
id: 'my-agent',
name: 'my-agent',
model: 'openai/gpt-5.6-sol',
inputProcessors: [
new ToolSearchProcessor({
tools: allTools,
search: { topK: 5 },
}),
// Place TokenLimiter last to ensure context fits
new TokenLimiter(127000),
],
})