Aller au contenu

Introduction à Langgraph

Voici ce que nous dit ChatGPT quand on l'interroge sur Langgraph :

LangGraph est une bibliothèque open source développée par LangChain, permettant de construire des agents IA contrôlés par des graphes d'état (stateful agents).

L'essentiel est là : "agent IA" et "graphes d'état".

Nous avons déjà vu la notion d'agent IA au chapitre précédent, commençons donc par quelques rappels rapides sur la notion de graphe :

Les graphes

Un graphe est un "objet" mathématique composé de nœuds et d'arêtes. Les arêtes permettent de relier les nœuds entre eux. Voici un exemple de graphe :

un graphe

Sur le schéma ci-dessus, A, B, C, D, E et F sont des nœuds, et les droites reliant les nœuds sont les arêtes (par exemple, il existe une arête reliant A et B, mais il n'existe pas d'arête reliant A et F).

Les graphes sont très utilisés en informatique. Par exemple, dans les applications de GPS, les cartes routières sont des graphes où les villes sont des nœuds et les routes reliant les villes sont des arêtes.

Notion d'état

La notion d'état est absolument fondamentale dans Langgraph. En effet, nous allons avoir un état d'origine que nous allons appeler Si. Cet état va "passer" dans un graphe. Le rôle de chaque nœud du graphe va être de modifier cet état. A la fin nous obtiendrons un état final que nous appellerons Sf. En passant par un nœud, l'état va donc être modifié. Il y a 2 nœuds particuliers dans un graphe Langgraph : un nœud Start et un nœud End. Au niveau du nœud Start l'état vaut Si et au niveau du nœud End l'état vaut Sf. Voici un schéma pour vous aider à comprendre le principe :

graphe+etat

Dans le graphe ci-dessus, vous pouvez constater que nous avons bien l'état Si au niveau du nœud Start. Ensuite, le nœud A fait évoluer l'état de Si à S1. Ensuite, le nœud B fait passer l'état de S1 à Sf. Au niveau du nœud End nous retrouvons bien Sf.

Créez des agents avec Langgraph va consister à définir un état qui va évoluer en "passant" par les nœuds que vous aurez créés.

Prenons tout de suite un exemple simple sans utiliser de LLM

Un premier exemple avec Langgraph

from pydantic import BaseModel
from langgraph.graph import StateGraph, START, END
Nous commençons par importer les bibliothèques nécessaires : nous allons utiliser langgraph et Pydantic (Pydantic est une bibliothèque Python qui permet de valider des données et de gérer des modèles de données)

Commençons par définir notre état (classe State):

class State(BaseModel):
    msg : str
    compt : int

Notre classe State va nous permettre de représenter notre état. Cette classe hérite de la classe BaseModel de la bibliothèque Pydantic. Cet héritage nous permet de définir les 2 variables qui vont réellement constituer notre état, dans notre exemple, modifier l'état consistera à apporter des modifications aux variables msg (qui est de type "chaine de caractères") et compt (qui est de type "nombre entier"). Quand vous commencerez à développer une nouvelle application avec Langgraph, la première chose à faire sera sans doute de définir les variables qui constitueront votre état.

Créons notre graphe Langgraph :

graph_builder = StateGraph(State)
Pour construire ce graphe, nous utilisons la classe StateGraph, cette classe doit hériter de la classe qui définit notre état (State). Nous obtenons une instance de la classe StateGraph nommée graph_builder.

Ensuite, nous allons définir notre premier (et unique) noeud :

def node_A(state : State):
    new_msg = state.msg + "World!"
    new_compt = state.compt + 1
    return {'msg':new_msg, 'compt':new_compt}
Comme vous pouvez le constater ci-dessus, un nœud Langgraphe est une fonction Python. Cette fonction Python node_A prend en paramètre l'état (de type State) et renvoie un nouvel état sous forme de dictionnaire Python (les objets Pydantic peuvent être manipulés comme des dictionnaires Python). Les 2 lignes au-dessus du return récupèrent les valeurs des variables msg et compt avec state.msg et state.compt, créent 2 nouvelles valeurs qui sont placées dans new_msg et new_compt.

Nous créons un nœud à partir de la fonction node_A :

graph_builder.add_node("node_A", node_A)

Nous appliquons la méthode add_node à l'instance graph_builder. Cette méthode prend 2 paramètres : le nom de notre nœud et la fonction Python (nous avons ici choisi le même nom pour le nom du nœud et la fonction, mais ce n'est pas une obligation)

Sachant que les nœuds STARTet END sont déjà fournis par Langgraph (voir l'importation), nous avons nos 3 nœuds qui sont prêts. Nous devons maintenant relier ces nœuds à l'aide d'arêtes.

graph_builder.add_edge(START, 'node_A') #arête entre START et node_A
graph_builder.add_edge('node_A', END) #arête entre node_A et END
Pour terminer, il est nécessaire de compiler le graphe :

graph = graph_builder.compile()
Notre graphe est désormais prêt à l'emploi. Avant de l'utiliser, nous pouvons jeter un oeil à sa représentation graphique :

graphe

Nous pouvons maintenant utiliser notre graphe :

init_state = State(msg = 'Hello ', compt = 0)

Nous commençons par définir notre état initial en attribuant une valeur initiale à msg et compt

result = graph.invoke(init_state)
Nous utilisons la méthode invoke sur l'instance graph. Cette méthode prend notre état initial en paramètre.

print(result)
[Out]
{'msg': 'Hello World!', 'compt': 1}
Comme vous pouvez le constater en exécutant la cellule ci-dessus, result est un dictionnaire, nous pouvons donc récupérer la valeur qui nous intéresse :

print(f"le message final est {result['msg']} et le compteur final est {result['compt']}")
[Out]
le message final est Hello World! et le compteur final est 1

En passant dans le graphe, notre état a bien été modifié par le nœud node_A

Cela termine ce chapitre. A ce stade, vous ne devez pas trop voir le rapport avec les agents et les LLM, mais rassurez-vous, nous allons y revenir dans les prochains chapitres. Il était important de poser les bases de Langgraph afin de pouvoir l'utiliser correctement dans la suite de ce cours.

Code complet :

from pydantic import BaseModel
from langgraph.graph import StateGraph, START, END

class State(BaseModel):
    msg : str
    compt : int

graph_builder = StateGraph(State)

def node_A(state : State):
    new_msg = state.msg + "World!"
    new_compt = state.compt + 1
    return {'msg':new_msg, 'compt':new_compt}

graph_builder.add_node("node_A", node_A)
graph_builder.add_edge(START, 'node_A') #arête entre START et node_A
graph_builder.add_edge('node_A', END) #arête entre node_A et END

graph = graph_builder.compile()

init_state = State(msg = 'Hello ', compt = 0)
result = graph.invoke(init_state) #on peut aussi directement écrire result = graph.invoke({'msg' : 'Hello', 'compt' : 0})
print(f"le message final est {result['msg']} et le compteur final est {result['compt']}")