Un agent chargé de corriger les liens cassés d’un site a rappelé le même outil de vérification quarante-sept fois d’affilée avant qu’un administrateur ne le stoppe manuellement, chaque appel confirmant que le lien restait cassé sans que l’agent ne change de stratégie. Ce comportement, observé sur un test avant mise en production, illustre exactement pourquoi une limite dure d’itérations reste indispensable, même quand l’agent semble globalement fiable.
Cette recette décrit la mise en place d’un compteur d’itérations simple, associé à un journal qui explique la raison de l’arrêt plutôt que de se contenter de couper l’exécution sans justification.
Le problème : une boucle qui progresse sans converger
Un agent multi-outils fonctionne par itérations successives : il appelle un outil, reçoit un résultat, décide de la suite. Rien, dans cette mécanique de base, n’empêche l’agent de rappeler le même outil avec les mêmes paramètres si sa stratégie de décision ne progresse pas vers une résolution, ce qui produit une boucle qui consomme des appels sans jamais atteindre son objectif.
Étape 1 : instrumenter un compteur d’itérations

La première étape consiste à ajouter un compteur simple, incrémenté à chaque appel d’outil par l’agent, et à interrompre l’exécution dès qu’un seuil fixé est atteint.
class Wpm_Agent_Runner {
private int $iterations = 0;
private int $max_iterations = 8;
public function executer_etape( callable $etape ) {
if ( $this->iterations >= $this->max_iterations ) {
$this->journaliser_arret( 'limite_iterations_atteinte' );
return false;
}
$this->iterations++;
return $etape();
}
}
Étape 2 : choisir un seuil qui correspond à la tâche
Sur nos projets, la valeur de huit itérations n’est pas arbitraire : elle vient de l’observation d’une centaine d’exécutions réussies, où la médiane d’itérations nécessaires se situait entre trois et cinq, avec un maximum observé de sept sur les cas légitimement complexes. Fixer la limite à huit laisse une marge raisonnable sans autoriser une dérive prolongée.
- Observer la médiane d’itérations sur des cas réussis avant de fixer un seuil
- Prévoir une marge au-dessus du maximum légitime observé
- Revoir le seuil si le type de tâche confiée à l’agent évolue
Étape 3 : journaliser la raison de l’arrêt
Un arrêt silencieux, sans trace exploitable, transforme chaque incident en enquête manuelle. Le journal doit indiquer la raison précise de l’interruption, ainsi que le dernier état connu de la tâche, pour permettre un diagnostic rapide sans avoir à rejouer l’exécution complète.
function wpm_journaliser_arret( string $raison ): void {
error_log( sprintf(
'[agent] Arret force : %s | iterations : %d | derniere action : %s',
$raison,
$this->iterations,
$this->derniere_action
) );
}
Étape 4 : distinguer un arrêt normal d’un arrêt anormal
Toutes les limites atteintes ne se valent pas : un agent qui atteint la limite après avoir résolu la tâche au dernier appel ne pose pas le même problème qu’un agent qui boucle sans le moindre progrès. Le journal doit donc aussi consigner si la tâche a été résolue avant l’arrêt ou non, pour distinguer les deux cas dans un tableau de suivi.
Sur nos projets, la limite d’itérations n’a jamais empêché une tâche légitime d’aboutir : elle a systématiquement arrêté des boucles qui n’auraient de toute façon pas convergé, quel que soit le nombre d’appels supplémentaires autorisés.
Variantes possibles
Sur des tâches particulièrement longues, une limite fixe peut être remplacée par une limite adaptative, augmentée progressivement si l’agent montre un progrès mesurable entre deux itérations. Cette variante demande davantage de code, mais évite d’interrompre prématurément une tâche complexe qui converge lentement mais sûrement.
En résumé
Une limite dure d’itérations, associée à un journal explicite sur la raison de l’arrêt, protège un agent multi-outils contre les boucles qui ne convergent jamais, sans pénaliser les tâches qui aboutissent normalement dans la marge fixée.