Human-in-the-loop en vrai
Un bon agent ne demande pas tout, et ne décide pas tout seul non plus. Il agit par défaut, et s'arrête pour demander quand c'est vraiment bloquant — puis reprend là où il s'était arrêté, même deux heures plus tard.
Un bon agent ne demande pas tout, et ne décide pas tout seul non plus. Il agit par défaut, et s'arrête pour demander quand c'est vraiment bloquant — puis reprend là où il s'était arrêté, même deux heures plus tard.
La première version de notre chat d'ajustement interrogeait l'utilisateur sans fin, une question après l'autre. Insupportable. La bonne posture est act-first : agir par défaut avec des hypothèses explicites, et ne demander que sur un vrai point bloquant.
LangGraph fournit interrupt() : appelé dans un nœud, il suspend le run et remonte une question à ton application. Le graphe ne « bloque » pas un thread serveur — il s'arrête et rend la main.
Côté application, le premier appel s'arrête à l'interrupt et te renvoie la question. Tu l'affiches, l'utilisateur répond, et tu relances le même run avec sa réponse via Command({ resume }).
Pour que l'agent reprenne exactement où il s'était arrêté — même après un redéploiement, ou une réponse deux heures plus tard — l'état doit être persisté. C'est le rôle du checkpointer : il sauvegarde l'état du graphe à chaque étape, ici dans Postgres.
Avec un checkpointer, un run devient une conversation reprenable, pas un appel jetable. C'est ce qui rend le human-in-the-loop utilisable en production réelle.
Réserve la pause aux vrais points de décision (actions destructrices, ambiguïté coûteuse). Un interrupt à chaque étape et tu recrées l'interrogatoire que tu voulais fuir.
L'act-first a une exception nette : les actions irréversibles. Supprimer des données, envoyer un e-mail à un client, valider un paiement, pousser en prod — là, l'agent ne devine pas, il demande une validation. Anthropic le formule bien dans Building effective agents : on place des points d'arrêt avant les actions à fort enjeu, pas à chaque étape. C'est exactement le même interrupt() que pour une clarification, mais ce qu'on remonte n'est plus une question ouverte — c'est un plan à approuver.
Une bonne porte d'approbation ne se limite pas à « oui / non ». On veut aussi laisser l'humain corriger l'action avant qu'elle parte. Le payload d'interrupt remonte l'action proposée, et la réponse via Command({ resume }) porte la décision — que le nœud réexécute ensuite.
Piège classique : au resume, LangGraph réexécute le nœud depuis le début, pas depuis la ligne de l'interrupt — voir la doc Interrupts. Donc tout effet de bord placé avant l'interrupt() (log, écriture, appel d'API) se rejoue. Garde le nœud « pur » avant la porte, et ne mets l'action irréversible qu'après la décision.
Une porte d'approbation sans limite de temps devient un run zombie : suspendu pour toujours parce que personne n'a répondu. Comme l'état est persisté par le checkpointer (thread_id + Postgres), le run ne coûte rien pendant qu'il attend — mais il faut quand même une politique. En pratique, je stocke un deadline avec le checkpoint et je fais tourner un job qui, passé le délai, relance le thread avec une décision par défaut (souvent reject) ou l'escalade à un autre approbateur.
Un checkpointer te donne la reprise : l'état est écrit à chaque étape, tu peux relancer un thread par son thread_id, même après un crash — c'est bien documenté côté Persistence et côté durable execution. Mais reprendre n'est pas exécuter automatiquement jusqu'au bout. Le checkpoint sauve la place ; c'est encore à toi de détecter l'échec et de relancer avec le bon identifiant.
Diagrid détaille bien la nuance dans Checkpoints are not durable execution. Pour du human-in-the-loop simple, le checkpointer suffit largement. Si tes workflows tournent des heures avec des retries automatiques et plusieurs workers, regarde un vrai moteur d'exécution durable (Temporal, Restate) qui met en cache les étapes finies au lieu de les rejouer.
Le human-in-the-loop, ce n'est pas « demander plus », c'est demander mieux : agir par défaut, s'arrêter uniquement quand ça compte, et reprendre proprement grâce à un état durable. L'agent devient un collègue à qui on fait confiance, pas un formulaire.
LangGraph — Human-in-the-loop ↗