Les Large Language Models présentnt certaines limites, notamment en termes de connaissances récentes et spécifiques. C’est là qu’intervient le RAG (Retrieval Augmented Generation), une technique qui permet d’enrichir les réponses des LLM avec des informations externes pertinentes.
Nous vous expliquons pas à pas comment construire un système RAG efficace pour améliorer vos applications basées sur les LLM.
Qu’est-ce qu’un LLM ?
Les Large Language Models (LLM) représentent une avancée majeure dans le domaine de l’intelligence artificielle et du traitement du langage naturel. Avant de plonger dans la construction d’un RAG, il est essentiel de comprendre ce qu’est un LLM et comment il fonctionne.
💡 Principe de base
Un Large Language Model est un modèle d’intelligence artificielle entraîné sur d’immenses corpus de textes pour apprendre à prédire et générer du langage humain. Ces modèles utilisent des architectures de réseaux de neurones complexes, généralement basées sur des transformers, qui leur permettent de comprendre le contexte et les nuances du langage.
Les LLM sont capables de réaliser une multitude de tâches linguistiques comme :
- La génération de texte
- La traduction
- La réponse à des questions
- Le résumé de documents
- La rédaction créative
- L’analyse de sentiment
⚙️ Fonctionnement
Le fonctionnement d’un LLM repose sur plusieurs principes fondamentaux :
- Entraînement massif : Les LLM sont entraînés sur des centaines de gigaoctets, voire des téraoctets de textes provenant d’internet, de livres, d’articles scientifiques et d’autres sources.
- Apprentissage non supervisé : Ils apprennent principalement en prédisant le mot suivant dans une séquence, ce qui leur permet de capturer la structure et les règles du langage sans intervention humaine directe.
- Architecture de transformer : Cette architecture permet au modèle de prêter attention à différentes parties du texte d’entrée simultanément, ce qui améliore sa compréhension du contexte.
- Tokenisation : Les textes sont découpés en « tokens » (mots ou parties de mots) qui sont ensuite convertis en représentations numériques que le modèle peut traiter.
- Inférence : Lors de l’utilisation, le modèle génère des réponses token par token, en s’appuyant sur ses connaissances acquises pendant l’entraînement.
Les LLM les plus populaires à ce jour
Plusieurs modèles se distinguent sur le marché actuel :
Modèle | Développeur | Particularités |
---|---|---|
GPT-4 | OpenAI | Capacités multimodales, raisonnement avancé |
Claude 3 | Anthropic | Réduction des erreurs factuelles de 62% par rapport à GPT-4 |
Gemini | Intégration profonde avec l’écosystème Google | |
Mistral | Mistral AI | Modèle français performant, versions open-source disponibles |
Llama 3 | Meta | Open-source, excellentes performances pour sa taille |
Vigogne | Zaion | Modèle français spécialisé pour le français |
Ces modèles continuent d’évoluer rapidement, avec de nouvelles versions et améliorations régulières qui repoussent les limites de ce qui est possible en matière de génération de texte.
Qu’est-ce qu’un RAG ?
Malgré leurs capacités impressionnantes, les LLM présentent certaines limitations significatives : ils ne peuvent accéder à des informations récentes (postérieures à leur date d’entraînement), ils peuvent « halluciner » des faits inexacts, et leurs connaissances sur des domaines spécifiques peuvent être limitées. C’est là qu’intervient le RAG.
💡 Principe de base
Le RAG (Retrieval Augmented Generation) est une technique qui combine la puissance des modèles de langage avec un système de recherche documentaire. Cette approche permet d’enrichir les réponses des LLM avec des informations externes pertinentes et à jour.
Le principe fondamental du RAG est simple mais puissant :
- Stocker des informations externes dans une base de données
- Rechercher les informations pertinentes en fonction de la requête de l’utilisateur
- Fournir ces informations au LLM pour qu’il génère une réponse informée et précise
⚙️ Fonctionnement
Le fonctionnement d’un système RAG peut être décomposé en trois étapes principales :
- Ingestion des données : Les documents (textes, PDF, pages web, etc.) sont traités, découpés en morceaux (chunks) de taille appropriée, puis transformés en vecteurs numériques (embeddings) qui capturent leur signification sémantique.
- Récupération (retrieval) : Lorsqu’une question est posée, le système recherche dans sa base de données vectorielle les documents ou passages les plus pertinents par rapport à cette question, généralement en utilisant des mesures de similarité comme la distance cosinus.
- Génération (generation) : Les passages récupérés sont fournis au LLM comme contexte supplémentaire, lui permettant de générer une réponse qui s’appuie sur ces informations spécifiques plutôt que sur ses seules connaissances générales.
Rappels du RAG
Le RAG résout plusieurs problèmes fondamentaux des LLM :
- Actualisation des connaissances : Les LLM sont limités aux informations disponibles lors de leur entraînement. Le RAG permet d’accéder à des informations récentes.
- Réduction des hallucinations : En ancrant les réponses dans des sources documentaires vérifiables, le RAG diminue considérablement le risque de génération d’informations erronées.
- Spécialisation sur des domaines précis : Le RAG permet de spécialiser un LLM généraliste sur un domaine particulier en lui fournissant une base documentaire spécifique.
- Traçabilité : Les réponses peuvent être associées à leurs sources, augmentant la transparence et la confiance.
Selon une étude de mars 2025, jusqu’à 60% des entreprises ont du mal à garantir la qualité et la fiabilité des données utilisées dans leurs systèmes d’IA, ce qui souligne l’importance d’une approche comme le RAG.
Génération de récupération augmentée (Retrieval Augmented Generation)
Le terme complet « Génération de récupération augmentée » (Retrieval Augmented Generation) a été introduit pour la première fois dans un article de recherche de Facebook AI en 2020. Cette technique représente une évolution significative par rapport aux approches précédentes de génération de texte, en combinant les avantages des modèles génératifs et des systèmes de recherche d’information.
Le RAG permet d’obtenir le meilleur des deux mondes :
- La fluidité et la cohérence des grands modèles de langage
- La précision et l’actualité des systèmes de recherche documentaire
Les différents composants d’un modèle RAG
Pour construire un système RAG efficace, il est essentiel de comprendre ses différents composants et leur interaction. Chaque élément joue un rôle crucial dans la performance globale du système.
Schéma de synthèse et exemple concret
Voici un aperçu des composants principaux d’un système RAG :
[Documents] → [Prétraitement] → [Vectorisation] → [Base de données vectorielle]
↓
[Question utilisateur] → [Vectorisation] → [Recherche de similarité] → [Passages pertinents]
↓
[LLM] → [Réponse finale]
Exemple concret : Imaginons un système d’assistance client pour une entreprise de télécommunications. Le système RAG pourrait être alimenté avec des manuels d’utilisation, des FAQ, des politiques d’entreprise et des historiques de résolution de problèmes. Lorsqu’un client pose une question sur un problème de facturation spécifique, le système récupère les documents pertinents sur cette question et génère une réponse précise et personnalisée.
L’encodage (ou vectorisation)
L’encodage est la première étape cruciale du processus RAG. Elle consiste à transformer les documents textuels en représentations numériques (vecteurs) qui peuvent être efficacement stockées et recherchées.
Processus d’encodage :
- Découpage des documents : Les documents sont d’abord divisés en morceaux (chunks) de taille appropriée, généralement des paragraphes ou des sections qui forment des unités de sens cohérentes.
- Création d’embeddings : Chaque chunk est ensuite transformé en un vecteur numérique à haute dimension (généralement plusieurs centaines de dimensions) à l’aide d’un modèle d’embedding comme BERT, USE (Universal Sentence Encoder), ou des modèles spécialisés comme text-embedding-ada-002 d’OpenAI.
- Indexation : Ces vecteurs sont stockés dans une base de données vectorielle qui permet des recherches rapides par similarité.
Modèles d’embedding populaires :
Modèle | Développeur | Dimensions | Particularités |
---|---|---|---|
text-embedding-ada-002 | OpenAI | 1536 | Haute précision, coût par token |
all-MiniLM-L6-v2 | HuggingFace | 384 | Bon compromis performance/ressources |
E5-large | Microsoft | 1024 | Optimisé pour les requêtes |
BGE-large-en | BAAI | 1024 | Excellentes performances en anglais |
Nomic-Embed-text | Nomic AI | 768 | Open-source, performant |
La récupération (retrieval)
La phase de récupération est responsable de l’identification des informations les plus pertinentes par rapport à une requête donnée.
Mécanismes de récupération :
- Vectorisation de la requête : La question de l’utilisateur est transformée en vecteur en utilisant le même modèle d’embedding que celui utilisé pour les documents.
- Recherche par similarité : Le système calcule la similarité (généralement par distance cosinus) entre le vecteur de la requête et les vecteurs des documents stockés.
- Sélection des passages : Les passages ayant les scores de similarité les plus élevés sont sélectionnés pour être utilisés comme contexte.
Techniques avancées de récupération :
- Recherche hybride : Combinaison de recherche sémantique (par vecteurs) et de recherche par mots-clés pour améliorer la précision.
- Expansion de requête : Enrichissement de la requête originale avec des termes connexes pour améliorer la récupération.
- Reranking : Utilisation d’un modèle secondaire pour reclasser les résultats initiaux et améliorer la pertinence.
MongoDB propose une recherche hybride combinant recherche plein texte et vectorielle pour les applications LLM, maintenant une disponibilité de 99,99% selon les données de mars 2025.
La génération de texte
La phase finale du processus RAG est la génération de la réponse en utilisant le LLM augmenté par les informations récupérées.
Processus de génération :
- Construction du prompt : Les passages récupérés sont intégrés dans un prompt structuré qui guide le LLM dans la génération de sa réponse.
- Génération de la réponse : Le LLM utilise à la fois ses connaissances générales et les informations spécifiques fournies pour générer une réponse cohérente et précise.
- Post-traitement : La réponse peut être raffinée, formatée ou enrichie avec des citations ou des références aux sources utilisées.
Techniques d’optimisation de la génération :
- Chain-of-thought prompting : Guider le LLM à travers un raisonnement étape par étape pour améliorer la qualité des réponses.
- Génération structurée : Demander au LLM de produire des réponses dans un format spécifique (JSON, tableau, liste à puces, etc.).
- Vérification factuelle : Utiliser un second passage pour vérifier la cohérence entre la réponse générée et les sources fournies.
Les points d’attention
Plusieurs aspects critiques doivent être pris en compte lors de la conception d’un système RAG :
- Qualité des données : La qualité des réponses dépend directement de la qualité et de la pertinence des documents sources.
- Stratégie de chunking : La taille et la méthode de découpage des documents influencent significativement la performance du système.
- Choix du modèle d’embedding : Différents modèles peuvent être plus ou moins adaptés selon la langue, le domaine ou le type de requêtes.
- Optimisation des prompts : La formulation des instructions au LLM a un impact majeur sur la qualité des réponses générées.
- Évaluation continue : Un système RAG doit être régulièrement évalué et ajusté pour maintenir et améliorer ses performances.
- Gestion des hallucinations résiduelles : Même avec RAG, des hallucinations peuvent survenir et doivent être détectées et gérées.
- Sécurité et confidentialité : La protection des données sensibles et la conformité aux réglementations doivent être assurées.
Selon une analyse de mars 2025, environ 70% des entreprises utilisant des LLM ont besoin d’améliorer la scalabilité de leurs systèmes pour répondre à la demande croissante.
Architecture d’un projet RAG
L’architecture globale d’un projet RAG doit être conçue de manière à assurer l’efficacité, la scalabilité et la maintenabilité du système. Voici les principaux éléments architecturaux à considérer :
Composants fondamentaux
- Pipeline d’ingestion : Responsable de l’acquisition, du prétraitement et de la vectorisation des documents.
- Connecteurs pour différentes sources de données (bases de données, systèmes de fichiers, API, etc.)
- Modules de prétraitement (nettoyage, déduplication, extraction de métadonnées)
- Système de chunking adaptatif
- Service de vectorisation
- Système de stockage : Gère le stockage efficace des documents et de leurs représentations vectorielles.
- Base de données vectorielle (Pinecone, Weaviate, Chroma, FAISS, etc.)
- Stockage des documents originaux
- Système de métadonnées et d’indexation
- Moteur de recherche : Responsable de la récupération efficace des informations pertinentes.
- Module de traitement des requêtes
- Système de recherche vectorielle
- Mécanismes de filtrage et de reranking
- Service de génération : Gère l’interaction avec le LLM et la génération des réponses.
- Gestionnaire de prompts
- Interface avec le LLM
- Post-processeur de réponses
- Couche d’API : Expose les fonctionnalités du système aux applications clientes.
- Points d’entrée REST/GraphQL
- Authentification et autorisation
- Gestion des quotas et limitations
Patterns architecturaux
Plusieurs patterns architecturaux peuvent être utilisés pour construire un système RAG efficace :
- Architecture microservices : Décompose le système en services indépendants et spécialisés, facilitant la scalabilité et la maintenance.
- Architecture événementielle : Utilise des files d’attente et des événements pour découpler les différents composants et améliorer la résilience.
- Architecture serverless : Exploite les services cloud sans serveur pour une scalabilité automatique et une réduction des coûts opérationnels.
Considérations pour le passage à l’échelle
Pour concevoir un système RAG capable de passer à l’échelle :
- Distribution de la base vectorielle : Utilisation de techniques de sharding et de réplication pour gérer de grandes quantités de vecteurs.
- Mise en cache : Implémentation de stratégies de mise en cache à différents niveaux pour réduire la latence et les coûts.
- Traitement asynchrone : Utilisation de files d’attente et de traitement asynchrone pour gérer les pics de charge.
- Monitoring et observabilité : Mise en place d’un système complet de surveillance pour identifier et résoudre rapidement les problèmes.
Agentforce Data Library de Salesforce, une plateforme utilisant RAG, maintient une disponibilité de 99,99% grâce à une architecture distribuée et résiliente selon les données de mars 2025.
Créer un RAG avec LangChain et Mistral à partir de zéro
LangChain est l’un des frameworks les plus populaires pour construire des applications RAG, offrant une grande flexibilité et de nombreuses intégrations. Combiné avec Mistral, un LLM performant et open-source, il permet de créer rapidement un système RAG efficace.
Ingestion des documents
La première étape consiste à ingérer et préparer les documents qui serviront de base de connaissances à notre système RAG.
Loading
Commençons par installer les dépendances nécessaires et charger nos documents :
# Installation des dépendances
!pip install langchain langchain_community chromadb sentence-transformers
# Importation des modules
from langchain.document_loaders import DirectoryLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
import os
# Définition du répertoire contenant les documents
data_dir = "./documents/"
# Chargement des documents texte
loader = DirectoryLoader(data_dir, glob="**/*.txt", loader_cls=TextLoader)
documents = loader.load()
print(f"Nombre de documents chargés : {len(documents)}")
Cette étape permet de charger tous les documents texte présents dans le répertoire spécifié. LangChain propose de nombreux autres chargeurs pour différents formats (PDF, HTML, CSV, etc.).
Chunking
Une fois les documents chargés, nous devons les découper en chunks de taille appropriée :
# Configuration du text splitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
length_function=len,
)
# Découpage des documents en chunks
chunks = text_splitter.split_documents(documents)
print(f"Nombre de chunks créés : {len(chunks)}")
Le chunk_size
détermine la taille maximale de chaque chunk, tandis que chunk_overlap
définit le chevauchement entre les chunks consécutifs pour éviter de perdre du contexte à la frontière des chunks.
Création de l’embedding
Après avoir préparé nos documents, nous devons les transformer en vecteurs numériques (embeddings) pour permettre la recherche sémantique.
Création du Vector Store
Nous allons utiliser ChromaDB comme base de données vectorielle et le modèle Sentence-Transformers pour la création des embeddings :
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma
# Configuration du modèle d'embedding
embedding_model = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2",
model_kwargs={'device': 'cuda'} # Utilisation du GPU si disponible
)
# Création de la base de données vectorielle
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embedding_model,
persist_directory="./chroma_db"
)
# Persistance de la base de données
vectorstore.persist()
Cette étape crée une base de données ChromaDB contenant les embeddings de tous nos chunks de documents. Cette base sera utilisée pour la recherche sémantique.
Insertion des vecteurs
Si vous avez besoin d’ajouter de nouveaux documents à votre base existante, vous pouvez le faire comme suit :
# Chargement de nouveaux documents
new_loader = TextLoader("./documents/nouveau_document.txt")
new_document = new_loader.load()
# Découpage en chunks
new_chunks = text_splitter.split_documents(new_document)
# Chargement de la base existante
existing_vectorstore = Chroma(
persist_directory="./chroma_db",
embedding_function=embedding_model
)
# Ajout des nouveaux chunks
existing_vectorstore.add_documents(new_chunks)
existing_vectorstore.persist()
Cette approche incrémentale permet de maintenir votre base de connaissances à jour sans avoir à recréer l’ensemble de la base à chaque modification.
Génération
Maintenant que notre base de connaissances est prête, nous pouvons configurer le LLM et créer notre pipeline RAG.
Exécution d’un LLM en local
Pour utiliser Mistral localement, nous allons nous appuyer sur la bibliothèque ctransformers
qui permet d’exécuter efficacement des modèles quantifiés :
!pip install ctransformers
from langchain.llms import CTransformers
# Configuration du modèle Mistral
llm = CTransformers(
model="TheBloke/Mistral-7B-Instruct-v0.1-GGUF",
model_file="mistral-7b-instruct-v0.1.Q4_K_M.gguf",
config={
'max_new_tokens': 512,
'temperature': 0.7,
'context_length': 2048,
}
)
Cette configuration charge le modèle Mistral 7B quantifié, ce qui permet de l’exécuter sur des machines avec des ressources limitées tout en maintenant de bonnes performances.
Création du pipeline RAG
Enfin, nous pouvons assembler tous les composants pour créer notre pipeline RAG complet :
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
# Définition du template de prompt
template = """
Tu es un assistant IA utile et précis. Utilise le contexte suivant pour répondre à la question de l'utilisateur.
Si tu ne connais pas la réponse, dis simplement que tu ne sais pas. N'invente pas d'information.
Contexte:
{context}
Question: {question}
Réponse:
"""
prompt = PromptTemplate(
template=template,
input_variables=["context", "question"]
)
# Création de la chaîne de requête
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True,
chain_type_kwargs={"prompt": prompt}
)
# Test du système RAG
question = "Quels sont les avantages des énergies renouvelables?"
result = qa_chain({"query": question})
print("Question:", question)
print("Réponse:", result["result"])
print("\nSources:")
for doc in result["source_documents"]:
print(f"- {doc.metadata.get('source', 'Unknown')}")
Notre système RAG est maintenant opérationnel ! Il récupère les passages les plus pertinents de notre base de connaissances en fonction de la question posée, puis utilise le modèle Mistral pour générer une réponse informée par ces passages.
Créez votre propre application RAG : un guide étape par étape pour configurer LLM localement à l’aide d’Ollama, Python et ChromaDB
Pour ceux qui préfèrent une approche plus légère et entièrement locale, la combinaison d’Ollama, Python et ChromaDB offre une solution efficace pour créer une application RAG personnalisée sans dépendre de services cloud.
Trop long; Pour lire
Si vous êtes pressé, voici un résumé des étapes principales :
- Installer Ollama, Python et les dépendances nécessaires
- Configurer ChromaDB comme base de données vectorielle
- Créer un pipeline d’ingestion de documents
- Développer un système de requête RAG
- Construire une interface utilisateur simple
Génération augmentée par récupération (RAG)
Le RAG est particulièrement utile dans un contexte local car il permet d’augmenter les capacités d’un LLM avec des connaissances spécifiques sans avoir besoin de le réentraîner, ce qui serait coûteux en ressources.
Ollama est un outil qui facilite le déploiement de LLM en local, offrant une API simple pour interagir avec différents modèles comme Llama 2, Mistral ou Vicuna.
Les avantages d’une implémentation locale incluent :
- Contrôle total sur vos données
- Absence de coûts d’API récurrents
- Possibilité de fonctionner hors ligne
- Personnalisation complète du système
GPU
Pour exécuter efficacement des LLM en local, un GPU est fortement recommandé, bien que non strictement nécessaire pour les modèles les plus légers.
Les LLM nécessitent des calculs matriciels intensifs que les GPU peuvent traiter beaucoup plus efficacement que les CPU. Pour des modèles de taille moyenne (7B-13B paramètres), une carte comme la NVIDIA RTX 3060 (12GB VRAM) ou équivalent est un bon point de départ.
Conditions préalables
Avant de commencer, assurez-vous d’avoir installé :
- Python 3.9+ : La base de notre application
- Ollama : Pour exécuter les modèles LLM en local
- ChromaDB : Notre base de données vectorielle
- Autres dépendances Python : langchain, sentence-transformers, etc.
Voici les commandes d’installation :
# Installation d'Ollama (Linux/macOS)
curl -fsSL https://ollama.com/install.sh | sh
# Installation des dépendances Python
pip install langchain chromadb sentence-transformers pydantic fastapi uvicorn
Créez l’application RAG
Notre application sera composée de plusieurs fichiers Python :
app.py
Le point d’entrée principal de notre application :
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
import uvicorn
from query import query_documents
app = FastAPI()
# Configuration des templates
templates = Jinja2Templates(directory="templates")
app.mount("/static", StaticFiles(directory="static"), name="static")
@app.get("/", response_class=HTMLResponse)
async def read_root(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
@app.post("/query")
async def process_query(request: Request):
form_data = await request.form()
query_text = form_data.get("query", "")
response, sources = query_documents(query_text)
return {"response": response, "sources": sources}
if __name__ == "__main__":
uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)
embed.py
Ce fichier gère l’ingestion et la vectorisation des documents :
import os
from langchain.document_loaders import DirectoryLoader, TextLoader, PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma
# Configuration des chemins
DOCUMENTS_DIR = "./documents"
DB_DIR = "./chroma_db"
def load_documents():
# Chargement des différents types de documents
loaders = [
DirectoryLoader(DOCUMENTS_DIR, glob="**/*.txt", loader_cls=TextLoader),
DirectoryLoader(DOCUMENTS_DIR, glob="**/*.pdf", loader_cls=PyPDFLoader)
]
documents = []
for loader in loaders:
documents.extend(loader.load())
return documents
def process_documents():
documents = load_documents()
# Découpage des documents
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
length_function=len,
)
chunks = text_splitter.split_documents(documents)
# Création des embeddings et stockage
embedding_model = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2"
)
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embedding_model,
persist_directory=DB_DIR
)
vectorstore.persist()
print(f"Base de données vectorielle créée avec {len(chunks)} chunks.")
return vectorstore
if __name__ == "__main__":
process_documents()
query.py
Ce fichier gère les requêtes et l’interaction avec le LLM :
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma
from langchain.llms import Ollama
from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQA
# Configuration
DB_DIR = "./chroma_db"
MODEL_NAME = "mistral" # ou autre modèle disponible dans Ollama
def get_vectorstore():
embedding_model = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2"
)
vectorstore = Chroma(
persist_directory=DB_DIR,
embedding_function=embedding_model
)
return vectorstore
def query_documents(query_text):
vectorstore = get_vectorstore()
llm = Ollama(model=MODEL_NAME)
# Configuration du prompt
template = """
Tu es un assistant IA utile et précis. Utilise le contexte suivant pour répondre à la question.
Si tu ne connais pas la réponse, dis simplement que tu ne sais pas. N'invente pas d'information.
Contexte:
{context}
Question: {question}
Réponse:
"""
prompt = PromptTemplate(
template=template,
input_variables=["context", "question"]
)
# Création de la chaîne RAG
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True,
chain_type_kwargs={"prompt": prompt}
)
# Exécution de la requête
result = qa_chain({"query": query_text})
# Préparation des sources
sources = []
for doc in result.get("source_documents", []):
source = {
"content": doc.page_content[:200] + "...",
"source": doc.metadata.get("source", "Unknown")
}
sources.append(source)
return result["result"], sources
get_vector_db.py
Un utilitaire pour interagir avec la base de données vectorielle :
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma
# Configuration
DB_DIR = "./chroma_db"
def get_vectorstore():
embedding_model = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2"
)
vectorstore = Chroma(
persist_directory=DB_DIR,
embedding_function=embedding_model
)
return vectorstore
Exécutez votre application !
Pour lancer l’application, suivez ces étapes :
- Créez un répertoire
documents
et ajoutez-y vos fichiers texte et PDF - Exécutez le script d’ingestion :
python embed.py
- Assurez-vous qu’Ollama est en cours d’exécution et que le modèle est téléchargé :
ollama pull mistral
- Lancez l’application :
python app.py
- Accédez à l’interface via votre navigateur à l’adresse
http://localhost:8000
Vous disposez maintenant d’une application RAG entièrement locale qui peut répondre à des questions en se basant sur vos propres documents !
Conclusion
Cette approche utilisant Ollama, Python et ChromaDB offre une solution flexible et économique pour créer une application RAG personnalisée. Elle est particulièrement adaptée pour les cas d’usage où la confidentialité des données est primordiale ou lorsque vous souhaitez éviter les coûts d’API récurrents.
Le code source complet peut être facilement adapté à vos besoins spécifiques, que ce soit pour une utilisation personnelle ou professionnelle.
Construire son RAG avec LlamaIndex et le modèle LLM Vigogne
LlamaIndex est un framework alternatif à LangChain qui offre des fonctionnalités spécifiquement optimisées pour la construction de systèmes RAG. Combiné avec Vigogne, un modèle LLM français, il permet de créer des applications RAG particulièrement performantes pour le traitement de documents en français.
Création de l’application RAG
Commençons par explorer les étapes nécessaires pour construire un RAG avec LlamaIndex et Vigogne.
Installation des dépendances/packages requis
!pip install llama-index transformers torch accelerate bitsandbytes sentencepiece
!pip install pypdf sentence_transformers
LlamaIndex nécessite plusieurs dépendances, notamment PyTorch pour l’exécution des modèles et diverses bibliothèques pour le traitement des documents et la création d’embeddings.
Préparation des documents textes
La première étape consiste à charger et préparer les documents qui constitueront notre base de connaissances :
import os
from llama_index import SimpleDirectoryReader, Document
from llama_index.node_parser import SimpleNodeParser
# Définition du répertoire contenant les documents
data_dir = "./documents/"
# Chargement des documents
documents = SimpleDirectoryReader(data_dir).load_data()
# Découpage des documents en nodes
parser = SimpleNodeParser.from_defaults(chunk_size=1024, chunk_overlap=20)
nodes = parser.get_nodes_from_documents(documents)
print(f"Nombre de documents chargés : {len(documents)}")
print(f"Nombre de nodes créés : {len(nodes)}")
LlamaIndex utilise le concept de « nodes » plutôt que de « chunks », mais l’idée reste la même : découper les documents en morceaux de taille gérable qui peuvent être traités indépendamment.
Création des chunks
LlamaIndex offre plusieurs options pour le découpage des documents, permettant de contrôler finement la granularité et la structure des chunks :
from llama_index.node_parser import SentenceSplitter
# Création d'un splitter basé sur les phrases
sentence_splitter = SentenceSplitter(
chunk_size=512,
chunk_overlap=50,
paragraph_separator="\n\n",
secondary_chunking_regex="(?<=\. )",
)
# Application du splitter aux documents
nodes = sentence_splitter.get_nodes_from_documents(documents)
print(f"Nombre de nodes créés avec le sentence splitter : {len(nodes)}")
Cette approche permet de respecter les frontières naturelles des phrases, ce qui peut améliorer la qualité des réponses générées.
Les embeddings et la base de données vecteurs
Configurons maintenant les embeddings et la base de données vectorielle :
from llama_index.embeddings import HuggingFaceEmbedding
from llama_index import VectorStoreIndex, ServiceContext
# Configuration du modèle d'embedding
embed_model = HuggingFaceEmbedding(
model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
)
# Configuration du LLM (Vigogne)
from llama_index.llms import HuggingFaceLLM
llm = HuggingFaceLLM(
model_name="bofenghuang/vigogne-7b-instruct",
tokenizer_name="bofenghuang/vigogne-7b-instruct",
context_window=2048,
max_new_tokens=256,
generate_kwargs={"temperature": 0.7, "do_sample": True},
device_map="auto",
model_kwargs={"torch_dtype": "auto", "load_in_8bit": True}
)
# Création du contexte de service
service_context = ServiceContext.from_defaults(
llm=llm,
embed_model=embed_model,
node_parser=sentence_splitter
)
# Création de l'index vectoriel
index = VectorStoreIndex(nodes, service_context=service_context)
# Sauvegarde de l'index
index.storage_context.persist("./storage")
Pour les documents en français, nous utilisons un modèle d’embedding multilingue qui offre de bonnes performances sur le français. Le modèle Vigogne est spécifiquement conçu pour le français, ce qui le rend particulièrement adapté pour notre cas d’usage.
Questions et réponses sur les documents
Une fois notre index créé, nous pouvons l’utiliser pour répondre à des questions :
from llama_index.prompts import PromptTemplate
# Définition d'un template de prompt en français
query_prompt_tmpl = (
"Nous avons fourni un contexte ci-dessous. \n"
"---------------------\n"
"{context_str}"
"\n---------------------\n"
"Étant donné ce contexte, réponds à la question suivante en français : {query_str}\n"
"Si la réponse ne se trouve pas dans le contexte, indique-le clairement."
)
query_prompt = PromptTemplate(query_prompt_tmpl)
# Création d'un moteur de requête
query_engine = index.as_query_engine(
service_context=service_context,
text_qa_template=query_prompt
)
# Test avec une question
response = query_engine.query(
"Quels sont les principaux avantages des énergies renouvelables?"
)
print(response)
Le template de prompt personnalisé permet d’adapter le comportement du LLM à notre cas d’usage spécifique, en lui demandant explicitement de répondre en français et de s’appuyer sur le contexte fourni.
L’embedding
L’embedding est une étape cruciale dans le processus RAG. Pour les documents en français, il est important de choisir un modèle d’embedding adapté :
# Comparaison de différents modèles d'embedding pour le français
from llama_index.embeddings import HuggingFaceEmbedding
# Modèle multilingue
multilingual_embed = HuggingFaceEmbedding(
model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
)
# Modèle spécifique au français
french_embed = HuggingFaceEmbedding(
model_name="dangvantuan/sentence-camembert-large"
)
# Test des embeddings
test_text = "Les énergies renouvelables sont essentielles pour lutter contre le changement climatique."
vector1 = multilingual_embed.get_text_embedding(test_text)
vector2 = french_embed.get_text_embedding(test_text)
print(f"Dimension du vecteur multilingue : {len(vector1)}")
print(f"Dimension du vecteur français : {len(vector2)}")
Le choix du modèle d’embedding peut avoir un impact significatif sur la qualité des résultats, en particulier pour les langues autres que l’anglais. Des modèles spécifiques à la langue peuvent offrir de meilleures performances que des modèles multilingues génériques.
Import et vectorisation des données du PDF
LlamaIndex offre des fonctionnalités spécifiques pour traiter les documents PDF :
from llama_index import download_loader
from pathlib import Path
# Chargement du loader PDF
PDFReader = download_loader("PDFReader")
loader = PDFReader()
# Chargement d'un fichier PDF
pdf_file = "./documents/rapport_energie.pdf"
pdf_docs = loader.load_data(file=Path(pdf_file))
# Traitement du PDF
nodes = sentence_splitter.get_nodes_from_documents(pdf_docs)
# Création d'un index spécifique pour le PDF
pdf_index = VectorStoreIndex(nodes, service_context=service_context)
# Création d'un moteur de requête
pdf_query_engine = pdf_index.as_query_engine(
service_context=service_context,
text_qa_template=query_prompt
)
# Test avec une question spécifique au PDF
response = pdf_query_engine.query(
"Quelles sont les principales conclusions du rapport?"
)
print(response)
Cette approche permet de créer un index spécifique pour chaque document PDF, ce qui peut être utile lorsque vous souhaitez interroger des documents particuliers plutôt que l’ensemble de votre base de connaissances.
Créer des agents LLM pour RAG à partir de zéro et au-delà : un guide complet
Les agents LLM représentent une évolution avancée des systèmes RAG, ajoutant une couche d’intelligence et d’autonomie qui permet de résoudre des problèmes complexes en plusieurs étapes. Voyons comment construire et déployer ces agents.
Unite.AI
Unite.AI est une plateforme qui fournit des ressources et des outils pour construire des applications d’IA avancées, y compris des agents LLM pour RAG.
Comprendre les agents LLM
Les agents LLM sont des systèmes qui vont au-delà de la simple génération de réponses pour devenir des entités capables de planifier, de raisonner et d’agir de manière autonome.
Un agent LLM pour RAG combine les capacités de récupération d’information du RAG avec la capacité de planification et de prise de décision, permettant de résoudre des problèmes complexes qui nécessitent plusieurs étapes de raisonnement et de recherche d’information.
Les composants clés d’un agent LLM incluent :
- Agent/Cerveau : Le LLM central qui coordonne le processus de réflexion et de prise de décision.
- Mémoire : Systèmes pour stocker et récupérer des informations à court et long terme.
- Outils : Fonctionnalités spécifiques que l’agent peut utiliser (recherche web, calcul, RAG, etc.).
- Planification : Capacité à décomposer des problèmes complexes en étapes gérables.
- Réflexion : Capacité à évaluer ses propres réponses et à s’améliorer.
Configuration de l’environnement
Pour construire un agent LLM pour RAG, commençons par configurer notre environnement :
!pip install langchain langchain_community langchain_openai chromadb sentence-transformers
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType
from langchain.llms import OpenAI
from langchain.chains import RetrievalQA
from langchain.memory import ConversationBufferMemory
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma
from langchain.document_loaders import DirectoryLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
Étape 1 : Préparer les documents
La première étape consiste à préparer les documents qui serviront de base de connaissances à notre agent :
# Chargement et préparation des documents
loader = DirectoryLoader("./documents", glob="**/*.txt", loader_cls=TextLoader)
documents = loader.load()
# Découpage des documents
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
length_function=len,
)
chunks = text_splitter.split_documents(documents)
# Création des embeddings et de la base vectorielle
embedding_model = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2"
)
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embedding_model,
persist_directory="./chroma_db"
)
Étape 2 : Créer une chaîne d’assurance qualité basée sur la récupération
Ensuite, nous créons une chaîne RAG standard qui servira d’outil pour notre agent :
# Création de la chaîne RAG
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
qa_chain = RetrievalQA.from_chain_type(
llm=OpenAI(temperature=0),
chain_type="stuff",
retriever=retriever,
return_source_documents=True,
)
Étape 3 : interroger le système
Nous pouvons maintenant créer des outils que notre agent pourra utiliser :
# Définition des outils
tools = [
Tool(
name="Knowledge Base",
func=qa_chain.run,
description="Useful for answering questions about specific topics in the knowledge base. The input should be a question related to information in the documents."
),
# Ajoutez d'autres outils selon vos besoins (recherche web, calculatrice, etc.)
]
Étape 4 : Création d’un agent LLM
Maintenant, nous pouvons initialiser notre agent avec les outils définis et une mémoire pour maintenir le contexte de la conversation :
# Configuration de la mémoire
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
# Initialisation de l'agent
agent = initialize_agent(
tools,
OpenAI(temperature=0.7),
agent=AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION,
memory=memory,
verbose=True
)
# Test de l'agent
response = agent.run(input="Quels sont les principaux défis du changement climatique selon nos documents?")
print(response)
Cet agent peut maintenant utiliser la chaîne RAG comme un outil parmi d’autres pour répondre aux questions, tout en maintenant un contexte conversationnel.
Améliorer l’agent avec des techniques RAG avancées
Pour améliorer les performances de notre agent, nous pouvons implémenter plusieurs techniques RAG avancées :
- Recherche sémantique avec récupération de passages denses (DPR) :
from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import EmbeddingsFilter # Création d'un filtre d'embeddings embeddings_filter = EmbeddingsFilter( embeddings=embedding_model, similarity_threshold=0.8 ) # Création d'un retriever avec compression contextuelle compression_retriever = ContextualCompressionRetriever( base_compressor=embeddings_filter, base_retriever=retriever ) # Mise à jour de la chaîne RAG advanced_qa_chain = RetrievalQA.from_chain_type( llm=OpenAI(temperature=0), chain_type="stuff", retriever=compression_retriever, return_source_documents=True, )
- Expansion des requêtes :
from langchain.retrievers.multi_query import MultiQueryRetriever # Création d'un retriever multi-requêtes multi_query_retriever = MultiQueryRetriever.from_llm( retriever=retriever, llm=OpenAI(temperature=0.3) )
- Raffinement itératif :
from langchain.chains import LLMChain from langchain.prompts import PromptTemplate # Création d'une chaîne pour raffiner les requêtes refine_prompt = PromptTemplate( input_variables=["question", "context"], template="Given the context: {context}\nRefine this question: {question}\nRefined question:" ) refine_chain = LLMChain( llm=OpenAI(temperature=0.3), prompt=refine_prompt )
En intégrant ces techniques avancées, notre agent peut améliorer significativement la qualité et la pertinence de ses réponses, en particulier pour les questions complexes ou ambiguës.
Quelle est la meilleure option pour mon problème, Fine tuning ou RAG?
Lorsqu’il s’agit d’améliorer les performances d’un LLM pour une application spécifique, deux approches principales se démarquent : le fine-tuning et le RAG. Chacune a ses avantages et ses inconvénients, et le choix dépend de plusieurs facteurs.
Technique Fine tuning
Le fine-tuning consiste à réentraîner un LLM pré-entraîné sur un jeu de données spécifique à votre domaine ou à votre tâche, afin d’adapter ses connaissances et ses capacités à vos besoins particuliers.
Avantages du fine-tuning :
- Intégration profonde des connaissances : Les informations sont directement intégrées dans les poids du modèle, ce qui peut conduire à des réponses plus naturelles et cohérentes.
- Performance potentiellement supérieure : Pour des tâches très spécifiques et bien définies, un modèle fine-tuné peut surpasser un système RAG.
- Latence réduite : Pas besoin de recherche externe lors de l’inférence, ce qui peut réduire la latence.
- Fonctionnement hors ligne : Une fois fine-tuné, le modèle peut fonctionner sans accès à une base de données externe.
Inconvénients du fine-tuning :
- Coût et complexité : Le fine-tuning nécessite des ressources computationnelles importantes et une expertise technique.
- Données d’entraînement importantes : Nécessite généralement un grand nombre d’exemples de haute qualité.
- Mise à jour difficile : Pour intégrer de nouvelles informations, il faut généralement réentraîner le modèle.
- Risque d’oubli catastrophique : Le modèle peut « oublier » certaines de ses connaissances générales en s’adaptant trop à un domaine spécifique.
Quand choisir le fine-tuning :
- Pour des tâches très spécifiques et bien définies
- Lorsque vous disposez d’un grand jeu de données d’entraînement de qualité
- Quand la latence est critique
- Pour des applications où les connaissances évoluent peu
Comparaison avec RAG :
Critère | Fine-tuning | RAG |
---|---|---|
Mise à jour des connaissances | Difficile (réentraînement) | Facile (ajout de documents) |
Coût initial | Élevé | Modéré |
Coût d’exploitation | Faible | Modéré à élevé |
Latence | Faible | Modérée |
Précision pour des domaines spécifiques | Très bonne | Bonne |
Flexibilité | Limitée | Élevée |
Traçabilité | Limitée | Excellente |
Selon une étude de mars 2025, 57% du contenu généré par l’IA apparaît dans les 10 premières positions de recherche, comparé à 58% pour le contenu humain, montrant que les techniques comme le RAG et le fine-tuning permettent de produire du contenu de plus en plus indiscernable de celui créé par des humains.
Conclusion
Le RAG (Retrieval Augmented Generation) représente une avancée majeure dans le domaine des applications LLM, offrant une solution élégante aux limitations inhérentes des grands modèles de langage. En combinant la puissance générative des LLM avec un système de récupération d’information externe, le RAG permet de créer des applications IA plus précises, plus à jour et plus transparentes.
Dans ce guide, nous avons exploré en détail les différents aspects de la construction d’un système RAG :
- Les fondements théoriques des LLM et du RAG, comprenant leurs principes de base et leur fonctionnement
- Les composants essentiels d’un système RAG, de l’encodage à la génération en passant par la récupération
- Différentes approches pratiques pour implémenter un RAG, utilisant des frameworks comme LangChain et LlamaIndex
- Des architectures avancées comme les agents LLM qui étendent les capacités du RAG
- Une comparaison entre le RAG et le fine-tuning pour choisir la meilleure approche selon vos besoins
Le choix entre les différentes implémentations dépendra de vos besoins spécifiques, de vos contraintes techniques et de vos ressources. LangChain offre une grande flexibilité et de nombreuses intégrations, tandis que LlamaIndex propose des fonctionnalités optimisées pour la recherche documentaire. Ollama permet une approche entièrement locale, idéale pour les cas où la confidentialité des données est primordiale.
Quelle que soit l’approche choisie, il est important de porter une attention particulière à la qualité des données, à la stratégie de chunking, et à l’optimisation des prompts pour obtenir les meilleurs résultats.
À mesure que la technologie évolue, nous pouvons nous attendre à voir de nouvelles innovations dans le domaine du RAG, comme l’intégration de graphes de connaissances (Knowledge-Augmented Generation) ou l’utilisation de techniques d’apprentissage par renforcement pour améliorer la pertinence des récupérations.
Pour aller plus loin
Si vous souhaitez approfondir vos connaissances et compétences dans le domaine du RAG, voici quelques pistes à explorer :
- Techniques avancées d’indexation : Explorez des approches comme l’indexation hiérarchique ou l’indexation hybride qui combinent recherche sémantique et recherche par mots-clés.
- Évaluation et optimisation : Développez des métriques et des processus pour évaluer la qualité de votre système RAG et l’optimiser continuellement.
- Multi-modalité : Étendez votre système RAG pour traiter non seulement du texte, mais aussi des images, des vidéos ou des données structurées.
- Personnalisation utilisateur : Implémentez des mécanismes pour adapter les réponses au profil et aux préférences de chaque utilisateur.
- Déploiement à grande échelle : Explorez les défis et solutions pour déployer des systèmes RAG à grande échelle, en gérant des millions de documents et d’utilisateurs.
- Sécurité et éthique : Approfondissez les considérations éthiques et de sécurité liées à l’utilisation des systèmes RAG, notamment en ce qui concerne la confidentialité des données et les biais potentiels.
- Intégration avec d’autres systèmes : Découvrez comment intégrer votre système RAG avec d’autres technologies comme les bases de données graphes, les systèmes experts ou les plateformes d’analyse.
En 2025, les entreprises utilisant des technologies avancées comme le RAG et le Knowledge-Augmented Generation (KAG) réduisent jusqu’à 62% leurs erreurs factuelles par rapport aux approches traditionnelles, selon une étude d’Anthropic.
En continuant à explorer et à expérimenter dans ce domaine en rapide évolution, vous pourrez créer des applications LLM toujours plus intelligentes, précises et utiles, ouvrant la voie à une nouvelle génération d’assistants IA véritablement capables de comprendre et de répondre aux besoins des utilisateurs.
1 Comment