Par Maxime Jumelle
CTO & Co-Founder
Publié le 14 févr. 2025
Catégorie IA Générative
Le RAG (Retrieval Augmented Generation) est sans doute l'un des cas d'usages de l'IA Générative les plus répandus aujourd'hui. Mais lorsque l'on débute avec les LLM, il peut être difficile de savoir comment il est possible de créer son propre RAG de toute pièce en partant de zéro.
Développé par Harrison Chase et Ankush Gola et lancé en octobre 2022, LangChain est une plateforme open source conçue pour la construction d'applications robustes alimentées par des LLM, telles que des chatbots comme ChatGPT et diverses applications sur mesure. En particulier, LangChain permet de créer des RAG très facilement, à la fois parce qu'il existe déjà des fonctions Python toutes prêtes, mais aussi parce que LangChain dispose de très nombreuses intégrations avec des API, des bases de données, et même des librairies pour exécuter des LLM en local.
Dans cet article, nous allons voir comment en quelques dizaines de lignes de code Python, nous pouvons créer un RAG de toute pièce sur des PDF de plusieurs centaines de pages, et ce grâce à LangChain.
De manière synthétique, les RAG sont constitués de deux étapes principales.
Mais avant de pouvoir exécuter ces deux étapes, les documents doivent d'abord être vectorisés : c'est la toute première phase du RAG avec l'ingestion de documents.
Ce qui est important de savoir lorsque l'on construit un RAG, c'est de déterminer les sources d'informations de manière précise. En effet, il ne sera pas nécessaire d'y ajouter des documents qui n'apportent pas de valeur ajoutée.
À découvrir : notre formation LLM Engineering
Dans notre situation, nous allons permettre à une banque américaine (Banque Inter-Américaine pour le Développement) de proposer à ses collaborateurs un chat qui va se baser sur ses rapports annuels en PDF pour pouvoir répondre à toutes les questions qui lui sont posées. Pour cela, nous allons utiliser l'ensemble des rapports annuels entre 2000 et 2022, totalisant plus de 2000 pages de textes et tableaux de données.
Afin de commencer le développement de notre RAG, la première étape consiste à télécharger les fichiers PDF.
!mkdir -p data !wget \ -O data/annuals_reports.zip \ https://blent-learning-user-ressources.s3.eu-west-3.amazonaws.com/training/ai_gen_engineering/data/annual_reports.zip !unzip -d data data/annuals_reports.zip !rm data/annuals_reports.zip
Comme nous l'avons évoqué plus haut, chacun de ces fichiers contient les informations et rapport annuels et la banque, avec de nombreuses informations financières que les collaborateurs pourront récupérer très facilement sans effectuer des recherches fastidieuses.
Un exemple du rapport de 2009 afin d'inspecter la forme du document et visualiser les différentes informations disponibles.
Afin de pouvoir charger et lire facilement des documents PDF avec LangChain, nous utilisons l'objet PyPDFLoader
qui facilite les opérations sur les PDF. À noter que nous pourrions nous-même créer nos propres loaders pour d'autres formats de fichier.
import glob from langchain_community.document_loaders import PyPDFLoader # Initialisation du loader de document pour charger un fichier PDF documents = [] for file in glob.glob("data/annual_reports/*.pdf"): try: loader = PyPDFLoader(file) # Retourne une liste de document (un pour chaque page) documents += loader.load() except Exception: print(f"Erreur survenue pour le fichier '{file}'.")
Une fois le contenu de tous les PDF chargés en mémoire, il nous faut maintenant découper chaque document en chunks, afin que ceux-ci soient vectorisés.
LangChain met à disposition différents splitters, c'est-à-dire des objets qui permettent de découper le texte de manière intelligente. En particulier, certains splitters sont plus adaptés pour découper du code, et d'autres du Markdown par exemple.
Le RecursiveCharacterTextSplitter
est le splitter le plus basique, qui permet de découper du texte en langage naturel sous forme de chunks.
from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.docstore.document import Document # Initialisation du séparateur de texte avec des paramètres spécifiques pour diviser le texte text_splitter = RecursiveCharacterTextSplitter( chunk_size=600, # Taille maximale des morceaux de texte chunk_overlap=60, # Chevauchement entre les morceaux pour garder le contexte length_function=len, # Fonction pour calculer la longueur des morceaux separators=["\n\n", "\n"] # Séparateurs utilisés pour diviser le texte en morceaux ) # Division du document en morceaux (chunks) chunks = text_splitter.split_documents(documents=documents) # Affichage du nombre de morceaux créés à partir du document PDF print(f"{len(chunks)} chunks ont été créés par le splitter à partir du document PDF.")
Maintenant que le texte est nettoyé, nous passons l'étape de création des embeddings pour ensuite les stocker dans la base de données vectorielle Pinecone.
Cette étape nécessite l'installation du client Pinecone et des bibliothèques transformers
, torch
et accelerate
qui nous permetterons de charger et d'utiliser un modèle d'encodage de texte de HuggingFace.
from langchain_huggingface.embeddings import HuggingFaceEmbeddings # Charger le modèle d'encodage de texte BAAI/bge-small-en-v1.5 de HuggingFace embedding = HuggingFaceEmbeddings(model_name="BAAI/bge-small-en-v1.5", encode_kwargs={"normalize_embeddings" : True})
Créons maintenant notre index Pinecone qui stockera tous nos vecteurs d'embedding.
from pinecone import Pinecone, ServerlessSpec from pinecone.exceptions import NotFoundException # TODO: Inscrire la clé API Pinecone pinecone = Pinecone(api_key="") try: pinecone.describe_index("rag") except NotFoundException: # Créer un index nommé "rag" de dimension 384 pinecone.create_index("rag", dimension=384, spec=ServerlessSpec(cloud="aws", region="us-east-1")) except Exception: print("Une erreur inconnue est survenue !")
Maintenant que notre index est créé, nous allons ajouter tous nos vecteurs de documents dedans. Le processus se décompose ainsi en plusieurs étapes.
Nous disposons de l'objet PineconeVectorStore
, qui nécessite à la fois l'index Pinecone, ainsi que le modèle d'embedding utilisé pour vectoriser les chunks.
from langchain_pinecone.vectorstores import PineconeVectorStore # Initialiser le VectorStore de LangChain avec l'index de Pinecone pinecone_index = pinecone.Index("rag") vector_store = PineconeVectorStore( index=pinecone_index, embedding=embedding )
Il ne reste maintenant plus qu'à vectoriser tous les chunks et à les ajouter dans Pinecone.
add_result = vector_store.add_documents(chunks) print(f"{add_result} vecteurs ont été ajoutés dans Pinecone.")
16211 vecteurs ont été ajoutés dans Pinecone.
Nous avons au total près de 16,000 vecteurs dans notre base, tous de dimension 384. Le principal intérêt ici d'avoir chargé tous les vecteurs dans une base est de ne pas avoir à répéter l'opération systématiquement. Cela est donc beaucoup plus facile si l'on souhaite à l'avenir modifier le LLM de génération sans modifier l'embedding.
Maintenant que notre embedding de documents est prêt, nous pouvons passer à l'étape de génération.
Passons maintenant à l'étape de génération. Tout d'abord, puisque nous allons télécharger un modèle depuis HuggingFace, il nous faut définir le token d'authentification.
!huggingface-cli login --token=...
Comme nous l'avons déjà fait par le passé avec la librairie transformers
, nous téléchargeons et chargeons en mémoire le modèle Mistral 7B.
import transformers import torch from transformers import BitsAndBytesConfig model_id = "mistralai/Mistral-7B-Instruct-v0.3" # Chargement de la configuration du modèle model_config = transformers.AutoConfig.from_pretrained(model_id) # Initialiser le tokeniseur tokenizer = transformers.AutoTokenizer.from_pretrained(model_id) model = transformers.AutoModelForCausalLM.from_pretrained( model_id, trust_remote_code=True, config=model_config, device_map='auto' )
À partir de là, LangChain offre une interface par l'intermédiaire de l'objet HuggingFacePipeline
, qui permet de créer un pipeline LangChain. Autrement dit, on utilise le LLM chargé avec transformers
pour être utilisable avec tous les autres composants de LangChain.
À lire : découvrez notre formation LLM Engineering
Cette logique est d'ailleurs fréquemment utilisée avec LangChain : il existe de nombreux objets qui fournissent cette inter-opérabilité entre un objet issu d'une autre librairie et LangChain.
from langchain_huggingface.llms import HuggingFacePipeline from transformers import pipeline llm=HuggingFacePipeline( pipeline=pipeline( "text-generation", model=model, tokenizer=tokenizer, max_new_tokens=4096, do_sample=False, return_full_text=False # Très important ! On ne veut pas le prompt initial ) )
Puisque l'on utilise transformers
, on retrouve ainsi tous les paramètres que nous avions déjà rencontrés auparavant.
La dernière étape consiste à créer notre pipeline RAG, que l'on encapsule dans une fonction sous forme de plusieurs étapes.
from langchain_core.prompts import PromptTemplate prompt_template = PromptTemplate.from_template("""You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise. Question: {question} Context: {context} Answer: """) def rag_pipeline(query): # Tout d'abord, on recherche les documents retrieved_docs = vector_store.similarity_search(query) # Ensuite, on injecte les documents dans le prompt prompt = prompt_template.invoke({ "question": query, "context": "\n\n".join(doc.page_content for doc in retrieved_docs) }) # Enfin, on envoit l'intégralité du prompt au LLM return llm.invoke(prompt).strip()
Il ne nous reste plus qu'à exécuter notre fonction pour réaliser une inférence de notre pipeline RAG.
query = """ In 2009, how much loans and guarantees were proposed by the bank? Please give details about this number, such as the biggest loans involved. Please also give the source. """ # Effectuer une requête response = rag_pipeline(query) print(response)
In 2009, the bank proposed loans and guarantees totaling $15.5 billion. The biggest loan involved was for $15.3 billion from the Ordinary Capital. This loan was part of a cumulative total of 2,225 loans for $160.8 billion from the Ordinary Capital. The source is the context provided.
Nous voyons que notre RAG fonctionne très bien, et que les réponses apportées sont cohérentes avec les documents stockés dans notre base vectorielle.
Comme nous venons de le voir, créer un RAG avec LangChain ne demande pas beaucoup de code. En revanche, si l'on souhaite avoir beaucoup plus de flexibilité et de possibilité de personnalisation, on pourra être tenté de développer son propre pipeline RAG, sans avoir recours à LangChain.
Ce qui est important à prendre en compte dans un RAG, c'est que la pertinence des documents et la qualité du chunking sont primordiales pour avoir de bonnes performances. Contrairement à ce que l'on pourrait penser, le point critique dans un RAG se situe dès la première étape, lorsque les documents sont découpés et vectorisés.
Vous souhaitez vous former à l'IA Générative ?
Articles similaires
4 mars 2025
Pour garantir l'efficacité d’un système RAG, , il est primordial de disposer de méthodes d’évaluation robustes et adaptées. Ces évaluations permettent non seulement d’identifier les faiblesses des différent composants du système (notamment du retriever et du générateur), mais aussi d’optimiser ses performances globales.
Maxime Jumelle
CTO & Co-Founder
Lire l'article
19 févr. 2025
Comme pour la plupart des outils, les systèmes RAG sont faciles à utiliser, mais difficiles à maîtriser. La réalité est que le RAG ne se limite pas à insérer des documents dans une base de données vectorielle et à y ajouter un modèle de langage. Cela peut fonctionner, mais ce n’est pas toujours suffisant pour garantir des résultats fiables et cohérents.
Maxime Jumelle
CTO & Co-Founder
Lire l'article
13 févr. 2024
Avec l'explosion de l'IA Générative appliquée à la génération de texte ces dernières années, de nombreuses entreprises ont souhaité pouvoir déployer en interne leur propres LLM. Elles disposent ainsi de deux possibilités : utiliser directement des modèles disponibles en version SaaS comme ChatGPT ou Cohere, ou déployer dans leur propre SI un LLM open source adaptés à leurs propres besoins.
Maxime Jumelle
CTO & Co-Founder
Lire l'article
60 rue François 1er
75008 Paris
Blent est une plateforme 100% en ligne pour se former aux métiers Tech & Data.
Organisme de formation n°11755985075.
Data Engineering
IA Générative
MLOps
Cloud & DevOps
À propos
Gestion des cookies
© 2025 Blent.ai | Tous droits réservés