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

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 retrieval

Options de configuration :

OptionTypeValeur par défautObjectif
llm_providerLLMProviderObligatoireLLM 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

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 combined

Options de configuration :

OptionTypeValeur par défautObjectif
llm_providerLLMProviderObligatoireLLM utilisé pour l'extension (MistralChat, etc.)
num_queriesint3Nombre 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 recall

Quand 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

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

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

Voir aussi