LangChain: Le guide essentiel

LangChain: Le guide essentiel

Article co-écrit avec Ilyes Talbi

Nous allons voir dans cet article les fondamentaux de LangChain, pour une prise en main rapide de cette bibliothèque si particulière et si puissante pour quiconque s’intéressant aux modèles de languages, ou autres agents augmentés/chatbots et de leurs déploiements dans la sphère du développement grand public et du monde du business.

Bien que la bibliothèque n’en soit qu’à ses débuts, elle est déjà remplie de fonctionnalités incroyables permettant de construire des outils, des applications ou encore des plugin extraordinaires autour du cœur des modèles de language dernière génération comme GPT-4.

Note: une fois cet article lu, nous vous recommandons d’utiliser ce notebook pour débutant sur langchain pour reprendre tout les concepts mis en avant un à un par vous même — mis en place par le génial Greg Kamradt.

Aperçu des use-cases possibles avec LangChain

  • création et déploiement d’assistants personnels (agents)
  • création d’Agents autonomes (rendu à la mode avec Auto-GPT, BabyAGI etc)
  • mise en place de Q&A sur vos documents propres (en local, vos pdf, drive, notion etc) avec récupération des sources exactes
  • création de résumés, d’analyses et de synthèses de tous vos documents et ce peu important la taille
  • création et personnalisation de Chatbots “vraiment” crédibles avec lesquels vous interagissez en language naturel
  • interrogation de vos données tabulaires
  • création de plugin personnalisés pour faire de la compréhension de votre code
  • interaction multiples et variés avec d’autres API (comme google search) pour combiner les LLM avec vos apps préférées
  • etc etc etc

La liste est beaucoup plus longue, et en évolution permanente grâce à la flexibilité de LangChain d’interagir et de suivre les dernières évolutions du GenerativeAI.

Historique: L’émergence de LangChain

Les grands modèles de langage (LLM) sont apparus sur la scène mondiale avec la publication du GPT-3 d’OpenAI en 2020. Depuis lors, leur popularité n’a cessé de croître avec le couronnement de ChatGPT en Décembre 2022 propulsant les LLM sous les feux des projecteurs.

L’intérêt pour les LLM et la discipline plus large de l’IA générative est monté en flèche. Ce progrès est très rapide que l’on peut résumer comme suit :

LangChain: Le guide essentiel

LangChain vient s’insérer à ce moment crucial de l’histoire ou la commodité des modèles de languages et leur efficacité les rend tout à fait matures pour être utilisées dans le monde applicatif. En d’autres termes, OpenAI a lancé le mouvement des modèles de language comme GPT-4 consumables via API, LangChain créée le pont entre le monde des applications et les modèles de language— qu’ils soient d’OpenAI ou votre propre version Open-Source fine-tuné d’un LLM comme celui de Stability.ai (StableLM).

La philosophie de LangChain

La philosophie de LangChain est résumé par son fondateur Harrison Chase comme suit:

LangChain: Le guide essentiel
source: LangChain Python official doc

Ce qui est intéressant de prime abord, c’est ce parti pris très affirmé, les applications les plus radicales de demain utiliseront des modèles de language via API, et ces modèles de language pourront se renforcer:

  1. en “apprenant” de nouvelles sources de données non-vues lors de leur entraînement, mais aussi se connecter à d’autres flux de données via API
  2. en “augmentant” leurs capacités en étant capable d’utiliser de nouveaux outils et d’interagir avec son propre environnement de données – le concept d’agents si cher à LangChain, que nous verrons en détail lors de cet article

Si on fait un schéma, LangChain vient donc pouvoir s’interfacer avec de nouvelles données et vous permet de construire vos applications en unifiant le tout sous la bannière d’un modèle de language.

LangChain: Le guide essentiel

Les 3 concepts fondamentaux de LangChain

Nous avons vu en introduction un petit aperçu des use-cases possibles avec LangChain comme pour les chatbots, les questions-réponses génératives (GQA), les résumés etc

L’idée centrale de la bibliothèque est que nous pouvons “enchaîner” différents composants pour créer des cas d’utilisation plus avancés autour des LLM. Les chaînes peuvent être constituées de plusieurs composants provenant de plusieurs modules :

Concept fondamental #1 Prompt Template :

ce sont des modèles ré-utilisables, des moules déjà faits permettant de réutiliser simplement et d’adapter ses prompts via un schéma pré-défini.

Concept fondamental #2 Agents :

les agents utilisent les LLM pour décider des actions à entreprendre. Des outils tels que la recherche sur le web ou les calculatrices peuvent être utilisés, et tous sont intégrés dans une boucle logique d’opérations.

Concept fondamental #3 Mémoire :

Mémoire à court terme, mémoire à long terme pour les bots et agents que vous mettez en place, afin qu’ils se souviennent de leurs interactions passés avec vous.

Le principe des « Prompt Template »dans LangChain

Commençons par un exemple simple, afin de mettre en place un template de prompt pour effectuer de simple question-réponse avec le modèle GPT 3.5 d’OpenAI. Nous devons d’abord installer la bibliothèque LangChain

!pip install langchain
from langchain import PromptTemplate

template = """Voila la question d'un utilisateur: {question}

Donnez votre Réponse: """
prompt = PromptTemplate(
        template=template,
    input_variables=['question']
)

# user question
question = "Qui a gagné la coupe du monde 2022 au Qatar ?"

Dans l’exemple ci-dessus, on ne fait simplement qu’injecter de manière dynamique le champ ‘question” dans le prompt.

Voila la question d'un utilisateur: {"Qui a gagné la coupe du monde 2022 au Qatar ?"}
Donnez votre réponse

ça n’a l’air de rien, mais c’est un des features clefs majeurs de LangChain, car c’est ce qui vous permet de construire votre propre use-case en utilisant la composabilité de l’outils et des prompts entre elles.

Les endpoints OpenAI dans LangChain se connectent à OpenAI directement. Vous aurez besoin d’un compte OpenAI et de votre clef secrète API pour aller plus loin si vous souhaitez utiliser le modèle de language

Une fois que vous avez une clé API, nous l’ajoutons à la variable d’environnement OPENAI_API_TOKEN. Nous pouvons le faire avec Python comme suit :https://larevueia.fr/langchain-le-guide-essentiel/

import os

os.environ['OPENAI_API_TOKEN'] = 'OPENAI_API_KEY'

Ensuite, nous devons installer la bibliothèque OpenAI via Pip.

!pip install openai

Nous pouvons maintenant générer du texte en utilisant le template de prompt que nous avons créé dans la précédente partie. On va utiliser la capacité de génération (ou de complétion) de GPT-3 d’OpenAI. Nous utiliserons text-davinci-003, pas de panique c’est juste le nom de code de GPT-3.

from langchain.llms import OpenAI

davinci = OpenAI(model_name='text-davinci-003')
llm_chain = LLMChain(
prompt=prompt,
llm=davinci
)

print(llm_chain.run(question))

Et voila donc votre première chaîne, vous venez d’exécuter votre requête en utilisant la classe “template de prompt” (PromptTemplate) et en faisant appel à l’API d’OpenAI pour activer le modèle GPT 3.

Les classes de “template de prompt” de LangChain sont donc conçues pour faciliter la construction de prompts avec des entrées dynamiques.

Voyons un exemple un prompt un peu plus compliqué en l’apparence mais reposant sur le même méchanisme.

from langchain import PromptTemplate

template = """Répondez à la question en vous basant sur le contexte ci-dessous.
Si les informations fournies ne permettent pas de répondre à la question,
répondez par "Je ne sais pas".


Contexte additionnelle : Les grands modèles de langage (LLM) sont les modèles les plus récents utilisés dans le domaine du NLP.
Leurs performances supérieures à celles des modèles plus petits les ont rendus incroyablement utiles pour les développeurs d'applications NLP.
pour les développeurs d'applications NLP. Ces modèles
Ces modèles sont accessibles via la bibliothèque `transformers` de Hugging Face, via OpenAI
en utilisant la bibliothèque `openai`, et via Cohere en utilisant la bibliothèque `cohere`.

Question : {requête}

Réponse :"""

prompt_template = PromptTemplate(
input_variables=["query"],
template=template
)

Naturellement, nous pouvons passer la sortie de votre prompt_template directement dans un objet LLM comme ceci.

Le concept de “few shot Prompt Template”: LangChain et l’art de l’ingiénérie de prompt

Le succès des LLM provient de leur grande taille et de leur capacité à stocker la “connaissance” dans les paramètres du modèle, le stock des connaissances est faite pendant la phase d’entraînement du modèle. Les deux méthodes principales pour transmettre des connaissances à un modèle de language sont les suivantes :

  • Connaissance paramétrique — la connaissance mentionnée ci-dessus est tout ce qui a été appris par le modèle pendant le temps de formation et est stocké dans les poids (ou paramètres) du modèle.
  • Connaissance extérieure— toute connaissances complémentaires fournies au modèle au moment de l’inférence par l’intermédiaire des prompts

Le modèle FewShotPromptTemplate de Langchain permet de spécialiser votre Bot, votre agent via des sources de connaissances extérieures, ou des indications d’exemples que vous donnez au modèle via vos prompts.

L’idée est d’”entraîner” le modèle sur quelques exemples que vous allez lui fournir — nous appelons cela l’apprentissage à la volée (Few Shot Learning) — et ces exemples sont donnés au modèle dans le prompt directement.

Cette capacité est très pratique et idéale lorsque notre modèle a besoin d’aide pour comprendre ce que nous lui demandons de faire. C’est ce que montre l’exemple suivant :

prompt = """Voici des extraits de conversations avec un assistant d'IA
assistant. L'assistant est généralement sarcastique et plein d'esprit,
produisant des réponses créatives et amusantes aux questions des utilisateurs.

Voici quelques exemples :

Utilisateur : Comment allez-vous ?
AI : Je n'ai pas à me plaindre, mais il m'arrive de le faire.

Utilisateur : Quelle heure est-il ?
IA : Il est temps d'acheter une montre.

Utilisateur : Quel est le sens de la vie ?
IA :"""

print(openai(prompt))

Si nos exemples renforcent les instructions que nous avons données dans le prompt, nous avons beaucoup plus de chances d’obtenir une réponse plus amusante et comme nous le souhaitons. Nous pouvons ensuite formaliser ce processus avec le FewShotPromptTemplate de Langchain :

from langchain import FewShotPromptTemplate

# création de nos exemples perso pour base d'entrainement
examples = [
{
"requête": "How are you?",
"réponse": "I can't complain but sometimes I still do."
}, {
"requête": "What time is it?",
"réponse": "It's time to get a watch."
}
]

# création du template de nos exemples
example_template = """
Utilisateur: {requête}
AI: {réponse}
"""

# création d'un prompt d'example basé sur notre template ci-dessus
example_prompt = PromptTemplate(
input_variables=["requête", "réponse"],
template=example_template
)

# décomposons notre précédent prompt en deux parties
# le prefix qui sont nos instructions
prefix = """The following are exerpts from conversations with an AI
assistant. The assistant is typically sarcastic and witty, producing
creative and funny responses to the users questions. Here are some
examples:
"""
# et le suffix qui sont la requête en input de notre utilisateur et l'indicateur de sorties
suffix = """
Utilisateur: {requête}
AI: """

# now create the few shot prompt template
few_shot_prompt_template = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
prefix=prefix,
suffix=suffix,
input_variables=["requête"],
example_separator="\n\n"
)

Naturellement, le prompt est l’élement incontournable et essentiel du monde merveilleux des LLM. Il vaut la peine d’explorer les outils disponibles dans LangChain et de se familiariser avec les différentes techniques d’ingénierie de prompts.

Ici, nous n’avons couvert que quelques exemples d’outils disponible dans Langchain pour manipuler et créer vos prompts pour répondre à votre use-case précis. Dans la prochaine section, nous explorerons une autre partie essentielle de Langchain — appelée “Agents”.

Le concept fondamental #2 de LangChain : les Agents

Bien que très performants dans pas mal de cas, les LLM ont des limitations majeures, et une en particulier qui les rend inutilisable dans certains cas.

Ils ne sont pas capables de donner des réponses toujours justes, sont limités aux informations vues pendant leur training, ont tendance à extrapoler sur plusieurs sujets et sont moins bons sur des choses simples comme le calcul.

Une solution à ces problèmes est proposée par Langchain avec le concept d’agents.

Qu’est-ce qu’un agent concrètement ?

Les agents peuvent être considérés comme des “outils” permettant aux LLMs de fonctionner. Tout comme un humain utiliserait une calculatrice pour les mathématiques ou effectuerait une recherche Google pour obtenir des informations, les agents permettent à un LLM d’étendre ses capacités en faisant la même chose.

LangChain: Le guide essentiel
source: LangChain AI handbook de Pinecone

En utilisant des agents, un LLM peut écrire et exécuter du code Python, utiliser une calculatrice. Il peut également rechercher des informations et interroger une base de données SQL.

Imaginez toutes les applications qui peuvent en découler !

Comment fonctionnent les agents en pratique ?

On va faire un exemple très concret en utilisant Python.

Pour utiliser les agents on doit avoir un modèle de langage de base, on va utiliser davinci d’OpenAI ici, un “outil” au sens de LangChain et un agent qui va contrôler l’interaction entre les 2.

Commençons par initialiser notre LLM :

from langchain import OpenAI
llm = OpenAI(
openai_api_key="OPENAI_API_KEY",
temperature=0,
model_name="text-davinci-003"
)

On va maintenant initialiser l’outil, qui peut être un outil déjà développé par LangChain ou un outil custom que vous pouvez créer.

Ici, on va utiliser l’outil llm_math, qui va permettre de faire des opérations mathématiques :

from langchain.agents import load_tools
tools = load_tools(
['llm-math'],
llm=llm
)

Le 3ème ingrédient, c’est l’agent, qui va gérer la connexion entre le llm et l’outil.

On initialise l’agent de cette manière :

from langchain.agents import initialize_agent
zero_shot_agent = initialize_agent(
agent="zero-shot-react-description",
tools=tools,
llm=llm,
verbose=True,
max_iterations=3
)

L’agent utilisé zero-shot-react-description repose sur le framework ReAct (Reasoning + Acting), proposé en 2022, dans ce papier de recherche.

C’est le framework qui va permettre au LLM de determiner quelle action il doit effectuer en fonction du prompt de l’utilisateur et des descriptions des outils auxquels il accès.

On peut tester notre agent :

zero_shot_agent("Combient fait (4.5*2.1)^2.2 ?")

Et voici le résultat obtenu :

LangChain: Le guide essentiel

En laissant verbose à True, on peut voir comment l’agent “raisonne” étape par étape.

On peut faire un nouvel exemple dans lequel on va aller un peu plus loin niveau raisonnement :

zero_shot_agent("Si Marie a 4 pommes et Georges en apporte 2"
"et une moitié de boîte"
"(une boîte contient 8 pommes), combien a-t-on de pommes ?")

L’agent réussi parfaitement bien le test, voici sa réponse :

LangChain: Le guide essentiel

Si vous avez bien suivi notre raisonnement, vous comprendrez qu’il manque quelque chose.

Notre agent, pour le moment, n’a accès qu’à un seul outil, qui est l’outil de calcul, à chaque requête il va tenter de l’utiliser ce qui peut renvoyer des erreurs lorsqu’il n’y a pas de calculs à effectuer.

Par exemple :

zero_shot_agent("Quelle est la capitale de la Norvége ?")
LangChain: Le guide essentiel

Pour résoudre ce problème, on peut ajouter l’outil de modèle de langage à notre agent.

On commence par initialiser l’outil :

from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.agents import Tool

prompt = PromptTemplate(
    input_variables=["query"],
    template="{query}"
)

llm_chain = LLMChain(llm=llm, prompt=prompt)

# initialize the LLM tool
llm_tool = Tool(
    name='Language Model',
    func=llm_chain.run,
    description='use this tool for general purpose queries and logic'
)

On l’ajoute ensuite à la “boîte à outils” de notre agent et on le réinitialise :

tools.append(llm_tool)
# reinitialize the agent
zero_shot_agent = initialize_agent(
agent="zero-shot-react-description",
tools=tools,
llm=llm,
verbose=True,
max_iterations=3
)

Si on repose la question :

zero_shot_agent("Quelle est la capitale de la Norvége ?")
LangChain: Le guide essentiel

A présent nous avons la bonne réponse, l’agent décide de prendre comme action par lui même d’utiliser le modèle de language qui contient la réponse à cette question dans les poids du modèle.

Quels sont les différents agents ?

Comme nous l’avons vu dans les 2 premières parties, les agents utilisent un LLM pour déterminer quelles actions entreprendre et dans quel ordre.

Une action peut consister à utiliser un outil et observer sa sortie, ou retourner une réponse à l’utilisateur.

Voici les agents qui sont disponibles dans LangChain de façon native :

  • zero-shot-react-description

Cet agent utilise le framework ReAct, que j’ai mentionné plus haut, pour déterminer l’outil à utiliser en se basant sur la description de l’outil. On peut utiliser plusieurs outils avec cet agent et il n’y a pas de limites de nombre, il faut par contre qu’une description soit fournie pour chacun des outils sélectionnés.

  • react-docstore

Cet agent utilise le framework ReAct pour interagir avec un docstore (un ensemble de documents). Deux outils doivent être fournis : un outil de recherche (Search tool) et un outil de consultation (Lookup tool) (ils doivent être nommés exactement comme cela). L’outil de recherche doit rechercher un document, tandis que l’outil de consultation doit rechercher un terme dans le document le plus récemment trouvé. Cet agent est équivalent à l’article ReAct original, spécifiquement l’exemple de Wikipédia.

  • self-ask-with-search

Cet agent utilise un seul outil qui doit être nommé Intermediate Answer. Cet outil doit être capable de rechercher des réponses factuelles aux questions. Cet agent est équivalent à l’article original self ask with search, où une API de recherche Google était fournie en tant qu’outil.

  • conversational-react-description

Cet agent est conçu pour être utilisé dans des contextes conversationnels. L’invite est conçue pour rendre l’agent utile et conversationnel. Il utilise le framework ReAct pour décider quel outil utiliser et se sert de la mémoire pour se souvenir des interactions de conversation précédentes.

Le concept fondamental #3 « Memory » décortiqué

Une des grandes forces de ChatGPT, et des Transformers en général, réside dans leur capacité à garder les informations en mémoire.

On avait ce comportement avec les LSTM mais c’était une mémoire court-terme qui fonctionnait le temps d’une phrase ou d’un court paragraphe.

C’est un comportement que l’on peut reproduire avec LangChain.

Par défaut les chains et les agents LangChain n’ont pas de mémoires, mais il y a des composantes proposer pour gérer les messages précédents.

Néanmoins, les interactions entre l’utilisateur et le modèle de langage peuvent être gardées en mémoire grâce au ChatMessages et construite avec les ConversationChains.

La mémoire peut renvoyer plusieurs éléments d’information (par exemple, les N messages les plus récents et un résumé de tous les messages précédents). Les informations renvoyées peuvent être une chaîne de caractères ou une liste de messages.

Nous allons décortiquer quelques types de mémoires ensemble, n’hésitez pas à creuser d’avantage le sujet avec ces tutoriels mis en ligne par LangChain.

Le concept de ConversationChain dans LangChain

On va commencer par initialiser notre modèle de langage, on utilise GPT-3.5 pour cet exemple.

from langchain import OpenAI
from langchain.chains import ConversationChain

# first initialize the large language model

llm = OpenAI(
	temperature=0,
	openai_api_key="OPENAI_API_KEY",
	model_name="gpt-3.5-turbo"
)

On initialise ensuite la ConversationChain :

# now initialize the conversation chain
conversation = ConversationChain(llm=llm)

Si on affiche l’objet conversation, voici ce qu’on a :

LangChain: Le guide essentiel

L’utilisateur est défini par “Human” et l’agent par “AI”. Après le prompt initial, nous voyons deux paramètres ; {history} et {input}.

LangChain: Le guide essentiel

Le paramètre {input} est l’endroit où nous placerions la dernière requête humaine ; il s’agit de l’entrée saisie dans une zone de texte du chatbot. Ici {history} fait donc référence à la conversation initiée avec GPT comme montré ci-dessus.

Les différentes types de mémoires

Comme l’humain a différentes mémoires (sensorielle, visuelle, sonore etc), nous pouvons utiliser plusieurs types de mémoire conversationnelle avec la ConversationChain. En fonction du type de mémoire choisie, le texte passé au paramètre {history} changera. On peut lister

Type de mémoire #1: ConversationBufferMemory

La mémoire tampon de conversation (ConversationBufferMemory en anglais) fait exactement ce que son nom suggère : elle conserve une mémoire tampon des extraits de conversation précédents dans le cadre du contexte du prompt.

Caractéristique Principale : la mémoire tampon de conversation conserve les éléments de conversation précédents sans aucune modification, dans leur forme brute.

LangChain: Le guide essentiel

Type de mémoire #2: ConversationSummaryMemory

Le problème avec la mémoire tampon de la conversation est qu’au fur et à mesure que la conversation progresse, le nombre de jetons (tokens) de l’historique du contexte augmente. C’est un problème parce que nous pourrions épuiser notre LLM avec une prompt qui est trop grande pour être traitée. Pour rappel les longs historiques de conversations ne peuvent pas être mémorisées car nous avons une limite de jetons lorsqu’on utilise un LLM comme ChatGPT (4096 jetons pour text-davinci-003 et gpt-3.5-turbo).

Pas de panique, il y a une solution avec ConversationSummaryMemory.

Encore une fois, nous pouvons déduire du nom ce qui se passe… nous allons conserver un résumé de nos bribes de conversation précédentes comme historique. Comment allons-nous les résumer ? LLM à la rescousse.

Caractéristique Principale: la mémoire de résumé de conversation conserve les bribes de conversation précédentes sous une forme résumée, le résumé étant effectué par votre LLM.

LangChain: Le guide essentiel

Type de mémoire #3: ConversationBufferWindowMemory

Une autre option intéressante pour ces cas est le ConversationBufferWindowMemory où nous conserverons quelques-unes des dernières interactions dans notre mémoire, mais où nous laisserons intentionnellement tomber les plus anciennes — une mémoire à court terme si vous voulez. Ici, le nombre de jetons agrégés et le nombre de jetons par appel diminueront sensiblement.

Si nous n’avons besoin que de la mémoire des interactions récentes, c’est une excellente option.

Caractéristique Principale : la mémoire tampon de la conversation conserve les derniers éléments de la conversation sous forme brute.

Vous pouvez voir une analyse comparative détaillée sur ce notebook proposé par Pinecone.

Il existe bien d’autres types de mémoires, nous voulions simplement vous en proposer quelques unes, pour aller plus loin c’est ici.

Conclusion

LangChain fournit un ensemble complet d’outils, de fonctionnalités et de capacités qui simplifient le processus de combinaison des LLM avec des sources de données externes et des outils via API, ce qui rend plus facile que jamais l’exploitation de la pleine puissance des LLM et la mise en œuvre de solutions et d’apps révolutionnaires. Grâce à la prise en charge intégrée de divers fournisseurs de LLM, d’outils de gestion de prompts, de chaînes, d’agents et d’évaluation, LangChain ouvre la voie aux développeurs pour créer des applications sophistiquées, spécifiques à un domaine, qui maximisent le potentiel des LLM.

Une des voies les plus prometteuses semblent la création d’agents autonomes, des projets comme Auto-GPT ou BabyAGI (qui sont d’ailleurs supportés par Langchain)

Concrètement, vous avez plus de use-cases ?

Yes ser — Matt Schlicht, CEO d’Octane AI, que nous vous recommandons de suivre et de lire, propose la petite analyse suivante des cas possibles ou LangChain devient hyper pertinent:

LangChain: Le guide essentiel

Voilà on espère que ça vous a plu, et on vous dit à bientôt pour des tutoriels hands-on dédiés à LangChain maintenant que nous avons vu la théorie et les grands concepts fondamentaux ensemble.

Nos recos de YouTube Playlists

  1. Aperçu complète de LangChain par la chaîne “Data Independant”
  2. Les tutos de James Briggs — évangéliste pour LangChain et Pinecone
  3. OpenAI — Streamlit Web Apps
  4. Streamlit-Python-Tutorials

Liens et crédits:

  1. LangChain Docs : https://langchain.readthedocs.io/en/latest/index.html
  2. Pinecone AI handbook (massive respect to James Brigg and the pinecone team for this)
  3. LangChain GitHub Repo : https://github.com/hwchase17/langchain
  4. Open AI document
  5. Matt schlicht blog sur les agents autonomes