Develop Generative AI Applications : LangChain

Ce que je retiens des piliers fondamentaux de LangChain, de la préparation des données jusqu’à la création d’Agents autonomes.

Étape 1 : Ingestion et Préparation des Données (Loaders & Splitters)

Pour que l’IA puisse travailler sur les données, il faut d’abord les charger et les découper.

  • Ils permettent d’importer des données de diverses sources : PDF, Word, Bases de données, et même des URL/Sites Web.
  • Par exemple avec le Web Loader, l’IA peut « lire » le contenu d’une page web externe en temps réel si on lui donnes l’URL.
  • D’abord c’est quoi un Chunk ? Un « chunk » est simplement un « morceau » ou un « bloc » de texte. Les LLM ont une limite de mémoire (le context window). On ne peut pas leur donner un livre entier d’un coup, on doit le couper en paragraphes (chunks).
  • L’exemple du document juridique ?? :
    • Comment couper alors ? Pour un contrat ou une loi, si tu coupes aveuglément tous les 500 caractères, tu risques de couper une phrase ou l’Article 3 en plein milieu. L’IA perdra le sens.
    • On utilise souvent le RecursiveCharacterTextSplitter. Il essaie de couper d’abord par paragraphes (\n\n), puis par phrases (.), puis par mots. Ainsi, un Article de loi reste entier dans un bloc.
  • Est ce que la façon de couper a un impact sur la performance : La façon de couper joue énormément sur la performance !
    • Trop petit : L’IA n’a pas assez de contexte pour comprendre de quoi parle le bloc.
    • Trop grand : On sature la mémoire du LLM et on pollue sa réponse avec des infos inutiles.
    • L’Overlap (chevauchement) : On fait en sorte que la fin du Chunk A soit le début du Chunk B (ex: 50 mots en commun). Ça évite de perdre le contexte entre deux blocs.

Étape 2 : Vectorisation et Stockage (Embeddings & Vector Stores)

1. Embedding Models

  • Les ordinateurs ne comprennent pas les mots, ils comprennent les chiffres. Un « Embedding » prend un chunk de texte et le transforme en une longue liste de nombres (un vecteur).
  • L’avantage : Les textes qui ont un sens similaire (ex: « Chien » et « Chiot ») auront des vecteurs proches géométriquement dans l’espace multidimensionnelle je precise hahaha.

2. Vector Stores (Bases de données vectorielles)

  • Une fois les textes transformés en vecteurs, on les stocke ici dans les bd vectorielles.
  • Chroma est très utilisé car il es open source et peut tourner en local par exemple. Pinecone est une alternative très puissante qui tourne dans le Cloud.
  • Le processus : Quand un utilisateur poses une question, la question est vectorisée aussi a son tour. Le Vector Store cherche mathématiquement les vecteurs (les morceaux de texte) les plus proches de la question posé pour trouver le contexe. C’est la Recherche Sémantique.

Étape 3 : La Récupération (Retrievers & RetrievalQA)

Un « Retriever » est l’interface qui va aller pêcher l’information.

  • C’est la version basique. C’est juste une « couche » mise par-dessus Chroma ou Pinecone pour dire : « Voici ma question, donne-moi les 4 chunks les plus pertinents ».
  • Pourquoi compliquer les choses avec ça si ya le premier Retriever ?
  • Le problème : Pour que la recherche mathématique (embedding) soit ultra-précise, il faut de petits chunks (ex: 1 phrase). Mais si on donne juste 1 phrase au LLM, il n’a pas assez de contexte pour faire une bonne réponse.
  • La solution : je prend l’exemple d’une bibliothèque. Les petits chunks sont les fiches cartonnées dans l’index. Le Parent Document est le Livre entier. Le Parent Document Retriever cherche la bonne fiche (petit chunk très précis), regarde à quel livre elle appartient, et donne le Chapitre ou le Livre entier (Parent) au LLM pour qu’il ait tout le contexte !

Voici comment preparer vous ca va être long :

  1. Le découpage « Parent » (Le contexte) : LangChain prend ton PDF et le coupe en gros morceaux (ex: des blocs de 2000 caractères). Il garde ces gros blocs de côté dans une petite base de données en mémoire (souvent appelée Docstore). Il ne les transforme pas en vecteurs mathématiques.
  2. Le découpage « Enfant » (La précision) : LangChain prend chaque bloc « Parent », et le recoupe en petits blocs (ex: des blocs de 200 caractères).
  3. Le lien : LangChain attache une étiquette à chaque petit bloc pour dire « Moi, petit bloc n°1, j’appartiens au gros bloc Parent A ».
  4. La Vectorisation : Ensuite, LangChain va vectoriser (créer les Embeddings) UNIQUEMENT pour les petits blocs (Enfants) et les stocker dans ton Vector Store (comme Chroma).

 La phase de question (Le Flux de Retrieval)

  1. Étape A (La Recherche ultra-précise) : La question est envoyée au Vector Store. Le moteur de recherche compare la question avec les petits blocs (Enfants). Comme les blocs sont très petits, la recherche mathématique est extrêmement précise. Il trouve la phrase exacte qui parle de résiliation.
  2. Étape B (L’Échange / La Magie) : Le système attrape ce petit bloc. Mais au lieu de l’envoyer bêtement au LLM (qui n’aurait qu’une phrase sans le contexte global), le Retriever regarde l’étiquette cachée du petit bloc.
  3. Étape C (La Récupération du Parent) : Le Retriever lit l’étiquette : « Ah, ce petit bloc vient du gros bloc Parent A ! ». Il va dans le Docstore chercher le gros bloc Parent A tout entier.
  4. Étape D (L’envoi au LLM) : Le système envoie le gros bloc Parent A (qui contient l’Article 4 en entier, les exceptions, les nuances) au LLM pour qu’il génère sa réponse.

Retriever Normal = 1 seul découpage (1 Splitter).

Parent Document Retriever = Double découpage (2 Splitters : parent enfant).

  • C’est quoi ca encore ?
  • C’est une chaîne (Chain) « clé en main » de LangChain. Elle assemble tout ce qu’on vient de voir :
    1. Elle prend la question.
    2. Elle utilise le Retriever pour trouver les documents.
    3. Elle met les documents et aa question dans un Prompt.
    4. Elle envoie tout au LLM pour qu’il génère (QA = Question Answering) une réponse basée uniquement sur ces documents.

Étape 4 : La Mémoire (History vs Buffer)

  • La différence :
    • ChatMessageHistory : C’est juste la base de données brute (une simple liste Python de messages : Human, AI, Human, AI). Le LLM ne la lit pas automatiquement.
    • ConversationBufferMemory : C’est le mécanisme qui va prendre cet Historique ou créer auto, le formater proprement, et l’injecter automatiquement dans le Prompt avant de l’envoyer au LLM.
  • ConversationSummaryMemory : Si la conversation devient trop longue, l’historique brut coûte trop cher en tokens. Le SummaryMemory demande au LLM de faire un résumé au fur et à mesure (ex: « L’utilisateur s’appelle Alice et aime le bleu ») pour gagner de la place.

Étape 5 : L’Orchestration (Chains & LCEL)

Les Chains permettent de relier les composants (Prompt -> LLM -> OutputParser).

  • C’était la première méthode de LangChain. Très verbeuse et parfois rigide. on crées la chaine 1, puis la chaine 2, et on les mets dans une SequentialChain.
  • C’est la nouvelle norme. Elle utilise l’opérateur « Pipe » (|).
  • Exemple : chain = prompt | llm | output_parser
  • Pourquoi c’est mieux ? C’est ultra lisible, facile à débugger, et ça gère parfaitement le « streaming » (l’affichage de la réponse mot par mot comme sur ChatGPT).

Étape 6 : L’Action avec le monde extérieur (Tools & Agents)

Jusqu’ici, l’IA ne fait que lire des textes et répondre. Les Agents lui donnent des « mains ».

  • Les « Outils » (Tools) sont des fonctions Python : Chercher sur Google, calculer (Python REPL), interroger une base SQL, vérifier la météo.
  • Le décorateur @tool permet de transformer n’importe quelle fonction Python classique en outil compréhensible par l’IA.
  • L’Agent est le « cerveau ». Le framework ReAct (Reasoning + Acting) est une boucle logique :
    1. Reasoning (Réflexion) : « L’utilisateur veut 250 * 34. Je suis nul en maths. Je dois utiliser l’outil Calculatrice. »
    2. Action : Il génère une commande pour la calculatrice.
    3. Observation : Il lit le résultat (8500).
    4. Repeat : Il réfléchit : « Ai-je répondu à la question ? Oui. Fin. »

3. Le grand mystère : Agent vs AgentExecutor

  • Ton observation était brillante ! Tu as remarqué que tools est passé à la fois à l’Agent et à l’AgentExecutor. Pourquoi ce doublon ?
    • agent = create_react_agent(llm, tools, prompt) : Ici, on passe les outils au LLM. On ne lui donne pas le code Python, on lui donne juste les Descriptions des outils pour qu’il sache ce qui existe et comment s’en servir (son intelligence).
    • agent_executor = AgentExecutor(agent, tools) : L’Executor est le moteur d’exécution. C’est le programme Python pur. Quand le LLM dit « Je veux utiliser l’outil Python_REPL », l’Executor regarde sa liste de tools, trouve le vrai code Python, l’exécute pour de vrai, et renvoie le résultat à l’Agent.