Créer un système RAG de zéro
Comment connecter un LLM à vos propres données locales. Un guide complet pour l'implémentation d'un RAG avec Python et ChromaDB.
La plus grande limitation des LLMs actuels est leur base de connaissances statique. Si vous interrogez Llama 3 sur la documentation interne de votre entreprise rédigée hier, il hallucinera une réponse.
Pour résoudre ce problème sans avoir à ré-entraîner le modèle (ce qui serait extrêmement coûteux), l’industrie a standardisé l’architecture RAG (Retrieval-Augmented Generation).
L’architecture RAG expliquée simplement
Le concept tient en trois étapes logiques.
- La Recherche (Retrieval). L’utilisateur pose une question. Votre backend cherche dans votre base de données les documents pertinents liés à cette question.
- L’Augmentation. Vous prenez la question de l’utilisateur, et vous y collez le texte des documents trouvés.
- La Génération. Vous envoyez ce méga-prompt au LLM en lui disant “Réponds à la question en utilisant uniquement les documents fournis ci-dessus”.
Le LLM n’a plus besoin de “savoir”, il lui suffit de “lire” ce qu’on lui donne.
Les Embeddings au coeur de la recherche
Faire une simple recherche par mots-clés (comme un LIKE %mot% en SQL) ne suffit pas pour du langage naturel. On utilise donc des modèles d’Embeddings.
Un modèle d’embedding lit une phrase et la transforme en un vecteur mathématique (un tableau de milliers de nombres). La magie des embeddings, c’est que des phrases sémantiquement proches (ex: “J’ai un problème réseau” et “Le routeur est tombé”) auront des vecteurs proches géométriquement.
On stocke ces vecteurs dans une base de données spéciale appelée Base Vectorielle (comme ChromaDB, Qdrant ou pgvector).
Implémentation basique en Python
À quoi ressemble l’ingestion d’un document et la recherche avec LangChain et ChromaDB.
1. Préparer et découper les données
On ne peut pas envoyer un livre de 500 pages au LLM. On découpe donc le texte en petits “chunks” d’environ 1000 caractères.
from langchain.document_loaders import TextLoader
from langchain.text_splitter import CharacterTextSplitter
# Chargement du fichier brut
loader = TextLoader("documentation_interne.txt")
documents = loader.load()
# Découpage en morceaux avec un léger chevauchement pour ne pas couper de phrases
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
chunks = text_splitter.split_documents(documents)
2. Créer la base vectorielle
On transforme nos chunks en vecteurs via un modèle open-source d’Hugging Face, puis on les stocke localement.
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma
# Ce modèle d'embedding est petit et très rapide
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
# Création de la base Chroma en mémoire
db = Chroma.from_documents(chunks, embeddings)
3. Interroger la base et générer la réponse
Quand l’utilisateur pose sa question, on la vectorise, on cherche les morceaux de texte les plus proches, et on assemble le prompt.
question = "Comment réinitialiser le mot de passe admin ?"
# Récupération des 3 morceaux de texte les plus pertinents
resultats = db.similarity_search(question, k=3)
# Concaténation des textes trouvés
contexte = "\n---\n".join([doc.page_content for doc in resultats])
# Construction du prompt final
prompt_final = f"""
Tu es un assistant technique. Utilise UNIQUEMENT le contexte fourni pour répondre à la question.
Si la réponse n'est pas dans le contexte, dis que tu ne sais pas.
CONTEXTE :
{contexte}
QUESTION : {question}
"""
# Il ne reste plus qu'à envoyer `prompt_final` à Ollama ou tout autre LLM.
Le RAG est devenu la base de la plupart des applications d’IA en entreprise. Des techniques plus avancées (Re-ranking, Hybrid Search) permettent ensuite d’améliorer la précision de la recherche.