Retour au catalogue
Season 49 18 Épisodes 1h 3m 2026

LangGraph

v1.1 — Édition 2026. Un cours audio complet sur LangGraph, un framework pour créer des workflows agentiques stateful et de longue durée. Couvre les modèles mentaux, les Graph API et Functional API, la mémoire, le voyage dans le temps, l'humain dans la boucle et le déploiement en production.

Orchestration LLM Systèmes multi-agents Frameworks AI/ML
LangGraph
Lecture en cours
Click play to start
0:00
0:00
1
Le problème de l'orchestration : Pourquoi LangGraph ?
Une introduction aux problèmes fondamentaux que LangGraph résout. Nous explorons la transition des workflows linéaires simples vers l'orchestration d'agents stateful et de longue durée.
3m 39s
2
Penser en LangGraph : Le modèle mental
Apprenez à traduire des tâches d'IA complexes dans le modèle mental de LangGraph. Nous décomposons les concepts fondamentaux de Nodes, Edges et State.
3m 34s
3
La Graph API : State et reducers
Plongez dans la mécanique de la Graph API. Nous expliquons comment TypedDict définit votre schéma et comment les reducers gèrent les mises à jour d'état provenant de plusieurs nœuds.
3m 03s
4
La Functional API : @entrypoint et @task
Explorez la Functional API comme alternative à la Graph API. Nous discutons de la manière d'obtenir une persistance de niveau entreprise en utilisant le flux de contrôle standard de Python.
3m 32s
5
Gérer l'historique de conversation avec MessagesState
Comprenez les défis liés à l'historique de chat dans les agents d'IA. Nous explorons MessagesState et le reducer add_messages pour gérer les modifications et la déduplication.
3m 25s
6
Choisir son abstraction : Graph vs Functional
Un cadre pour décider quelle API utiliser. Nous opposons le routage visuel explicite de la Graph API au flux impératif de la Functional API.
3m 38s
7
Routage dynamique et conditional edges
Allez au-delà de la logique codée en dur. Nous discutons de l'utilisation des LLMs avec des sorties structurées aux côtés des conditional edges pour router dynamiquement les workflows.
3m 21s
8
Workflows Map-Reduce avec la Send API
Maîtrisez le modèle Orchestrator-Worker. Nous plongeons dans la Send API pour déployer dynamiquement des nœuds travailleurs parallèles en fonction des plans d'exécution.
4m 07s
9
Persistance : Threads et Checkpoints
Découvrez les fondations de la gestion d'état. Nous expliquons les Threads, les Checkpoints et les Super-steps, en montrant comment LangGraph garantit la survie aux plantages.
3m 49s
10
Exécution durable et idempotence
Comprenez les nuances de la reprise des workflows. Nous expliquons pourquoi les effets de bord doivent être idempotents et comment structurer les nœuds pour une exécution durable.
3m 36s
11
Humain dans la boucle : Interrupts
Apprenez à figer les agents en pleine exécution. Nous détaillons la fonction interrupt et comment reprendre les workflows avec une approbation humaine externe.
3m 58s
12
Déboguer le passé : Voyage dans le temps et forking
Explorez les capacités de voyage dans le temps de LangGraph. Nous montrons comment naviguer dans l'historique des états, rejouer d'anciens Checkpoints et créer des forks pour des chemins d'exécution alternatifs.
3m 20s
13
Mémoire à long terme : Stores à travers les Threads
Allez au-delà des Threads isolés. Nous introduisons l'interface Store et expliquons comment accorder à vos agents une mémoire persistante inter-sessions.
3m 23s
14
Exécution en streaming et le format v2
Améliorez l'expérience utilisateur avec des retours en temps réel. Nous décomposons les modes de stream (values, updates, messages) et le format unifié StreamPart v2.
3m 33s
15
Composer la complexité : Subgraphs
Faites évoluer vos workflows en traitant les graphes compilés comme des nœuds. Nous discutons de la composition de Subgraphs et de la gestion des schémas d'état partagés par rapport aux schémas privés.
3m 01s
16
Persistance des Subgraphs et modèles multi-agents
Maîtrisez la portée de la mémoire dans les systèmes multi-agents. Nous expliquons la différence entre la persistance de Subgraph par invocation, par thread et stateless.
3m 17s
17
Structure de l'application et préparation au déploiement
Passez des prototypes à la production. Nous explorons langgraph.json, la structure de fichiers appropriée et la gestion des dépendances pour les déploiements stateful.
3m 56s
18
Tester l'exécution du Graph de bout en bout
Apprenez des stratégies de test robustes pour les workflows de graphes. Nous couvrons l'intégration de pytest, l'exécution isolée de nœuds et la simulation d'états partiels.
3m 22s

Épisodes

1

Le problème de l'orchestration : Pourquoi LangGraph ?

3m 39s

Une introduction aux problèmes fondamentaux que LangGraph résout. Nous explorons la transition des workflows linéaires simples vers l'orchestration d'agents stateful et de longue durée.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. LangGraph, épisode 1 sur 18. La plupart des scripts de LLM fonctionnent parfaitement pour un prompt rapide et une réponse instantanée. Mais quand une tâche prend vingt minutes à tourner et que le réseau lâche à mi-chemin, tout s'effondre. Tu perds ta progression, ton contexte et ton budget API. "Le problème d'orchestration : pourquoi LangGraph ?" parle exactement de comment corriger cette fragilité. LangGraph est un framework d'orchestration conçu pour gérer des applications multi-acteurs stateful. Tu pourrais entendre le nom et supposer que c'est juste une feature de LangChain. Ce n'est pas le cas. LangGraph est un moteur d'orchestration bas niveau, et tu n'as pas du tout besoin d'utiliser LangChain pour t'en servir. Il existe spécifiquement pour modéliser des workflows d'agents sous forme de graphes stateful plutôt que de simples scripts linéaires. Les scripts standards s'exécutent en mémoire. Si un script s'arrête d'un coup, toutes ces données de runtime disparaissent. Imagine un scénario où tu as un agent en background qui fait des recherches sur un document de cent pages. L'agent lit, extrait des faits et croise des informations depuis vingt minutes sans interruption. Si un timeout serveur survient à la dix-neuvième minute, un script standard perd tout ce state. Tu dois recommencer tout le job depuis le début. LangGraph résout ce problème d'orchestration grâce à l'exécution durable. En modélisant ton workflow sous forme de graphe, chaque étape distincte devient un nœud, et les connexions logiques entre elles sont des edges. Quand l'application passe d'un nœud au suivant, LangGraph sauvegarde automatiquement sa progression. Il traite les processus longs comme une série de checkpoints sécurisés. Si le système crashe, LangGraph reprend l'exécution exactement là où il s'était arrêté. Ce mécanisme de checkpointing repose sur une mémoire complète. La mémoire dans LangGraph n'est pas juste une liste continue de messages de chat. C'est le state complet du graphe. Quand un nœud termine son traitement, il met à jour un objet de state partagé. Le nœud suivant dans la séquence lit son input directement depuis ce state. Ça veut dire que la mémoire persiste sur tout le cycle de vie de l'application. L'agent en background qui fait des recherches sur ton document n'oublie pas les données critiques qu'il a trouvées à la page cinq quand il arrive enfin à la page quatre-vingt-dix, parce que le state du graphe les conserve en toute sécurité. Voici le point clé. Parce que le state du graphe est mis en pause et sauvegardé proprement entre les étapes, tu gagnes la possibilité d'intégrer un human in the loop. Parfois, un agent autonome atteint un point de décision où il a besoin d'une permission avant de continuer, comme envoyer un e-mail final ou exécuter une transaction financière. Dans un script standard, faire une pause pour qu'un utilisateur clique sur un bouton entraîne souvent des timeouts de connexion. Dans LangGraph, tu configures simplement un nœud spécifique pour stopper l'exécution. Le système se met en veille et préserve parfaitement le state actuel. Un opérateur humain peut alors revoir les données collectées, approuver l'action suivante, ou même modifier manuellement le state de l'agent avant de cliquer sur continuer. Une fois approuvé, le graphe se réveille et reprend son travail avec le contexte mis à jour. Ce qu'il faut retenir, c'est que construire des agents complexes repose énormément sur la gestion du state et des échecs. LangGraph éloigne ton architecture des scripts fragiles et limités par la mémoire, vers des graphes résilients qui survivent aux interruptions, se souviennent de leur passé, et attendent patiemment les instructions humaines. Si tu veux soutenir l'émission, tu peux chercher DevStoriesEU sur Patreon. C'est tout pour cet épisode. Merci d'avoir écouté, et continue de développer !
2

Penser en LangGraph : Le modèle mental

3m 34s

Apprenez à traduire des tâches d'IA complexes dans le modèle mental de LangGraph. Nous décomposons les concepts fondamentaux de Nodes, Edges et State.

Télécharger
Salut, ici Alex de DEV STORIES DOT EU. LangGraph, épisode 2 sur 18. Tu crées un agent d'intelligence artificielle en bourrant un énorme prompt avec des instructions, des edge cases et des exemples, puis tu l'envoies en espérant qu'il fasse ce qu'il faut. Ça marche jusqu'à ce que ça plante, et déboguer le résultat est une vraie galère. Pour corriger ça, tu dois arrêter d'écrire des prompts monolithiques et commencer à concevoir des systèmes. Ce qui nous amène à Penser en LangGraph : le modèle mental. LangGraph t'oblige à t'éloigner des scripts linéaires. Il te demande de penser à ton application comme à une state machine. La toute première étape est simplement de définir ton processus. Avant d'écrire la moindre ligne de code, regarde ce que tu veux que le système accomplisse et décompose-le en actions distinctes. Prends l'exemple d'un système de triage pour le support client. Un utilisateur envoie un message. Un opérateur humain lirait l'email, déciderait s'il s'agit d'un problème de facturation ou de support technique, puis rédigerait une réponse appropriée. Cette séquence, c'est ton processus. Tu mappes ce processus sur un workflow LangGraph en utilisant trois composants principaux qui sont le State, les Nodes et les Edges. Commençons par le State. Le State est la mémoire partagée de ton graph. C'est une structure qui contient le contexte de toute ton opération à un instant donné. Chaque étape de ton workflow va lire depuis ce State et y écrire des mises à jour. Les auditeurs font souvent une erreur spécifique ici. Ils essaient de stocker des strings de prompt entièrement formatées à l'intérieur du State. Ne fais pas ça. Le State ne devrait contenir que des données brutes. Il contient le texte original de l'email du client, la catégorie extraite, ou une liste brute des messages précédents. Le formatage se fait à la demande, plus tard, exactement dans l'étape où il est nécessaire. Ensuite, on a les Nodes. Si le State est la mémoire, les Nodes sont les workers qui font les vraies tâches. Un Node est juste une fonction Python qui exécute une seule étape logique de ton processus. Il reçoit le State actuel, effectue une action, et retourne une mise à jour. Dans notre exemple de triage, tu créerais trois Nodes séparés. Le premier est un Node Read. Il prend l'email entrant et sauvegarde le texte brut dans le State. Le deuxième est un Node Classify. Il regarde le texte brut dans le State, demande à un modèle de langage de le catégoriser comme facturation ou technique, et sauvegarde cette catégorie résultante dans le State. Le troisième est un Node Draft. Il lit à la fois l'email et la catégorie depuis le State, les formate dans un prompt localement, et génère une réponse. Chaque Node fait exactement un seul job. Enfin, tu as besoin d'un moyen de connecter ces workers. C'est le rôle des Edges. Les Edges représentent la logique de routage. Ils dictent ce qui se passe après qu'un Node a terminé son travail. Un Edge standard dit simplement : une fois que le Node Read a terminé, va toujours vers le Node Classify. Mais LangGraph utilise aussi des conditional edges. C'est là que ça devient intéressant. Après le Node Classify, tu peux utiliser un conditional edge pour inspecter le State. Si la catégorie est facturation, l'Edge route le flow vers un Node Draft spécifique à la facturation. Si c'est technique, il route vers un Node Draft technique. Les Edges prennent des décisions de trafic basées sur les données que tes Nodes viennent de produire. Tu commences par le processus, tu isoles les données dans un State partagé, tu définis les workers comme des Nodes, et tu dictes le flow avec des Edges. En décomposant le problème, tu isoles les erreurs. Traite ton application non pas comme un simple générateur de texte, mais comme une chaîne de montage coordonnée où les données brutes passent systématiquement d'un worker spécialisé au suivant. Merci d'avoir écouté, happy coding tout le monde !
3

La Graph API : State et reducers

3m 03s

Plongez dans la mécanique de la Graph API. Nous expliquons comment TypedDict définit votre schéma et comment les reducers gèrent les mises à jour d'état provenant de plusieurs nœuds.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. LangGraph, épisode 3 sur 18. Deux fonctions parallèles terminent leur exécution à la milliseconde près et essaient d'écrire dans la même liste partagée. Généralement, l'une écrase l'autre, et les données disparaissent. Pour éviter ça, LangGraph s'appuie sur la Graph API : le state et les reducers. La base de n'importe quel workflow LangGraph, c'est son state. Tu le définis en utilisant un TypedDict Python standard. Ce dictionnaire définit les clés exactes que ton graph va tracker et les types de données pour chaque clé. Vois ça comme le schema qui est passé de node en node pendant que ton graph tourne. Voici le truc important à retenir. Les nodes dans LangGraph ne mutent pas le state directement. Un node reçoit une copie du state actuel, fait son boulot, et retourne un dictionnaire d'updates. Une erreur classique, c'est de penser que retourner un dictionnaire remplace tout l'objet state. Ce n'est pas le cas. Si ton state a cinq clés et que ton node retourne un dictionnaire avec une seule clé, LangGraph laisse les quatre autres intactes et applique uniquement ton update spécifique. Comment LangGraph applique cet update ? C'est là que les reducers entrent en jeu. Un reducer, c'est simplement une fonction qui dicte comment une valeur retournée merge avec la valeur existante pour une clé spécifique. Par défaut, LangGraph utilise un reducer de type overwrite. Si ton node retourne une nouvelle string pour une clé status, l'ancienne string disparaît, complètement remplacée par la nouvelle. Parfois, l'overwrite, c'est exactement ce que tu veux éviter. Prends l'exemple d'un workflow de data fetching en parallèle. Tu as une clé de state partagée qui s'appelle results, et c'est une liste. Tu lances deux nodes qui tournent en même temps pour fetcher différents batchs de data. Si les deux nodes retournent un dictionnaire qui met à jour la clé results, le comportement overwrite par défaut fait que le node qui finit en dernier efface le travail de l'autre. Pour corriger ça, tu annotes la clé results dans ton TypedDict avec un reducer spécifique, comme l'operator point add natif de Python. Maintenant, quand les deux nodes retournent leurs listes, le reducer agit comme un agent de la circulation. Il prend la liste existante et ajoute en toute sécurité les outputs des deux nodes. Rien ne passe à la trappe. Il y a un edge case. Que se passe-t-il si tu as un reducer de type append sur ta clé results, mais que tu arrives à un point dans ton graph où tu as vraiment besoin de vider la liste et de recommencer ? Si ton node retourne une liste vide, le reducer ajoute simplement une liste vide à celle existante, laissant l'ancienne data intacte. Pour ce scénario, LangGraph fournit un type spécial : Overwrite. Quand ton node encapsule son update dans un objet Overwrite, LangGraph le détecte et bypasse complètement le reducer. Il jette l'ancienne liste et force un hard reset. Le state dans un graph complexe n'est pas une variable globale fragile constamment mutée, mais un log append-only d'updates contrôlées, régi par des règles de réduction claires. C'est tout pour cet épisode. Merci de m'avoir écouté, et continue à développer !
4

La Functional API : @entrypoint et @task

3m 32s

Explorez la Functional API comme alternative à la Graph API. Nous discutons de la manière d'obtenir une persistance de niveau entreprise en utilisant le flux de contrôle standard de Python.

Télécharger
Salut, ici Alex de DEV STORIES DOT EU. LangGraph, épisode 4 sur 18. Parfois, tu veux juste écrire un script Python standard avec des if-statements et des for-loops normaux, mais tu as quand même besoin d'une state persistence de niveau entreprise. Tu ne veux pas construire une state machine explicite juste pour lancer quelques appels à un modèle de langage en séquence. C'est là que la Functional API, et plus précisément les decorators entrypoint et task, résout cette tension. Créer des applications avec des nodes et des edges explicites t'oblige à définir manuellement comment la data route d'une étape à l'autre. Cette structure offre un contrôle immense, mais elle peut paraître lourde quand ta logique est très séquentielle ou qu'elle repose sur des loops de programmation standards. La Functional API te permet d'écrire du code Python normal, de haut en bas, tout en gardant les fonctionnalités intégrées de streaming et de recovery. Au lieu d'instancier un objet graph, tu appliques des decorators à tes fonctions Python existantes. Tu commences avec le decorator task. Tu l'appliques aux unités de travail individuelles de ton application. Imagine une task comme une étape distincte qui fait quelque chose de spécifique, comme requêter une base de données, calculer une métrique ou prompter un modèle. Quand une fonction porte le decorator task, le framework l'encapsule dans une couche de tracking pour surveiller son exécution. Ensuite, tu utilises le decorator entrypoint. Tu le places sur la fonction d'orchestration principale qui dirige le flow global. À l'intérieur de cette fonction entrypoint, tu appelles tes tasks décorées en utilisant le control flow standard de Python. Tu assignes l'output d'une task à une variable, puis tu passes cette variable à la task suivante. Tu peux utiliser des blocs try-except, des list comprehensions ou des while-loops. La logique d'orchestration se comporte exactement comme du Python natif. Comme le code a l'air tout à fait standard, tu pourrais supposer qu'il lui manque la mémoire d'une state structure formelle. C'est une idée fausse courante. La Functional API checkpoint quand même automatiquement ta progression en arrière-plan. Chaque fois qu'une task se termine, LangGraph intercepte la valeur de retour et la sauvegarde dans un persistent store. Le framework enregistre de manière sécurisée les inputs et outputs de chaque fonction décorée au fur et à mesure de leur exécution. Prends l'exemple d'un script de rédaction automatique de dissertations. Tu définis trois tasks décorées : une fonction pour générer un plan, une fonction pour écrire un paragraphe et une fonction pour relire le brouillon. Dans ta fonction entrypoint principale, tu appelles d'abord le générateur de plan. Ensuite, tu écris une for-loop standard qui itère sur les sections de ce plan, en appelant la task d'écriture de paragraphe pour chacune d'elles. Tu ajoutes les résultats à une liste locale. Enfin, tu lances la task de relecture. Tu utilises un simple if-statement pour vérifier le score obtenu. Si le score est mauvais, ton code déclenche simplement une while-loop pour réécrire certains paragraphes jusqu'à ce que le score s'améliore. Voici le point clé. Grâce au checkpointing caché, si ton script rencontre un timeout réseau pendant l'écriture du troisième paragraphe, tu ne perds pas ton travail. Quand tu redémarres le processus avec le même identifiant de thread, LangGraph sait que le plan et les deux premiers paragraphes sont déjà terminés. Il ignore complètement l'exécution de ces tasks, récupère leurs outputs en cache depuis le state store, et reprend l'exécution précisément au troisième paragraphe. La Functional API déplace la charge cognitive de la visualisation de routing topologies abstraites vers la simple lecture du code de haut en bas, te donnant la résilience d'une state machine avec la simplicité d'un simple script. C'est tout pour cette fois. Merci pour ton écoute, et continue de builder !
5

Gérer l'historique de conversation avec MessagesState

3m 25s

Comprenez les défis liés à l'historique de chat dans les agents d'IA. Nous explorons MessagesState et le reducer add_messages pour gérer les modifications et la déduplication.

Télécharger
Salut, ici Alex de DEV STORIES DOT EU. LangGraph, épisode 5 sur 18. Tu développes une application de chat, et un utilisateur repère une faute de frappe dans son prompt. Il clique sur modifier, le corrige, et appuie sur envoyer. Mais au lieu de remplacer son ancien message, ton backend ajoute simplement la version corrigée à la fin de l'historique, laissant la faute de frappe d'origine exactement là où elle était, comme un doublon fantôme. Gérer l'historique de conversation avec MessagesState, c'est comme ça que tu évites ce problème. Quand les développeurs créent leur premier graph, ils définissent généralement un dictionnaire de state custom pour stocker leur historique de chat. Une erreur courante est d'utiliser un append de liste standard pour gérer cet historique. Ils attachent la fonction de l'opérateur standard point add à leur liste de messages. Ça dit au graph de simplement prendre les nouveaux messages et de les coller à la fin de l'array existant. Cette approche append-only fonctionne très bien pour un simple bot ping-pong où un utilisateur parle, l'IA répond, et l'historique grandit de façon séquentielle. Mais ça casse complètement quand le state doit être mutable. Si un humain édite un ancien prompt, ou qu'un agent décide de regénérer sa dernière réponse, l'addition standard ne peut pas gérer ça. Tu te retrouves avec des doublons. LangGraph fournit une structure de state built-in pour résoudre ça, qui s'appelle MessagesState. Elle contient une seule clé appelée messages. Voici le point essentiel. La puissance de MessagesState, ce n'est pas la clé en elle-même, mais la fonction reducer spécifique qui y est attachée, appelée add messages. Le reducer add messages ne fait pas juste un append aveugle des données. Il traque les ID des messages. À chaque fois qu'un nouveau message entre dans le state, le reducer vérifie son ID unique. Si cet ID existe déjà quelque part dans l'historique de conversation, le reducer écrase l'ancien message avec le nouveau. Si l'ID est nouveau, ou si le message n'a pas encore d'ID, le reducer fait un append à la fin de la liste. Repense à notre scénario de faute de frappe. L'utilisateur humain envoie un prompt. Le système donne à ce message l'ID 123. L'utilisateur se rend compte de son erreur, édite le texte, et soumet la correction. Le frontend envoie le nouveau texte, en le taguant explicitement avec l'ID 123. Quand ces données arrivent dans le graph, le reducer add messages scanne l'historique, trouve le message d'origine à l'ID 123, et remplace le texte sur place. Le doublon fantôme a disparu. La conversation se déroule exactement comme prévu. Au-delà de la gestion des ID, le reducer add messages gère aussi la désérialisation des données. Dans une application en production, tes messages arrivent souvent dans différents formats. Ton frontend pourrait envoyer des dictionnaires JSON bruts contenant des strings pour le role et le content. Tes nodes de graph internes pourraient générer des objets de message LangChain natifs. Le reducer agit comme un traducteur universel pour ces inputs. Si tu passes une liste de simples dictionnaires Python dans le state, la fonction add messages les convertit automatiquement dans les bonnes classes de message LangChain. Tu n'as pas besoin d'écrire du code boilerplate pour parser un dictionnaire en HumanMessage ou AIMessage avant de mettre à jour le state. Ça normalise les données pour toi. Quand tu construis des agents de chat, l'historique du state n'est pas un log append-only, c'est un document vivant, et lier tes mises à jour à des ID de message uniques, c'est ce qui garde ce document précis. Merci de ton écoute. À la prochaine !
6

Choisir son abstraction : Graph vs Functional

3m 38s

Un cadre pour décider quelle API utiliser. Nous opposons le routage visuel explicite de la Graph API au flux impératif de la Functional API.

Télécharger
Salut, ici Alex de DEV STORIES DOT EU. LangGraph, épisode 6 sur 18. Si tu choisis le mauvais paradigme de conception dès le départ, tu vas te retrouver soit à écrire une centaine de lignes de boilerplate pour un simple script, soit à tisser un cauchemar de code spaghetti à partir de fonctions Python basiques. Aujourd'hui, on se concentre sur le choix de ton abstraction : Graph ou Functional. Il est facile de supposer que l'une de ces API est intrinsèquement plus puissante ou plus production-ready que l'autre. C'est faux. Sous le capot, la Graph API et la Functional API compilent vers exactement le même runtime engine. Elles supportent toutes les deux la persistance, le streaming et le contrôle d'exécution exactement de la même manière. Le choix entre les deux dépend uniquement de ton modèle mental et de la façon dont tu veux exprimer ta logique. Regardons d'abord la Functional API. Elle repose sur le control flow impératif standard de Python. Tu écris des fonctions Python normales, tu les marques avec un decorator, et tu routes ton exécution en utilisant des if-statements et des boucles standards. Ici, le state management est entièrement function-scoped. Les données circulent strictement de la return value d'une fonction vers les arguments de la suivante. Il n'y a pas d'objet de mémoire globale partagée qui flotte en arrière-plan. Si ton workflow est linéaire, ou s'il a une logique prévisible et bien délimitée, la Functional API garde ton code léger et familier. Tu évites complètement l'overhead de définir des structures de graph. La Graph API demande un état d'esprit différent. Au lieu d'appeler des fonctions directement, tu définis un state schema global et partagé. Ensuite, tu écris des nodes, qui sont de petites fonctions dont le seul boulot est de lire et de muter ce state partagé. Enfin, tu relies explicitement ces nodes entre eux en utilisant des edges. Le routing n'est pas géré par une instruction conditionnelle cachée au fond du corps d'une fonction. Au lieu de ça, la logique qui dicte où l'application va ensuite est extraite dans des conditional edges explicites, déclarés au top level du graph. Voici l'idée clé. Tu choisis entre les deux en fonction de la façon dont ton système gère le state et le routing au fil du temps. Imagine un développeur qui construit un outil d'extraction de données basique. Il fait tourner un seul language model, parse l'output, et le sauvegarde. La Functional API est parfaite pour ça. C'est rapide à écrire et facile à lire. Mais avance de trois mois. Ce simple script est refactoré en un système multi-agent complexe. Maintenant, tu as un agent chercheur qui transmet des données à un agent rédacteur, un agent critique qui renvoie des corrections, et une pause d'exécution qui attend qu'un manager humain approuve le draft final. Si tu essaies de construire ce workflow multi-agent asynchrone avec la Functional API, l'approche impérative s'effondre. Tu finis par faire passer des payloads de données massifs de haut en bas dans des call stacks de fonctions très profondes. Ta logique de routing se retrouve enterrée dans des conditions profondément imbriquées. C'est le moment exact où tu migres vers la Graph API. L'abstraction Graph brille ici parce qu'elle découple le state de l'exécution. Parce que le state est global et partagé, tes nodes agents individuels n'ont pas besoin de se passer de lourdes structures de données entre eux. Un node lit simplement le state partagé, met à jour la clé spécifique dont il est responsable, et termine. Les edges explicites prennent le relais, rendant le routing très visible. Tu peux regarder la définition du graph et cartographier immédiatement tout le workflow sans lire une seule ligne de business logic. Tu utilises la Functional API quand le control flow est assez simple pour être lu de haut en bas, mais tu passes à la Graph API quand le routing devient assez complexe pour que tu aies besoin de le dessiner sur un tableau blanc. Merci de m'avoir écouté. Prenez soin de vous, tout le monde.
7

Routage dynamique et conditional edges

3m 21s

Allez au-delà de la logique codée en dur. Nous discutons de l'utilisation des LLMs avec des sorties structurées aux côtés des conditional edges pour router dynamiquement les workflows.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. LangGraph, épisode 7 sur 18. Les conditions hardcoded ont leurs limites quand tu construis un agent. Si un utilisateur pose une question complexe, une simple recherche par mot-clé ne peut pas déterminer de façon fiable ce que ton application doit faire ensuite. Et si l'IA elle-même pouvait dicter le chemin de ton workflow ? C'est là qu'interviennent le routing dynamique et les conditional edges. Dans une configuration de graphe standard, tu connectes le node A au node B. C'est un chemin statique et garanti. Mais quand tu construis un mécanisme de routing intelligent, le chemin doit changer en fonction des données entrantes. Tu pourrais penser que les edges dans LangGraph n'acceptent que des connexions de type string hardcoded. Ce n'est pas le cas. Un edge peut être une fonction Python qui lit le state actuel de ton graphe et calcule dynamiquement le nom du prochain node. Tu attaches cette logique à ton graphe en utilisant la méthode add conditional edges. Cette méthode a besoin de trois composants. Premièrement, le node de départ. Deuxièmement, une fonction de routing. Troisièmement, un dictionnaire qui mappe les string outputs possibles de ta fonction de routing vers les nodes de destination réels dans ton graphe. Voici l'idée clé. La façon la plus fiable de piloter un conditional edge, c'est de le combiner avec un large language model qui génère des données structurées. Tu ne veux pas que la fonction de routing elle-même fasse des évaluations complexes ou du natural language processing. À la place, tu as un node en amont où le modèle est forcé de renvoyer une structure stricte, comme un modèle Pydantic. Prends l'exemple d'un router de service client. Un utilisateur envoie un message. Le premier node de ton graphe est un classifieur d'intent. À l'intérieur de ce node, tu passes le message de l'utilisateur à un language model et tu lui demandes de renvoyer un output structuré avec un seul champ appelé intent. Le modèle évalue le texte et remplit ce champ avec une valeur spécifique, comme billing, tech support ou sales. Cette réponse structurée est ensuite sauvegardée dans le state du graphe. Maintenant, le conditional edge prend le relais. L'edge est attaché au node du classifieur. Quand le node du classifieur se termine, le conditional edge déclenche une courte fonction Python. Cette fonction prend le state du graphe en input, regarde à l'intérieur du state, et extrait la valeur intent que le modèle vient de générer. Si l'intent est billing, la fonction retourne la string billing. Le conditional edge regarde son dictionnaire de mapping, voit que la string billing correspond à ton node billing, et passe l'exécution à ce node spécifique. Si l'intent est tech support, elle retourne une string différente, qui route le flow vers le node tech support. Tu utilises le language model pour ses capacités de raisonnement afin de catégoriser l'input, mais tu gardes la logique de routing réelle déterministe. La fonction Python dans le conditional edge lit simplement une variable et retourne une string. C'est très prévisible et facile à tester. Le point le plus utile à retenir ici, c'est que tu dois toujours découpler la décision de la direction. Laisse le language model décider de l'intent et l'écrire dans le state, puis utilise un conditional edge en pur Python pour lire ce state et diriger le graphe. Avant de terminer, si tu trouves ces épisodes utiles et que tu veux soutenir l'émission, tu peux chercher DevStoriesEU sur Patreon — ça nous aide vraiment beaucoup. C'est tout pour cet épisode. Merci pour ton écoute, et continue à développer !
8

Workflows Map-Reduce avec la Send API

4m 07s

Maîtrisez le modèle Orchestrator-Worker. Nous plongeons dans la Send API pour déployer dynamiquement des nœuds travailleurs parallèles en fonction des plans d'exécution.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. LangGraph, épisode 8 sur 18. Tu ne peux pas hardcoder tes chemins d'exécution quand tu n'as aucune idée du nombre de sous-tâches que ton agent va décider de créer avant le runtime. Si ton système décide à la volée qu'il doit traiter trois éléments, ou trente, le routage statique standard va planter. Pour régler ça, tu as besoin des workflows Map-Reduce avec l'API Send. Une erreur super courante quand tu développes avec LangGraph, c'est d'essayer d'utiliser des conditional edges standards pour faire du fan-out dynamique. Les conditional edges sont parfaits quand tu veux choisir entre des chemins connus et prédéterminés en te basant sur un test logique. Par contre, ils ne marchent plus du tout quand tu as besoin de spawner un nombre inconnu de tâches parallèles identiques au runtime. La parallélisation standard te permet de router vers plusieurs nodes fixes. Tu nommes les nodes, et le graph les déclenche. Mais qu'est-ce qui se passe quand tu dois faire tourner exactement le même node plusieurs fois en simultané, avec à chaque fois des données différentes ? Tu ne peux pas faire ça avec du routage basique. Ça nous amène au pattern Orchestrator-worker. Dans cette architecture, un node central analyse les données entrantes, calcule combien de tâches séparées sont requises, et dispatche des workers dynamiques pour les gérer en parallèle. LangGraph permet ce pattern spécifiquement grâce à l'API Send. Prends l'exemple d'un agent qui a pour tâche de rédiger un rapport de recherche complet. Le premier node sert d'orchestrator. Il lit le prompt de l'utilisateur et génère un plan. Selon la complexité du sujet, ce plan peut contenir trois sections, ou il peut en contenir douze. Tu veux qu'un worker node séparé rédige chaque section exactement en même temps. Pour y arriver, tu définis une fonction de conditional edge juste après ton orchestrator node. Au lieu de renvoyer une simple string qui pointe vers le prochain node statique dans le graph, cette fonction d'edge renvoie une liste d'objets Send. C'est là qu'est l'astuce. Un objet Send package une destination et ses données ensemble. Il prend deux arguments. Le premier argument, c'est le nom du worker node que tu veux déclencher. Le deuxième argument, c'est le payload spécifique pour ce worker isolé. Dans notre scénario de rapport, la fonction orchestrator itère sur le plan généré. Pour chaque sujet de section qu'elle trouve, elle crée un nouvel objet Send qui pointe vers un seul node appelé draft_section, en passant la string du sujet individuel comme payload. Quand LangGraph évalue cette fonction d'edge, il reçoit la liste des objets Send. Il va ensuite spawner dynamiquement une instance parallèle du node draft_section pour chaque élément de cette liste. Si l'orchestrator a généré un plan avec sept sections, LangGraph lance sept nodes de rédaction en parallèle. Chaque node exécute un code identique, mais opère sur son propre payload unique. Générer ces workers dynamiques, c'est la phase de map. Rassembler leurs outputs indépendants, c'est la phase de reduce. Vu que ces worker nodes tournent en simultané, ils ne peuvent pas écraser une seule string dans le state de ton graph en toute sécurité. Ton state global doit être configuré pour collecter plusieurs updates entrants en simultané. Tu gères ça en attachant une fonction reducer au champ spécifique du state qui va contenir tes sections rédigées, en lui disant d'append les nouveaux éléments à une liste plutôt que d'écraser la valeur précédente. À mesure que chaque draft worker parallèle termine d'écrire, il renvoie son bloc de texte. LangGraph attrape ces réponses et utilise ton reducer pour empiler chaque bloc de texte en toute sécurité dans l'array partagé. Une fois que chaque worker dynamique a terminé son exécution, toute l'étape parallèle est résolue. Le workflow avance ensuite, en transportant une liste complète et remplie de toutes les sections rédigées. L'API Send sort l'exécution parallèle de la définition statique de ton graph et la met directement entre les mains de tes données au runtime. Merci d'avoir écouté. J'espère que tu as appris un truc nouveau.
9

Persistance : Threads et Checkpoints

3m 49s

Découvrez les fondations de la gestion d'état. Nous expliquons les Threads, les Checkpoints et les Super-steps, en montrant comment LangGraph garantit la survie aux plantages.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. LangGraph, épisode 9 sur 18. Ton serveur plante en plein traitement d'un dataset massif par un agent. Il ne devrait pas avoir à repartir de zéro. Il devrait reprendre exactement là où il s'est arrêté. C'est cette résilience qu'on aborde aujourd'hui avec la persistance, et plus précisément les threads et les checkpoints. Pour ajouter la persistance à une application LangGraph, tu dois comprendre le concept de thread. Un thread représente une séquence d'exécution unique et isolée, ou une conversation utilisateur spécifique. Il conserve le state de travail du graphe quand il passe de node en node. Laisse-moi clarifier un truc tout de suite. On confond souvent la mémoire d'un thread avec la mémoire cross-session à long terme. Un thread n'est pas une base de données globale où ton agent retient des infos sur différentes tâches pour toujours. C'est la mémoire de travail à court terme, strictement liée à une séquence en cours. Tu actives cette mémoire en fournissant un checkpointer quand tu compiles ton graphe. Un checkpointer est un objet qui gère la sauvegarde et le chargement du state du graphe vers un backend de stockage. Une fois ton graphe compilé avec un checkpointer, tu déclenches la persistance en passant un objet de configuration qui contient un thread ID à chaque fois que tu invoques le graphe. Cet ID est la clé unique que le checkpointer utilise pour tracker l'historique de ce run spécifique. Quand tu lances le graphe avec ce thread ID, le checkpointer sauvegarde automatiquement le state. Mais il ne sauvegarde pas en continu. Il sauvegarde à des limites spécifiques qu'on appelle des super-steps. Un super-step est un cycle d'exécution distinct dans le graphe. Si ton graphe exécute le node A suivi du node B, ça fait deux super-steps. Si ton graphe se divise et exécute le node C et le node D en même temps, cette exécution parallèle est regroupée en un seul super-step. Le checkpointer n'interrompt pas un node pendant qu'il travaille. Il attend la limite du super-step. Une fois que tous les nodes planifiés pour ce super-step ont terminé leur exécution et renvoyé leurs updates, LangGraph crée un checkpoint. Ce checkpoint contient un state snapshot, qui capture exactement à quoi ressemblent les variables de state du graphe à ce moment précis. Voyons comment ça se comporte en pratique. Supposons que tu as un agent qui analyse un dataset massif. Le graphe a quatre étapes. L'étape un fait un fetch des données. L'étape deux les nettoie. L'étape trois lance une analyse coûteuse. L'étape quatre formate le résumé. Tu lances le run, en passant une configuration avec le thread ID un deux trois. Le graphe termine avec succès les étapes de fetch, de nettoyage et d'analyse. À la fin de l'étape trois, le checkpointer sauvegarde un state snapshot. Ensuite, avant que l'étape quatre ne puisse se terminer, ton serveur plante. Parce que tu as utilisé un checkpointer et un thread ID, le state est en sécurité. Quand ton serveur redémarre, tu as juste à invoquer le graphe à nouveau, en passant exactement le même thread ID un deux trois. Le checkpointer cherche le dernier checkpoint. Il trouve le state snapshot sauvegardé juste après l'étape d'analyse. Le graphe charge ce state et reprend l'exécution immédiatement à l'étape quatre. Les nodes de fetch, de nettoyage et d'analyse sont complètement ignorés car leurs outputs sont déjà stockés en toute sécurité dans le checkpoint du thread. Voici le point clé. En compilant ton graphe avec un checkpointer et en liant ton exécution à un thread ID, tu transformes des opérations in-memory fragiles en workflows durables qui survivent automatiquement aux interruptions. C'est tout pour aujourd'hui. Merci d'avoir écouté, et continue à développer !
10

Exécution durable et idempotence

3m 36s

Comprenez les nuances de la reprise des workflows. Nous expliquons pourquoi les effets de bord doivent être idempotents et comment structurer les nœuds pour une exécution durable.

Télécharger
Bonjour, ici Alex de DEV STORIES DOT EU. LangGraph, épisode 10 sur 18. Ton workflow traite un paiement, se prend un rate limit à l'étape suivante et plante. Quand le système récupère et que le workflow reprend, ton client est facturé une deuxième fois. Ton code n'a pas changé, mais l'hypothèse que tu as faite sur la façon dont le graphe reprend était fausse. La solution demande de comprendre l'exécution durable et l'idempotence. Beaucoup de développeurs pensent que quand un processus long se met en pause ou échoue, il reprend exactement à la ligne de code Python où il s'est arrêté. Ils s'attendent à ce que le runtime se souvienne par magie des variables locales au beau milieu de la fonction. Ce n'est pas ce qui se passe. LangGraph ne fige pas l'interpréteur Python sur place. Le state est uniquement sauvegardé aux frontières entre les nodes. L'exécution durable dans LangGraph signifie que le système suit ta progression en persistant le state du graphe une fois qu'un node a terminé son travail et fait son return. Si un node échoue au milieu de sa logique, le système ne garde aucune trace de sa progression partielle. Le dernier state valide connu est celui qui a été passé au node au moment de son démarrage. Quand tu redémarres ou que tu fais un retry du graphe, l'exécution reprend en relançant entièrement ce node qui a planté, depuis sa toute première ligne. Repense au scénario du paiement. Supposons que tu écrives un seul node qui fait deux actions. Premièrement, il appelle une API externe pour débiter une carte de crédit. Deuxièmement, il met à jour une base de données distante pour enregistrer la transaction. Le débit de la carte réussit, mais la connexion à la base de données fait un timeout, ce qui fait planter le node. Le state du graphe n'avance pas. Quand le workflow reprend, il repasse l'ancien state dans ce même node. Le node recommence depuis le début. Il tape sur l'API externe et débite la carte une deuxième fois. Voici le point clé. Comme les nodes peuvent redémarrer depuis le début, n'importe quel side effect à l'intérieur d'un node doit être idempotent. L'idempotence, c'est la propriété qui fait que l'exécution d'une opération plusieurs fois donne exactement le même résultat que si on l'exécutait une seule fois. Si ton node interagit avec le monde extérieur, tu dois écrire le code en partant du principe qu'il va tourner plusieurs fois pour la même étape. Comment tu garantis cette sécurité ? Tu as deux approches pratiques. La première, c'est d'utiliser des clés d'idempotence avec tes services externes. Quand tu appelles l'API de paiement, tu passes un identifiant unique dérivé du state actuel du graphe. Si le node plante et se relance, il envoie le même identifiant unique. Le service externe reconnaît la requête dupliquée et renvoie une réponse de succès sans vraiment déplacer l'argent une nouvelle fois. La deuxième approche, c'est le design structurel du graphe. Si une opération spécifique n'est pas nativement idempotente, ne la regroupe pas avec d'autres étapes qui pourraient échouer. Mets l'opération dangereuse dans son propre node dédié. Fais en sorte que ce soit la seule chose que ce node fasse. Si tu mets le paiement dans le node A et la mise à jour de la base de données dans le node B, un timeout de la base de données fait planter uniquement le node B. Le graphe reprend au node B. Le paiement dans le node A est totalement en sécurité, parce que le node A a terminé, et que le graphe a sauvegardé son state avant d'avancer. Tu contrôles où le système sauvegarde sa progression par la façon dont tu dessines les frontières de tes nodes. Ne mets jamais une action irréversible et non idempotente dans le même node que quelque chose qui pourrait planter aléatoirement. C'est tout pour cet épisode. Merci de ton écoute, et continue à développer !
11

Humain dans la boucle : Interrupts

3m 58s

Apprenez à figer les agents en pleine exécution. Nous détaillons la fonction interrupt et comment reprendre les workflows avec une approbation humaine externe.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. LangGraph, épisode 11 sur 18. Parfois, une IA ne devrait pas avoir le dernier mot. Tu pourrais avoir envie de figer un agent en pleine réflexion, de demander l'approbation d'un humain et d'injecter sa réponse directement dans la logique en cours d'exécution. C'est exactement ce que font les interrupts Human-in-the-Loop. Quand tu as besoin qu'un humain prenne une décision dans un workflow LangGraph, tu utilises une fonction spécifique appelée interrupt. Il est vital de comprendre ce que ça fait vraiment sous le capot. Tu pourrais confondre ça avec un input prompt Python standard. Ce n'est pas le cas. Un input prompt standard bloque un thread actif, ce qui monopolise la mémoire système en attendant qu'un utilisateur appuie sur une touche. Dans LangGraph, appeler interrupt se comporte très différemment. Ça sérialise entièrement le state du graphe, ça le sauvegarde dans ta base de données checkpointer, et ça suspend complètement l'exécution. Le graphe se met en veille. Il peut attendre une réponse indéfiniment sans consommer de ressources de calcul actives. Le flow se déroule en deux phases distinctes : la pause et la reprise. D'abord, regardons la mise en pause. Dans l'un de tes nodes, ton agent atteint un point où il a besoin d'une autorisation humaine. À cette ligne de code précise, tu appelles la fonction interrupt. Tu passes un payload à cette fonction, qui est généralement un objet JSON contenant le contexte dont l'humain a besoin. Prends l'exemple d'un agent qui gère un support client automatisé. Il décide de préparer un remboursement de cinq cents dollars. Avant de traiter le paiement, le node de l'agent appelle interrupt. Il transmet un payload précisant que l'action proposée est un remboursement et que le montant est de cinq cents. Au moment où cette fonction est appelée, le graphe s'arrête. Le runtime LangGraph attrape cet événement et fait remonter le payload JSON jusqu'à ton application cliente. Le processus du graphe s'éteint, laissant le payload en attente d'une validation humaine sur une web UI. Maintenant, passons à la deuxième phase : le réveil du graphe. Un processus externe, comme ton serveur backend qui reçoit un appel API depuis la web UI, est responsable du redémarrage du graphe. Le manager humain clique sur approuver sur son dashboard. Ton backend récupère cette approbation et relance le graphe en utilisant une instruction spéciale appelée Command. En envoyant cette Command, tu inclus un argument resume contenant la réponse de l'humain. Dans notre scénario, cette réponse est une simple valeur booléenne true. Voici le point clé. Quand le graphe se réveille, il ne relance pas le node en pause depuis le tout début. Il reprend l'exécution exactement à la ligne de code où il s'était arrêté. La fonction interrupt qui avait initialement mis le graphe en pause termine son exécution, et elle retourne la valeur que tu as envoyée via la commande resume. La réponse booléenne de l'humain est injectée directement dans la variable qui attend le résultat de l'interrupt. L'agent lit ensuite cette valeur true, passe son check conditionnel, et finalise le remboursement de cinq cents dollars. Cette architecture crée une frontière propre. La logique du graphe n'a pas besoin de gérer des webhooks, des emails ou des interfaces utilisateur. Elle appelle juste une fonction qui balance un payload par-dessus le mur et attend une valeur de retour. Le système externe gère toute l'interaction utilisateur et se contente de réinjecter la réponse. En injectant la réponse humaine directement dans le retour de la fonction, tu évites de polluer le state principal de ton graphe avec des données d'interaction temporaires. La puissance de la fonction interrupt réside dans le fait de traiter le feedback humain non pas comme un détour architectural complexe, mais comme un appel de fonction standard qui peut mettre l'univers en pause en toute sécurité jusqu'à ce qu'il obtienne une réponse. C'est tout pour cet épisode. Merci pour ton écoute, et continue à développer !
12

Déboguer le passé : Voyage dans le temps et forking

3m 20s

Explorez les capacités de voyage dans le temps de LangGraph. Nous montrons comment naviguer dans l'historique des états, rejouer d'anciens Checkpoints et créer des forks pour des chemins d'exécution alternatifs.

Télécharger
Salut, ici Alex de DEV STORIES DOT EU. LangGraph, épisode 12 sur 18. Ton agent déraille, il fait une action que tu ne voulais pas ou génère une réponse catastrophique. Normalement, tu dois recommencer tout le processus depuis le début et espérer qu'il se comporte mieux la deuxième fois. Et si tu pouvais littéralement rembobiner l'exécution jusqu'au moment précis avant l'erreur, modifier manuellement le state, et le laisser tourner sur une timeline alternative ? C'est exactement ce qu'on va voir aujourd'hui avec le debugging du passé : le Time Travel et le Forking. Pour manipuler le passé, tu dois d'abord le voir. Tu fais ça en utilisant une méthode qui s'appelle get state history, en lui passant ton thread ID. Cette méthode renvoie un itérateur contenant chaque state par lequel le graph est passé pendant l'exécution de ce thread. Chacun de ces states historiques possède un identifiant unique appelé checkpoint ID. Tu peux voir cet ID comme tes coordonnées exactes dans le temps. Si tu veux simplement rejouer le graph à partir d'un point précis, tu récupères le checkpoint ID cible dans cet historique. Ensuite, tu appelles la méthode invoke de ton graph, en lui passant un objet de configuration qui inclut à la fois le thread ID et ce checkpoint ID spécifique. Le graph reprend immédiatement son exécution à partir de ce state précis. Il ne relance aucun des nodes précédents, ce qui te fait gagner du temps et du compute. Le replay est utile, mais la vraie puissance, c'est de changer le passé pour forker l'exécution. Prenons un scénario pratique. Imaginons que ton agent avait pour tâche d'écrire une blague, et qu'il a généré une blague nulle sur un chien. Tu vérifies le state history et tu trouves le checkpoint ID du state juste avant l'étape de génération. Au lieu de juste faire un replay à partir de ce point, tu utilises la méthode update state. Tu fournis le thread ID, le checkpoint ID historique spécifique, et les nouvelles valeurs du state que tu veux injecter. Dans ce cas, tu mets à jour manuellement la variable topic, en la changeant de chien à poulets. Voici le point clé. Les développeurs pensent souvent que mettre à jour un state passé fait un rollback de l'exécution, en écrasant ou en supprimant l'historique d'origine. Ce n'est pas le cas. LangGraph fonctionne sur une architecture append-only. Quand tu appelles update state sur un checkpoint historique, le système crée de manière sécurisée un tout nouveau checkpoint qui branche à partir de l'ancien. Ta timeline d'origine, avec la blague nulle sur le chien, reste totalement intacte et accessible. Tu n'as pas effacé le passé, tu as forké une nouvelle réalité. Une fois que tu appliques cette mise à jour, le graph se trouve sur un checkpoint nouvellement créé avec le state modifié. Pour continuer sur cette nouvelle timeline, tu invoques simplement le graph à nouveau avec le thread ID, en omettant tout checkpoint ID spécifique. Le graph prend par défaut le state le plus récent sur cette branche nouvellement forkée et reprend l'exécution. Ton agent lit le state mis à jour et génère une blague sur les poulets à la place. Si tu trouves ces analyses techniques utiles et que tu veux soutenir l'émission, tu peux chercher DevStoriesEU sur Patreon. Le Time Travel transforme le debugging : au lieu de deviner ce qui a planté, tu manipules précisément l'historique du graph pour explorer des résultats alternatifs, sans perdre la moindre trace du run original. C'est tout pour cet épisode. Merci de ton écoute, et continue à développer !
13

Mémoire à long terme : Stores à travers les Threads

3m 23s

Allez au-delà des Threads isolés. Nous introduisons l'interface Store et expliquons comment accorder à vos agents une mémoire persistante inter-sessions.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. LangGraph, épisode 13 sur 18. Tu crées un agent, et un utilisateur lui dit qu'il veut toujours son code en Python 3.11. Le lendemain, il lance une nouvelle conversation, et l'agent oublie complètement, et sort du Python 3.9 à la place. La mémoire du thread est isolée à une seule conversation. Quand ton agent a besoin de retenir des infos entre des sessions complètement séparées, tu as besoin d'une mémoire à long terme en utilisant des Stores cross-thread. Une erreur classique, c'est d'essayer de résoudre ça en bourrant les infos à long terme dans le state du checkpointer. Les checkpointers, c'est de la mémoire à court terme. Ce sont strictement des snapshots de state par thread, conçus pour faire pause, resume ou replay sur une seule conversation. Si un utilisateur donne une préférence dans le thread A, le thread B n'a absolument aucun moyen de la voir. Pour partager la connaissance entre plusieurs threads, LangGraph fournit l'interface Store. Un Store, c'est une couche de mémoire key-value qui se trouve en dehors des states individuels des threads. Tu le mets en place en passant un objet store, comme un PostgresStore, en argument quand tu compiles ton graph. Une fois compilé, ce store est attaché à l'environnement d'exécution du graph. À l'intérieur de ton graph, les nodes accèdent à cette couche de mémoire via l'objet Runtime. Quand tu définis un node, tu peux accéder au contexte du runtime, qui expose le store. Tu accèdes simplement à runtime point store pour interagir avec ta mémoire à long terme. Voici le truc important. Les données dans un store sont organisées en utilisant des namespaces. Un namespace, c'est une liste hiérarchique de strings qui partitionne tes données, un peu comme un chemin de dossier sur ton ordi. Pour une application multi-tenant, tu pourrais définir un namespace qui commence par la string users, suivi d'un user ID spécifique, et qui se termine par preferences. Repense à ce scénario d'assistant de code. Un utilisateur lance une session le lundi. Pendant le chat, il mentionne qu'il préfère Python 3.11 et le dark mode. Un node dans ton graph reconnaît ça comme une préférence permanente. Il appelle la méthode put sur runtime point store. Il passe le namespace pour cet utilisateur spécifique, une key unique pour l'item, et un dictionary contenant les préférences. Les données sont maintenant sauvegardées en dehors du thread. Le vendredi, le même utilisateur ouvre ton application et lance un tout nouveau thread. Le state du checkpointer pour ce nouveau thread est complètement vide. Cependant, ton graph inclut un setup node qui tourne en premier. Ce node appelle la méthode search sur runtime point store, en fournissant le préfixe du namespace de l'utilisateur. Le store retourne les préférences sauvegardées. Le node place ensuite ces préférences dans le state du thread courant. À partir de là, l'agent sait qu'il doit utiliser Python 3.11 et le dark mode pour cette nouvelle conversation. L'interface du store propose trois opérations principales. Tu utilises put pour sauvegarder ou écraser un item. Tu utilises get pour récupérer un seul item quand tu connais son namespace et sa key exacts. Tu utilises search pour récupérer plusieurs items qui partagent un préfixe de namespace. Faire un search est particulièrement utile quand tu as sauvegardé plusieurs fragments de mémoire distincts pour un utilisateur au fil du temps et que tu dois tous les ramener dans le contexte actuel. En séparant le state à court terme d'un store cross-thread, tu découples la durée de vie de la connaissance de ton agent de la durée de vie d'une seule conversation. Merci de ton écoute, à la prochaine.
14

Exécution en streaming et le format v2

3m 33s

Améliorez l'expérience utilisateur avec des retours en temps réel. Nous décomposons les modes de stream (values, updates, messages) et le format unifié StreamPart v2.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. LangGraph, épisode 14 sur 18. Les utilisateurs détestent rester plantés devant un loading spinner statique pendant trente secondes pendant que ton système tourne en arrière-plan. Tu veux leur montrer le processus de réflexion du système en temps réel, mais capturer ces signaux internes nécessite souvent de câbler des custom callbacks complexes. L'exécution en streaming et le format v2 résolvent ça en unifiant chaque événement interne dans un flux unique et prévisible. D'abord, clarifions un malentendu très courant. Les ingénieurs confondent souvent le streaming de tokens d'un language model avec le streaming du state de l'application. Ce sont des couches d'information totalement différentes. Un stream de tokens, c'est juste du texte qui apparaît mot par mot. Un stream de state suit la progression globale de ton workflow, qui passe d'une tâche à l'autre. LangGraph gère les deux en même temps. Tu accèdes à ce comportement en demandant un stream et en passant l'argument version v2 à ta méthode d'exécution. Ça standardise l'output. Au lieu de gérer des types de données mélangés, chaque événement qui sort de ton graph devient un dictionnaire unifié qui contient exactement trois champs : type, ns, et data. Le champ type définit la catégorie de l'événement. Le champ ns signifie namespace, et indique le chemin exact dans la hiérarchie de ton graph d'où provient l'événement. Ça devient critique quand tu as des subgraphs imbriqués et que tu dois savoir exactement quel sous-composant a déclenché l'événement. Enfin, le champ data contient le payload. Tu contrôles exactement ce qui est envoyé dans ce stream en sélectionnant un ou plusieurs stream modes. Le mode values te pousse le state complet et mis à jour du graph à chaque fois qu'un node termine son travail. C'est super utile si ton application a besoin d'avoir une vue d'ensemble à chaque étape. Le mode updates est beaucoup plus léger. Il streame uniquement les données spécifiques renvoyées par un node, ce qui représente juste le delta ou la modification apportée au state global. Le mode messages fonctionne à un niveau plus granulaire, en streamant les chunks individuels d'un message de chat généré, au fur et à mesure qu'ils sont produits par un language model sous-jacent. Imagine une interface frontend. Tu veux un indicateur de statut lumineux qui met en évidence l'étape en cours, par exemple le fetching du contexte, puis l'évaluation des documents, puis le drafting, tout en affichant en même temps le texte du brouillon, token par token. Pour construire ça, tu lances l'exécution de ton graph avec les stream modes définis sur updates et messages, en t'assurant de passer le flag de version v2. Ton frontend commence à recevoir un stream continu et unifié de ces dictionnaires. Quand un dictionnaire arrive avec le type défini sur updates, tu lis le champ namespace. Ça t'indique exactement quel node vient de terminer son travail. Tu utilises ce signal pour faire passer ton indicateur de statut lumineux à l'étape suivante sur l'interface utilisateur. Quelques millisecondes plus tard, le stream délivre un nouveau dictionnaire avec le type défini sur messages. Tu récupères le token de texte brut depuis le champ data et tu l'ajoutes directement au paragraphe que ton utilisateur est en train de lire. Les changements de state de haut niveau et la génération de texte de bas niveau arrivent par exactement le même pipe. Voici l'idée clé. En forçant les tokens, les changements de state et la progression des nodes dans une structure de dictionnaire unique à trois champs, le format v2 élimine complètement le besoin d'écrire une logique de gestion séparée ou des callbacks asynchrones complexes pour différents types d'événements en temps réel. C'est tout pour cet épisode. À la prochaine !
15

Composer la complexité : Subgraphs

3m 01s

Faites évoluer vos workflows en traitant les graphes compilés comme des nœuds. Nous discutons de la composition de Subgraphs et de la gestion des schémas d'état partagés par rapport aux schémas privés.

Télécharger
Salut, ici Alex de DEV STORIES DOT EU. LangGraph, épisode 15 sur 18. Quand ton agent IA devient complexe, tu te retrouves souvent avec un mega-graph massif et illisible où une seule modification casse tout. Tu n'es pas obligé de coder comme ça — tu peux créer des mini-graphs spécialisés et les assembler à la place. On va parler de la composition de la complexité : les subgraphs. Les subgraphs te permettent de réutiliser la logique et de répartir le développement entre différentes équipes. Au lieu de mettre chaque étape de ton application dans un seul fichier, tu crées des graphs plus petits et autonomes. Une fois qu'un graph est compilé, il se comporte exactement comme une fonction callable standard. Ça veut dire que tu peux prendre un graph compilé entier et le glisser directement dans un autre graph comme un simple node. Imagine un système de routing principal pour un assistant d'entreprise. Le graph principal gère les inputs utilisateur, vérifie la sécurité et décide de ce qu'il faut faire ensuite. Quand un utilisateur pose une question technique pointue, le router doit faire une collecte de data complexe. Au lieu de coder cette logique directement dans le router, tu la délègues à un subgraph de recherche dédié. Une équipe d'ingénierie complètement différente peut développer, tester et affiner ce graph de recherche de manière isolée. Le graph parent se fiche de savoir comment la recherche est faite. Il se contente d'appeler le node. On a souvent tendance à trop complexifier la façon dont la data passe entre ces graphs. Si le graph parent et le subgraph utilisent exactement le même state schema — c'est-à-dire qu'ils partagent exactement les mêmes state keys — tu n'as besoin d'aucun adaptateur spécial. Tu passes simplement le subgraph de recherche compilé directement dans la fonction add node de ton graph principal. Le moteur injecte automatiquement le state parent dans le subgraph, exécute la logique, et merge les résultats dans le state parent quand il a fini. Maintenant, que se passe-t-il quand les équipes ne se coordonnent pas parfaitement ? Supposons que ton router parent utilise une state key appelée user query, mais que l'autre équipe a construit le subgraph de recherche pour attendre une key appelée search term. Tu ne peux pas glisser le subgraph compilé directement dans le graph parent. Les keys ne vont pas matcher, et l'exécution va planter. Voici l'astuce clé. Tu contournes ce décalage en utilisant une simple fonction wrapper. Dans ton graph parent, tu définis une fonction de node standard qui accepte le state parent. À l'intérieur de cette fonction, tu extrais la valeur de user query. Ensuite, tu appelles manuellement le subgraph de recherche compilé, en lui passant un payload où tu mappes ce user query sur la key search term. Le subgraph exécute sa logique interne et retourne son state final. Ta fonction wrapper prend cet output, retraduit les résultats dans les keys spécifiques que le parent attend, et les retourne. Pour le graph parent, ce wrapper ressemble à n'importe quel node normal. Il n'a aucune idée qu'un subgraph massif et complexe vient de s'exécuter à l'intérieur. Il a simplement passé de la data en entrée et récupéré un state mis à jour en sortie. Ce pattern t'offre une modularité stricte sans sacrifier le contrôle. Traiter un graph compilé comme une simple fonction callable derrière un simple wrapper est le moyen le plus puissant de scaler une architecture IA sans t'effondrer sous ton propre code. C'est tout pour cet épisode. Merci d'avoir écouté, et continue de coder !
16

Persistance des Subgraphs et modèles multi-agents

3m 17s

Maîtrisez la portée de la mémoire dans les systèmes multi-agents. Nous expliquons la différence entre la persistance de Subgraph par invocation, par thread et stateless.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. LangGraph, épisode 16 sur 18. Si un subagent expert est appelé deux fois dans une même conversation, est-ce qu'il doit se souvenir du premier appel, ou repartir de zéro avec une amnésie totale ? Ce choix change tout sur la façon dont les systèmes multi-agents se comportent, et c'est entièrement contrôlé par la persistance des subgraphs et les patterns multi-agents. Quand tu construis un parent graph qui route les tâches vers des subgraphs, le state management devient compliqué. Imagine un bot de service client principal qui gère le chat général. Quand un utilisateur pose une question complexe sur une facture, le bot principal route la requête vers un subgraph dédié, l'expert en facturation. Par défaut, les subgraphs sont totalement stateless. Quand tu compiles cet expert en facturation sans spécifier de checkpointer, il fonctionne strictement par invocation. Le bot principal lui passe les inputs requis, l'expert exécute ses steps internes, retourne un résultat, et supprime immédiatement son state interne. Si l'utilisateur pose une question de suivi sur la facturation cinq minutes plus tard, le bot principal rappelle l'expert. L'expert n'a aucun souvenir de l'échange précédent. Il repart complètement de zéro. Pour un simple subgraph d'extraction de données, cette amnésie ne pose aucun problème. Mais pour un agent interactif et spécialisé, c'est incroyablement frustrant pour l'utilisateur. Pour corriger ça, l'expert a besoin de sa propre mémoire entre les tours. Une erreur très courante ici, c'est de passer une toute nouvelle instance de checkpointer, comme un objet memory saver, directement dans la méthode compile du subgraph. Ne fais surtout pas ça, à moins que tu veuilles que le subgraph partage exactement le même state entre des utilisateurs et des sessions complètement différents. Si l'utilisateur A et l'utilisateur B parlent tous les deux au système en même temps, passer une instance explicite de checkpointer au subgraph signifie que leurs données vont se mélanger dans un seul state global. Ça crée un cross-talk massif entre des threads parents isolés. À la place, tu passes juste la valeur booléenne True à l'argument checkpointer quand tu compiles le subgraph. C'est là que ça devient intéressant. Le mettre sur True dit au subgraph de s'appuyer sur le mécanisme de checkpointer du parent graph, mais de maintenir un historique multi-tours complètement isolé, spécifiquement pour lui-même. En coulisses, le framework gère le namespacing. Il crée automatiquement un thread ID unique pour le subgraph, qui est lié de façon permanente au thread ID du parent. Maintenant, regarde à nouveau le scénario de l'expert en facturation avec cette configuration. L'utilisateur pose une question sur une facture. Le bot principal la route vers l'expert. L'expert répond et se met en veille. Plus tard dans la même conversation, l'utilisateur pose une question de suivi. Le bot principal route à nouveau vers l'expert. Parce qu'il a été compilé avec le checkpointer sur True, l'expert se réveille, vérifie son sub-thread dédié, et charge le contexte de la facture du tour précédent. Il agit comme un participant persistant dans la conversation. Et parce que ce sub-thread est strictement scopé au thread du parent, un autre utilisateur qui parle au système obtient sa propre instance complètement vierge de l'expert en facturation. La façon dont tu configures le checkpointer d'un subgraph dicte toute son identité dans ton système : le laisser vide crée une fonction utilitaire jetable et stateless, alors que le mettre sur True crée un collaborateur continu et context-aware. Merci d'avoir passé quelques minutes avec moi. À la prochaine, porte-toi bien.
17

Structure de l'application et préparation au déploiement

3m 56s

Passez des prototypes à la production. Nous explorons langgraph.json, la structure de fichiers appropriée et la gestion des dépendances pour les déploiements stateful.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. LangGraph, épisode 17 sur 18. Un script Python qui tourne bien sur ton ordi portable n'est pas une application de production. Si tu essaies de faire tourner des agents stateful en exécutant des scripts standalone, tu vas inévitablement te heurter à un mur quand il faudra scale. Pour résoudre ça, on va regarder la structure de l'application et comment la rendre prête pour le deploy. Quand tu construis un agent LangGraph pour la première fois, tu le prototypes probablement dans un notebook Jupyter ou un seul fichier Python. Tu définis les nodes, tu connectes les edges, tu compiles le graph, et tu appelles la méthode invoke directement dans le même fichier pour voir si ça marche. C'est très bien pour tester. Mais un serveur de production ne peut pas lire dans tes pensées. Il a besoin d'une méthode standardisée pour servir ce graph comme une API, installer les packages requis et injecter les variables d'environnement. Pour que ton prototype soit prêt pour le deploy, tu dois organiser ton code dans une structure de répertoires propre. Disons que tu crées un nouveau dossier nommé my-app. Tu déplaces ton code Python hors du notebook vers un fichier propre à l'intérieur de ce dossier. Ensuite, tu ajoutes un fichier de dépendances, généralement requirements point txt. Enfin, tu crées un fichier de configuration nommé langgraph point json à la racine du dossier my-app. Le fichier langgraph point json est le blueprint central de ton application. Quand tu utilises la Command Line Interface de LangGraph, ou que tu fais un deploy vers un environnement de production, ce fichier de configuration dit au système sous-jacent exactement comment build et run ton projet. Il a besoin de trois infos principales concernant les dépendances, les variables d'environnement et les entry points du graph. D'abord, tu déclares tes dépendances. C'est juste une string de chemin dans le fichier JSON qui pointe vers ton fichier requirements. Ça garantit que le serveur de deploy installe les packages Python exacts dont ton agent a besoin, ce qui évite les erreurs de modules manquants en production. Ensuite, tu définis la string d'environnement. Elle pointe vers ton fichier point env. Les agents stateful ont toujours besoin de secrets, comme des credentials de base de données ou des clés API de modèles. Pointer vers le fichier d'environnement garantit que le runtime charge ces clés de manière sécurisée avant d'essayer de démarrer le graph. C'est la partie qui compte. La troisième exigence dans le fichier de configuration, c'est le mapping des graphs. Ça dit au serveur exactement où se trouve ton graph compilé dans le code source. Ça agit comme un dictionnaire. Tu assignes un ID à ton graph, qui devient son nom officiel dans l'API générée. Ensuite, tu mappes cet ID à un module Python et un nom de variable spécifiques. Par exemple, tu pourrais mapper l'ID customer-support-agent à la string agent point py deux-points compiled-graph. Le serveur regarde le fichier agent point py, trouve la variable nommée compiled-graph, et la charge en mémoire. Cette structure demande un vrai changement dans ta façon d'écrire ton code. Les débutants font souvent tourner les graphs via des scripts Python standalone qui exécutent des actions dès qu'ils sont lancés. Mais le runtime LangGraph s'appuie sur langgraph point json pour exposer le graph dynamiquement comme un web service. Il ne run pas ton script de haut en bas. Il importe uniquement l'objet graph compilé que tu as spécifié dans le fichier de configuration. À cause de ça, ton fichier Python devrait seulement définir les nodes, les connecter, et assigner le graph compilé à une variable. Tu dois supprimer tout code de test restant en bas qui invoque manuellement le graph. Si tu laisses du code de test dans le fichier, il va s'exécuter pendant la phase d'import sur le serveur, ce qui va causer des échecs de deploy ou des appels API non désirés juste en démarrant le service. En déclarant explicitement tes dépendances, ton environnement et les chemins de ton graph dans un fichier JSON central, tu sépares la définition de ton agent de son exécution, transformant un script local en un service robuste et déployable. C'est tout pour cet épisode. Merci d'avoir écouté, et continue de build !
18

Tester l'exécution du Graph de bout en bout

3m 22s

Apprenez des stratégies de test robustes pour les workflows de graphes. Nous couvrons l'intégration de pytest, l'exécution isolée de nœuds et la simulation d'états partiels.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. LangGraph, épisode 18 sur 18. Tu as un workflow multi-agent complexe, et tu dois tester un edge case de routing bien précis à l'étape quatre. Tu ne devrais pas avoir à faire tourner tout le système du début à la fin juste pour atteindre cette condition. Tester l'exécution du graph end-to-end, c'est comme ça que tu cibles exactement la logique dont tu as besoin. Quand les développeurs essaient d'isoler des parties d'un graph pour les tests, ils se tournent souvent vers des objets mock complexes. Ils essaient de mocker la structure du graph autour, ou de stubber tous les nœuds précédents. Dans LangGraph, tu n'as pas besoin de faire ça. L'architecture tourne entièrement autour du state. Comme les nœuds sont juste des fonctions qui lisent et écrivent le state, tu peux injecter manuellement un payload de state spécifique, et tester des fragments de nœuds isolés de façon native. C'est là que l'injection de state et les breakpoints deviennent super utiles dans ta suite de tests. Tu as juste besoin de deux outils pour sauter directement au milieu d'un graph. Le premier, c'est la méthode update state. Le deuxième, c'est un paramètre de configuration qui s'appelle interrupt after. Les utiliser dans un framework de test standard comme pytest te permet de simuler des conditions exactes sans exécuter toute l'application. Appliquons ça à un scénario concret. Imagine que tu as un graph où le nœud trois fait un call API externe, et le nœud quatre vérifie le résultat. Tu veux vérifier que si le payload de l'API contient un code d'erreur spécifique, le nœud quatre route correctement le flux d'exécution vers ton nœud error handler. Au lieu de faire tourner les nœuds un et deux pour déclencher ça, tu isoles le problème. Tu initialises le graph avec un identifiant de thread. Ensuite, tu utilises update state pour insérer un payload d'API simulé en échec directement dans le state du thread. Tu agis comme si le nœud trois était sur le point de s'exécuter avec ces données spécifiques. Ensuite, tu invoques le graph, mais tu passes un dictionnaire de configuration qui règle interrupt after sur le nœud quatre. Quand tu démarres le graph, l'exécution commence immédiatement au nœud trois en utilisant ton state d'erreur injecté. Le nœud trois traite le mauvais payload et passe le state qui en résulte au nœud quatre. Le nœud quatre évalue la logique et décide de router vers l'error handler. Comme tu as défini un breakpoint, le graph met l'exécution en pause au moment où le nœud quatre se termine. Maintenant, ton test peut évaluer le résultat. Tu récupères le state actuel du graph. Tu peux écrire tes assertions pour t'assurer que le nœud quatre a correctement mis à jour les variables de state. Plus important encore, tu peux inspecter le plan d'exécution du graph. En regardant le prochain nœud en attente dans les métadonnées du state, tu peux confirmer que la logique de routing a fonctionné parfaitement et que l'error handler est en file d'attente. Voici l'idée clé. En manipulant le state directement, tu transformes une chaîne d'agents hautement interconnectée et imprévisible en un test case déterministe, étape par étape. Tu vérifies exactement comment le graph transitionne d'un nœud à l'autre sans attendre les modèles de langage ou les calls réseau des étapes précédentes. Comme c'est le dernier épisode de la série, je t'encourage à explorer la documentation officielle et à essayer de construire ces workflows toi-même. Si tu as des idées sur ce qu'on devrait aborder ensuite, visite devstories dot eu et suggère un sujet. C'est tout pour cet épisode. Merci d'avoir écouté, et continue de développer !