Les grands modèles de langage (LLM) sont incroyablement puissants, mais leurs connaissances se limitent à leurs jeux de données d’entraînement. Lorsqu’ils répondent à des questions, surtout celles portant sur des informations spécifiques, évolutives ou propriétaires, les LLM sont sujets aux hallucinations ou peuvent fournir des réponses générales, non pertinentes. La génération augmentée par récupération (RAG) fournit aux LLM des informations pertinentes, récupérées à partir de sources de données externes.
Cependant, tous les outils RAG ne se valent pas. La génération augmentée par récupération corrective (cRAG) ne se contente pas de s’appuyer sur la RAG traditionnelle, mais l’améliore de manière significative. Elle évalue la qualité et la pertinence des résultats récupérés pour renforcer la fiabilité. Si le contexte est faible, non pertinent ou provenant d’une source non fiable, la cRAG tente de trouver de meilleures informations grâce à des actions correctives, ou refuse explicitement de répondre au lieu de fabriquer une réponse. Cette technique rend les systèmes cRAG plus fiables dans des applications critiques telles que les questions-réponses liées aux politiques.
Dans ce tutoriel, vous apprendrez à créer un système RAG correctif (cRAG) robuste en utilisant les modèles IBM® Granite sur Watsonx et LangChain. Des cadres similaires tels que LlamaIndex et LangGraph peuvent également être utilisés pour créer des flux RAG complexes, avec des nœuds distincts. Des techniques comme le réglage fin peuvent améliorer davantage la performance des LLM avec RAG spécialisée. Si les LLM comme ceux d’OpenAI (par exemple, les modèles GPT comme ChatGPT), sont également très utilisés pour ces agents, ce tutoriel porte sur IBM Granite.
Ici, nous nous concentrerons sur le cas d’utilisation suivant : répondre à des questions sur une police d’assurance (document PDF). Ce tutoriel vous guidera pour mettre en œuvre un algorithme RAG avancé qui :
Récupère les informations de votre document PDF.
Si les documents internes ne suffisent pas pour générer la réponse, l’agent peut avoir recours à une solution de recherche Web externe (Tavily).
L’agent filtre intelligemment les résultats externes non pertinents, afin que les réponses soient adaptées aux politiques privées.
L’agent donnera des réponses claires et limitées avec des informations partielles lorsqu’elles sont disponibles, ou refusera de répondre si le contexte est manquant.
Ce tutoriel explique comment créer un agent de requête sur les polices d’assurance, conçu pour analyser les documents correspondants (une brochure PDF) et répondre avec précision aux questions de l’utilisateur. Nous utilisons les modèles IBM Granite et LangChain pour créer l’agent avec des étapes de récupération et de vérification robustes, qui garantissent des réponses de qualité, limitées à la source.
Découvrons comment les principes clés de la RAG s’appliquent à notre cas d’utilisation.
Base de connaissances interne (PDF) : la principale source d’information de l’agent est la police d’assurance que vous avez fournie en PDF. Il convertit ce document en base de données vectorielle interrogeable.
Recherche externe de secours (Tavily) : si la base de connaissances interne ne dispose pas de suffisamment d’informations, l’agent peut consulter des sources Web externes via Tavily. Tavily est un moteur de recherche spécialement conçu pour les agents IA et les LLM. Il permet des résultats plus rapides, en temps réel, grâce à son interface de programmation d'application (API) pour les applications avec RAG.
Notation du contexte : l’évaluateur de récupération basé sur LLM fournira un score de pertinence aux éléments récupérés de votre PDF interne, tout en s’assurant que seuls les éléments de qualité sont inclus.
Réécriture de la requête : pour les recherches Web, l’agent peut reformuler la requête de l’utilisateur afin d’améliorer les chances de trouver des informations externes pertinentes.
Vérification des sources : une vérification basée sur LLM permet d’évaluer si les résultats de la recherche Web externe sont pertinents pour une police d’assurance privée, en filtrant les informations générales ou les détails sur les programmes de santé publique (comme Medi-Cal). Cette fonction empêche la génération de réponses trompeuses et permet l’autocorrection, ce qui contribue à l’affinement des connaissances.
Génération sous contrainte : le prompt final adressé au LLM lui demande strictement d’utiliser uniquement le contexte fourni, d’offrir des réponses exactes, d’indiquer si les informations ne sont pas disponibles, ou de fournir des réponses partielles avec des limitations explicites. Cette fonction améliore l’adaptabilité et la fiabilité des réponses générées.
Vous devez disposer d’un compte IBM® Cloud pour créer votre projet watsonx.ai. Assurez-vous d’avoir accès à votre clé d’API watsonx et à votre ID de projet. Vous aurez également besoin d’une clé d’API pour utiliser les capacités de recherche Web de Tavily AI.
Bien que vous puissiez choisir parmi plusieurs outils, ce tutoriel vous explique comment configurer un compte IBM à l’aide d’un Jupyter Notebook.
Cette étape ouvre un environnement de notebook dans lequel vous pourrez copier le code de ce tutoriel. Vous pouvez également télécharger ce notebook localement sur votre système, et le charger dans votre projet watsonx.ai en tant qu’actif. Pour voir d’autres tutoriels Granite, consultez les pages de la Communauté IBM Granite. Ce tutoriel est également disponible sur Github.
Pour travailler avec le cadre LangChain et intégrer IBM WatsonxLLM, nous devons installer plusieurs bibliothèques essentielles. Commençons par installer les paquets requis. Cet ensemble comprend langchain pour le cadre RAG, langchain-ibm pour l’intégration de watsonx, faiss-cpu pour un stockage vectoriel efficace, PyPDF2 pour le traitement des PDF, sentence-transformers pour obtenir un embedding et requests pour les appels d’API Web. Ces bibliothèques sont critiques pour appliquer les solutions de machine learning et TAL.
Remarque : aucun GPU n’est nécessaire, mais l’exécution peut être plus lente sur les systèmes avec CPU. Cette étape ouvre un environnement de notebook dans lequel vous pourrez copier le code de ce tutoriel. Ce tutoriel est également disponible sur GitHub.
Ensuite, importez tous les modules requis et fournissez de manière sécurisée vos clés d’API pour watsonx et Tavily, ainsi que votre ID de projet watsonx.
os permet de travailler avec le système d’exploitation.
io permet de travailler avec les flux de données.
getpass utilise un moyen sûr de capter des informations sensibles telles que les clés d’API et n’affiche pas les entrées à l’écran.
PyPDF2.PdfReader permet l’extraction de contenu à partir des PDF.
langchain_ibm.WatsonxLLM facilite l’utilisation du LLM IBM watsonx Granite dans le cadre LangChain.
langchain.embeddings.HuggingFaceEmbeddings utilise un modèle HuggingFace et génère les embeddings textuels importants pour la recherche sémantique.
langchain.vectorstores.FAISS est une bibliothèque conçue pour favoriser un stockage vectoriel et une recherche de similarités efficaces, qui nous permet de créer un index de vecteurs et de l’interroger.
langchain.text_splitter.RecursiveCharacterTextSplitter permet de découper de grandes parties de texte en petits morceaux pour traiter les documents qui ne tiendraient autrement pas en mémoire.
langchain.schema.Document représente une unité de texte arbitraire, avec les métadonnées associées, ce qui en fait un élément constitutif de Langchain.
requests permet d’effectuer des requêtes HTTP en externe aux API.
botocore.client.Config est une classe de configuration utilisée pour définir les paramètres de configuration pour les clients AWS/IBM Cloud Object Storage.
ibm_boto3 est le SDK d’IBM Cloud Object Storage pour Python ; il permet d’interagir avec le stockage d’objets cloud.
langchain.prompts.PromptTemplate permet de créer des prompts structurés et réutilisables pour les modèles de langage.
langchain.tools.BaseTool est la classe de base à partir de laquelle vous créez des outils personnalisés, qui peuvent être fournis aux agents LangChain.
Cette étape met en place tous les outils et modules nécessaires pour traiter le texte, créer des embeddings, les stocker dans une base de données vectorielle et interagir avec le LLM IBM watsonx. Elle établit tous les éléments nécessaires à la création d’un système RAG capable d’obtenir, d’interroger et de rechercher différents types de données.
Lors de cette étape, nous chargerons le PDF de la police d’assurance à partir d’IBM Cloud Object Storage. Le code lit le PDF, ainsi que le contenu du texte, et divise le texte en blocs plus faciles à gérer. Ces morceaux sont convertis en embeddings numériques et stockés dans une base de données vectorielle FAISS qui préparera la recherche de similarités sémantiques ultérieurement, dans le contexte local, afin d’optimiser les résultats de la recherche.
ibm_boto3.client permet au client d’interagir avec IBM Cloud Object Storage.
Bucket est le nom du compartiment de stockage d’objets cloud qui contient le PDF.
object_key est le nom du PDF dans le compartiment de stockage d’objets cloud.
cos_client.get_object(...).read() récupère le contenu du fichier PDF sous forme d’octets dans le stockage d’objets cloud.
io. BytesIO convertit les octets bruts du PDF en flux binaire en mémoire, dans un format utilisable par PdfReader.
PdfReader crée un objet capable d’analyser et d’extraire le texte du PDF.
page.extract_text() extrait le texte d’une seule page du PDF.
RecursiveCharacterTextSplitter est configuré pour diviser le texte extrait en morceaux de 500 caractères, avec un chevauchement de 50 caractères, afin de préserver le contexte.
splitter.split_text(text) divise toutes les pages du texte PDF en petits morceaux.
HuggingFaceEmbeddings charge un modèle transformateur de phrases qui a été pré-entraîné pour convertir les morceaux de texte en représentations vectorielles denses.
FAISS.from_texts(chunks, embeddings) construit un index FAISS en mémoire qui permet de rechercher les morceaux de texte en fonction de leur similarité sémantique.
Cette étape assure l’ingestion complète d’un document PDF, du cloud au texte prêt pour LLM, ainsi qu’une indexation confortable pour une récupération en temps réel.
Lors de cette étape, vous allez configurer le LLM IBM Granite pour piloter le raisonnement de votre agent et l’intégrer à la fonction de recherche Web Tavily. Les paramètres du LLM sont configurés pour garantir des réponses factuelles et stables.
WatsonxLLM instancie le wrapper LLM pour IBM watsonx, permettant l'interaction avec les modèles Granite.
model_id="ibm/granite-3-2b-instruct" est le modèle IBM Granite (un modèle d’instructions avec 2,7 milliards de paramètres) conçu pour les tâches d’IA générative basées sur les instructions.
class TavilySearch(BaseTool) définit un outil LangChain personnalisé pour effectuer des recherches Web à l’aide de l’API Tavily.
tavily_tool = TavilySearch() crée une instance exécutable de l’outil de recherche personnalisé Tavily.
Lors de l’initialisation de watsonxLLM, les valeurs url, apikey et projet_id de nos identifiants précédemment configurés sont transmises pour l’authentification et la connexion au service. Ses paramètres, comme "max_new_tokens": 300, limitent la longueur de la réponse, tandis que "température": 0,2 contrôle la créativité de la sortie, favorisant des résultats plus déterministes.
La définition de la classe TavilySearch comprend une description de sa fonction. Sa logique est contenue dans la méthode def _run(self, query: str). Dans cette méthode, nous envoyons une requête HTTP POST au point de terminaison de l’API Tavily, y compris TAVILY_API_KEY et la requête de recherche dans la charge utile JSON. Nous vérifions ensuite s’il y a des erreurs HTTP avec response.raise_for_status() et analysons la réponse JSON pour accéder à l’extrait de contenu à partir du premier résultat de recherche.
Cette étape consiste à configurer le modèle de langage pour la génération de texte et inclut un outil de recherche Web externe permettant d’enrichir les connaissances du modèle.
Cette étape définit les différents templates de prompt qui guideront le comportement du LLM à différentes étapes du processus RAG. Cette approche inclut des prompts permettant d’évaluer la pertinence des morceaux de documents internes, de réécrire les requêtes des utilisateurs pour une meilleure recherche Web, ainsi qu’un nouveau prompt, essentiel pour vérifier la source des résultats de recherche Web. Des fonctions d’assistance permettant de noter les morceaux et de les récupérer dans la base de données vectorielle sont également définies.
Cette étape définit les différents templates de prompt qui guideront le comportement du LLM aux différentes étapes du processus RAG. Sont inclus les prompts permettant d’évaluer la pertinence des morceaux de documents internes, de réécrire les requêtes des utilisateurs pour une meilleure recherche Web, ainsi qu’un nouveau prompt, essentiel pour vérifier la source des résultats de recherche Web. Des fonctions d’assistance permettant de noter les morceaux et de les récupérer dans la base de données vectorielle sont également définies.
PromptTemplate.from_template est une fonctionnalité LangChain permettant de créer un template réutilisable pour la création de prompts.
scoring_prompt_template définit un prompt qui demande au LLM d’agir en tant qu’évaluateur et d’attribuer un score de pertinence (0-5) à un morceau de contexte donné en fonction d’une question.
rewrite_prompt_template définit un prompt qui guide le LLM pour améliorer ou clarifier la question de l’utilisateur à des fins de recherche.
CONTEXT_SOURCE_VERIFICATION_PROMPT définit un prompt qui demande au LLM de vérifier si un texte (par exemple, provenant d’une recherche Web) est issu d’un contexte de politique privée, ou d’une source générale ou publique.
def score_chunks(chunks, requête) définit une fonction qui prend une liste de morceaux de texte et une requête, et utilise ensuite le LLM pour évaluer la pertinence de chaque morceau.
def retrieve_from_vectorstore(query) définit une fonction pour récupérer les documents avec le plus haut degré de similarité auprès de la base de données vectorielle FAISS.
Dans la fonction score_chunks, une liste de notation vierge est initialisée. Pour chaque morceau, le scoring_prompt_template est formaté avec la requête et le morceau correspondants. Ce prompt formaté est ensuite envoyé au LLM, et la réponse est dépouillée. La fonction tente d’extraire le score entier (un score binaire s’il est simplifié en pertinent ou non pertinent) en identifiant la ligne « Score: » dans la réponse du modèle. Le morceau et son score analysé ou attribué par défaut sont ensuite ajoutés à la liste des scores. Cette partie du système sert d’évaluateur de récupération.
La fonction react_from_vectorstore met en œuvre une vectorstore.similarity_search pour trouver les 8 morceaux de documents les plus pertinents en fonction de la requête, et récupérer le page_content à partir des objets LangChain Document récupérés.
Cette étape permet de construire l’échafaudage conceptuel du système RAG correctif, afin que le LLM évalue le contexte et détermine comment récupérer les connaissances à partir des sources internes et externes.
La récupération initiale est la fonction qui analyse la base de données vectorielle du PDF.
La notation du contexte consiste à évaluer les morceaux de PDF récupérés en fonction de leur pertinence.
Recours à Tavily : si le contexte pertinent du PDF n’est pas suffisant, il interroge Tavily (recherche Web).
La vérification des sources est une étape alimentée par LLM qui consiste à vérifier si les résultats de Tavily sont pertinents pour une politique privée avant de les utiliser. Cette fonction permet d’éviter les réponses trompeuses relatives aux programmes de santé publique.
Réécriture de la requête et deuxième recherche Tavily : s’il n’y a toujours pas de contexte adéquat, il réécrit la requête et retente une recherche Tavily.
Décision finale : lorsqu’il y a un contexte pertinent, il est envoyé au LLM avec un prompt (strict) pour créer la réponse. S’il n’y a pas de contexte pertinent après toutes les tentatives viables, il envoie un refus poli.
Le premier passage du paramètre policy_context_keywords vous permet d’ajouter des termes de votre police (par exemple, son nom, l’assureur) afin d’affiner la recherche sur Tavily.
MIN_CONTEXT_LENGTH définit la longueur minimale acceptable du contexte récupéré.
SIMILARITY_THRESHOLD définit le score minimum de pertinence qu’un morceau doit avoir pour être considéré comme « bon ».
def corrective_rag(...) définit la fonction principale qui orchestre l’ensemble du workflow de RAG corrective.
La fonction corrective_rag crée retrieved_context_pieces pour recueillir le contexte pertinent. Elle récupère et note d’abord les chunks_from_vectorstore à partir de la base de données vectorielle PDF en fonction de la requête, puis scored_chunks_vector évalue leur pertinence en utilisant le modèle de langage. Seuls les good_chunks_vector qui respectent SIMILARITY_THRESHOLD sont retenus. current_context est ensuite compilé à partir de ces éléments.
Si la valeur current_context est inférieure à MIN_CONTEXT_LENGTH, le système tente d’effectuer une recherche Web. Il construit tavily_search_query, intégrant potentiellement policy_context_keywords. Une recherche directe (tavily_context_direct) est effectuée. Un prompt de vérification est obligatoirement créé et envoyé au LLM pour déterminer si le résultat de la recherche Web(is_relevant_source) provient d’une politique privée, ou d’un programme public. Si la réponse est OUI, le contexte est ajouté.
Si le contexte reste insuffisant, le système se prépare à réécrire la requête. Il utilise rewrite_prompt pour obtenir une improved_query du LLM, puis effectue une deuxième recherche Web (tavily_context_rewritten). Ce nouveau contexte fait également l’objet d’une vérification des sources.
Enfin, si len(current_context.strip()) == 0 est la dernière vérification. Si aucun contexte pertinent n’est trouvé après toutes ces tentatives, un message de refus prédéfini est renvoyé. Sinon, un final_prompt est créé avec tout le contexte vérifié et envoyé au modèle de langage pour générer sa réponse finale.
La fonction corrective_rag gère en détail les fonctions de récupération, de notation et de vérification du système de RAG corrective. Elle permet une mise à jour constante de la base de connaissances et du flux de connaissances, et favorise des réponses efficaces et contextuelles.
Pour finir, exécutez la fonction corrective_rag avec un exemple de requête. Il est essentiel de fournir des policy_context_keywords spécifiques à votre document PDF. Ces mots-clés rendront la recherche Web Tavily plus pertinente par rapport à votre politique réelle, empêchant les informations générales ou publiques relatives aux programmes de santé de polluer votre contexte.
Observez les instructions d’impression pour connaître le contexte, la longueur et les résultats de vérification afin de comprendre le flux d’informations.
policy_specific_keywords = ["Super Star Health", "Care Health Insurance"] définit une liste de mots-clés pertinents pour la police d’assurance téléchargée, ce qui permet d’affiner les résultats de la recherche Web.
query = "..." définit la question que l’utilisateur est susceptible de poser.
result = corrective_rag(query, policy_context_keywords=policy_specific_keywords) appelle la fonction corrective_rag principale et transmet la requête de l’utilisateur et les mots-clés associées à la police pour lancer le processus RAG.
print("\n FINAL ANSWER (...)") affiche un en-tête clair avant l’impression de la réponse générée.
print(result) produit la réponse finale renvoyée par le système corrective_rag.
Cette étape montre comment invoquer le système de RAG corrective complet à l’aide d’un exemple de requête et de mots-clés, afin de démontrer ses fonctionnalités de bout en bout dans un scénario réel.
Le système de RAG corrective a mis en place une base de connaissances PDF interne entièrement coordonnée avec un service externe (Tavily), afin de récupérer des informations complètes pour les requêtes complexes.
Il a évalué et filtré avec précision le contexte récupéré à l’aide d’une notation basée sur LLM et d’une vérification des sources indispensable pour garantir l’utilisation d’informations valides et fiables.
Le système a démontré sa capacité à améliorer la recherche externe en réécrivant intelligemment les requêtes des utilisateurs pour demander des informations plus ciblées et de meilleure qualité.
Grâce à la génération sous contrainte, une réponse fiable et contextualisée a été générée ; le système a poliment refusé de répondre lorsqu’il n’y avait pas suffisamment d’informations vérifiées connues.
Cet exemple nous a montré comment utiliser LangChain et les LLM IBM Granite sur watsonx pour développer des applications d’IA puissantes et dignes de confiance dans un domaine aussi sensible que les questions-réponses relatives aux polices d’assurance.
Créez, déployez et gérez de puissants assistants et agents IA qui automatisent les workflows et les processus grâce à l’IA générative.
Construisez l’avenir de votre entreprise avec des solutions d’IA en lesquelles vous pouvez avoir confiance.
IBM Consulting et ses services d'IA accompagnent les entreprises dans la redéfinition de leurs activités avec l'intelligence artificielle pour mener leur transformation.