Tout le carnet
Architecture IA13 min

Un agent IA devient utile quand sa boucle devient observable

L'ingénierie logicielle assistée par l'intelligence artificielle souffre d'un péché originel : la croyance qu'un modèle de langage plus grand, doté d'une fenêtre de contexte de plusieurs millions de tokens,...

Le prompt parfait échoue dès qu’il rencontre le runtime

L’ingénierie logicielle assistée par l’intelligence artificielle repose encore trop souvent sur une hypothèse séduisante : un modèle plus puissant, une fenêtre de contexte plus large et des instructions plus détaillées finiraient par produire le correctif juste du premier coup. Il suffirait de décrire précisément le problème, de charger une grande partie du dépôt dans le contexte, puis d’attendre une réponse exploitable.

Scène du mode de panne : Un agent IA devient utile quand sa boucle devient observable
Scène du mode de panne : Un agent IA devient utile quand sa boucle devient observable
Vue éditoriale du système : Un agent IA devient utile quand sa boucle devient observable
Vue éditoriale du système : Un agent IA devient utile quand sa boucle devient observable

Cette hypothèse confond compréhension textuelle et vérité d’exécution. Un modèle ne sait pas, par lecture seule, qu’une dépendance installée expose une API différente de celle attendue, qu’un type est rejeté par le compilateur, qu’un test d’intégration dépend d’une contrainte de données ou qu’un service externe répond de manière intermittente. Il peut proposer du code plausible ; il ne peut pas déclarer ce code viable sans confrontation avec l’environnement réel.

Le paradoxe est simple : plus l’équipe cherche à remplacer la vérification par la quantité de contexte, plus elle augmente le coût, la latence et le bruit, sans augmenter proportionnellement la fiabilité. Le bon objectif n’est donc pas de faire écrire davantage de code à l’agent. Il consiste à rendre chacune de ses tentatives vérifiable, traçable et arrêtable.

Le lecteur repart avec un modèle concret pour concevoir un agent de développement : une boucle locale qui apprend du runtime, une frontière d’autorisations explicite, un contrat de sortie vérifiable et des critères permettant de décider si l’agent est prêt pour la production.

Le modèle de la Boucle de rétroaction à double entrée

Appelons ce modèle la Boucle de rétroaction à double entrée. Il distingue deux niveaux de décision qui ne doivent pas être confondus.

La première entrée est celle du runtime : compilation, tests, analyse statique, retours d’API simulés, contraintes de schéma, journaux d’exécution. Elle pilote la boucle technique. Son rôle est de déterminer si la modification proposée tient dans les conditions définies par le produit et le code.

La seconde entrée est humaine : priorités, risque métier, arbitrage de sécurité, coût acceptable, validation du changement. Elle pilote la boucle de gouvernance. Son rôle est de décider si une modification techniquement valide mérite d’être intégrée.

La séparation est décisive. Un agent ne devrait pas demander une validation humaine parce qu’il a oublié une virgule ou choisi une mauvaise signature de fonction : le compilateur et les tests doivent absorber ces erreurs locales. À l’inverse, il ne devrait pas décider seul d’élargir une permission, de modifier une migration irréversible ou d’appeler un service externe coûteux : ces décisions dépassent la correction locale.

                         BOUCLE DE GOUVERNANCE
              Priorité, risque, autorisations, acceptation
                                      |
                                      v
                         [Plan de changement accepté]
                                      |
                                      v
+------------------------------------------------------------------+
|                         BOUCLE TECHNIQUE                         |
|                                                                  |
|  Observer -> Formuler une hypothèse -> Modifier -> Vérifier      |
|      ^                                                |          |
|      |---------------- rapport de validation ---------|          |
|                                                                  |
|  Recherche ciblée -> lecture des fichiers -> patch minimal       |
|  -> test ciblé -> contrôles globaux -> état final                |
+------------------------------------------------------------------+
                                      |
                                      v
                    [Preuves, diff, limites et verdict]
                                      |
                                      v
                           Décision humaine finale

Ce modèle ne rend pas l’agent infaillible. Il réduit en revanche la surface des erreurs silencieuses. Une sortie d’agent utile n’est pas une explication convaincante ; c’est une proposition accompagnée de preuves reproductibles.

Décomposer le travail en séquence vérifiable

Une tâche formulée comme « corrige ce bug » ne doit jamais devenir une instruction unique envoyée au modèle. Elle doit être transformée en une séquence courte, où chaque étape produit un artefact contrôlable.

Prenons une API TypeScript qui renvoie une erreur 500 lorsqu’un client envoie un champ numérique sous la forme d’une chaîne. Le symptôme semble mineur, mais plusieurs interprétations sont possibles : le contrat d’API peut interdire cette valeur, une transformation peut manquer, ou une erreur de validation peut être mal convertie en réponse HTTP.

La séquence de travail doit rester stable :

  1. Qualifier le symptôme. L’agent enregistre la route concernée, le comportement observé, le comportement attendu et les éléments encore inconnus.
  2. Localiser sans surcharger le contexte. Il cherche les occurrences liées à la route, au schéma et au code d’erreur, puis lit seulement les fichiers nécessaires.
  3. Écrire ou identifier un test de régression. Le bug doit être reproduit avant toute correction. Sans reproduction, un test vert peut seulement signaler que l’agent n’a pas touché la bonne zone.
  4. Émettre une hypothèse limitée. Exemple : « la validation lève une exception non interceptée, alors que le contrôleur doit retourner une réponse 400 structurée ».
  5. Appliquer un patch minimal. Une correction locale est plus simple à vérifier, à relire et à annuler qu’une refonte préventive.
  6. Exécuter le test ciblé. Le résultat doit confirmer que le symptôme disparaît.
  7. Exécuter les garde-fous globaux. Typage, lint, tests concernés et, selon le risque, tests d’intégration.
  8. Produire un verdict. L’agent conclut par « prêt à revoir », « bloqué » ou « rejeté », jamais par une simple impression de réussite.

Cette décomposition impose une discipline essentielle : l’agent doit savoir ce qu’il cherche à prouver avant de modifier le code. Elle évite le comportement le plus coûteux des systèmes agentiques : multiplier les changements jusqu’à obtenir accidentellement un résultat vert.

Un contre-exemple éclaire la différence. Un agent reçoit une erreur de test liée à l’authentification et décide de désactiver la vérification du jeton dans le test. Le test repasse, mais la cause n’est pas résolue ; le signal a été supprimé. Une boucle observable doit détecter ce type de réussite trompeuse : le diff modifie-t-il le comportement métier attendu, contourne-t-il une assertion ou élargit-il un privilège ? Le vert seul n’est pas une preuve suffisante.

Chercher le code utile plutôt que déverser le dépôt

Le context dumping — charger un dépôt entier dans le contexte d’un modèle — semble rassurant parce qu’il donne l’impression que rien n’est omis. En pratique, il détériore la décision. Le modèle reçoit un mélange de fichiers pertinents, de code historique, de dépendances, de tests sans rapport et de commentaires parfois obsolètes. La question n’est plus « l’agent possède-t-il toute l’information ? », mais « peut-il isoler l’information qui compte ? ».

Une stratégie de recherche doit donc précéder toute lecture importante. Elle s’appuie sur trois primitives complémentaires :

  • Recherche de fichiers : trouver les modules par nom, extension ou emplacement.
  • Recherche textuelle : repérer les routes, classes, erreurs, variables et imports à partir de termes exacts.
  • Recherche sémantique : retrouver des implémentations liées à une intention lorsque les noms divergent.

La recherche sémantique ne remplace pas la recherche textuelle. Pour localiser une fonction appelée validateCheckoutPayload, une recherche exacte est plus rapide et plus vérifiable. La recherche sémantique devient utile lorsqu’un incident est décrit en langage métier, par exemple « le panier est débité deux fois », alors que le code parle d’« idempotence », de « transaction » ou de « clé de requête ».

La règle opérationnelle est la suivante : l’agent doit d’abord construire une carte de dépendances minimale, puis lire les fichiers dans un ordre explicable. Pour un défaut de validation, cet ordre peut être :

Route HTTP
  -> contrôleur
    -> DTO ou schéma de validation
      -> service métier
        -> test existant
          -> gestionnaire d’erreurs commun

Cette carte limite le contexte à ce qui est nécessaire et fournit un point de contrôle à la revue humaine. Si l’agent modifie soudainement une couche éloignée — configuration de déploiement, facturation, permissions — il doit justifier ce saut ou être arrêté.

La qualité de la recherche se mesure aussi. Un agent qui lit cinquante fichiers pour modifier une ligne ne prouve pas sa rigueur ; il signale souvent une indexation insuffisante, une mauvaise formulation de tâche ou une absence de convention de code exploitable.

Ancrer l’agent dans un contrat de projet

Un agent ne doit pas deviner les conventions d’un produit à partir de fragments de code. Il a besoin d’un contrat local, versionné et concis, qui décrit ce qu’il peut faire, comment il doit vérifier son travail et ce qu’il doit éviter.

Vue de la décision d'architecture : Un agent IA devient utile quand sa boucle devient observable
Vue de la décision d'architecture : Un agent IA devient utile quand sa boucle devient observable

Ce contrat ne remplace pas la documentation d’architecture. Il condense les règles nécessaires à l’exécution : commandes autorisées, zones sensibles, standards de test, conventions de validation et conditions d’escalade. Son intérêt est de réduire les décisions implicites.

Voici un exemple de contrat de travail pour une API de réservation :

# Contrat d’exécution de l’agent

## Commandes de vérification
- Test ciblé : `npm test -- <fichier>`
- Tests unitaires : `npm run test:unit`
- Vérification des types : `npm run typecheck`
- Lint : `npm run lint`

## Règles métier sensibles
- Ne jamais confirmer une réservation sans contrôle d’idempotence.
- Toute erreur de saisie utilisateur doit produire une réponse 4xx structurée.
- Les montants sont manipulés en unités mineures entières.
- Une modification du calcul de prix exige un test de non-régression.

## Frontières techniques
- Les migrations de données sont interdites sans approbation explicite.
- Aucun appel réseau n’est autorisé pendant les tests.
- Les secrets ne doivent jamais être lus ni affichés.
- Toute modification de rôle ou de permission exige une escalade.

## Format de sortie obligatoire
- Hypothèse traitée
- Fichiers lus et modifiés
- Commandes exécutées et codes de sortie
- Risques résiduels
- Verdict : prêt à revoir, bloqué ou rejeté

Ce contrat doit être interprété comme une interface entre l’orchestrateur et l’agent, non comme une décoration documentaire. L’orchestrateur doit le charger au début de la tâche, vérifier les commandes avant leur exécution et conserver la version du contrat associée à chaque trace.

Il faut également prévoir le cas où le contrat est ambigu. Par exemple, « éviter les changements de schéma » n’indique pas si une migration additive est autorisée. L’agent ne doit pas compléter cette ambiguïté par une supposition. Il doit marquer la tâche comme bloquée et demander une décision. L’autonomie utile commence là où les frontières sont suffisamment explicites.

Encadrer les outils par une politique de capacité

Le risque d’un agent ne provient pas seulement du code qu’il écrit. Il provient des capacités qu’on lui accorde : lire des fichiers, modifier un espace de travail, lancer des commandes, accéder au réseau, consulter des secrets ou déclencher des effets externes.

La bonne granularité n’est pas « agent fiable » contre « agent non fiable ». Elle est : quelle capacité précise est nécessaire pour cette étape, dans quel environnement, avec quelle limite et avec quel moyen de récupération ?

CapacitéExempleRisque principalPolitiqueRéaction en cas d’échec
Lecture localerecherche, lecture de code, inspection de logsExposition d’informations non nécessairesAutorisée sur périmètre définiRéduire le périmètre ou masquer les données sensibles
Écriture contrôléepatch de fichiers applicatifsRégression ou modification excessiveAutorisée avec diff traçableAnnuler le patch et conserver la trace
Vérification localetests, compilateur, lintConsommation de ressources ou boucleAutorisée avec délais et quotasInterrompre, conserver les sorties, escalader
Réseau sortanttéléchargement, appel d’APIFuite de données et dépendance non maîtriséeInterdit par défautRejet explicite
Effet externedéploiement, paiement, suppression, migrationImpact métier ou irréversibilitéApprobation humaine obligatoireArrêt de la session et revue manuelle

Une politique de capacité efficace repose sur le principe du moindre privilège. L’agent de test n’a pas besoin d’écrire dans le code de production. L’agent chargé de cartographier le dépôt n’a pas besoin d’exécuter des commandes arbitraires. Un orchestrateur central peut coordonner plusieurs rôles, mais chaque rôle doit recevoir une capacité minimale.

Il faut également se méfier des listes d’autorisation trop littérales. Autoriser une commande connue ne suffit pas si ses arguments restent libres. Une commande de téléchargement peut extraire des données ; une commande de test peut lancer un script non prévu ; une commande de suppression peut cibler un répertoire inattendu. La politique doit donc contrôler la commande, ses arguments, son répertoire de travail, sa durée, ses ressources et, si nécessaire, son accès réseau.

Le bac à sable n’est pas un détail d’infrastructure. C’est ce qui rend acceptable l’expérimentation rapide. Sans isolation, une boucle de correction peut contaminer l’environnement, toucher des données de test partagées ou suivre une instruction injectée dans un fichier traité comme contenu fiable.

Rendre l’exécution lisible par la télémétrie

Un agent qui ne laisse qu’un message final est impossible à améliorer. Lorsqu’il échoue, on ne sait pas s’il a mal compris la tâche, mal cherché le code, appliqué une correction fragile, rencontré un problème d’environnement ou dépassé une limite de permission.

L’observabilité doit porter sur les transitions, pas seulement sur les résultats. À chaque étape, l’orchestrateur doit enregistrer l’état de la tâche, l’hypothèse de travail, l’outil appelé, ses arguments normalisés, sa sortie résumée, sa durée, son coût éventuel et le verdict de contrôle.

Un événement utile ressemble à ceci :

{
  "sessionId": "run-4f32",
  "step": 6,
  "state": "verification",
  "hypothesis": "La validation convertit une erreur de saisie en erreur serveur.",
  "action": {
    "tool": "run_test",
    "target": "src/booking/booking.controller.spec.ts",
    "timeoutSeconds": 90
  },
  "result": {
    "exitCode": 0,
    "durationMs": 1240,
    "summary": "Le cas de montant invalide retourne 400."
  },
  "evidence": {
    "changedFiles": [
      "src/booking/booking.controller.ts",
      "src/booking/booking.controller.spec.ts"
    ],
    "policyVersion": "2026-07"
  }
}

Le journal ne doit pas enregistrer aveuglément tout le contenu des prompts, des fichiers ou des sorties. Il doit être conçu pour l’investigation sans devenir un canal de fuite de secrets, de données clients ou de code inutilement sensible. Des identifiants de corrélation, des résumés, des empreintes de diff et des références à des artefacts protégés suffisent souvent.

Les métriques les plus utiles ne sont pas celles qui glorifient le volume d’activité. Il faut suivre la capacité de l’agent à conclure correctement :

IndicateurCe qu’il mesureSignal d’alerte
Taux de réussite vérifiéeTâches terminées avec les contrôles requis au vertHausse des succès sans hausse de couverture ou de preuves
Nombre médian d’itérationsEfficacité de la boucle localeBoucles longues sur les mêmes erreurs
Taux d’escaladePart des tâches nécessitant un arbitrage humainEscalades tardives ou trop fréquentes
Ratio lecture / écriturePrécision de l’explorationLecture massive pour changements mineurs
Taux de rejet de politiquePertinence des capacités demandéesTentatives répétées de sortie de périmètre
Diff moyen par tâcheAmpleur du changement réellement nécessaireCorrectifs disproportionnés au symptôme
Coût par résolution vérifiéeEfficience globaleCoût en hausse sans gain de fiabilité

Ces mesures servent à améliorer le système, non à forcer l’agent à aller vite. Une baisse artificielle du nombre d’itérations peut simplement encourager l’abandon prématuré ou les corrections risquées.

Orchestrer plusieurs agents sans multiplier les angles morts

La division du travail peut améliorer la qualité, mais seulement si elle correspond à des frontières réelles. Répartir une tâche complexe entre plusieurs agents sans contrat d’intégration produit souvent le même désordre qu’une équipe humaine sans responsable de livraison : des conclusions contradictoires, des modifications concurrentes et une absence de responsabilité claire.

Le découpage le plus robuste sépare les fonctions plutôt que de dupliquer les tentatives :

  • un agent d’exploration établit la carte des composants concernés ;
  • un agent de correction propose un diff minimal ;
  • un agent de test conçoit ou contrôle la reproduction ;
  • un agent de revue vérifie la conformité du changement au contrat de projet ;
  • un orchestrateur conserve le contexte de décision et tranche les incompatibilités.

L’indépendance doit être réelle. L’agent de test ne reçoit pas seulement l’explication de l’agent de correction : il reçoit le symptôme, le contrat métier et le diff proposé. Sinon, il risque de confirmer le raisonnement qu’il devrait challenger.

Cette organisation est particulièrement utile pour les changements à risque modéré : évolution d’un calcul tarifaire, correction d’une règle d’éligibilité, modification d’un traitement asynchrone. Elle est moins pertinente pour une correction triviale. Le coût de coordination doit rester inférieur au risque évité.

Un bon orchestrateur ne demande pas à chaque sous-agent de « résoudre le problème ». Il définit un livrable court et vérifiable : liste des fichiers candidats, scénario de reproduction, hypothèse réfutable, résultat de commande ou commentaire de revue. La sortie d’un sous-agent devient alors une entrée structurée pour le suivant.

Décider du rejet et de la mise en production

Le dernier danger consiste à transformer la réussite des tests locaux en autorisation implicite de mise en production. Les tests sont nécessaires, mais ils ne couvrent ni toutes les données réelles, ni tous les effets de concurrence, ni toutes les dépendances externes.

Une décision de production doit donc reposer sur des critères de rejet explicites. L’agent doit être arrêté ou renvoyé en revue lorsque l’un des cas suivants apparaît :

  • le bug n’est pas reproduit avant correction et aucun scénario équivalent ne peut être justifié ;
  • le patch modifie une zone hors périmètre sans lien démontré avec le symptôme ;
  • une commande sort de la politique de capacité ou exige un accès non prévu ;
  • les contrôles obligatoires ne sont pas exécutés, échouent ou sont contournés ;
  • le changement modifie une règle métier sans validation du responsable concerné ;
  • le diff contient une migration, une suppression ou une modification de permission non approuvée ;
  • l’agent ne peut pas expliquer le risque résiduel en termes concrets.

La mise en production doit être graduelle. Pour une correction de comportement applicatif, le paquet de livraison peut inclure le diff, le test de régression, les commandes exécutées, les résultats, les zones non couvertes et un plan de retour arrière. Selon le niveau de risque, l’équipe peut ajouter un déploiement progressif, une métrique métier de surveillance et un seuil d’arrêt.

Le verdict final doit rester sobre et opérationnel :

Verdict : prêt à revoir

Symptôme traité :
- Une valeur numérique mal formée provoquait une réponse 500.

Preuves :
- Test de régression ajouté et initialement en échec.
- Test ciblé, vérification de types et lint au vert.
- Réponse attendue : 400 avec erreur de validation structurée.

Limites :
- Aucun test de charge exécuté.
- Le comportement des clients historiques doit être surveillé après déploiement.

Retour arrière :
- Revenir au commit précédent ; aucun changement de données ni de schéma.

Un agent devient donc réellement utile non lorsqu’il paraît autonome, mais lorsqu’il rend son autonomie gouvernable. Le modèle propose, le runtime contredit ou confirme, la politique limite les effets et l’humain conserve les décisions qui engagent le produit. C’est cette boucle — observable, bornée et fondée sur des preuves — qui transforme une génération de code en capacité d’ingénierie.