
Les IA qui corrigent des bugs apprennent à rédiger leurs propres tests avant d’appliquer un correctif
Lorsqu’une IA modifie du code pour corriger un bug, elle a besoin de preuves que sa modification fonctionne. Les suites de tests officielles peuvent confirmer le succès, mais elles ne constituent pas le seul signal disponible pendant qu’un modèle apprend encore à réparer. Une voie consiste à laisser un autre modèle rédiger de petits tests de régression à partir du rapport d’incident et à utiliser leurs signaux de réussite ou d’échec lors de la réparation. Le défi est de rendre ces contrôles générés suffisamment fiables pour orienter une correction sans remplacer la vérification finale.
Le rédacteur de tests a une mission stricte : son contrôle doit échouer sur le code défectueux et passer après la correction.
Le rédacteur de tests a une mission précise : produire un contrôle qui échoue sur le code défectueux et passe après la correction juste. Ce n’est pas suffisant. Le test doit aussi permettre de distinguer les correctifs valides des invalides. S’il ne rate que pour une mauvaise raison, il risque d’orienter la réparation dans une fausse direction.
Les règles d’apprentissage sont strictes à ce sujet. Une soumission sans test valide est pénalisée de -0,2. Un lot de tests valide qui n’échoue pas sur le code bogué ou ne passe pas après l’application du correctif de référence obtient un score nul. Une fois cette condition minimale remplie, la qualité prime. Si le contrôle distingue les correctifs justes des incorrects avec une précision équilibrée inférieure à 80 %, il rapporte 0,2. Entre 80 % et moins de 100 %, le score est de 0,5. Une séparation parfaite vaut 1,0. Autrement dit, un test qui se contente de trébucher sur le bug n’est pas récompensé : il doit se comporter comme une sonde fiable.
Le rédacteur ne peut pas consulter l’historique caché des solutions. Il a accès uniquement à l’arborescence de travail actuelle et aux documents publics. Il peut utiliser git status, add -AN et un simple diff. En revanche, toute commande révélant des commits ou des branches passés est interdite. Le comportement attendu doit découler du texte du problème, de la documentation publique, du code bogué et des tests existants.
Avant d’écrire les assertions, il doit identifier le plus petit comportement public en jeu. Il doit également envisager une interprétation alternative plausible du problème avant de l’écarter. Un échec net sur le code défectueux est nécessaire mais pas suffisant : il ne prouve qu’un désaccord avec le bug, non un accord avec la correction prévue.
Le test doit viser quelque chose que les utilisateurs peuvent observer : un appel d’API, un résultat en ligne de commande, une sortie rendue ou une étape de migration. Il faut éviter de tester l’état interne privé simplement parce qu’il est facile à inspecter. Si le problème concerne le rendu, le contrôle doit examiner le résultat affiché, pas un indicateur interne.
Les messages exacts, les compteurs et les ordres ne peuvent être verrouillés que si le problème, la documentation ou un test public existant les impose. Sinon, le rédacteur s’appuie sur la propriété observable la plus faible garantie par le problème et violée par le code bogué.
Le lot de tests doit former un groupe de régression cohérent pour ce problème précis. Il peut ajouter plusieurs tests et mettre à jour des fixtures, mais il ne doit pas glisser de couverture non liée. La commande d’exécution doit nommer chaque nœud de test déclaré. Une commande au niveau du fichier ou de la suite est rejetée, même si elle échoue sur le code bogué.
Le rédacteur soumet aussi un court contrat de comportement. Il y précise le point d’entrée public, le déclencheur qui expose le bug et la sortie corrigée minimale attendue. Il consigne également les preuves à l’appui, les hypothèses alternatives rejetées, les littéraux tentants laissés non verrouillés et chaque nœud de test exact exercé.
La soumission est mécanique. Le rédacteur écrit un contrat JSON et une commande d’exécution en une ligne dans des fichiers temporaires. Il crée ensuite un patch non vide contenant uniquement les modifications de tests de régression prévues. Ce patch est soumis lors d’un unique appel final bash. La prose seule ne compte pas.
L’agent de réparation commence par une tentative complète pour corriger le bug. Il dispose d’un maximum de 200 tours de travail avant que tout retour ne soit autorisé. À ce stade, il a produit un correctif complet. Ce correctif n’est pas encore jugé par la suite de tests officielle. En revanche, il est confronté à une vérification figée.
Une vérification figée est un test automatisé unique qui reste inchangé pendant toute la phase de réparation. L’agent ne voit ni son code source ni son origine. Il ne reçoit qu’un retour d’exécution limité : le code corrigé a-t-il permis à cette vérification fixe de passer ? Ce signal local est peu coûteux. Il indique si le correctif actuel gère bien le comportement visé. Il ne tranche pas sur la réussite finale.
Si la vérification figée échoue, l’agent de réparation peut réviser. Il peut utiliser jusqu’à cinq tours de révision. Chaque tour autorise 40 tours de travail. L’agent modifie le même correctif cumulatif. Avant chaque vérification, le système revient au code bogué d’origine et applique à nouveau la totalité du candidat. Cela évite les chaînes désordonnées de petites modifications. Une erreur dans le code source du correctif compte comme un retour d’échec utile. Un plantage de l’environnement ne compte pas.
La récompense pendant l’apprentissage suit cette répartition. Un correctif manquant ou invalide obtient zéro. Un correctif utilisable qui échoue encore à la vérification figée rapporte un petit score. S’il passe la vérification figée mais échoue à l’évaluation officielle, il en rapporte davantage. Si les deux passent après révision, il obtient le plein crédit. Si les deux passent déjà au tour 0, il gagne un bonus supplémentaire. Cela apprend à l’agent à utiliser la vérification locale comme une boussole, et non comme la destination.
L’agent de réparation s’attaque d’abord au bug sans aucune aide extérieure. Il dispose de 200 tours pour produire un correctif complet avant que le moindre retour ne lui soit accordé. Ce premier jet n’est pas encore soumis à la suite de tests officielle. Il est confronté à une seule vérification figée.
Une vérification figée, c’est un test automatisé unique qui reste inchangé durant toute la phase de réparation. L’agent ignore son code source et son origine. Il ne reçoit qu’un retour d’exécution borné : le correctif a-t-il permis à ce test fixe de passer ? Ce signal local est peu coûteux. Il indique si le patch actuel gère bien le comportement visé, mais il ne tranche pas sur la réussite finale.
Si cette vérification échoue, l’agent peut réviser son travail. Le système lui accorde jusqu’à cinq tours de révision, chacun limité à 40 tours de travail. L’agent modifie toujours le même correctif cumulatif. Avant chaque nouvelle vérification, le système revient au code bogué d’origine et y applique l’intégralité du candidat. Cette procédure évite les chaînes désordonnées de petites modifications. Une erreur dans le code source du correctif compte comme un échec utile. Un plantage de l’environnement, non.
La récompense pendant l’apprentissage suit cette logique. Un correctif manquant ou invalide rapporte zéro. Un correctif utilisable qui échoue encore à la vérification figée obtient un score modeste. S’il passe ce test local mais échoue à l’évaluation officielle, le score augmente. Si les deux passent après révision, il reçoit le plein crédit. Et si tout est déjà validé dès le tour 0, il gagne un bonus supplémentaire. L’objectif est d’apprendre à l’agent à utiliser la vérification locale comme une boussole, et non comme la destination finale.
Dans un scénario d’évaluation donné, cette révision guidée par le retour a fait passer le taux de résolution de 68,3 % à 72,6 %. Parmi les 120 trajectoires ayant engagé une révision, le coût médian n’était que de 11 tours supplémentaires. La moyenne s’établissait à 13. Près de la moitié se sont terminées en moins de dix tours. Lors des exécutions avec tests générés, un lot de tests qualifié est réutilisé sur trois exécutions de réparation et toutes leurs révisions associées. La génération des tests n’a lieu qu’une fois par problème et par source. Le verdict final émane toujours d’un évaluateur officiel neuf. Les résultats des portes locales sont enregistrés séparément : ils guident la révision, mais ne remplacent pas la vérification complète.
De combien le duo progresse-t-il quand on entraîne les tests au lieu de les emprunter ?
Les tests entraînés apportent au duo un bien meilleur rendement que les tests empruntés. La comparaison la plus parlante part d’un agent de réparation non spécialisé dans cette tâche. Sans aucun retour des tests, il résout 61,2 % des cas. Si un générateur de tests lui aussi non entraîné fournit ses contrôles, le taux chute à 57,3 %, soit une perte de 3,9 points : ces tests générés sont trop bruités et finissent par induire la réparation en erreur.
Une source externe plus robuste aide davantage. Les tests issus de GPT-5.6-sol remontent ce même agent de base à 65,3 %, un gain de 4,1 points par rapport à l’absence de retour. Les contrôles « fail-to-pass » de l’oracle le portent encore plus loin, à 69,4 %, soit une hausse de 8,2 points. Ces tests empruntés sont utiles car ils ciblent des défaillances réelles, mais ils n’atteignent pas encore la performance de la meilleure configuration entraînée.
Le bond est maximal lorsque les deux rôles sont entraînés. Un agent de réparation formé sur le retour d’information atteint 73,5 % avec les tests GPT-5.6-sol et 77,6 % avec les contrôles Oracle. Face à sa référence sans test entraîné (64,6 %), le résultat Oracle représente un gain de 13 points. Cet écart prouve que la qualité des tests compte autant que l’habileté de réparation.
Le rédacteur de tests entraîné améliore aussi l’agent de réparation plus faible. Remplacer les tests du modèle de base par ceux issus d’un apprentissage par renforcement fait passer le taux de résolution de 57,3 à 64,1 %, soit une hausse de 6,8 points. Pour l’agent de réparation entraîné, ce même remplacement le porte de 64,6 à 72,6 %, un gain de 8,0 points. Ces deux progrès ne sont pas des effets magiques indépendants : ils vont dans le même sens, car de meilleurs contrôles rendent la réparation plus fiable.
Le duo entièrement entraîné atteint 72,6 % sur SWE-bench Verified sans s’appuyer sur le retour d’information de GPT-5.6 ou de l’Oracle pendant l’évaluation. C’est 11,4 points au-dessus de l’agent de base original, qui plafonne à 61,2 % sans aucun retour. Si l’on ajoute le retour Oracle au duo entraîné, la résolution grimpe à 77,6 %. L’écart restant de cinq points suggère qu’une meilleure qualité des tests et une couverture plus large pourraient encore acheter du progrès.
Deux cas concrets où un test généré a corrigé le tir
Deux cas concrets illustrent comment un test généré peut rediriger la réparation vers la bonne piste, en évitant les fausses solutions. Dans le premier, Django devait mettre en place une solution de repli pour les bases de données dépourvues d’un support natif du XOR. Ce dernier est un opérateur logique qui vérifie si un nombre impair d’entrées est vrai. Or, la solution de repli calculait incorrectement la parité, c’est-à-dire le résultat pair ou impair attendu du XOR. Un test généré couvrait trois cas de parité et échouait nettement sur le code défectueux initial. Cet échec était pertinent car il correspondait au comportement décrit dans le rapport d’incident. La première tentative de correction obtenait bien la bonne parité, mais réutilisait un connecteur XOR lors de la reconstruction du nœud de repli, ce qui provoquait une erreur de récursion (RecursionError) à l’intérieur du test figé. Après un tour de retour d’information et 23 tours supplémentaires, la réparation a remplacé ce connecteur XOR réutilisé par un AND. Le correctif final passait alors à la fois le test généré et une vérification officielle indépendante. Le test ne se contentait pas de signaler l’échec. Il désignait précisément le sous-système défaillant.
Le second cas concernait Sphinx et l’analyse des paramètres typés. Sphinx interprétait la virgule présente dans dict(str, str) comme un séparateur, ce qui produisait une sortie rendue malformée. Un test généré utilisant un type de dictionnaire entre parenthèses échouait sur le code défectueux et passait après l’application du correctif de référence. La première tentative modifiait un analyseur Napoleon sans rapport avec le problème. Le test figé a mis en lumière la véritable incohérence du rendu, redirigeant ainsi la réparation vers l’analyseur des champs typés dans docfields.py. Après deux tours et 75 tours supplémentaires, le correctif final conservait le type complet avant le nom du dernier paramètre, sans plus couper à la virgule. Il passait également les tests générés et la vérification officielle. Un cas de type entre crochets fonctionnait aussi via ce même chemin d’analyse. Le correctif Sphinx incluait par ailleurs une modification antérieure sans rapport. Elle était correcte, mais non minimale.
Ces faibles nombres de tours sont révélateurs : le test fournissait un indice précis. La réparation n’avait pas besoin de parcourir l’intégralité du code. Il suffisait d’examiner un chemin suspect, de le corriger et de relancer le même test figé. Le test généré guidait la réparation, tandis qu’un évaluateur officiel distinct rendait le verdict final.
Séparer les deux rôles demeure la conception la plus sûre
Un modèle unique paraîtrait plus simple : un seul point de contrôle pourrait rédiger les tests et réparer le code, tout en transférant des connaissances entre ces deux tâches. Mais cette apparente commodité masque un risque réel. Les deux rôles n’apprennent pas à partir des mêmes signaux. Le rédacteur de tests est récompensé pour produire des vérifications qui échouent sur le code défectueux, passent après une correction juste et distinguent les correctifs valides des invalides. Sa plus haute récompense ne vient que lorsque cette distinction est nette. Le réparateur, lui, n’est récompensé que si la modification apportée au seul code source résiste à un contrôle final indépendant. Ces objectifs tirent dans des directions opposées. Si un même modèle joue les deux rôles, il risque d’optimiser la boucle plutôt que le bug : écrire des tests faciles à satisfaire pour lui-même, ou réparer le code en s’alignant sur le test au lieu de corriger le comportement public décrit dans le rapport d’incident. Cette coordination paraît efficace au premier abord. En pratique, elle peut dégrader les performances du duo.
Garder les rôles séparés protège la vérification contre le risque de devenir une cible. Le lot de tests est généré une fois, puis maintenu fixe pendant toute la phase de réparation. L’agent de réparation ne reçoit qu’un retour d’exécution borné. Il ignore la source cachée et l’identité du test. Il ne peut donc pas réécrire la vérification pour faire paraître son correctif meilleur. C’est crucial, car des tests faibles peuvent induire en erreur la réparation : un test qui échoue pour la mauvaise raison peut envoyer une correction sur une fausse piste. L’entraînement séparé facilite le maintien de l’honnêteté de chaque rôle : l’un doit produire des vérifications fiables, l’autre doit résoudre le problème sous ces contraintes. Le résultat n’est pas une garantie universelle. C’est un design plus sûr, car l’indépendance limite les façons dont le système peut se tromper lui-même.
Un test n’est utile que s’il dit la vérité
Le résultat le plus solide ne tient pas à un tour de passe-passe ingénieux, mais à une boucle. Le rédacteur de tests produit un petit contrôle tourné vers l’extérieur. L’agent de réparation tente de le satisfaire. En cas d’échec, ce dernier reçoit un signal bref et ajuste son travail. Ce mécanisme a fait passer le taux de réussite de 61,2 % à 72,6 %. Les deux modèles ont été entraînés, sans recours à un oracle externe lors de l’évaluation.
Ce gain n’est pas magique : il découle d’un retour d’information peu coûteux. Un lot de tests généré peut être réutilisé sur plusieurs tentatives de réparation. La révision n’a ajouté en moyenne que treize tours d’agent, le système autorisant jusqu’à cinq cycles de correction. C’est un point crucial, car les corrections logicielles réelles consomment du temps et des ressources de calcul. Un contrôle local peu onéreux permet ainsi de guider de nombreuses petites ajustements sans épuiser le budget.
Il subsiste toutefois une limite. Avec un retour d’information oracle plus robuste, la réussite atteint 77,6 %. L’écart restant pointe vers la qualité et la couverture des tests : de meilleurs contrôles pourraient révéler davantage du comportement réel autour d’un bug, tandis que des vérifications indépendantes supplémentaires réduiraient les fausses certitudes. Un modèle partagé, capable à la fois de rédiger des tests et de réparer le code, serait plus simple à déployer et permettrait aux connaissances de circuler entre les deux rôles.
L’obstacle principal est la confiance. Des tests faibles ou mal calibrés peuvent orienter la réparation loin du problème initial. Le rédacteur de tests ne doit pas se focaliser sur des détails internes faciles, un état privé ou un simple symptôme du bug. Il doit viser le comportement observable que l’incident promet réellement. L’entraînement conjoint complique cette tâche, car les deux rôles obéissent à des récompenses et des actions différentes. S’ils apprennent à se satisfaire mutuellement au lieu de résoudre le problème, la boucle devient auto-justificatrice.
L’enjeu reste considérable. Un système peut générer un contrôle utile, l’exécuter localement, réviser le code puis soumettre le tout pour vérification officielle. L’étape suivante concerne moins la capacité brute de codage que l’autotest discipliné. Une machine peut-elle apprendre à douter de son propre test ?
Vous aimerez aussi
Technologie et numérique · Génétique et biotechnologies · Sciences du vivant
Comment une machine apprend à choisir le prochain lot de gènes dans un crible CRISPR
Technologie et numérique
Un robot peut-il anticiper l’avenir avant de bouger ? Des nouveaux modèles testent les futurs en quelques millisecondes
Santé et médecine · Technologie et numérique
Les robots logiciels se multiplient dans les hôpitaux américains mais le mauvais choix de tâches gaspille l’argent de l’automatisation
Technologie et numérique
Des agents IA parviennent-ils à trouver les leviers cachés qui orientent les modèles de langage vers n’importe quel sujet ?
Poursuivre la lecture
Un modèle tenant compte de la physique du scanner pourrait rendre l’image de visée radiothérapie fiable pour le calcul des doses sans nouveau CT
24 septembre 2026
Un flux de patinage en direct peut ne nécessiter que des kilo-octets s'il envoie des squelettes et des angles de caméra, plutôt que des images vidéo
18 septembre 2026
Un robot peut-il apprendre à corriger ses échecs dans la préparation du café sans qu'un humain montre chaque étape ?
18 septembre 2026



