jguillaumesio
ai

Un prompt unique a laissé le code cassé dans quatre runs sur cinq

J'avais publié un benchmark disant que les boucles d'agent ne changent rien, et un lecteur m'a répondu que tous les bons ingénieurs en utilisent. Il avait raison et mon expérience était mal construite : mes tâches tenaient en une passe. Sur une tâche assez grosse pour exiger une boucle, un prompt unique laisse un code qui ne s'importe même pas, et la boucle fait passer de 1 test réussi à 8.

J’ai publié une version de cet article qui disait que les boucles d’agent ne changent rien. Un lecteur m’a répondu que les meilleurs ingénieurs qu’il connaît utilisent tous des boucles, donc soit ils ont tort, soit c’est moi.

C’est moi. Mon expérience était mal construite, et voici la version corrigée.

Le défaut n’était pas dans la mesure, il était dans la tâche. Toutes les tâches de mon benchmark tenaient en une seule passe, donc la boucle vérifiait sa condition d’arrêt, trouvait les tests déjà verts, et sortait. J’en ai conclu que les boucles ne bouclent presque jamais. C’est un fait sur mes tâches, pas sur les boucles, et j’aurais dû le voir avant de publier.

J’ai donc construit une tâche assez grosse pour en avoir réellement besoin. Dix exigences qui interagissent, une dizaine de fichiers touchés. Voici ce qu’un prompt unique en fait :

BrasTests visibles réussisTests cachés réussis, sur 12
Prompt unique1/51,4
Boucle5/58,0
Boucle ingéniérée4/57,8

Dans quatre des cinq runs en prompt unique, le code produit ne s’importait même pas. Le lanceur de tests échouait à la première ligne du fichier. Le modèle a travaillé deux minutes, produit un gros diff, et laissé le dépôt cassé.

C’est le régime dans lequel vivent les boucles, et je ne l’avais jamais testé.

Ce que la première version ratait

Deux erreurs, qu’il vaut la peine de nommer parce qu’elles sont faciles à répéter.

Les tâches étaient trop petites. Corriger une fonction, ajouter une fonctionnalité, migrer six fichiers. Un modèle actuel termine ça en une passe, donc il n’y a rien à faire pour une deuxième. Mesurer une boucle sur du travail qui n’en a pas besoin n’apprend rien sur les boucles.

La métrique masquait l’effet. Je publiais la réussite de la suite cachée en binaire : les douze tests ou rien. Sur de petites tâches ça va, parce qu’un run marche ou ne marche pas. Sur une grosse tâche, ça efface tout ce qui est intéressant. En binaire, les trois bras ci-dessous font 0/5. En comptant combien des douze tests cachés passent, le prompt unique fait 1,4 et la boucle fait 8,0. Mêmes données, conclusion inverse.

Un taux de réussite binaire sur une tâche difficile mesure si vous avez franchi la barre, pas la distance parcourue. Quand personne ne franchit la barre, il déclare une égalité entre un run qui a cassé le build et un run qui a correctement traité 9 exigences sur 12.

La tâche enfin assez grosse

Dix exigences sur une petite bibliothèque de tarification, entièrement spécifiées et conçues pour interagir : corriger une répartition qui perd des centimes, négatifs compris, migrer tous les montants en centimes entiers, valider les devises, arrondir la TVA à l’unité supérieure en s’éloignant de zéro, gérer l’autoliquidation B2B, les remises par ligne, des remises en pourcentage qui restent entières, des codes promo avec cumul, expiration et minimums, un découpage de facture, et la validation des entrées.

Une solution de référence passe 10/10 en visible et 12/12 en caché, donc la tâche est faisable. La base intacte échoue.

Trois bras, cinq runs chacun, sur Haiku 4.5, délibérément pas le modèle le plus fort disponible. Un modèle qui résout la tâche en une passe vous ramène au régime où les boucles ne servent à rien, et ce régime-là, je l’avais déjà mesuré jusqu’à l’épuisement.

Prompt unique : une invocation. Boucle : le même prompt relancé avec un contexte neuf jusqu’à ce que les tests visibles passent, plafond de huit passes. Boucle ingéniérée : l’implémentation de ce que l’article Loop Engineering décrit comme les briques d’une vraie boucle, soit une passe de planification qui écrit une checklist dans PLAN.md, un élément par passe au lieu de la tâche entière, un sous-agent vérificateur qui lit la spécification plutôt que les tests et tourne avec Edit et Write désactivés pour qu’il ne puisse physiquement pas toucher au code, un budget de passes, et un détecteur de blocage qui arrête quand deux passes ne produisent aucun changement.

Chaque tâche porte une suite de tests cachée que l’agent ne voit jamais, couvrant les règles que le prompt énonce mais que les tests visibles ne vérifient pas. La suite visible est la condition d’arrêt de la boucle. La suite cachée est la note.

Résultats

BrasVisibleTests cachés réussis, par runPasses médianesTempsCoût par run
Prompt unique1/50, 0, 0, 7, 01117 s0,17 $
Boucle5/58, 8, 8, 9, 71101 s0,19 $
Boucle ingéniérée4/52, 9, 10, 10, 84362 s0,73 $

Trois choses dans ce tableau.

La boucle marche, et l’effet est gros. Un prompt unique passe la suite visible une fois sur cinq. La boucle la passe cinq fois sur cinq. Un test exact de Fisher bilatéral sur 5/5 contre 1/5 donne p = 0,048, soit à peu près tout ce que ce protocole peut produire comme significativité avec cinq runs par bras. Sur la note cachée graduée, la boucle fait passer un run de 1,4 exigence correcte à 8,0.

Les échecs du prompt unique ne sont pas des quasi-réussites. Quatre sur cinq laissent un code impossible à importer. L’apport de la boucle ici n’est pas du polissage, c’est la différence entre un dépôt cassé et un dépôt qui compile et passe ses tests.

L’ingénierie n’a pas été rentable. Le bras ingéniéré fait 7,8 contre 8,0 pour la boucle naïve, en trois fois et demie plus de temps et presque quatre fois plus cher. Un run s’effondre à 2 sur 12. Les cinq ont déclenché le détecteur de blocage. J’attendais du sous-agent vérificateur qu’il soit le composant décisif, et sur cette tâche il ne l’a pas été.

Ce qu’aucun bras n’a corrigé

Pas un seul run sur quinze n’a passé les douze tests cachés. Les échecs résiduels sont concentrés et constants : les règles de codes promo échouent dans 5 runs sur 5 dans les deux bras à boucle, et l’ordre de composition du pipeline échoue dans la plupart.

Ces règles sont écrites dans le prompt. « Un code est expiré quand now >= expiresAt, donc un code qui expire exactement maintenant ne s’applique pas. » Les tests visibles ne le vérifiaient pas, donc ça n’a pas été construit, et rien dans la boucle ne l’a remarqué. Le sous-agent vérificateur, à qui on dit explicitement qu’une suite verte ne prouve pas qu’une exigence est satisfaite, a validé quand même.

Ce qui survit de mon résultat initial est donc plus étroit et plus précis que ce que j’avais publié : une boucle relève le plancher, pas le plafond. Elle vous fait passer de cassé à fonctionnel de façon fiable. Elle ne vous fait pas passer de fonctionnel à correct, parce que rien dans la boucle ne sait ce que correct veut dire, sauf les tests que vous avez écrits.

Ça colle mieux à la littérature que ma première version. DeLoop montre que les runs qui tombent dans la répétition échouent 59,5 pour cent du temps contre 33,4 pour cent pour les autres, ce qui justifie le détecteur de blocage. Prompt-Induced Waste montre que le choix du harness déplace le coût par succès d’un facteur 5 à 30, bien plus que la formulation du prompt. La boucle, c’est le harness. C’est le levier.

Et Huntley, qui a publié la technique en juillet 2025, le dit depuis le début : « engineers are still needed », et il n’y a « no way this is possible without senior expertise guiding Ralph ». La boucle fait le travail. Savoir ce que « fini » veut dire reste le métier.

Avantages et inconvénients, sur 95 runs

Ce que la boucle apporteCe que la boucle coûte
Sur une tâche assez grosse, 1/5 à 5/5 sur la suite visibleRien du tout sur ce qu’un modèle résout en une passe, donc la plupart des petites tâches
Un plancher : elle évite de livrer du code qui ne s’importe pasUne impression de justesse supérieure à ce que le travail mérite
Un contexte neuf par passe, donc pas de dégradation sur le travail long4 fois le coût et 3,5 fois le temps dès qu’on ajoute planification et vérificateur
De l’avancement sans surveillance au-delà d’une sessionLes exigences absentes de vos tests restent silencieusement non construites

Ce que j’en fais

Adapter la boucle à la taille de la tâche, et arrêter d’en débattre dans l’absolu. Petite tâche, un prompt. Tâche où la première tentative risque de laisser le build cassé, une boucle. Cette frontière est toute la décision, et mon article initial se trompait en ne testant qu’un seul côté.

Investir dans la condition d’arrêt, pas dans la machinerie de la boucle. Le bras ingéniéré a ajouté planification, revue et escalade pour quatre fois le prix sans bouger la note cachée. Ce qui l’a bougée, c’est d’avoir une boucle tout court. Ce qui l’aurait bougée davantage, ce sont des tests qui encodent les exigences, et aucune orchestration ne remplace ça.

Écrire l’assertion de chaque règle qui compte avant de commencer. Chaque échec résiduel ici était une phrase du prompt sans test derrière. L’agent lit votre suite de tests comme la spécification, quoi que dise votre prompt.

Limites

Cinq runs par bras, un modèle, une base de code, une grosse tâche plus les quatre petites de la première version. La comparaison sur le niveau epic atteint la significativité sur la suite visible et nulle part ailleurs. Le bras ingéniéré est mon implémentation d’une description publiée, pas la configuration de production de qui que ce soit, et une meilleure version battra peut-être la boucle naïve là où la mienne a échoué. Les coûts sont calculés par la CLI à partir des tarifs publics sur un abonnement, donc ce sont des conversions et pas des factures. Quatre-vingt-quinze runs, 16,34 $ équivalents.

Le harness n’est pas publié, donc prenez ces chiffres comme la mesure d’une seule personne plutôt que comme quelque chose de rejouable en l’état. Le protocole est décrit plus haut avec assez de détail pour être reconstruit : une bibliothèque de base, une suite visible qui sert aussi de condition d’arrêt à la boucle, une suite cachée copiée seulement après que l’agent a terminé, et une solution de référence qui prouve que chaque tâche est faisable avant le moindre run.

À retenir

J’ai benchmarké des boucles sur des tâches qui n’en avaient pas besoin, avec une métrique incapable de voir les progrès partiels, et j’ai publié que les boucles ne servent à rien. Un lecteur a contesté, j’ai construit la tâche que j’aurais dû construire en premier, et un prompt unique a laissé le code impossible à importer dans quatre runs sur cinq.

La boucle n’est pas un multiplicateur de qualité. C’est un plancher, et ce plancher compte énormément : c’est la différence entre un dépôt cassé et un dépôt qui marche. Ce qu’elle n’est pas, c’est un substitut au fait de savoir ce que vous avez demandé, et c’est pour ça que le douzième test caché échoue toujours, et pour ça que ceux qui tirent le plus des boucles sont ceux qui écrivent les conditions d’arrêt à la main.