Actualités

Percée de décomposition de requête : DecomposeRAG gère les questions complexes 50% mieux

5 novembre 2025
4 min de lecture
Équipe de Recherche Ailog

Les chercheurs d'UC Berkeley introduisent DecomposeRAG, un framework de décomposition de requête automatisé qui améliore significativement les réponses aux questions multi-sauts.

Aperçu de la recherche

Le laboratoire NLP d'UC Berkeley a publié DecomposeRAG, un framework qui décompose automatiquement les requêtes complexes en sous-requêtes plus simples, atteignant des résultats state-of-the-art sur les benchmarks de QA multi-sauts.

Le problème

Les questions complexes nécessitent un raisonnement multi-sauts :

Exemple : "Quelle est la population de la capitale du pays où se trouve la Tour Eiffel ?"

Nécessite :

  1. Où se trouve la Tour Eiffel ? → France
  2. Quelle est la capitale de la France ? → Paris
  3. Quelle est la population de Paris ? → 2,1 millions

Le RAG traditionnel récupère du contexte pour la question complète, manquant souvent les étapes intermédiaires.

Approche DecomposeRAG

Décomposition automatique

Utilise GPT-4 pour décomposer les requêtes en sous-questions :

DEVELOPERpython
def decompose_query(complex_query): prompt = f"""Break this question into simple sub-questions that must be answered in order. Question: {complex_query} Sub-questions (in order): 1.""" response = gpt4.generate(prompt) sub_questions = parse_questions(response) return sub_questions

Récupération séquentielle

Répondre aux sous-questions dans l'ordre, en utilisant les réponses précédentes comme contexte :

DEVELOPERpython
def sequential_rag(sub_questions): context = "" for i, sub_q in enumerate(sub_questions): # Retrieve for this sub-question docs = retrieve(sub_q + " " + context, k=5) # Generate answer answer = llm.generate( query=sub_q, context=docs, previous_answers=context ) # Add to cumulative context context += f"\nQ{i+1}: {sub_q}\nA{i+1}: {answer}\n" return answer # Answer to final sub-question

Validation des réponses

Valide chaque réponse intermédiaire avant de continuer :

DEVELOPERpython
def validate_answer(question, answer, retrieved_docs): prompt = f"""Is this answer supported by the documents? Question: {question} Answer: {answer} Documents: {retrieved_docs} Supported? (yes/no):""" validation = llm.generate(prompt) return "yes" in validation.lower()

Si la validation échoue, réessayer avec plus de contexte ou une stratégie de récupération alternative.

Résultats de benchmark

Testé sur quatre datasets de QA multi-sauts :

DatasetRAG BaselineDecomposeRAGAmélioration
HotpotQA45.3%68.7%+51.7%
2WikiMultihopQA38.2%57.9%+51.6%
MuSiQue32.1%49.8%+55.1%
IIRC41.7%62.3%+49.4%

Amélioration moyenne : +52%

Comparaison avec d'autres méthodes

MéthodeF1 moyCoût (relatif)
RAG Standard39.3%1x
Chain-of-Thought43.8%2x
ReACT48.2%3x
DecomposeRAG59.7%2.5x

DecomposeRAG atteint la meilleure précision à un coût modéré.

Insights clés

Quand la décomposition aide

L'efficacité varie selon la complexité de la requête :

SautsBaselineDecomposeRAGGain
1 (simple)68.2%69.1%+1.3%
2 (moyen)51.3%67.4%+31.4%
3 (complexe)28.7%52.3%+82.2%
4+ (très complexe)15.2%38.9%+156.3%

Constat : Plus de sauts = gains plus importants de la décomposition.

Qualité de la décomposition

Analyse de la qualité des décompositions générées par LLM :

  • Décomposition correcte : 87.3%
  • Étapes manquantes : 8.2%
  • Ordre incorrect : 3.1%
  • Logique circulaire : 1.4%

Même des décompositions imparfaites améliorent les résultats.

Analyse d'erreurs

Où DecomposeRAG échoue-t-il ?

  1. Erreurs de décomposition (23%) : Mauvaises sous-questions
  2. Échecs de récupération (34%) : Impossible de trouver des docs pertinents pour la sous-question
  3. Erreurs de réponse (28%) : Mauvaise réponse intermédiaire qui se propage
  4. Échecs d'intégration (15%) : Impossible de combiner les sous-réponses

Le plus courant : La récupération échoue toujours pour les sous-questions.

Implémentation

Version basique

DEVELOPERpython
class DecomposeRAG: def __init__(self, retriever, llm): self.retriever = retriever self.llm = llm async def query(self, complex_question): # Step 1: Decompose sub_questions = await self.decompose(complex_question) # Step 2: Sequential RAG context = "" for sub_q in sub_questions: # Retrieve docs = await self.retriever.retrieve( sub_q + " " + context, k=5 ) # Generate answer = await self.llm.generate( query=sub_q, context=docs, previous=context ) context += f"\n{sub_q} -> {answer}" # Return final answer return answer async def decompose(self, query): # Use LLM to decompose return await self.llm.decompose(query)

Avancé : Avec validation

DEVELOPERpython
class ValidatedDecomposeRAG(DecomposeRAG): async def query(self, complex_question, max_retries=2): sub_questions = await self.decompose(complex_question) context = "" for sub_q in sub_questions: for attempt in range(max_retries): docs = await self.retriever.retrieve(sub_q + " " + context) answer = await self.llm.generate(sub_q, docs, context) # Validate if await self.validate(sub_q, answer, docs): context += f"\n{sub_q} -> {answer}" break elif attempt == max_retries - 1: # Failed validation, use best-effort answer context += f"\n{sub_q} -> {answer} (unverified)" return answer

Optimisations

Sous-requêtes parallèles

Quand les sous-questions sont indépendantes :

DEVELOPERpython
# Identify independent sub-questions dependencies = analyze_dependencies(sub_questions) # Group independent questions independent_groups = group_by_dependencies(sub_questions, dependencies) # Process groups in parallel for group in independent_groups: # Parallel retrieval for group results = await asyncio.gather(*[ self.retrieve_and_answer(q, context) for q in group ]) # Add all to context for q, answer in zip(group, results): context += f"\n{q} -> {answer}"

Cache des résultats intermédiaires

DEVELOPERpython
class CachedDecomposeRAG(DecomposeRAG): def __init__(self, retriever, llm): super().__init__(retriever, llm) self.cache = {} async def retrieve_and_answer(self, sub_q, context): cache_key = hash(sub_q + context) if cache_key in self.cache: return self.cache[cache_key] result = await super().retrieve_and_answer(sub_q, context) self.cache[cache_key] = result return result

Considérations pratiques

Latence

DecomposeRAG est 2-3x plus lent :

  • Requête 2-sauts : +2-3 secondes
  • Requête 3-sauts : +4-6 secondes
  • Requête 4-sauts : +6-10 secondes

Atténuation :

  • Sous-requêtes parallèles quand possible
  • Cache des décompositions courantes
  • Utiliser des LLMs plus rapides pour les étapes intermédiaires

Coût

Plus d'appels LLM = coût plus élevé :

  • Décomposition : 1 appel LLM
  • Chaque sous-question : 1 appel LLM
  • Validation (optionnelle) : 1 appel par sous-question

Exemple :

  • 3 sous-questions + validation = 7 appels LLM
  • vs. 1 appel pour RAG standard

Multiplicateur de coût : 2-5x selon la complexité

Quand utiliser

Utiliser DecomposeRAG quand :

  • Les questions sont complexes (multi-sauts)
  • La précision est plus importante que la vitesse
  • Le budget permet des coûts plus élevés

Utiliser RAG standard quand :

  • Recherches simples
  • Vitesse critique
  • Sensible au coût

Directions futures

Améliorations prévues :

  1. Meilleure décomposition : Affiner des modèles plus petits
  2. Stratégie adaptive : Détecter automatiquement quand décomposer
  3. Raffinement itératif : Réessayer les sous-questions échouées
  4. Multimodal : Décomposer à travers modalités

Ressources

  • Article : "DecomposeRAG: Automatic Query Decomposition for Multi-Hop Question Answering"
  • Code : github.com/berkeley-nlp/decomposerag
  • Démo : decomposerag.demo.berkeley.edu

Conclusion

DecomposeRAG démontre qu'une décomposition explicite des requêtes améliore significativement les réponses aux questions multi-sauts. Bien que plus coûteux et plus lent que le RAG standard, les gains de précision justifient le surcoût pour les requêtes complexes où l'exactitude est critique.

Tags

query optimizationmulti-hopresearchdecomposition

Articles connexes

Ailog Assistant

Ici pour vous aider

Salut ! Pose-moi des questions sur Ailog et comment intégrer votre RAG dans vos projets !