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 :
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 :
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
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)
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}
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 START
et 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
graph = graph_builder.compile()
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)
invoke
sur l'instance graph
. Cette méthode prend notre état initial en paramètre.
print(result)
{'msg': 'Hello World!', 'compt': 1}
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']}")
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']}")