← retour Schéma d'engrenages et de tuyaux interconnectés formant une boucle fermée avec un manomètre et une soupape de sortie.

Boucles Ralph dans Claude Code : vérificateurs, budgets et conditions d'arrêt

Une boucle Ralph se compose de trois éléments : une tâche, un vérificateur et un budget. C'est tout le schéma. Geoffrey Huntley l'a nommée d'après le personnage des Simpsons qui continue simplement, peu importe si quelque chose fonctionne ou non, et l'analogie tient bon. Le risque n'est pas que Claude s'arrête trop tôt. Le risque est qu'il déclare le succès sur un état défaillant, épuise votre budget de jetons, ou les deux. Cet article explique comment définir précisément chacun des trois éléments, choisir la bonne implémentation pour votre situation et arrêter la boucle en toute sécurité quand c'est vraiment fait. Le flux de travail de développement agentique est une préoccupation distincte ; cet article se limite strictement à l'itération jusqu'à ce qu'une condition soit satisfaite.

Définir l'objectif, le vérificateur et le budget

Avant d'écrire une seule commande, écrivez trois phrases. À quoi ressemble « terminé » ? Comment vérifierez-vous cela mécaniquement ? Et combien d'itérations êtes-vous prêt à payer ?

L'objectif doit être vérifiable par machine. « Le code est bon » n'est pas un objectif. « Tous les tests dans npm test réussissent et git status est propre » est un objectif. La différence compte car un vérificateur ne peut évaluer que ce qu'il peut observer. Si votre condition d'achèvement repose sur un humain qui regarde quelque chose, vous n'avez pas de vérificateur ; vous avez une étape de révision, et ces deux choses ne devraient pas être dans la même boucle.

Le vérificateur n'est pas l'opinion propre de Claude. C'est la partie que la plupart des gens se trompent. Comme l'a dit un commentateur de Hacker News, « si l'IA pense que les choses fonctionnent, elle dira COMPLETE même si vous ne penseriez pas que c'est complet ». Un DONE généré par le modèle est un signal, pas un vérificateur. Un vrai vérificateur est un processus externe : votre test runner, votre linter, une requête curl contre un endpoint en direct renvoyant le code de statut attendu. Il s'exécute après chaque itération et produit un vrai/faux déterministe. Construisez d'abord cela.

Le budget est votre soupape de sécurité. Choisissez un nombre que vous êtes réellement à l'aise de dépenser. L'implémentation communautaire frankbria/ralph-claude-code exige à la fois des indicateurs d'achèvement heuristiques (deux ou plus) et un EXIT_SIGNAL: true explicite dans le bloc RALPH_STATUS du modèle avant qu'il ne quitte. Cette conception à double condition existe précisément parce que les signaux simples ne sont pas fiables. Votre --max-iterations devrait être conservateur pour la première exécution. Vous pouvez l'augmenter une fois que vous avez vu jusqu'où la boucle s'étend réellement.

Pour le dire clairement, votre configuration avant d'exécuter quoi que ce soit devrait répondre :

  • Quelle commande shell retourne le code de sortie 0 uniquement quand la tâche est réellement terminée ?
  • Quel est le nombre maximum d'itérations que je vais autoriser ?
  • Quel fichier ou log enregistrera les résultats des itérations pour que je puisse les inspecter plus tard ?

Choisir l'implémentation de la boucle

Claude Code 2.1 embarque trois primitives intégrées qui couvrent la plupart des cas d'usage Ralph sans aucun plugin. Le guide Awesome Claude documente tous les trois :

Schéma d'un tuyau bifurqué avec deux chemins, l'un ouvert et l'autre avec une soupape d'arrêt, représentant les choix d'implémentation de boucle.
  1. /goal, qui continue à travailler à travers les tours jusqu'à ce qu'une condition soit vérifiée. Utilise un modèle plus petit séparé (Haiku par défaut, selon l'article de Ranjan Kumar) pour lire la transcription de la session après chaque tour et répondre à une question : l'objectif a-t-il été atteint ? Si non, Claude prend un autre tour. Si oui, la boucle se termine.
  2. /loop réexécute une invite à intervalle fixe ou auto-adapté. Appuyez sur Échap pour arrêter. Utile pour les tâches de polling.
  3. /batch répartit un grand changement sur 5 à 30 agents worktree parallèles. C'est une tout autre bête ; ce n'est pas ce qu'il vous faut pour une tâche unique et bien définie.

Ensuite il y a le chemin du plugin et la boucle bash brute. Voici où ils diffèrent d'une façon qui compte réellement pour votre travail.

Le plugin (ralph-wiggum@claude-plugins-official ou le fork communautaire) s'exécute dans une seule session à l'aide d'un hook d'arrêt. Quand Claude tente de quitter, le hook l'intercepte et renvoie l'invite. Le contexte s'accumule à travers les itérations. C'est pratique, mais cela signifie qu'à l'itération 15, la fenêtre de contexte porte les résidus de chaque tentative précédente, ce qui peut dégrader la qualité de chaque nouveau tour.

L'approche bash brute, qui tuyaute PROMPT.md dans claude -p à l'intérieur d'une boucle while, crée un processus complètement nouveau à chaque fois. Comme l'indique Steve Kinney, « Chaque invocation de claude -p obtient une fenêtre de contexte complètement vierge. C'est tout l'intérêt de la technique, éviter la dégradation du contexte en recommençant délibérément de zéro. » Le compromis : vous perdez la mémoire implicite de ce qui a été essayé, donc votre PROMPT.md et vos fichiers d'état de tâche doivent porter tout le contexte explicitement entre les itérations.

Lequel choisir ? Si votre tâche est courte (moins de 10 itérations attendues) et le contexte reste gérable, /goal avec un plafond de tours est l'option la moins contraignante. Si la tâche est longue ou si la qualité du contexte pose problème, la boucle bash à contexte frais est plus fiable. L'article sur les tests et les outils d'IA explique comment structurer votre suite de tests pour que le vérificateur puisse fonctionner correctement dans les deux cas.

Exécuter une tâche unique avec itérations limitées

Voici une configuration illustrative pour une tâche limitée en utilisant la primitive /goal :

/goal Tous les tests dans npm test réussissent et git status est propre, ou arrêtez après 20 tours

Cette simple ligne donne à Claude une condition de sortie vérifiable et un plafond strict. L'évaluateur Haiku lit la transcription après chaque tour et vérifie les deux conditions. La clause or stop after 20 turns est votre garde-fou budgétaire.

Pour une approche par boucle bash, la structure de l'équipe Geocodio est un bon modèle à suivre. Ils utilisent un fichier JSON (un simple prd.json) où chaque tâche a un champ "passes": false. Chaque itération trouve l'histoire prioritaire la plus haute avec passes: false, l'implémente, exécute le vérificateur et bascule le drapeau à true en cas de succès. La boucle while s'arrête quand chaque histoire a passes: true. Leur article mérite d'être lu ne serait-ce que pour la structure des critères d'acceptation.

Le flux numéroté pour une boucle bash à contexte frais ressemble à ceci :

  1. Écrivez PROMPT.md avec la tâche actuelle, la commande du vérificateur et le chemin du journal de réussite/échec.
  2. Démarrez la boucle while, en tuyautant PROMPT.md dans claude -p.
  3. Claude lit les instructions, effectue une unité de travail, valide vers git.
  4. Le vérificateur s'exécute. Le code de sortie 0 signifie réussite ; tout autre chose signifie échec.
  5. Enregistrez le résultat (numéro d'itération, réussite/échec, coût en tokens si disponible) dans un fichier.
  6. Si toutes les tâches réussissent, écrivez le signal d'arrêt et sortez. Sinon, retournez à l'étape 2.

Gardez chaque itération à une unité de travail. Essayer de faire trop par boucle, c'est comment on finit avec un état inachevé que le vérificateur ne peut pas évaluer proprement.

Détecter l'absence de progrès et l'achèvement faux

Deux modes de défaillance sont bien plus courants que les boucles infinies : la boucle n'avance pas à travers les itérations, et le modèle déclare la réussite sur un état cassé.

La détection de non-progression nécessite de comparer quelque chose de concret entre l'itération N et l'itération N+1. Un git diff est le signal le plus simple. Si git diff HEAD~1 est vide après une itération qui n'a pas produit de vérificateur réussi, la boucle tourne en rond. Vous devez le signaler immédiatement plutôt que de gaspiller trois itérations supplémentaires en espérant que quelque chose change.

L'achèvement faux est plus délicat. Le modèle produira DONE , COMPLETE , ou EXIT_SIGNAL: true dans des circonstances où votre vérificateur réel retournerait un code de sortie non zéro. La vérification à double condition de l'implémentation frankbria (deux indicateurs heuristiques et plus, et le signal explicite) est une atténuation raisonnable. Mais la correction plus efficace est : ne laissez jamais la boucle sortir sur la base de la seule production du modèle. Le script du vérificateur s'exécute quel que soit ce que dit le modèle, et la boucle continue si le vérificateur échoue, point final.

Attention à un piège connexe : le vérificateur lui-même retournant un faux positif. Si votre suite de tests contient des tests instables qui réussissent parfois sans que le bug sous-jacent soit corrigé, vous obtiendrez un achèvement faux que le modèle n'a même pas provoqué. C'est la section suivante.

Gérer les tests instables et les vérificateurs échoués

Les tests instables sont l'ennemi de toute boucle automatisée. Un test qui réussit 80 % du temps finira par déclencher une sortie de boucle sur les 20 % où il ne devrait pas. Et comme chaque itération coûte des tokens, une sortie fausse suivie d'une réexécution est coûteuse.

L'atténuation n'est pas compliquée, mais elle nécessite un travail préalable :

  • Exécutez votre commande de vérificateur trois fois de suite avant de faire confiance à un succès. S'il échoue une fois sur trois, traitez-le comme un échec.
  • Séparez vos tests « le travail est-il terminé » de vos tests « l'environnement fonctionne-t-il ». Les tests dépendants du réseau, les assertions sensibles au timing, et tout ce qui nécessite un état externe ne devraient pas être dans le vérificateur qui contrôle la sortie de la boucle.
  • Enregistrez chaque exécution du vérificateur avec sa sortie. Si la boucle s'arrête et que quelque chose semble mal, vous voulez la sortie complète du vérificateur de l'itération finale, pas seulement le code de sortie.

Si le vérificateur lui-même échoue (crash, timeout, ou retourne un code d'erreur inattendu qui n'est pas un échec de test), traitez cela comme un arrêt de boucle, pas une continuation de boucle. Tenter d'itérer à travers un vérificateur cassé produira juste des itérations qui ne peuvent pas être évaluées.

Les hooks Claude Code vous permettent d'attacher des scripts à des points spécifiques du cycle de vie de la session. Le guide des hooks Claude Code couvre le mécanisme en détail. Pour les boucles Ralph, le hook pertinent est celui qui s'active quand Claude tente de sortir : interceptez-le, exécutez votre vérificateur, et ne permettez la sortie que si le vérificateur réussit. Si vous utilisez le chemin du plugin, c'est déjà câblé. Si vous êtes sur une boucle bash, la décision de sortie se fait dans votre script shell plutôt que dans un hook.

Enregistrer les coûts et s'arrêter en toute sécurité

Vous devriez savoir ce que chaque itération coûte avant la fin de la boucle. Vous n'avez pas besoin de nombres exacts en temps réel, mais vous avez besoin d'un fichier journal qui enregistre le numéro d'itération, le résultat du vérificateur, et suffisamment d'informations sur les tokens pour estimer les dépenses après coup.

Le post de la communauté Alibaba Cloud sur les boucles Ralph identifie trois conditions d'arrêt qui méritent d'être intégrées à toute implémentation :

  • Succès : le vérificateur retourne 0 et toutes les tâches ont passes: true.
  • Arrêt de non-progression : deux itérations consécutives ou plus sans git diff et sans amélioration du vérificateur.
  • Épuisement du budget : le compteur d'itération atteint --max-iterations quel que soit l'état du vérificateur.

L'épuisement du budget n'est pas un mode de défaillance, c'est un arrêt conçu. Quand cela se déclenche, la boucle devrait écrire un résumé de ce qui a réussi, ce qui n'a pas réussi, et ce que la dernière itération a tenté. Cela vous donne un transfert propre pour un examen manuel ou une nouvelle boucle avec un prompt révisé.

Une chose à construire explicitement : une distinction entre « la boucle s'est arrêtée parce qu'elle a réussi » et « la boucle s'est arrêtée parce qu'elle a atteint la limite ». Si vous revenez à un terminal et voyez « la boucle s'est arrêtée à l'itération 20 », vous devez savoir lequel des deux c'était. Un simple flag dans le fichier journal, STOP_REASON: BUDGET_EXHAUSTED versus STOP_REASON: SUCCESS, c'est tout ce qu'il faut.

Si vous faites ce genre de travail à grande échelle ou si vous voulez une configuration gérée plutôt que des scripts faits main, l'agentic engineering work couvre ce qu'une configuration de boucle prête pour la production ressemble avec une observabilité appropriée.

FAQ

`/goal` est-il vraiment une boucle Ralph, ou quelque chose de différent ?

Ils partagent le même principe (itérer jusqu'à ce qu'une condition soit satisfaite) mais diffèrent en architecture. /goal s'exécute dans une seule session persistante ; une boucle bash Ralph classique crée un processus frais à chaque itération. Comme Ranjan Kumar le documente, /goal utilise un modèle Haiku séparé pour évaluer la transcription après chaque tour. La boucle bash n'évalue rien, votre script shell le fait. Les deux sont valides ; choisissez en fonction de si l'accumulation de contexte est un problème pour votre tâche.

Puis-je utiliser la propre sortie `DONE` du modèle comme vérificateur ?

Non. La sortie du modèle est un signal que vous pouvez utiliser comme entrée, mais elle ne peut pas être la seule condition de sortie. Un modèle affichera des marqueurs d'achèvement quand il croit que la tâche est terminée, ce qui n'est pas la même chose qu'en réalité. Associez tout signal généré par le modèle à une commande externe qui vérifie l'état réel du système.

Que se passe-t-il dans la boucle si Claude Code compacte le contexte en cours d'exécution ?

Dans une boucle de session persistante (plugin ou /goal), la compaction se fait automatiquement quand le contexte devient long, et la qualité de cette compaction est inégale. Un commentateur de Hacker News a observé que « les compactions Claude Code sont tellement de mauvaise qualité que c'est fondamentalement la même chose que d'effacer l'historique tous les quelques tours ». La boucle bash à contexte frais contourne entièrement ce problème car chaque itération démarre proprement. Si vous êtes sur le chemin du plugin et que vous exécutez de longues tâches, surveillez une dégradation de la qualité des résultats après les points de compaction.

Quelle devrait être la taille minimale de l'unité de travail de chaque itération ?

Aussi petite que possible tout en restant significative. Une tâche par itération est le bon modèle mental. L'approche Geocodio (une histoire de prd.json par boucle) et la description du commentateur de Hacker News (« il sélectionne la tâche la plus importante, la complète et termine sa boucle ») pointent toutes deux vers la même réponse : des chunks petits et vérifiables sont bien plus fiables que des itérations grandes et ambitieuses.

Ai-je vraiment besoin d'un plugin ?

Pour la plupart des tâches, non. La primitive /goal intégrée avec un plafond de tours couvre le cas courant. Utilisez le plugin (ralph-wiggum@claude-plugins-official ou frankbria/ralph-claude-code) quand vous voulez spécifiquement le comportement d'interception stop-hook ou la logique de conditions doubles que ces implémentations offrent. N'installez pas un plugin juste parce que vous l'avez vu dans un tutoriel.

La mise en garde la plus importante de tout ce modèle : un signal d'achèvement généré par un modèle n'est pas un vérificateur. Construisez la vérification externe en premier, avant tout le reste, et laissez chaque autre décision découler de cela.

← retour