RAG : Techniques de Rétroprojection et Génération Augmentée en Pratique
Cet article fait partie de la série "Analyse Technique des LLM", abordant en profondeur les principes de la technologie RAG, son architecture et son implémentation en entreprise.
Pourquoi avons-nous besoin de RAG ?
1.1 Les limites des LLM purs
Les grands modèles de langage, bien que puissants, présentent trois problèmes fondamentaux :
| Problème | Explication | Impact |
|----------|-------------|--------|
| **Connaissance datée** | Les données d'entraînement ont des limites temporelles claires | Incapables de répondre aux événements récents |
| **Hallucinations** | Génèrent du contenu apparemment cohérent mais en réalité incorrect | Réduction de la crédibilité |
| **Limites de domaine** | Maîtrise insuffisante des connaissances spécialisées | Difficulté à répondre aux besoins des entreprises |
1.2 La valeur fondamentale de RAG
RAG (Rterieval-Augmented Generation) combine la recherche de connaissances externes avec la génération de texte, résolvant efficacement les problèmes mentionnés :
Question utilisateur → Rétrovectorisation → Récupération de documents pertinents → Assemblage du Prompt → Génération de réponse par LLM
**Comparaison des avantages :**
| Dimension | LLM pur | Fine-tuning | RAG |
|----------|---------|-------------|-----|
| Mise à jour des connaissances | ❌ Difficile | ⚠️ Nécessite un réentraînement | ✅ Mise à jour en temps réel |
| Coût d'implémentation | ✅ Faible | ❌ Élevé | ⚠️ Moyen |
| Explicabilité | ❌ Faible | ⚠️ Moyenne | ✅ Sources traçables |
| Contrôle des hallucinations | ❌ Difficile | ⚠️ Amélioration partielle | ✅ Réduction significative |
Architecture de système RAG
2.1 Flux RAG classique
class SystemeRAG:
def __init__(self, base_vecteur, modele_langage):
self.base_vecteur = base_vecteur # Base de données vectorielle
self.modele_langage = modele_langage # Grand modèle de langage
def repondre(self, requete: str) -> str:
# Étape 1 : Vectorisation de la requête
vecteur_requete = self.embed(requete)
# Étape 2 : Recherche par similarité
docs_releves = self.base_vecteur.recherche_similarite(
vecteur_requete,
k=5 # Top-K recherche
)
# Étape 3 : Construction du prompt augmenté
contexte = "\n\n".join([doc.contenu for doc in docs_releves])
prompt = f"""En vous basant sur les références suivantes, répondez à la question :
{contexte}
Question : {requete}
Réponse :"""
# Étape 4 : Génération par LLM
return self.modele_langage.generer(prompt)
2.2 Architecture modulaire
┌─────────────────────────────────────────────────────────────┐
│ Pipeline RAG │
├──────────────┬──────────────┬──────────────┬──────────────┤
│ Chargement │ Traitement │ Indexation │ Recherche │
│ de données │ de documents│ et stockage │ et génération│
├──────────────┼──────────────┼──────────────┼──────────────┤
│ • PDF │ • Stratégie │ • Base de │ • Recherche │
│ • Word │ de découpe │ données │ par similarité│
│ • Pages web │ • Extraction│ • Index │ • Réorganisation│
│ • Bases de │ de métadon-│ inversé │ • Multi-chemin │
│ données │ nées │ • Index │ de rappel │
│ │ • Nettoyage │ hybride │ • Ingénierie │
│ │ • Filtrage │ │ de prompts │
│ │ qualité │ │ │
└──────────────┴──────────────┴──────────────┴──────────────┘
Détails techniques clés
3.1 Stratégies de découpage de documents
La granularité du découpage affecte directement la qualité de la recherche :
| Stratégie | Taille | Contexte d'utilisation | Avantages/Inconvénients |
|-----------|--------|------------------------|------------------------|
| **Caractères fixes** | 500-1000 caractères | Contextes généraux | Simple mais peut couper le sens |
| **Caractères récursifs** | Ajustement dynamique | Documents structurés | Préserve l'intégrité des paragraphes |
| **Découpage sémantique** | Basé sur les phrases | Haute qualité | Préserve le sens complet mais complexe |
| **Découpage Markdown** | Par niveau de titre | Documentation technique | Préserve la structure informationnelle |
**Exemple de code (LangChain) :**
from langchain.text_splitter import RecursiveCharacterTextSplitter
decoupeur_texte = RecursiveCharacterTextSplitter(
taille_bloc=500, # Nombre max de caractères par bloc
chevauchement=50, # Nombre de caractères de chevauchement
separateurs=["\n\n", "\n", "。", "!", "?"] # Priorité de coupure
)
blocs = decoupeur_texte.diviser_documents(documents)
3.2 Choix de modèles d'embedding
| Modèle | Dimensions | Support linguistique | Caractéristiques |
|--------|------------|----------------------|------------------|
| **text-embedding-ada-002** | 1536 | Multilingue | OpenAI, résultats stables |
| **BGE-large-zh** | 1024 | Optimisé chinois | Open source, excellent en chinois |
| **M3E-base** | 768 | Chinois | Léger, adapté aux environnements contraints |
| **GTE-large** | 1024 | Multilingue | Alibaba, bon support de longs textes |
3.3 Sélection de base de données vectorielles
| Base de données | Caractéristiques | Échelle appropriée | Mode de déploiement |
|-----------------|------------------|--------------------|---------------------|
| **Chroma** | Légère et facile | Centaines de milliers | Local/Cloud |
| **Milvus** | Fonctionnalités d'entreprise | Milliards de vecteurs | Distribué |
| **Pinecone** | Service géré | Extension illimitée | SaaS |
| **Qdrant** | Haute performance Rust | Tiers de millions | Local/Cloud |
| **Weaviate** | Interface GraphQL | Tiers de millions | Local/Cloud |
Techniques d'optimisation avancées
4.1 Réécriture de requête
La requête initiale peut manquer de précision et nécessite une expansion ou reformulation :
def reecrire_requete(requete_originale: str) -> List[str]:
"""Génère plusieurs variantes de requête pour améliorer le rappel"""
# HyDE : Hypothetical Document Embedding
doc_hypothetique = modele_langage.generer(
f"Écrivez une réponse courte à cette question : {requete_originale}"
)
# Décomposition en sous-requêtes
sous_requetes = modele_langage.generer(
f"Décomposez '{requete_originale}' en 2-3 sous-questions, séparées par des sauts de ligne"
).split("\n")
return [requete_originale, doc_hypothetique] + sous_requetes
4.2 Réorganisation (Re-ranking)
Les résultats Top-K après un premier filtrage nécessitent un classement plus fin :
from sentence_transformers import CrossEncoder
# Charger le cross-encodeur (plus précis mais plus lent que les modèles à deux tours)
reorganisateur = CrossEncoder('BAAI/bge-reranker-large')
def reorganiser_resultats(requete: str, candidats: List[Document]) -> List[Document]:
paires = [[requete, doc.contenu] for doc in candidats]
scores = reorganisateur.predict(paires)
# Trier par score de réorganisation
classes = sorted(
zip(candidats, scores),
key=lambda x: x[1],
reverse=True
)
return [doc for doc, _ in classes[:3]] # Garder Top-3
4.3 Recherche hybride
Combiner les avantages de la recherche vectorielle et de la recherche par mots-clés :
def recherche_hybride(requete: str, alpha: float = 0.7):
"""
alpha: Poids de la recherche vectorielle, 1-alpha pour la recherche par mots-clés
"""
# Résultats de recherche vectorielle
resultats_vecteur = base_vecteur.recherche_similarite(requete, k=10)
# Recherche BM25 par mots-clés
resultats_mots_cles = index_bm25.rechercher(requete, k=10)
# Fusion de classement (algorithme RRF)
result_fusionnes = fusion_rang_reciproque(
resultats_vecteur,
resultats_mots_cles,
k=60 # Constante RRF
)
return result_fusionnes
Implémentation RAG en entreprise
5.1 Scénarios d'application typiques
| Scénario | Sources de données | Valeur principale |
|----------|-------------------|------------------|
| **Service client intelligent** | Manuels produits, FAQ | Réponses automatiques 24/7 |
| **Questions-réponses sur la base de connaissances** | Documents internes, normes | Localisation rapide d'informations |
| **Assistant de code** | Documentation API, code source | Amélioration de la productivité |
| **Conformité juridique** | Textes réglementaires, jurisprudence | Aide à l'analyse décisionnelle |
| **Conseil médical** | Littérature médicale, guides | Proposition de références et conseils |
5.2 Checklist d'optimisation des performances
- [ ] **Optimisation d'indexation** : Utiliser des algorithmes d'approximation des plus proches voisins comme HNSW
- [ ] **Stratégie de cache** : Mettre en cache les résultats des requêtes populaires
- [ ] **Traitement asynchrone** : Asynchroniser l'ajout de documents
- [ ] **Mise à jour incrémentale** : Mettre à jour uniquement les parties modifiées
- [ ] **Surveillance et alertes** : Surveiller la qualité de recherche et les temps de réponse
5.3 Dépannage des problèmes courants
| Symptôme | Cause porbable | Solution |
|----------|---------------|----------|
| Aucun contenu pertinent trouvé | Découpe trop grande/Embedding inapproprié | Ajuster la stratégie de découpe, changer de modèle |
| Réponse contenant des informations non pertinentes | Contexte trop long | Compresser le prompt, filtrer les documents peu pertinents |
| Réponse imprécise | Problème d'hallucination | Ajouter des citations, réduire la température |
| Réponse lente | Recherche lente | Optimiser l'indexation, ajouter un cache |
Conclusion et perspectives
La technologie RAG évolue rapidement, avec les tendances suivantes :
- **RAG multimodal** : Prise en charge de contenu non textuel comme images et vidéos
- **RAG Agentif** : Combinaison d'appels d'outils pour une récupération d'information proactive
- **GraphRAG** : Utilisation de graphes de connaissances pour améliorer le raisonnement
- **Optimisation bout-en-bout** : Entraînement conjoint du récupérateur et du générateur
**Série d'articles :**
- (1) Analyse approfondie de l'architecture Transformer
- (2) Meilleures pratiques en ingénierie des prompts
- (3) Guide pratique du fine-tuning de modèles
- (4) Introduction au développement d'agents intelligents
- **(5) RAG : Techniques de Rétroprojection et Génération Augmentée en Pratique ← Article actuel**
- (6) Exploration des applications de modèles multimodaux
- Suivez-moi pour partager en continu les dernières technologies et expériences pratiques sur les LLM !