Intelligence Articielle Générative
Créer une Application
RAG avec LLM
Retrieval-Augmented Generation
De la théorie à la pratique :
PDFs → Vector Database → Chatbot intelligent
Dr BADR EL KHALYLY
Docteur en Informatique
Intelligence Articielle & Data Science
Cours RAG LLM Dr BADR EL KHALYLY
Table des matières
1 Introduction : Pourquoi le RAG ? 2
1.1 Le problème des LLM classiques . . . . . . . . . . . . . . . . . . . . . . 2
1.2 La solution : RAG (Retrieval-Augmented Generation) . . . . . . . . . . 2
2 Architecture Générale d'un Système RAG 3
2.1 Vue d'ensemble . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.2 Les composants clés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
3 Concepts Fondamentaux 4
3.1 L'Embedding : transformer du texte en vecteurs . . . . . . . . . . . . . 4
3.2 La Vector Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
3.3 Le Chunking (découpage) . . . . . . . . . . . . . . . . . . . . . . . . . 5
4 Stack Technologique 6
4.1 Vue d'ensemble des outils . . . . . . . . . . . . . . . . . . . . . . . . . 6
4.2 Installation de l'environnement . . . . . . . . . . . . . . . . . . . . . . 6
5 Implémentation Étape par Étape 7
5.1 Étape 1 Chargement et préparation des PDFs . . . . . . . . . . . . . 7
5.2 Étape 2 Création de la Vector Database . . . . . . . . . . . . . . . . 7
5.3 Étape 3 Chargement de la base existante . . . . . . . . . . . . . . . . 8
5.4 Étape 4 Construction de la chaîne RAG . . . . . . . . . . . . . . . . 9
5.5 Étape 5 Interface utilisateur avec Streamlit . . . . . . . . . . . . . . . 11
6 Structure du Projet Complet 13
7 Techniques d'Optimisation 13
7.1 Améliorer la qualité de la récupération . . . . . . . . . . . . . . . . . . 13
7.2 Évaluation du système RAG . . . . . . . . . . . . . . . . . . . . . . . . 14
8 Récapitulatif et Feuille de Route 15
8.1 Ce que vous savez maintenant . . . . . . . . . . . . . . . . . . . . . . . 15
8.2 Feuille de route d'apprentissage . . . . . . . . . . . . . . . . . . . . . . 15
8.3 Ressources pour aller plus loin . . . . . . . . . . . . . . . . . . . . . . . 15
1
Cours RAG LLM Dr BADR EL KHALYLY
1 Introduction : Pourquoi le RAG ?
1.1. Le problème des LLM classiques
Les grands modèles de langage (LLM) comme GPT-4 ou Claude sont remarquables,
mais ils sourent de deux limitations majeures :
(1) Connaissance gée dans le temps : le modèle est entraîné jusqu'à une
certaine date (cuto). Il ne connaît pas vos documents internes, vos rapports
récents, vos PDF d'entreprise.
(2) Hallucinations : face à une question dont il n'a pas la réponse exacte, le
modèle peut inventer une réponse convaincante mais fausse.
Problème concret
Quel est le budget alloué au projet X dans le rapport Q3 2024 ?
Un LLM seul ne peut pas répondre : ce document n'a jamais été dans ses données
d'entraînement.
1.2. La solution : RAG (Retrieval-Augmented Generation)
Dénition du RAG
Le RAG est une architecture qui combine deux étapes :
1. Retrieval (Récupération) : chercher les passages pertinents dans une base de
documents.
2. Augmented Generation (Génération augmentée) : injecter ces passages dans
le contexte du LLM avant de générer la réponse.
En résumé : on donne au LLM le bon contexte juste avant de lui poser la
question.
2
Cours RAG LLM Dr BADR EL KHALYLY
2 Architecture Générale d'un Système RAG
2.1. Vue d'ensemble
Un pipeline RAG se décompose en deux grandes phases :
Phase 1 : Indexation (oine)
PDFs Découpage Embedding Vector
sources en chunks (vecteurs) Database
Phase 2 : Requête (online)
Question Rechercheembed Vector chunks Réponse
sémantique DB LLM
utilisateur nale
2.2. Les composants clés
1. Chargeur de documents : lit et parse les chiers PDF.
2. Splitter (découpeur) : divise les textes longs en petits morceaux (chunks )
de 2001000 tokens.
3. Modèle d'embedding : transforme chaque chunk en vecteur numérique (ex.
768 ou 1536 dimensions).
4. Vector Database : stocke et indexe ces vecteurs pour une recherche rapide
par similarité cosinus.
5. LLM : génère la réponse nale à partir de la question + contexte récupéré.
6. Orchestrateur : (LangChain, LlamaIndex) relie tous les composants.
3
Cours RAG LLM Dr BADR EL KHALYLY
3 Concepts Fondamentaux
3.1. L'Embedding : transformer du texte en vecteurs
Qu'est-ce qu'un embedding ?
Un embedding est une représentation numérique d'un texte sous forme de vec-
teur. Deux textes sémantiquement proches produiront des vecteurs proches dans
l'espace mathématique.
Exemple intuitif :
dim 2
chiot
chien
canin
voiture
automobile
dim 1
La similarité entre deux vecteurs se mesure avec la similarité cosinus :
A·B
sim(A, B) = ∈ [−1, 1]
∥A∥ ∥B∥
Plus la valeur est proche de 1, plus les textes sont sémantiquement similaires.
3.2. La Vector Database
Vector Database
Une base de données vectorielle stocke des vecteurs (embeddings) et permet
de retrouver rapidement les k vecteurs les plus proches d'un vecteur requête (Ap-
proximate Nearest Neighbor search ).
Principales solutions :
Solution Type Idéal pour
ChromaDB Local, open-source Prototypage rapide
FAISS Local (Meta) Haute performance locale
Pinecone Cloud managé Production scalable
Weaviate Open-source/Cloud Multimodal
Qdrant Open-source/Cloud Filtres avancés
4
Cours RAG LLM Dr BADR EL KHALYLY
Conseil pour débuter
Utilisez ChromaDB pour vos premiers projets : installation en une ligne pip,
stockage local sur disque, aucune conguration requise.
3.3. Le Chunking (découpage)
Pourquoi découper les documents ?
Les LLM ont une fenêtre de contexte limitée (ex. 4096, 8192 tokens). On ne
peut pas injecter un document de 100 pages entier. Il faut donc le découper en petits
morceaux.
PDF (100 pages)
Splitter
Texte brut (500K chars) C1 C2 C3 C4 C5 ...
Stratégies de chunking :
Taille xe (CharacterTextSplitter) : simple, 5001000 chars avec che-
vauchement (overlap ) de 50100 chars pour préserver le contexte.
Récursif (RecursiveCharacterTextSplitter) : respecte la structure du
texte (paragraphes, phrases). Recommandé.
Sémantique : découpe selon le sens. Plus complexe, meilleurs résultats.
5
Cours RAG LLM Dr BADR EL KHALYLY
4 Stack Technologique
4.1. Vue d'ensemble des outils
Interface utilisateur : Streamlit / Gradio / FastAPI
Orchestration : LangChain / LlamaIndex
LLM : OpenAI (GPT-4) / Anthropic (Claude) / Ollama (local)
Embeddings : OpenAI text-embedding / HuggingFace (gratuit)
Vector DB : ChromaDB / FAISS / Pinecone
Sources de données : PDFs, DOCX, TXT, Web...
4.2. Installation de l'environnement
Terminal Installation des dépendances Python
1 # Creer un environnement virtuel
2 python -m venv venv_rag
3 source venv_rag/bin/activate # Linux/Mac
4 # venv_rag\Scripts\activate # Windows
5
6 # Installer les packages essentiels
7 pip install langchain langchain-community
8 pip install langchain-openai # ou langchain-anthropic
9 pip install chromadb # Vector DB locale
10 pip install pypdf # Lecture de PDFs
11 pip install sentence-transformers # Embeddings gratuits (
HuggingFace)
12 pip install streamlit # Interface web
13 pip install python-dotenv # Gestion des cles API
6
Cours RAG LLM Dr BADR EL KHALYLY
5 Implémentation Étape par Étape
5.1. Étape 1 Chargement et préparation des PDFs
etape1_chargement.py
1 import os
2 from langchain_community.document_loaders import PyPDFLoader,
DirectoryLoader
3 from langchain.text_splitter import RecursiveCharacterTextSplitter
4
5 # -- 1. Charger un seul PDF ----------------------------------------
6 loader = PyPDFLoader("documents/rapport_annuel.pdf")
7 pages = [Link]()
8
9 print(f"Nombre de pages : {len(pages)}")
10 print(f"Extrait (200 chars) : {pages[0].page_content[:200]}")
11
12 # -- 2. Charger tous les PDFs d’un dossier ------------------------
13 loader_dir = DirectoryLoader(
14 path="documents/",
15 glob="**/*.pdf",
16 loader_cls=PyPDFLoader
17 )
18 tous_les_docs = loader_dir.load()
19 print(f"Documents charges : {len(tous_les_docs)}")
20
21 # -- 3. Decouper en chunks -----------------------------------------
22 splitter = RecursiveCharacterTextSplitter(
23 chunk_size=1000, # Taille max d’un chunk (en caracteres)
24 chunk_overlap=150, # Chevauchement pour garder le contexte
25 separators=["\n\n", "\n", ". ", " ", ""]
26 )
27
28 chunks = splitter.split_documents(tous_les_docs)
29 print(f"Nombre de chunks : {len(chunks)}")
30 print(f"Exemple de chunk :\n{chunks[0].page_content[:300]}")
Bonne pratique
Dénissez un chunk_overlap d'environ 1015% de la taille du chunk. Cela évite
de couper une information importante entre deux chunks.
5.2. Étape 2 Création de la Vector Database
etape2_vectordb.py
1 import os
2 from langchain_community.vectorstores import Chroma
3 from langchain_community.embeddings import HuggingFaceEmbeddings
4 # Alternatif payant : from langchain_openai import OpenAIEmbeddings
7
Cours RAG LLM Dr BADR EL KHALYLY
5
6 # -- 1. Initialiser le modele d’embedding -------------------------
7 # Option A : HuggingFace (100% GRATUIT, tourne en local)
8 embedding_model = HuggingFaceEmbeddings(
9 model_name="sentence-transformers/paraphrase-multilingual-mpnet-
base-v2",
10 # Ce modele supporte le francais !
11 )
12
13 # Option B : OpenAI (payant, tres performant)
14 # embedding_model = OpenAIEmbeddings(
15 # model="text-embedding-3-small",
16 # openai_api_key=[Link]("OPENAI_API_KEY")
17 # )
18
19 # -- 2. Creer la Vector DB et indexer les chunks -------------------
20 CHROMA_PATH = "chroma_db" # Dossier de persistance sur disque
21
22 vectordb = Chroma.from_documents(
23 documents=chunks, # Nos chunks de l’etape 1
24 embedding=embedding_model,
25 persist_directory=CHROMA_PATH # Sauvegarde sur disque
26 )
27
28 print(f"Base vectorielle creee : {vectordb._collection.count()} chunks
indexes")
Modèle multilingue recommandé
Le modèle paraphrase-multilingual-mpnet-base-v2 de HuggingFace
est gratuit, fonctionne en français, et produit des résultats très satisfaisants
pour la plupart des cas d'usage.
5.3. Étape 3 Chargement de la base existante
etape3_chargement_db.py
1 from langchain_community.vectorstores import Chroma
2 from langchain_community.embeddings import HuggingFaceEmbeddings
3
4 # Recharger une base deja creee (sans re-indexer)
5 embedding_model = HuggingFaceEmbeddings(
6 model_name="sentence-transformers/paraphrase-multilingual-mpnet-
base-v2"
7 )
8
9 vectordb = Chroma(
10 persist_directory="chroma_db",
11 embedding_function=embedding_model
12 )
13
14 # Tester une recherche par similarite
15 resultats = vectordb.similarity_search(
16 query="Quel est le chiffre d’affaires du Q3 ?",
8
Cours RAG LLM Dr BADR EL KHALYLY
17 k=4 # Retourner les 4 chunks les plus pertinents
18 )
19
20 for i, doc in enumerate(resultats):
21 print(f"\n--- Resultat {i+1} ---")
22 print(f"Source : {[Link](’source’,’?’)}, "
23 f"Page : {[Link](’page’,’?’)}")
24 print(doc.page_content[:300])
5.4. Étape 4 Construction de la chaîne RAG
etape4_chaine_rag.py
1 import os
2 from langchain_openai import ChatOpenAI
3 from [Link] import RetrievalQA
4 from [Link] import PromptTemplate
5
6 # -- 1. Initialiser le LLM ----------------------------------------
7 llm = ChatOpenAI(
8 model_name="gpt-4o-mini", # Modele economique
9 temperature=0, # 0 = reponses deterministes
10 openai_api_key=[Link]("OPENAI_API_KEY")
11 )
12
13 # -- 2. Creer le retriever -----------------------------------------
14 retriever = vectordb.as_retriever(
15 search_type="similarity",
16 search_kwargs={"k": 4} # Recuperer 4 chunks
17 )
18
19 # -- 3. Definir un prompt personnalise ----------------------------
20 PROMPT_TEMPLATE = """
21 Vous etes un assistant expert qui repond aux questions
22 en vous basant UNIQUEMENT sur le contexte fourni ci-dessous.
23 Si la reponse n’est pas dans le contexte, dites :
24 "Je ne trouve pas cette information dans les documents fournis."
25
26 CONTEXTE :
27 {context}
28
29 QUESTION :
30 {question}
31
32 REPONSE (en francais, structuree et precise) :
33 """
34
35 prompt = PromptTemplate(
36 input_variables=["context", "question"],
37 template=PROMPT_TEMPLATE
38 )
39
40 # -- 4. Assembler la chaine RAG ------------------------------------
41 chaine_rag = RetrievalQA.from_chain_type(
42 llm=llm,
9
Cours RAG LLM Dr BADR EL KHALYLY
43 chain_type="stuff", # Injecte tout le contexte d’un
coup
44 retriever=retriever,
45 return_source_documents=True, # Retourner les sources
46 chain_type_kwargs={"prompt": prompt}
47 )
48
49 # -- 5. Poser une question -----------------------------------------
50 question = "Quelles sont les conclusions principales du rapport ?"
51 resultat = chaine_rag.invoke({"query": question})
52
53 print("REPONSE :")
54 print(resultat["result"])
55
56 print("\nSOURCES UTILISEES :")
57 for doc in resultat["source_documents"]:
58 print(f" - {[Link](’source’)}, page {[Link](’
page’)}")
10
Cours RAG LLM Dr BADR EL KHALYLY
5.5. Étape 5 Interface utilisateur avec Streamlit
[Link] Interface Web Streamlit
1 import streamlit as st
2 import os
3 from langchain_community.vectorstores import Chroma
4 from langchain_community.embeddings import HuggingFaceEmbeddings
5 from langchain_openai import ChatOpenAI
6 from [Link] import RetrievalQA
7 from [Link] import PromptTemplate
8
9 st.set_page_config(page_title="RAG - Analyse de PDFs", page_icon="[
livre]")
10 [Link]("Chatbot RAG -- Analysez vos PDFs")
11 [Link]("*Posez des questions sur vos documents PDF*")
12
13 # -- Cache : ne charger les modeles qu’une seule fois -------------
14 @st.cache_resource
15 def charger_chaine():
16 embeddings = HuggingFaceEmbeddings(
17 model_name="sentence-transformers/paraphrase-multilingual-mpnet
-base-v2"
18 )
19 vectordb = Chroma(
20 persist_directory="chroma_db",
21 embedding_function=embeddings
22 )
23 llm = ChatOpenAI(model_name="gpt-4o-mini", temperature=0)
24 retriever = vectordb.as_retriever(search_kwargs={"k": 4})
25
26 prompt = PromptTemplate(
27 input_variables=["context", "question"],
28 template="""Repondez en francais uniquement avec le contexte
fourni.
29 Contexte: {context}\nQuestion: {question}\nReponse:"""
30 )
31
32 return RetrievalQA.from_chain_type(
33 llm=llm, retriever=retriever,
34 return_source_documents=True,
35 chain_type_kwargs={"prompt": prompt}
36 )
37
38 # -- Historique de conversation ------------------------------------
39 if "messages" not in st.session_state:
40 st.session_state.messages = []
41
42 # Afficher l’historique
43 for msg in st.session_state.messages:
44 with st.chat_message(msg["role"]):
45 [Link](msg["content"])
46
47 # -- Zone de saisie ------------------------------------------------
48 if question := st.chat_input("Posez votre question..."):
49 st.session_state.[Link]({"role": "user", "content":
question})
11
Cours RAG LLM Dr BADR EL KHALYLY
50 with st.chat_message("user"):
51 [Link](question)
52
53 with st.chat_message("assistant"):
54 with [Link]("Recherche en cours..."):
55 chaine = charger_chaine()
56 resultat = [Link]({"query": question})
57
58 [Link](resultat["result"])
59
60 with [Link]("Sources utilisees"):
61 for doc in resultat["source_documents"]:
62 [Link](
63 f"(*) {[Link](’source’,’?’)} "
64 f"| Page {[Link](’page’,’?’)}"
65 )
66
67 st.session_state.[Link](
68 {"role": "assistant", "content": resultat["result"]}
69 )
Lancer l'application :
Terminal
1 streamlit run [Link]
2 # Ouvrir [Link] dans le navigateur
12
Cours RAG LLM Dr BADR EL KHALYLY
6 Structure du Pro jet Complet
Structure des chiers du projet
1 rag_project/
2 |
3 |-- documents/ # Vos fichiers PDF source
4 | |-- rapport_2024.pdf
5 | |-- guide_technique.pdf
6 | ‘-- ...
7 |
8 |-- chroma_db/ # Base vectorielle (auto-generee)
9 |
10 |-- etape1_chargement.py # Script d’indexation des PDFs
11 |-- etape2_vectordb.py # Creation de la Vector DB
12 |-- [Link] # Interface Streamlit
13 |
14 |-- .env # Cles API (ne jamais versionner !)
15 | # OPENAI_API_KEY=sk-...
16 |
17 ‘-- [Link] # Dependances Python
Sécurité Ne jamais exposer vos clés API
Créez un chier .env à la racine du projet avec vos clés API. Ajoutez .env à votre
.gitignore pour ne jamais le publier. Utilisez python-dotenv pour charger
les variables : from dotenv import load_dotenv; load_dotenv()
7 Techniques d'Optimisation
7.1. Améliorer la qualité de la récupération
1. Hybrid Search : combiner la recherche vectorielle (sémantique) avec BM25
(mots-clés). Utile quand les noms propres ou codes sont importants.
2. Re-ranking : après avoir récupéré les k premiers chunks, les re-classer avec
un modèle Cross-Encoder plus précis (ex. cross-encoder/ms-marco).
3. MMR (Maximal Marginal Relevance) : diversier les résultats pour évi-
ter de retourner des chunks trop similaires entre eux :
1 retriever = vectordb.as_retriever(search_type="mmr",
2 search_kwargs={"k":6, "fetch_k":20})
4. Contextual Compression : extraire seulement les phrases pertinentes d'un
chunk avant injection, pour économiser les tokens.
5. Métadonnées enrichies : ajouter au moment de l'indexation le nom du
chier, l'auteur, la date, le numéro de page comme métadonnées ltrables.
13
Cours RAG LLM Dr BADR EL KHALYLY
7.2. Évaluation du système RAG
Un bon système RAG doit être évalué sur quatre critères :
Fidélité (Faithfulness) Pertinence (Relevancy)
La réponse est-elle an- Les chunks récupé-
crée dans les sources ? rés sont-ils pertinents ?
Exactitude (Correctness) Complétude
La réponse est-elle Toute l'information né-
factuellement juste ? cessaire est-elle présente ?
Outil recommandé : RAGAS (pip install ragas) framework d'évaluation au-
tomatique des pipelines RAG.
14
Cours RAG LLM Dr BADR EL KHALYLY
8 Récapitulatif et Feuille de Route
8.1. Ce que vous savez maintenant
Points clés retenus
✓ Le RAG résout les limitations des LLM en leur fournissant du contexte
ciblé.
✓ Les PDFs sont découpés en chunks, transformés en vecteurs (embeddings)
et stockés dans une Vector DB.
✓ La question de l'utilisateur est elle aussi convertie en vecteur pour trouver
les chunks les plus similaires (semantic search ).
✓ Ces chunks sont injectés dans le prompt du LLM qui génère une réponse
grounded dans les sources.
✓ LangChain + ChromaDB + HuggingFace forment un trio entièrement
open-source pour débuter.
8.2. Feuille de route d'apprentissage
Niveau 1 Niveau 2 Niveau 3 Niveau 4
RAG basique avec ChromaDB Hybrid Search + Métadonnées Re-ranking + Streaming Agents + Multi-source
8.3. Ressources pour aller plus loin
Doc [Link] Documentation ocielle LangChain
Doc [Link] Documentation ChromaDB
Code [Link]/langchain-ai/rag-from-scratch RAG from scratch
Éval [Link] Framework RAGAS d'évaluation
Modèles [Link]/sentence-transformers Modèles d'embedding gra-
tuits
Bon courage dans votre implémentation !
La connaissance sans la pratique est inutile,
la pratique sans la connaissance est dangereuse.
Dr BADR EL KHALYLY
Intelligence Articielle & Data Science
15