Aller au contenu principal

Recherche et indexation

Ajouté dans : @mastra/core@1.1.0

La recherche permet aux agents de trouver du contenu pertinent dans les fichiers indexés du workspace. Lorsqu'un agent doit répondre à une question ou trouver une information, il peut effectuer une recherche dans le contenu indexé au lieu de lire chaque fichier.

Fonctionnement
Lien direct vers Fonctionnement

La recherche dans un workspace comporte deux phases : l'indexation et l'interrogation.

Indexation
Lien direct vers Indexation

Le contenu doit être indexé avant de pouvoir faire l'objet d'une recherche. Lorsque vous indexez un document :

  • le contenu est tokenisé (divisé en termes interrogeables) ;
  • pour BM25, les fréquences des termes et les statistiques du document sont calculées ;
  • pour la recherche vectorielle, le contenu est converti en embedding à l'aide de votre fonction d'embedding, puis stocké dans le magasin vectoriel.

Chaque document indexé contient :

  • id : un identifiant unique (généralement le chemin du fichier) ;
  • content : le contenu textuel ;
  • metadata : des données clé-valeur facultatives stockées avec le document.

Interrogation
Lien direct vers Interrogation

Lorsque vous effectuez une recherche :

  1. La requête est traitée avec la même tokenisation ou le même embedding que lors de l'indexation.
  2. Un score est attribué aux documents en fonction de leur pertinence par rapport à la requête.
  3. Les résultats sont classés par score et renvoyés avec le contenu correspondant.

Les workspaces prennent en charge trois modes de recherche : la recherche BM25 par mots-clés, la recherche sémantique vectorielle et la recherche hybride, qui combine les deux précédentes.

BM25 attribue un score aux documents en fonction de la fréquence des termes et de la longueur du document. Ce mode est particulièrement adapté aux correspondances exactes et à la terminologie spécifique.

src/mastra/workspaces.ts
import { Workspace, LocalFilesystem } from '@mastra/core/workspace'

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
bm25: true,
})

Pour personnaliser les paramètres BM25 (k1 correspond à la saturation de la fréquence des termes et b à la normalisation de la longueur du document) :

src/mastra/workspaces.ts
const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
bm25: {
k1: 1.5,
b: 0.75,
},
})

La recherche vectorielle utilise des embeddings pour trouver du contenu sémantiquement similaire. Elle nécessite un magasin vectoriel et une fonction d'embedding.

src/mastra/workspaces.ts
import { Workspace, LocalFilesystem } from '@mastra/core/workspace'
import { PineconeVector } from '@mastra/pinecone'
import { embed } from 'ai'
import { openai } from '@ai-sdk/openai'

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
vectorStore: new PineconeVector({
apiKey: process.env.PINECONE_API_KEY,
index: 'workspace-index',
}),
embedder: async (text: string) => {
const { embedding } = await embed({
model: openai.embedding('text-embedding-3-small'),
value: text,
})
return embedding
},
})

Embedding par lots
Lien direct vers Embedding par lots

La fonction d'embedding ci-dessus traite un seul texte à la fois. Indexer un workspace contenant des centaines de fichiers appelle le fournisseur des centaines de fois, ce qui est lent et coûteux.

Lorsque le fournisseur prend en charge le traitement par lots (par exemple, avec embedMany d'OpenAI), transmettez une fonction d'embedding qui reçoit un tableau de textes et renvoie plusieurs embeddings en un seul appel. Pour activer cette fonctionnalité, définissez une propriété batch: true sur la fonction. Mastra vérifie la présence de cette propriété lors de l'exécution et utilise alors le traitement par lots.

L'exemple suivant remplace la fonction d'embedding traitant un seul texte par une version par lots. Cette fonction reçoit un tableau et renvoie un tableau d'embeddings dans le même ordre. Elle possède également deux propriétés supplémentaires :

  • batch: true : indique que la fonction peut traiter des lots. Sans cette propriété, Mastra l'appelle avec un seul texte à la fois.
  • maxBatchSize : taille maximale du tableau que le fournisseur accepte en un seul appel. Mastra divise les requêtes plus volumineuses en lots de cette taille et les envoie en parallèle. Définissez cette valeur sur la limite documentée par votre fournisseur (par exemple, 2048 pour OpenAI, 96 pour Cohere ou 128 pour Voyage). Omettez-la pour envoyer tous les textes en attente dans une seule requête.
src/mastra/workspaces.ts
import { Workspace, LocalFilesystem } from '@mastra/core/workspace'
import { PineconeVector } from '@mastra/pinecone'
import { embedMany } from 'ai'
import { openai } from '@ai-sdk/openai'

const model = openai.embedding('text-embedding-3-small')

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
vectorStore: new PineconeVector({
apiKey: process.env.PINECONE_API_KEY,
index: 'workspace-index',
}),
embedder: Object.assign(
async (texts: string[]) => {
const { embeddings } = await embedMany({ model, values: texts })
return embeddings
},
{ batch: true as const, maxBatchSize: 2048 },
),
})

Object.assign ajoute les propriétés batch et maxBatchSize à la fonction d'embedding. Mastra les lit comme des métadonnées et ne les transmet jamais au fournisseur.

Les fonctions d'embedding qui traitent un seul texte continuent de fonctionner. La signature de fonction (text: string) => Promise<number[]> reste inchangée, de sorte que le code existant continue de s'exécuter sans modification.

Configurez à la fois BM25 et la recherche vectorielle pour activer le mode hybride, qui associe la correspondance par mots-clés à la compréhension sémantique.

src/mastra/workspaces.ts
const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
bm25: true,
vectorStore: pineconeVector,
embedder: embedderFn,
})

Nom d'index personnalisé
Lien direct vers Nom d'index personnalisé

Par défaut, le nom de l'index de recherche est dérivé de l'identifiant du workspace. Pour définir un nom personnalisé, utilisez searchIndexName :

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
bm25: true,
searchIndexName: 'my_workspace_vectors',
})

Le nom de l'index doit être un identifiant SQL valide : il doit commencer par une lettre ou un trait de soulignement, contenir uniquement des lettres, des chiffres ou des traits de soulignement et ne pas dépasser 63 caractères.

Indexer du contenu
Lien direct vers Indexer du contenu

Indexation manuelle
Lien direct vers Indexation manuelle

Utilisez workspace.index() pour ajouter du contenu à l'index de recherche par programmation. Les chemins de fichiers deviennent les identifiants des documents. Vous pouvez également transmettre des métadonnées pour chaque document.

// Basic indexing
await workspace.index('/docs/guide.md', 'Content of the guide...')

// Index with metadata for filtering or context
await workspace.index('/docs/api.md', apiDocContent, {
metadata: {
category: 'api',
version: '2.0',
},
})

L'indexation manuelle est utile lorsque :

  • vous indexez du contenu qui ne provient pas de fichiers (par exemple, des enregistrements de base de données ou des réponses d'API) ;
  • vous souhaitez prétraiter ou découper le contenu avant de l'indexer ;
  • vous devez ajouter des métadonnées personnalisées aux documents.

Indexation automatique
Lien direct vers Indexation automatique

Configurez autoIndexPaths pour indexer automatiquement les fichiers lors de l'initialisation du workspace. Chaque entrée peut être un chemin de répertoire (indexé récursivement) ou un motif glob pour une indexation sélective.

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
bm25: true,
autoIndexPaths: ['docs', 'support/faq'],
})

await workspace.init()

Lorsque init() est appelé, tous les fichiers correspondants sont lus et indexés pour la recherche. Le chemin du fichier devient l'identifiant du document.

Les motifs glob permettent d'indexer des types de fichiers précis :

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
bm25: true,
autoIndexPaths: ['docs/**/*.md', 'support/**/*.txt'],
})

Effectuer une recherche
Lien direct vers Effectuer une recherche

Utilisez workspace.search() pour trouver du contenu pertinent. Les résultats sont classés par score de pertinence.

const results = await workspace.search('password reset')

for (const result of results) {
console.log(`${result.id}: ${result.score}`)
console.log(result.content)
}

Options de recherche
Lien direct vers Options de recherche

Vous pouvez personnaliser le comportement de la recherche à l'aide d'options :

const results = await workspace.search('authentication flow', {
topK: 10,
mode: 'hybrid',
minScore: 0.5,
vectorWeight: 0.5,
})
OptionDescription
topKNombre maximal de résultats à renvoyer. Valeur par défaut : 5.
modeMode de recherche : 'bm25', 'vector' ou 'hybrid'. Utilise par défaut le meilleur mode disponible selon la configuration.
minScoreExclut les résultats dont le score est inférieur à ce seuil (0 à 1).
vectorWeightEn mode hybride, pondération des scores vectoriels par rapport à BM25. 0 = uniquement BM25, 1 = uniquement la recherche vectorielle, 0,5 = pondération égale.

Résultats de recherche
Lien direct vers Résultats de recherche

Chaque résultat contient :

interface SearchResult {
id: string // Document ID (typically file path)
content: string // The matching content
score: number // Relevance score (0-1)
lineRange?: {
// Lines where the match was found
start: number
end: number
}
metadata?: Record<string, unknown> // Metadata stored with the document
scoreDetails?: {
// Score breakdown (hybrid mode only)
vector?: number
bm25?: number
}
}

Comprendre les scores :

  • Les scores sont compris entre 0 et 1, où 1 correspond à une concordance parfaite.
  • Les scores BM25 sont normalisés par rapport à la meilleure concordance de l'ensemble de résultats.
  • Les scores vectoriels représentent la similarité cosinus entre les embeddings de la requête et ceux du document.
  • En mode hybride, les scores sont combinés à l'aide du paramètre vectorWeight.

Quand utiliser chaque mode
Lien direct vers Quand utiliser chaque mode

ModeIdéal pourExemples de requêtes
bm25Termes exacts, requêtes techniques, code« hook useState », « erreur 404 », « config.yaml »
vectorRequêtes conceptuelles, langage naturel« comment gérer l'authentification des utilisateurs », « bonnes pratiques de gestion des erreurs »
hybridRecherche générale, types de requêtes inconnusLa plupart des cas d'usage des agents

Outils des agents
Lien direct vers Outils des agents

Lorsque vous configurez la recherche sur un workspace, les agents reçoivent des outils pour rechercher et indexer du contenu. Consultez la référence de la classe Workspace pour plus de détails.