Samen Steeve
MES SERVICES.
Retour au blog
IADevOpsPostgreSQL
28 juin 20265 min de lecture

Pourquoi mon agent LangGraph a détruit 48h de données de staging

Il y a quelques semaines, un agent autonome basé sur LangGraph, conçu pour nettoyer périodiquement les comptes de test obsolètes en staging, s'est emballé. En moins de 10 minutes, il a purgé l'équivalent de 48 heures d'activité de test de notre équipe de QA. Voici le post-mortem technique de cette défaillance et comment nous avons repensé la sécurité des graphes.

L'origine du bug : une faille de boucle infinie sur l'état du graphe

Dans LangGraph, l'état (State) est partagé entre les différents nœuds. Notre agent avait un nœud de décision (router) et un outil d'effacement. Le flux nominal était :

1. Nœud A : Lister les IDs des comptes de test inactifs (stockés dans state.target_ids).
2. Nœud B : Si la liste n'est pas vide, appeler l'outil de suppression pour le premier ID, puis passer au nœud de décision.
3. Nœud C (Router) : Si d'autres IDs restent, boucler sur le Nœud B. Sinon, s'arrêter.

Ce qui a cassé : À cause d'une régression mineure sur l'outil de suppression, celle-ci renvoyait un statut de succès même si la ressource n'existait plus (ID introuvable). De plus, le nœud de suppression ne supprimait pas l'ID traité de la liste state.target_ids en cas d'erreur réseau transitoire.

L'agent s'est retrouvé bloqué dans une boucle infinie de décision :

[LangGraph Exec] Node: delete_tool -> Returns: SUCCESS (Resource not found / Network timeout)
[LangGraph Exec] Router -> Check: target_ids still has 12 items -> Loop back to delete_tool
[LangGraph Exec] LLM Context depletion -> The agent started hallucinating IDs and deleted real staging accounts matching wildcard queries.

La parade technique : Redesigner avec des gardes-fous stricts

Nous avons reconstruit le graphe d'exécution en y injectant trois principes de défense en profondeur :

1. Immutabilité de l'état et réducteurs (Reducers) stricts

Nous avons redéfini le schéma d'état de LangGraph en forçant l'usage de réducteurs stricts pour éviter les persistances d'ID obsolètes.

import { Annotated } from "@langchain/langgraph";

// Reducer qui remplace proprement la liste ou filtre les IDs traités
function updateTargetIds(current: string[], next: string[]): string[] {
  // Garantir l'élimination des doublons et des IDs déjà traités
  return Array.from(new Set(next));
}

interface AgentState {
  targetIds: Annotated<string[], typeof updateTargetIds>;
  processedCount: number;
  maxIterations: number;
}

2. Le garde-fou des itérations maximums (Max Iterations Guard)

Aucune boucle d'agent ne doit tourner indéfiniment. Nous avons ajouté un nœud de contrôle global qui lève une exception et arrête le graphe dès qu'une limite d'itérations est franchie.

function routeDecision(state: typeof AgentState.State) {
  if (state.processedCount > 50) {
    throw new Error("Loop Guard Triggered: Max iterations exceeded in staging cleaning.");
  }
  if (state.targetIds.length > 0) {
    return "delete_node";
  }
  return "__end__";
}

3. Double validation humaine (Human-in-the-loop) pour les suppressions de masse

Pour toute action destructive touchant plus de 5 enregistrements, le graphe utilise la fonctionnalité d'interruption (interrupt) de LangGraph pour demander une validation manuelle via Slack/Webhook avant de poursuivre.

Ce que cela change pour vos projets

Les frameworks d'agents comme LangGraph ou CrewAI sont puissants, mais ils masquent la complexité de l'exécution non déterministe. Ne laissez jamais un agent autonome interagir avec des APIs destructives sans un système d'isolation strict (sandboxing), un nombre maximum d'itérations codé en dur, et une interruption humaine pour valider les suppressions.

Besoin d'intégrer des agents IA ?

Sécurisons et orchestrons vos agents autonomes en production sans failles logiques.

Démarrer un projet