Apprends Ă connecter un LLM Ă tes propres documents
Tu as dĂ©jĂ utilisĂ© ChatGPT ou Claude pour poser des questions gĂ©nĂ©rales. Mais que faire quand tu veux interroger tes propres documents â un PDF de cours, une base de connaissances interne, ou ta collection de notes ?
C’est exactement le problĂšme que rĂ©sout le RAG (Retrieval-Augmented Generation). Dans cet article, tu vas construire pas Ă pas un moteur de RAG complet avec Python, en utilisant des modĂšles open-source et une base vectorielle. Ă la fin, tu pourras poser des questions en langage naturel Ă n’importe lequel de tes documents.
Qu’est-ce que le RAG ?
Le RAG, c’est un pattern qui combine recherche documentaire et gĂ©nĂ©ration de texte. ConcrĂštement :
- Tu découpes tes documents en petits morceaux (segments)
- Tu convertis chaque morceau en un vecteur (une représentation mathématique du sens)
- Tu stockes ces vecteurs dans une base vectorielle
- Quand un utilisateur pose une question, tu cherches les vecteurs les plus proches
- Tu envoies les morceaux trouvés + la question à un LLM, qui génÚre une réponse contextuelle
L’avantage ? Pas besoin de fine-tuner un modĂšle. Tu changes les documents, le comportement change. C’est modulaire, rapide Ă mettre en place, et trĂšs efficace.
Prérequis
Pour suivre ce tutoriel, installe les bibliothĂšques suivantes :
pip install chromadb sentence-transformers langchain langchain-community pypdfSi tu dĂ©butes avec Python et que tout ça te semble flou, dĂ©couvre notre formation Python â on part de zĂ©ro, jusqu’Ă coder tes propres projets IA.
Ătape 1 : Charger et dĂ©couper un document PDF
Commençons par charger un fichier PDF. Pour l’exemple, prenons un rapport ou un article que tu souhaites interroger.
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# Charge le PDF
loader = PyPDFLoader("mon_document.pdf")
documents = loader.load()
# Découpe en segments de 1000 caractÚres
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", ". ", " ", ""]
)
chunks = splitter.split_documents(documents)
print(f"Nombre de segments : {len(chunks)}")
print(f"Premier segment : {chunks[0].page_content[:100]}...")Le chunk_overlap de 200 caractĂšres garantit qu’un morceau d’information coupĂ© en deux ne soit pas perdu â la fin d’un segment chevauche le dĂ©but du suivant.
Ătape 2 : CrĂ©er les embeddings avec Sentence Transformers
Les embeddings sont le cĆur du RAG. Ce sont des vecteurs (listes de nombres) qui capturent le sens des phrases. Deux phrases similaires auront des vecteurs proches.
On utilise sentence-transformers, un framework open-source avec des modÚles pré-entraßnés :
from sentence_transformers import SentenceTransformer
# ModÚle français performant et léger
model = SentenceTransformer("dangvantuan/sentence-camembert-large")
# Convertir un texte en vecteur
texte = "Qu'est-ce que le RAG en intelligence artificielle ?"
vecteur = model.encode(texte)
print(f"Dimension du vecteur : {len(vecteur)}")
print(f"PremiĂšres valeurs : {vecteur[:5]}")đĄ Astuce : Tu peux utiliser un modĂšle multilingue (comme intfloat/multilingual-e5-small) si tes documents sont en anglais ET en français.
Ătape 3 : Stocker les vecteurs dans ChromaDB
Maintenant qu’on a les vecteurs, il faut les stocker pour pouvoir les chercher rapidement. On utilise ChromaDB, une base vectorielle lĂ©gĂšre qui tourne en local :
import chromadb
from chromadb.config import Settings
# Initialise la base vectorielle
client = chromadb.Client(Settings(
chroma_db_impl="duckdb+parquet",
persist_directory="./rag_db"
))
# Crée une collection
collection = client.create_collection(name="mes_documents")
# Ajoute les segments avec leurs vecteurs
for i, chunk in enumerate(chunks):
vecteur = model.encode(chunk.page_content).tolist()
collection.add(
embeddings=[vecteur],
documents=[chunk.page_content],
metadatas=[{"source": chunk.metadata.get("source", "inconnu")}],
ids=[f"chunk_{i}"]
)
print("â
Base vectorielle créée avec succÚs !")Chaque segment est stocké avec :
- Son embedding (le vecteur)
- Le texte original
- Des métadonnées (source, date, etc.)
Ătape 4 : Rechercher les documents pertinents
Quand un utilisateur pose une question, on la convertit en vecteur, puis on cherche les N segments les plus proches dans la base :
def recherche_documents(question, k=3):
"""Trouve les k segments les plus pertinents pour une question."""
vecteur_question = model.encode(question).tolist()
resultats = collection.query(
query_embeddings=[vecteur_question],
n_results=k
)
return resultats["documents"][0]
question = "Quels sont les avantages du RAG ?"
docs = recherche_documents(question)
print("\nđ Documents trouvĂ©s :")
for i, doc in enumerate(docs):
print(f"\n--- Résultat {i+1} ---")
print(doc[:200])La fonction collection.query() calcule automatiquement la similaritĂ© cosinus entre le vecteur de la question et tous les vecteurs stockĂ©s. Elle retourne les K plus proches voisins â c’est ultra-rapide, mĂȘme avec des millions de segments.
Ătape 5 : GĂ©nĂ©rer une rĂ©ponse avec un LLM
DerniÚre étape : envoyer les documents trouvés à un LLM pour générer une réponse contextuelle.
On peut utiliser OpenAI, ou un modĂšle local via Ollama :
from langchain_openai import ChatOpenAI
def repondre_question(question, documents):
"""GénÚre une réponse basée sur les documents trouvés."""
contexte = "\n\n".join(documents[:3])
prompt = f"""Tu es un assistant spécialisé. Réponds à la question
uniquement en utilisant les informations ci-dessous.
Si les documents ne contiennent pas la réponse, dis-le poliment.
DOCUMENTS :
{contexte}
QUESTION : {question}
RĂPONSE :"""
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
reponse = llm.invoke(prompt)
return reponse.content
# Test complet
question = "Qu'est-ce que le RAG et comment ça fonctionne ?"
docs = recherche_documents(question)
reponse = repondre_question(question, docs)
print(f"\nđ€ Question : {question}")
print(f"\nđŹ RĂ©ponse : {reponse}")đ Le secret : en passant les documents dans le contexte du prompt, on Ă©vite les hallucinations. Le LLM n’invente pas â il s’appuie sur des faits rĂ©els que tu lui fournis.
Aller plus loin
Ce pipeline de base peut ĂȘtre amĂ©liorĂ© de plusieurs façons :
- Hybrid search : combine recherche vectorielle et recherche par mots-clés (BM25) pour de meilleurs résultats
- Re-ranking : ajoute un modÚle de classement entre la recherche et la génération pour affiner les résultats
- Streaming : affiche la réponse mot par mot comme ChatGPT pour une meilleure UX
- Conversation mĂ©moire : conserve l’historique des Ă©changes pour des questions en follow-up
- Multi-format : supporte PDF, DOCX, HTML, pages web, et mĂȘme des bases de donnĂ©es SQL
Le RAG est devenu le standard de facto pour tous les projets d’IA documentaire. Pas cher, efficace, sans fine-tuning. Google, Notion, et des centaines de startups l’utilisent en production.
PrĂȘt Ă maĂźtriser Python pour l’IA ?
Ce tutoriel suppose que tu sais dĂ©jĂ exĂ©cuter du Python, installer des packages et manipuler des fichiers. Si ce n’est pas encore le cas, pas de panique â notre formation Python : les fondamentaux te donne toutes les bases pour coder en Python, du premier script jusqu’Ă tes premiers projets IA.
Construis ton avenir dans l’IA, une ligne de code Ă la fois. đ