Agentic AI

Human-in-the-loop : garder le contrôle sur les agents IA

Les agents IA ne se contentent plus de produire du texte : ils cliquent dans des interfaces avec le computer use, appellent des outils qui modifient des bases de données, envoient des emails ou déclenchent des paiements, et délèguent des tâches à d'autres agents via le protocole A2A. Chaque action réelle est une action potentiellement irréversible. Comment laisser un agent travailler en autonomie tout en gardant la main sur les décisions qui engagent l'entreprise ? C'est le rôle du human-in-the-loop.

Équipe Blent

Publié le · 13 min de lecture

Dans cet article

Les agents IA ne se contentent plus de produire du texte : ils cliquent dans des interfaces avec le computer use, appellent des outils qui modifient des bases de données, envoient des emails ou déclenchent des paiements, et délèguent des tâches à d'autres agents via le protocole A2A. Chaque action réelle est une action potentiellement irréversible. Comment laisser un agent travailler en autonomie tout en gardant la main sur les décisions qui engagent l'entreprise ? C'est le rôle du human-in-the-loop.

Le principe consiste à insérer, à des endroits précis du déroulement d'un agent, des points où l'exécution s'arrête et attend l'avis d'une personne : approuver, refuser, corriger ou reprendre la main. L'enjeu n'est pas de superviser chaque étape, ce qui annulerait le gain d'automatisation, mais de placer l'humain là où son jugement apporte réellement de la valeur. Un agent qui demande une confirmation avant de lire un fichier est inutilisable ; un agent qui rembourse un client sans validation l'est tout autant.

Les guardrails filtrent automatiquement les entrées, les sorties et les appels d'outils, mais ils ne couvrent pas les cas où la bonne décision dépend d'un contexte métier, d'une responsabilité légale ou d'une simple prudence. Dans cet article, nous allons présenter les principaux patterns de validation humaine, puis leur implémentation avec les interruptions et les checkpoints de LangGraph, et enfin les compromis entre latence, coût et sécurité à arbitrer en production.

Pourquoi insérer l'humain dans la boucle

Un agent fonctionne en boucle : il raisonne, choisit un outil, observe le résultat, puis recommence. À chaque tour, une erreur de jugement du modèle peut se transformer en effet de bord réel. Les causes sont connues : hallucination d'un paramètre, mauvaise interprétation de la demande, prompt injection cachée dans un document consulté, ou simple ambiguïté de la consigne. Plus l'agent dispose d'outils et de droits, plus le coût d'une erreur augmente.

La validation humaine répond à trois besoins distincts :

  • Sécurité : empêcher qu'une action destructrice ou irréversible (suppression de données, virement, déploiement en production) soit exécutée sur la base d'un raisonnement erroné
  • Responsabilité : documenter qu'une personne identifiée a pris la décision, ce qu'exigent de plus en plus les cadres réglementaires et les audits internes
  • Qualité : permettre à un expert de corriger un brouillon, un plan ou une requête avant qu'il ne produise ses effets, plutôt que de réparer après coup

La difficulté tient à l'équilibre. Un humain sollicité trop souvent finit par valider machinalement (on parle de fatigue d'approbation), ce qui revient à ne plus contrôler du tout. À l'inverse, un agent trop autonome concentre tout le risque sur la qualité du modèle. La bonne approche consiste à classer les actions selon leur impact (réversible ou non, périmètre touché, montant engagé) et à ne solliciter l'humain que pour les catégories qui le justifient.

Les quatre patterns de validation humaine

Quatre patterns couvrent l'essentiel des besoins. Ils se combinent librement dans un même agent, chacun répondant à un niveau de risque et à une contrainte de latence différents.

PatternDéclencheurL'humain peutLatence induite
Approbation avant actionAppel d'un outil sensibleApprouver ou refuserBloquante
Édition de l'étatPoint de contrôle défini ou erreurModifier les paramètres, le plan ou l'étatBloquante
Escalade sur seuil de confianceConfiance ou score sous un seuilTrancher le cas difficileBloquante, mais rare
Revue asynchroneAction déjà exécutée ou mise en fileAuditer, annuler ou compenserAucune sur le flux principal

Approbation avant action sensible

C'est le pattern le plus répandu. L'agent prépare son appel d'outil, mais l'exécution est suspendue tant qu'une personne n'a pas validé. L'écran de validation doit présenter l'action de manière lisible : l'outil appelé, ses arguments, la raison invoquée par l'agent et, si possible, l'effet attendu. Une approbation sur un JSON brut n'a aucune valeur.

La difficulté consiste à définir ce qui est « sensible ». Une liste explicite d'outils soumis à validation est la solution la plus simple. On peut la compléter par des règles dépendant des arguments : un remboursement de 20 euros passe seul, un remboursement de 2 000 euros demande une approbation. Cette logique rejoint le contrôle des appels d'outils décrit dans l'article sur les guardrails, avec une décision finale confiée à un humain plutôt qu'à une règle.

Édition de l'état

Approuver ou refuser est binaire. Dans bien des cas, la meilleure réponse est une correction : changer le destinataire d'un email, reformuler une requête SQL, retirer une étape d'un plan. L'édition de l'état consiste à exposer l'état courant de l'agent (messages, plan, paramètres de l'appel d'outil) à l'humain, qui le modifie avant que l'exécution ne reprenne. L'agent poursuit alors comme si ces valeurs étaient les siennes.

Ce pattern suppose que l'état soit explicite, structuré et persisté, ce que nous avons détaillé dans l'article sur la gestion de l'état des agents. Un état éparpillé dans le contexte d'un prompt est impossible à éditer proprement.

Escalade sur seuil de confiance

Plutôt que de valider systématiquement une catégorie d'actions, l'agent décide lui-même quand demander de l'aide. Il escalade lorsque son incertitude dépasse un seuil : score d'un classificateur trop bas, plusieurs outils en désaccord, requête hors du périmètre prévu, trois échecs consécutifs sur la même étape. L'humain n'intervient alors que sur les cas difficiles, ce qui préserve l'autonomie sur le flux courant.

Le point faible est la fiabilité de la confiance elle-même. Les LLM sont souvent mal calibrés : un modèle peut être sûr de lui et se tromper. Il vaut mieux fonder le seuil sur des signaux externes mesurables (score d'un évaluateur, vérification d'un schéma, résultat d'un test) que sur la confiance auto-déclarée du modèle, et les calibrer à l'aide d'une évaluation des agents sur des cas réels.

Revue asynchrone

Certaines actions ne justifient pas de bloquer l'agent, mais méritent d'être relues. Dans la revue asynchrone, l'agent exécute l'action (ou la place dans une file d'attente avec un délai de grâce), puis une personne l'examine plus tard, souvent par échantillonnage. Elle peut valider, annuler ou déclencher une action de compensation. Ce pattern convient aux actions réversibles ou à faible enjeu, par exemple l'étiquetage de tickets, des brouillons de réponses ou des modifications annulables.

Son efficacité dépend de la capacité à défaire l'action. Un email envoyé ne se rappelle pas ; une écriture en base peut l'être si l'on a prévu un mécanisme de rollback. La revue asynchrone repose aussi sur une bonne traçabilité, avec des traces détaillées dans un outil comme Langfuse pour reconstituer ce que l'agent a fait et pourquoi.

À lire : découvrez notre formation Agentic AI

Implémenter les interruptions avec LangGraph

LangGraph modélise un agent comme un graphe dont chaque étape lit et écrit un état partagé. Deux mécanismes rendent le human-in-the-loop naturel : les checkpoints, qui sauvegardent l'état après chaque étape, et les interruptions, qui suspendent le graphe en attendant une valeur extérieure.

Interrupt, checkpointer et reprise

La fonction interrupt() de langgraph.types met le graphe en pause et renvoie à l'appelant la charge utile qu'on lui passe. Lorsque le graphe est repris avec Command(resume=...), la valeur de reprise devient le résultat de l'appel à interrupt(). Ce mécanisme impose un checkpointer pour persister l'état, et un thread_id dans la configuration pour retrouver la bonne exécution. Trois règles sont à retenir : le nœud est rejoué depuis son début à la reprise, les effets de bord placés avant interrupt() doivent donc être idempotents ; l'appel ne doit pas être enveloppé dans un try/except générique, car il fonctionne en levant une exception ; la charge utile doit être sérialisable.

L'exemple ci-dessous met en place le pattern d'approbation avant action : un nœud demande l'accord de l'humain avant d'exécuter un remboursement.

python
from typing import Literal, TypedDict
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import StateGraph, START, END
from langgraph.types import Command, interrupt


class State(TypedDict):
    amount: float
    customer_id: str
    status: str


def validation_humaine(state: State) -> Command[Literal["rembourser", "annuler"]]:
    """Suspend le graphe et attend la décision d'un humain."""
    approuve = interrupt({
        "question": "Valider ce remboursement ?",
        "client": state["customer_id"],
        "montant": state["amount"],
    })
    return Command(goto="rembourser" if approuve else "annuler")


def rembourser(state: State) -> dict:
    # Appel réel à l'API de paiement (placé après l'interruption)
    return {"status": "remboursé"}


def annuler(state: State) -> dict:
    return {"status": "refusé par l'opérateur"}


builder = StateGraph(State)
builder.add_node("validation_humaine", validation_humaine)
builder.add_node("rembourser", rembourser)
builder.add_node("annuler", annuler)
builder.add_edge(START, "validation_humaine")
builder.add_edge("rembourser", END)
builder.add_edge("annuler", END)

# En production, remplacer InMemorySaver par un checkpointer durable
graph = builder.compile(checkpointer=InMemorySaver())

Le graphe peut ensuite être lancé, puis repris, éventuellement bien plus tard et depuis un autre processus tant que le checkpointer est partagé :

python
config = {"configurable": {"thread_id": "remboursement-42"}}

# Premier appel : le graphe s'arrête sur l'interruption
graph.invoke({"amount": 2000.0, "customer_id": "C-1087", "status": "en attente"}, config)

# Plus tard, l'opérateur approuve depuis l'interface de validation
resultat = graph.invoke(Command(resume=True), config)
print(resultat["status"])  # remboursé

Le nœud de validation joue le rôle de point de passage obligé : l'appel de paiement n'est atteint que si la décision est positive. Entre les deux appels, aucun processus n'a besoin de rester en mémoire, ce qui rend possible une validation qui prend plusieurs heures.

LangGraph propose aussi des points d'arrêt statiques avec interrupt_before et interrupt_after lors de la compilation du graphe. Ils sont pratiques pour le débogage, mais la documentation recommande plutôt interrupt() pour le human-in-the-loop, car il permet de transmettre une charge utile précise et de décider dynamiquement s'il faut s'arrêter.

Formulaire d'interruption dans LangGraph Studio avec les champs de la réponse attendue

Édition de l'état et escalade

L'édition de l'état se déduit du même mécanisme : la valeur de reprise peut être un objet structuré plutôt qu'un booléen. Le nœud applique alors les modifications de l'humain à l'état avant de poursuivre.

python
def validation_avec_edition(state: State) -> Command[Literal["rembourser", "annuler"]]:
    """Permet d'approuver, de refuser ou de corriger le montant."""
    decision = interrupt({
        "question": "Valider, corriger ou refuser ?",
        "montant": state["amount"],
    })

    if decision["action"] == "refuser":
        return Command(goto="annuler")
    if decision["action"] == "corriger":
        # L'humain a modifié le montant : on met à jour l'état
        return Command(goto="rembourser", update={"amount": decision["montant"]})
    return Command(goto="rembourser")

La reprise se fait alors avec Command(resume={"action": "corriger", "montant": 1500.0}). Pour l'escalade sur seuil de confiance, il suffit de conditionner l'appel : le nœud ne lève l'interruption que si un score tombe sous le seuil, et traite le cas courant sans pause. Ce routage dynamique est l'avantage principal de interrupt() sur un simple point d'arrêt fixe.

Enfin, comme chaque étape est sauvegardée dans un checkpoint, l'historique d'un thread constitue une piste d'audit et permet de revenir à un état antérieur pour rejouer l'exécution avec une autre décision. C'est aussi ce qui rend la revue asynchrone praticable, à condition de conserver ces checkpoints dans une base durable comme PostgreSQL.

À découvrir : notre formation Agentic AI

Arbitrer entre latence, coût et sécurité

Chaque point de validation a un prix. Il faut le chiffrer avant de le généraliser, et choisir le pattern selon le profil de risque de chaque action plutôt qu'appliquer une règle uniforme.

  • Latence : une approbation bloquante fait passer le temps de réponse de quelques secondes à plusieurs minutes, voire plusieurs heures si elle dépend d'un opérateur disponible. Pour une interface de chat, cela se traduit par une expérience dégradée ; pour un traitement en arrière-plan, c'est souvent acceptable
  • Coût : le temps humain est la ressource la plus chère de la chaîne. Il faut aussi tenir compte du coût de persistance (checkpoints stockés pour chaque thread suspendu) et du coût des agents qui continuent de consommer des tokens si la reprise relance trop de contexte
  • Sécurité : plus on valide tôt et souvent, plus on réduit le risque, mais la fatigue d'approbation annule le bénéfice si l'interface noie les demandes critiques dans un flot de validations anodines
  • Charge opérationnelle : il faut prévoir des délais d'expiration, une file de validation, des règles de repli (refus par défaut ou escalade vers un autre responsable) et une gestion des droits sur qui peut approuver quoi
SituationPattern recommandéJustification
Action irréversible à fort enjeu (paiement, suppression)Approbation avant actionLe risque justifie une latence bloquante
Action complexe dont les paramètres sont souvent à ajusterÉdition de l'étatCorriger avant exécution coûte moins que réparer
Flux à fort volume avec quelques cas ambigusEscalade sur seuil de confianceL'humain traite seulement l'exception
Action réversible à faible enjeuRevue asynchroneL'autonomie est préservée, le contrôle reste possible

Ces choix se complètent d'un durcissement technique. Une validation humaine ne remplace pas l'isolation des agents : pour des actions de type computer use, exécuter l'agent dans un bac à sable limite les dégâts même lorsqu'une approbation a été donnée à tort. Il est également utile de mesurer le taux d'approbation par type d'action. Un taux proche de 100 % signale une validation devenue inutile, qui peut être automatisée avec des règles, ou à l'inverse une interface qui pousse à valider sans lire. Cette boucle de mesure permet d'ajuster progressivement le niveau d'autonomie accordé à chaque agent.

Conclusion

Le human-in-the-loop n'est pas un frein à l'autonomie des agents, c'est la condition pour la leur accorder sur des actions réelles. En plaçant la validation humaine là où l'impact est élevé et en laissant l'agent opérer librement ailleurs, on obtient un système à la fois utile et maîtrisable. Les quatre patterns présentés, approbation avant action, édition de l'état, escalade sur seuil de confiance et revue asynchrone, répondent à des niveaux de risque et de latence différents et se combinent dans un même agent.

Côté implémentation, les interruptions et les checkpoints de LangGraph fournissent les briques nécessaires : un état persisté, une pause qui peut durer le temps qu'il faut, et une reprise qui injecte la décision humaine dans le déroulement du graphe. Ils s'appuient sur une gestion d'état propre, complètent les guardrails automatiques et gagnent à être accompagnés d'une bonne observabilité et d'un bac à sable.

Le véritable travail consiste à calibrer les points de contrôle : trop nombreux, ils lassent les opérateurs et perdent leur valeur ; trop rares, ils laissent passer les erreurs coûteuses. Mesurer le taux d'approbation, la latence de validation et les incidents évités permet d'affiner ce réglage en continu.

Pour les équipes qui déploient des solutions d'Agentic AI, savoir concevoir ces points de validation devient une compétence différenciante. Les agents qui seront confiés à des tâches critiques seront ceux dont les décisions restent traçables, corrigibles et, quand il le faut, tranchées par un humain.

Pour aller plus loin

D'autres articles Agentic AI

Approfondir le sujet

Formation Agentic AI

Apprenez à outiller, évaluer et déployer des agents autonomes en production.

  • Agents IA
  • Tool calling
  • Multi-agents
  • Évaluation