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’utilisationLien 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 constructeurLien direct vers Paramètres du constructeur
options:
tools:
includeResolvedTools?:
search?:
search.topK?:
search.minScore?:
search.autoLoad?:
storage?:
ttl?:
filter?:
RenvoieLien direct vers Renvoie
id:
name:
processInputStep:
MéthodesLien 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êteLien 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 parsearch_tools.load: empêcheload_toolde 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êteLien 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 :
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 étenduLien direct vers Exemple d’utilisation étendu
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 :
- L’Agent reçoit un message utilisateur
- L’Agent appelle
search_toolsavec des mots-clés (par exemple, "github issue") - L’Agent examine les résultats et appelle
load_toolavec le nom de l’outil - L’outil chargé devient disponible au tour suivant
- L’Agent utilise normalement l’outil chargé
Découverte en une étape avec autoLoadLien 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 :
- L’Agent reçoit un message utilisateur
- L’Agent appelle
search_toolsavec des mots-clés - Les outils correspondants sont activés automatiquement et deviennent disponibles au tour suivant
- 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ésLien 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 ProcessorsLien 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),
],
})