Prétraitement des requêtes
Le prétraitement des requêtes améliore celles des utilisateurs avant la phase de recherche. Des techniques comme la reformulation et l'expansion peuvent améliorer significativement la qualité de la recherche, même si elles augmentent la latence et le coût.
Réécriture de requête par LLM
Utilise un LLM pour reformuler les requêtes dans des formes plus susceptibles de correspondre au contenu indexé. Convertit le langage informel en requêtes structurées, développe les abréviations et clarifie l'intention.
Installation : Bibliothèque principale (aucune dépendance supplémentaire requise)
Exemple :
from mistralai.search.toolkit.retrieval.pre_processors import LLMQueryRewriter
from mistralai.search.toolkit.llm import MistralChat, LLMConfig
from mistralai.client import Mistral
llm = MistralChat(
client=Mistral(api_key="your-api-key"),
config=LLMConfig(model="mistral-small-latest"),
)
rewriter = LLMQueryRewriter(llm_provider=llm)
# Original: "rag mistral"
rewritten = await rewriter.rewrite("rag mistral")
# Result: "What is Retrieval-Augmented Generation with Mistral AI?"
# Use in QueryEngine
query_engine = QueryEngine(
retriever=vector_retriever,
query_rewriter=rewriter,
)
result = await query_engine.search(query="rag mistral")
# Query is rewritten before retrievalOptions de configuration :
| Option | Type | Valeur par défaut | Objectif |
|---|---|---|---|
llm_provider | LLMProvider | Obligatoire | LLM utilisé pour la reformulation (MistralChat, etc.) |
Prompt de reformulation personnalisé :
from mistralai.search.toolkit.retrieval.pre_processors import LLMQueryRewriter
from mistralai.search.toolkit.llm import MistralChat, LLMConfig
llm = MistralChat(
client=Mistral(api_key="your-api-key"),
config=LLMConfig(model="mistral-small-latest"),
)
# With custom prompt
rewriter = LLMQueryRewriter(
llm_provider=llm,
prompt="Reformulate this query for a technical documentation search engine: ",
)
rewritten = await rewriter.rewrite("how to set up django")Quand l'utiliser :
- Requêtes courtes ou informelles nécessitant une expansion
- Requêtes contenant des acronymes ou abréviations spécifiques à un domaine
- Amélioration du rappel pour les entrées utilisateur vagues
- Conversion de questions en combinaisons de mots-clés
Considérations de coût :
- 1 appel LLM par requête
- Ajoute une latence de 100 à 500 ms
- À utiliser de manière stratégique, pas pour chaque requête
Extension de requête par LLM
Décompose une requête en plusieurs sous-requêtes explorant différents aspects. Chaque sous-requête exécute une recherche indépendante ; les résultats sont combinés pour un rappel plus large.
Installation : Bibliothèque principale (aucune dépendance supplémentaire requise)
Exemple :
from mistralai.search.toolkit.retrieval.pre_processors import LLMQueryExtension
from mistralai.search.toolkit.llm import MistralChat, LLMConfig
from mistralai.client import Mistral
llm = MistralChat(
client=Mistral(api_key="your-api-key"),
config=LLMConfig(model="mistral-small-latest"),
)
extender = LLMQueryExtension(
llm_provider=llm,
num_queries=3, # Generate 3 sub-queries
)
# Original: "How does RAG work?"
extended = await extender.extend("How does RAG work?")
# Results in:
# - "What is RAG?"
# - "How does retrieval work in RAG?"
# - "How does generation work in RAG?"
# Use in QueryEngine
query_engine = QueryEngine(
retriever=vector_retriever,
query_rewriter=extender,
)
result = await query_engine.search(query="How does RAG work?")
# All 3 sub-queries are retrieved and results combinedOptions de configuration :
| Option | Type | Valeur par défaut | Objectif |
|---|---|---|---|
llm_provider | LLMProvider | Obligatoire | LLM utilisé pour l'extension (MistralChat, etc.) |
num_queries | int | 3 | Nombre de sous-requêtes à générer |
Coût et performances :
# num_queries=3 means:
# - 1 LLM call to generate sub-queries
# - 3 retrieval passes (3x slower)
# - 3x embedding calls (for each sub-query)
# - Results combined/deduplicated
# Choose based on tolerance:
extender = LLMQueryExtension(llm_provider=llm, num_queries=2) # Faster, lower recall
extender = LLMQueryExtension(llm_provider=llm, num_queries=5) # Slower, higher recallQuand l'utiliser :
- Requêtes complexes avec plusieurs aspects
- Amélioration du rappel entre différents aspects
- Questions multifacettes nécessitant une couverture large
- Quand la tolérance à la latence permet plusieurs recherches
Meilleure pratique : combiner avec un reclassement :
query_engine = QueryEngine(
retriever=vector_retriever,
query_rewriter=LLMQueryExtension(llm_provider=llm, num_queries=3),
rerankers=[LLMReRanker(llm_provider=llm, top_k=10)],
)
# Flow:
# 1. 1 query → 3 sub-queries (LLM call)
# 2. 3 retrieval passes (3x embedding + vector search)
# 3. ~30 results combined
# 4. LLM reranking narrows to top 10 (1 more LLM call)
# Total: 2 LLM calls + 3 vector searches + deduplication + reranking
result = await query_engine.search(query="...", top_k=10)Prétraitements personnalisés des requêtes
Implémentez le protocole QueryPreprocessor :
from mistralai.search.toolkit.retrieval.pre_processors import QueryPreprocessor
class DomainSpecificRewriter(QueryPreprocessor):
"""Rewrite queries for a specific domain."""
async def preprocess(self, query: str) -> str:
"""Preprocess the query."""
# 1. Normalize
normalized = query.lower().strip()
# 2. Expand domain-specific abbreviations
expansions = {
"ml": "machine learning",
"nlp": "natural language processing",
"ai": "artificial intelligence",
}
for abbr, expansion in expansions.items():
normalized = normalized.replace(f" {abbr} ", f" {expansion} ")
# 3. Add domain context if needed
if "algorithm" in normalized:
normalized += " implementation approach"
return normalized
rewriter = DomainSpecificRewriter()
query_engine = QueryEngine(
retriever=vector_retriever,
query_rewriter=rewriter,
)Quand utiliser le prétraitement des requêtes
Utilisez le prétraitement si :
- Les requêtes utilisateur sont courtes/vagues (ex. « RAG » au lieu de questions complètes)
- Votre index utilise une terminologie très spécifique
- Vous avez besoin d'un meilleur rappel pour les questions multifacettes
- La tolérance à la latence permet des appels LLM supplémentaires
Évitez le prétraitement si :
- Les requêtes utilisateur sont déjà bien formulées
- La latence est critique (chat en temps réel)
- Le coût est une préoccupation (chaque appel LLM a un impact)
- La recherche vectorielle seule donne de bons résultats
Approche hybride :
# Optional preprocessing based on query length
class OptionalRewriter(QueryPreprocessor):
async def preprocess(self, query: str) -> str:
# Only rewrite short queries
if len(query.split()) < 3:
# Expand short query
return await llm.rewrite(query)
return query
query_engine = QueryEngine(
retriever=vector_retriever,
query_rewriter=OptionalRewriter(),
)Voir aussi
- Vue d'ensemble de la recherche — Architecture de la pipeline de recherche
- Retrievers — Recherche vectorielle et par mots-clés
- Reclasseurs — Affiner les résultats après la recherche
- Cache sémantique — Mettre en cache les requêtes prétraitées