spinny:~/writing $ less rag-langchain-deep-dive.md
12Les Large Language Models (LLMs) comme GPT-4 et Claude sont extraordinairement puissants, mais ils souffrent d'une limitation fondamentale : leurs connaissances sont figées au moment de l'entraînement. Ils ne peuvent pas accéder à vos documents internes, à votre base de données ou à des informations en temps réel. La **Retrieval-Augmented Generation (RAG)** résout exactement ce problème en combinant la puissance générative des LLMs avec la capacité de récupérer des informations à partir de sources externes.34## Le Problème : Les Limites des LLMs56Avant de parler de RAG, il est important de comprendre pourquoi nous en avons besoin.781. **Connaissances statiques** : Un LLM ne sait que ce qu'il a vu pendant l'entraînement. Si vous lui demandez un événement survenu après sa date de coupure, il ne peut pas répondre.92. **Hallucinations** : Quand un LLM ne connaît pas la réponse, il a tendance à en inventer une, générant des informations plausibles mais complètement fausses.103. **Pas d'accès aux données privées** : Un LLM générique n'a pas accès à la documentation interne de votre entreprise, aux tickets ou à votre base de code.1112La RAG résout ces trois problèmes en fournissant au modèle un **contexte pertinent** récupéré depuis des sources externes au moment de la requête.1314## Qu'est-ce que la RAG ?1516La Retrieval-Augmented Generation est une architecture qui enrichit le prompt envoyé à un LLM avec des informations récupérées depuis une base de connaissances externe. Au lieu de s'appuyer uniquement sur les connaissances paramétriques du modèle, la RAG **recherche** d'abord les informations pertinentes puis les **injecte** dans le prompt, permettant au modèle de générer des réponses précises et fondées.1718```mermaid19graph LR20 User["Utilisateur"] -- "Question" --> Retriever21 Retriever -- "Recherche documents\npertinents" --> VectorStore["Vector Store"]22 VectorStore -- "Documents\npertinents" --> Retriever23 Retriever -- "Contexte + Question" --> LLM24 LLM -- "Réponse\nfondée" --> User25```2627## Comment la RAG Fonctionne en Détail2829L'architecture RAG se compose de deux phases principales : l'**Indexation** (hors ligne) et la **Récupération + Génération** (en ligne).3031### Phase 1 : Indexation (Ingestion des Documents)3233La phase d'indexation prépare vos documents pour la recherche sémantique. Elle se compose de quatre étapes.3435```mermaid36graph TD37 A["Documents\n(PDF, HTML, MD, DB)"] --> B["Document Loader"]38 B --> C["Text Splitter"]39 C --> D["Chunks de Texte"]40 D --> E["Modèle d'Embedding"]41 E --> F["Vecteurs Numériques"]42 F --> G["Vector Store\n(ChromaDB, Pinecone, FAISS)"]43```4445#### 1. Chargement des Documents4647Les documents peuvent provenir de n'importe quelle source : fichiers PDF, pages web, bases de données, fichiers Markdown, APIs. Le **Document Loader** lit ces documents et les convertit en texte structuré.4849#### 2. Découpage du Texte (Chunking)5051Les LLMs ont une fenêtre de contexte limitée, et les documents peuvent être très longs. Le **Text Splitter** divise les documents en fragments plus petits appelés *chunks*. La qualité du chunking est critique : des chunks trop petits perdent le contexte, des chunks trop grands diluent la pertinence.5253Les stratégies les plus courantes sont :54- **Recursive Character Splitting** : Divise le texte récursivement en utilisant des séparateurs comme `\n\n`, `\n`, `. `, en respectant la structure du document.55- **Semantic Splitting** : Utilise les embeddings pour trouver les points de rupture naturels dans le texte.56- **Chunk Overlap** : Inclut un chevauchement entre les chunks consécutifs pour préserver le contexte aux frontières.5758#### 3. Embedding5960Chaque chunk est transformé en un **vecteur numérique** (embedding) via un modèle d'embedding (comme `text-embedding-3-small` d'OpenAI). Ces vecteurs capturent le sens sémantique du texte : des phrases avec des significations similaires auront des vecteurs proches dans l'espace multidimensionnel.6162#### 4. Vector Store6364Les vecteurs sont sauvegardés dans un **Vector Store** (ou base de données vectorielle), comme ChromaDB, Pinecone, Weaviate ou FAISS. Cette base de données est optimisée pour la **recherche de similarité** : étant donné une requête, elle trouve les vecteurs (et donc les chunks de texte) les plus similaires.6566### Phase 2 : Récupération + Génération6768Quand l'utilisateur pose une question :69701. La question est transformée en embedding en utilisant le même modèle d'embedding.712. Le Vector Store trouve les chunks les plus similaires via la **recherche de similarité** (typiquement la similarité cosinus ou la distance euclidienne).723. Les chunks récupérés sont insérés dans le prompt comme contexte.734. Le LLM génère la réponse basée sur le contexte fourni.7475## Construire une Pipeline RAG avec LangChain7677**LangChain** est le framework Python (et JavaScript) le plus populaire pour construire des applications basées sur les LLMs. Il fournit des abstractions de haut niveau pour chaque composant de la pipeline RAG.7879### Installation8081```bash82pip install langchain langchain-openai langchain-community chromadb83```8485### Étape 1 : Charger les Documents8687LangChain fournit des dizaines de Document Loaders pour différentes sources de données.8889```python90from langchain_community.document_loaders import (91 PyPDFLoader,92 WebBaseLoader,93 DirectoryLoader,94 TextLoader,95)9697# Charger un PDF98pdf_loader = PyPDFLoader("docs/manuel.pdf")99pdf_docs = pdf_loader.load()100101# Charger une page web102web_loader = WebBaseLoader("https://docs.example.com/guide")103web_docs = web_loader.load()104105# Charger tous les fichiers .md d'un répertoire106dir_loader = DirectoryLoader("./knowledge_base", glob="**/*.md", loader_cls=TextLoader)107md_docs = dir_loader.load()108109all_docs = pdf_docs + web_docs + md_docs110```111112### Étape 2 : Découper les Documents en Chunks113114```python115from langchain.text_splitter import RecursiveCharacterTextSplitter116117text_splitter = RecursiveCharacterTextSplitter(118 chunk_size=1000,119 chunk_overlap=200,120 separators=["\n\n", "\n", ". ", " ", ""],121)122123chunks = text_splitter.split_documents(all_docs)124print(f"Documents originaux : {len(all_docs)}, Chunks : {len(chunks)}")125```126127Le paramètre `chunk_overlap` est crucial : il crée un chevauchement entre les chunks consécutifs pour que le contexte ne soit pas perdu aux frontières.128129### Étape 3 : Créer les Embeddings et le Vector Store130131```python132from langchain_openai import OpenAIEmbeddings133from langchain_community.vectorstores import Chroma134135embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")136137vectorstore = Chroma.from_documents(138 documents=chunks,139 embedding=embedding_model,140 persist_directory="./chroma_db",141)142```143144### Étape 4 : Créer le Retriever145146Le retriever est le composant qui, étant donné une requête, récupère les chunks les plus pertinents du vector store.147148```python149retriever = vectorstore.as_retriever(150 search_type="similarity",151 search_kwargs={"k": 4},152)153154relevant_docs = retriever.invoke("Comment fonctionne l'authentification ?")155for doc in relevant_docs:156 print(doc.page_content[:200])157 print("---")158```159160### Étape 5 : Construire la Chain RAG161162Maintenant, assemblons le tout avec un LLM et un template de prompt.163164```python165from langchain_openai import ChatOpenAI166from langchain_core.prompts import ChatPromptTemplate167from langchain_core.runnables import RunnablePassthrough168from langchain_core.output_parsers import StrOutputParser169170llm = ChatOpenAI(model="gpt-4o", temperature=0)171172prompt = ChatPromptTemplate.from_template("""173Réponds à la question en te basant uniquement sur le contexte fourni.174Si le contexte ne contient pas assez d'informations, dis que tu ne sais pas.175176Contexte :177{context}178179Question : {question}180181Réponse :182""")183184def format_docs(docs):185 return "\n\n".join(doc.page_content for doc in docs)186187rag_chain = (188 {"context": retriever | format_docs, "question": RunnablePassthrough()}189 | prompt190 | llm191 | StrOutputParser()192)193194response = rag_chain.invoke("Comment fonctionne l'authentification dans le système ?")195print(response)196```197198## Techniques RAG Avancées199200La pipeline de base fonctionne bien, mais il existe plusieurs techniques pour améliorer significativement la qualité des réponses.201202### Multi-Query Retrieval203204Parfois, la requête de l'utilisateur est ambiguë ou n'est pas alignée avec le langage utilisé dans les documents. Le **Multi-Query Retriever** génère automatiquement des variantes de la question originale pour capturer plusieurs perspectives.205206```python207from langchain.retrievers import MultiQueryRetriever208209multi_retriever = MultiQueryRetriever.from_llm(210 retriever=vectorstore.as_retriever(),211 llm=llm,212)213214docs = multi_retriever.invoke("Quelles sont les bonnes pratiques de sécurité ?")215```216217### Contextual Compression218219Tout le contenu d'un chunk n'est pas pertinent pour la requête. Le **Contextual Compression Retriever** utilise un LLM pour extraire uniquement les parties pertinentes de chaque chunk récupéré.220221```python222from langchain.retrievers import ContextualCompressionRetriever223from langchain.retrievers.document_compressors import LLMChainExtractor224225compressor = LLMChainExtractor.from_llm(llm)226compression_retriever = ContextualCompressionRetriever(227 base_compressor=compressor,228 base_retriever=retriever,229)230```231232### Hybrid Search233234La recherche purement sémantique n'est pas toujours optimale. L'**Hybrid Search** combine la recherche sémantique (embeddings) avec la recherche lexicale (BM25, correspondance de mots-clés) pour obtenir de meilleurs résultats.235236```python237from langchain.retrievers import EnsembleRetriever238from langchain_community.retrievers import BM25Retriever239240bm25_retriever = BM25Retriever.from_documents(chunks)241bm25_retriever.k = 4242243semantic_retriever = vectorstore.as_retriever(search_kwargs={"k": 4})244245hybrid_retriever = EnsembleRetriever(246 retrievers=[bm25_retriever, semantic_retriever],247 weights=[0.4, 0.6],248)249```250251### RAG Conversationnel (avec Mémoire)252253Pour construire un chatbot RAG qui se souvient du contexte de la conversation, il faut ajouter une mémoire qui reformule les questions de l'utilisateur en tenant compte de l'historique.254255```python256from langchain.chains import create_history_aware_retriever257from langchain_core.prompts import MessagesPlaceholder258259contextualize_prompt = ChatPromptTemplate.from_messages([260 ("system", "Étant donné l'historique du chat et la dernière question de l'utilisateur, "261 "reformule la question pour qu'elle soit compréhensible sans l'historique."),262 MessagesPlaceholder("chat_history"),263 ("human", "{input}"),264])265266history_aware_retriever = create_history_aware_retriever(267 llm, retriever, contextualize_prompt268)269```270271## Bonnes Pratiques2722731. **Choisir la bonne taille de chunk** : Expérimentez avec différentes tailles (500-1500 tokens). Des chunks plus petits pour des réponses précises, plus grands pour un contexte élargi.2742. **Utiliser les métadonnées des documents** : Ajoutez source, date et catégorie comme métadonnées aux chunks. Cela permet de filtrer les résultats lors de la récupération.2753. **Évaluer la qualité** : Utilisez des frameworks comme [RAGAS](https://docs.ragas.io/) pour mesurer des métriques telles que la *faithfulness*, la *relevancy* et la *context precision*.2764. **Gérer les mises à jour des documents** : Implémentez un pipeline de ré-ingestion pour maintenir le vector store synchronisé avec vos sources de données.2775. **Ajouter un re-ranker** : Après la récupération initiale, utilisez un modèle de re-ranking (comme Cohere Rerank) pour réordonner les résultats selon la pertinence réelle.278279## Conclusion280281La RAG est devenue l'architecture standard pour construire des applications IA qui nécessitent un accès à des connaissances spécifiques et à jour. LangChain simplifie énormément l'implémentation en fournissant des abstractions pour chaque composant de la pipeline.282283**Prochaines étapes :**284- **Expérimenter en local** : Commencez avec ChromaDB et quelques documents pour vous familiariser avec la pipeline.285- **Explorer LangSmith** : Utilisez [LangSmith](https://smith.langchain.com/) pour surveiller et déboguer vos chains en production.286- **Essayer différents modèles d'embedding** : Comparez des modèles comme `text-embedding-3-small`, `text-embedding-3-large` et des modèles open-source de Sentence Transformers.287- **Consulter la documentation** : La [documentation LangChain](https://python.langchain.com/docs/) est une ressource excellente et constamment mise à jour.288
:RAG et LangChain : Guide Complet de la Retrieval-Augmented Generationlines 1-288 (END) — press q to close