Depuis longtemps, je construis un système d'IA complexe.
Il a commencé comme un produit assez spécifique. Avec le temps, il en est venu à inclure l'exécution, des outils, la mémoire, les permissions, des tâches de longue durée, des capacités externes et de multiples formes de produit. À mesure qu'il grandissait, je passais moins de temps à me demander comment ajouter une fonctionnalité de plus et davantage à me poser une autre classe de questions : où une capacité devrait-elle résider ? Qui détient l'autorité finale pour l'interpréter ? Quelles décisions faut-il laisser au modèle, et quelles garanties doivent provenir du logiciel ? Le mécanisme qui nous aide à livrer aujourd'hui deviendra-t-il demain une frontière dont nous ne pourrons plus sortir ?
Ces questions ressemblent à des questions d'architecture. En pratique, elles ont changé ma façon de penser le logiciel lui-même.
Le logiciel traditionnel est construit autour d'instructions. Un programmeur définit le chemin à l'avance, et la machine le suit précisément : si A se produit, faire B ; si l'entrée est C, renvoyer D. Une grande partie de la valeur du logiciel est venue de la transformation du jugement humain en étapes stables et reproductibles.
L'IA change cet arrangement. Pour la première fois, les machines peuvent décider quoi faire ensuite dans un éventail significatif de situations. Nous n'avons plus besoin d'énumérer chaque chemin. Nous pouvons décrire un objectif, fournir un environnement et un retour d'information, et laisser un modèle former un plan, choisir des outils, observer le résultat et s'ajuster.
Cela peut donner l'impression qu'à mesure que les modèles deviennent plus capables, le logiciel devient moins important.
Mon expérience m'a conduit à la conclusion inverse. Le logiciel ne disparaît pas. L'objet de sa responsabilité change.
Auparavant, le logiciel disait à la machine quoi faire ensuite. Désormais, un modèle peut choisir l'étape suivante, mais le logiciel doit toujours répondre à un autre ensemble de questions : qu'est-ce qui est vrai ? Qu'est-ce qui est autorisé ? Qu'est-ce qui compte comme achevé ? Qui est responsable quand quelque chose tourne mal ? Comment une action intelligente devient-elle un résultat auquel les gens peuvent se fier ?
J'en suis venu à croire ceci :
Les modèles génèrent des possibilités. Le logiciel transforme la possibilité en ordre.
Le logiciel n'est plus simplement l'ensemble d'instructions autour de l'IA. Il devient la couche institutionnelle du monde qu'habite l'IA.
Les principes ci-dessous ont émergé progressivement à mesure que je construisais, écartais, reconstruisais et vérifiais différentes parties de ce système. Ce n'est pas un guide d'architecture pour un produit particulier, ni une méthodologie universelle. Ce sont simplement les idées que je considère désormais dignes d'être emportées dans tout ce que je construirai ensuite.
1. Définissez ce que vous construisez avant que l'interface d'aujourd'hui ne définisse la destination
La dérive la plus dangereuse dans un projet de longue haleine n'est pas de se tromper sur une fonctionnalité. C'est d'oublier peu à peu ce que l'on s'était fixé de construire.
Les produits commencent toujours par un point d'entrée concret. Cela peut être une interface de chat, un utilitaire de bureau, un éditeur, un navigateur ou un établi. Parce que ces choses sont visibles et utilisables, elles en viennent facilement à définir l'ensemble du projet à rebours : le projet devient ce que les écrans existants sont aujourd'hui, et son avenir devient l'expansion de la fonctionnalité actuellement la plus mature.
Mais le produit actuel n'est souvent qu'un véhicule pour valider la destination, pas la destination elle-même.
J'ai appris à séparer délibérément trois choses : ce que je veux finalement construire, la fondation commune nécessaire pour soutenir cet objectif, et ce que les utilisateurs peuvent réellement utiliser aujourd'hui.
Les trois doivent être liés, mais ils ne doivent jamais se faire passer les uns pour les autres.
Si vous ne parlez que de la destination, le projet devient une vision sans livrable. Si vous ne parlez que du présent, le projet se retrouve piégé par son interface actuelle et son implémentation temporaire. S'il n'y a pas de fondation commune, chaque nouvelle forme de produit doit réinventer l'exécution, l'état, les permissions et la mémoire.
Cette distinction a changé beaucoup de mes décisions. J'ai cessé de décider où une fonctionnalité devait résider en me demandant uniquement où elle pouvait être implémentée le plus vite. J'ai commencé à me demander s'il s'agissait d'une expérience propre au produit actuel ou d'une capacité système dont les produits futurs auraient aussi besoin. Une implémentation temporaire peut être imparfaite, mais on ne doit pas la laisser redéfinir l'objectif à long terme.
La première chose dont un système durable a besoin n'est pas une feuille de route. C'est un sens des couches : quelle est la direction, quelle est la fondation, et qu'est-ce qui n'est que la manière dont le système atteint les utilisateurs aujourd'hui.
2. Demandez « À qui cela appartient-il ? » avant « Comment le construisons-nous ? »
Une grande partie de la complexité logicielle n'est pas causée par un excès de code. Elle est causée par une responsabilité qui n'a pas de foyer.
Quand un concept n'a pas de propriétaire clair, il commence naturellement à se répliquer. L'interface conserve une version de l'état, le backend en conserve une autre, et un fichier de configuration en conserve une troisième. Aucune n'est manifestement fausse, mais aucune ne peut donner la réponse finale lorsqu'elles sont en désaccord.
Le système commence alors à développer une logique de synchronisation. Un changement à un endroit doit notifier les deux autres. Une notification est parfois perdue, alors on ajoute une récupération. L'ordre de récupération s'avère instable, alors on ajoute une compensation. La compensation crée un autre état, alors on ajoute une réconciliation. Le code travaille de plus en plus dur tandis que le système devient plus difficile à expliquer.
J'étais autrefois immédiatement attiré par la question de savoir comment tout connecter. Maintenant, je pose d'abord d'autres questions :
- À qui ce concept appartient-il vraiment ?
- Qui le crée, l'interprète et y met fin ?
- S'il est désactivé, migré ou supprimé, quelle partie du système comprend le sens complet de ce changement ?
- Les autres composants reçoivent-ils la vérité, ou simplement une vue projetée à partir de celle-ci ?
Ce n'est qu'une fois ces questions résolues que la stratégie de connexion importe.
« Propriété » ressemble à un terme d'organisation du code, mais je pense qu'il exprime un principe bien plus large : tout ce qui persiste a besoin d'une partie responsable de l'ensemble de son cycle de vie. Sinon, tout le monde peut le modifier, mais personne ne le comprend vraiment.
Cela m'a aussi donné une forte préférence pour un interpréteur final unique de chaque fait. Les autres composants peuvent observer, mettre en cache, présenter et utiliser ce fait, mais ils ne devraient pas inventer des versions tout aussi faisant autorité.
Les systèmes complexes ne craignent pas d'avoir beaucoup d'état. Ils craignent d'avoir trop d'autorités.
3. Consolidez les mécanismes, pas les expériences utilisateur
À mesure qu'un système grandit, créer un nouveau mécanisme pour chaque nouvelle exigence est à la fois le choix le plus naturel et le plus dangereux.
Une capacité est légèrement différente, alors elle obtient un nouveau chemin d'enregistrement. Un cas d'usage est quelque peu inhabituel, alors il obtient un nouveau mode. Un élément d'état n'a pas encore de foyer évident, alors il obtient un stockage séparé. Un nouveau chemin d'exécution est peu pratique à intégrer, alors il contourne le chemin existant et en crée un autre.
Chaque décision semble raisonnable isolément. Ensemble, elles produisent des systèmes parallèles pour la découverte, les permissions, le chargement, les mises à jour, la désactivation et la récupération. Le coût réel n'est pas quelques classes supplémentaires. C'est que chaque capacité future doit répondre à nouveau aux mêmes questions à travers tous ces mécanismes parallèles.
J'ai peu à peu appris à distinguer deux types de diversité : la diversité que vivent les utilisateurs et la diversité que le système doit maintenir en interne.
Les utilisateurs peuvent et doivent rencontrer des produits, des points d'entrée et des interactions très différents. Des contextes différents méritent leur propre langage et leurs propres modèles mentaux. Mais sous ces expériences, les capacités peuvent entrer dans le système, recevoir des permissions et acquérir des cycles de vie par un petit nombre de mécanismes stables.
Consolidez le mécanisme, pas l'expérience.
La partie difficile de ce principe est de résister à la tentation de créer quelque chose de nouveau parce que c'est plus facile à câbler. Avant d'introduire un autre mécanisme, je me demande maintenant si le mécanisme actuel ne peut vraiment pas exprimer le besoin, ou si un nouveau est simplement plus pratique à court terme.
« Cela semble un peu différent » n'est généralement pas une raison suffisante pour créer un nouvel axe dans le système.
Les mécanismes stables ne sont pas là pour rendre les schémas d'architecture plus propres. Ils réduisent le coût cognitif futur : les modèles, les développeurs et les produits n'ont plus à deviner entre plusieurs façons presque équivalentes de faire la même chose.
4. Le logiciel doit contraindre les conséquences, pas prescrire le processus de pensée du modèle
C'est ici que ma réflexion a le plus changé.
Lorsque le comportement du modèle est incohérent, l'impulsion naturelle est d'encoder le flux de travail idéal dans le logiciel. Exiger une recherche avant la planification, une planification avant l'exécution, et une étape terminée avant que la suivante puisse commencer. Exécuter une vérification après chaque changement. Ajouter une autre barrière d'exécution chaque fois que le modèle semble susceptible de prendre le mauvais chemin.
Ces systèmes sont généralement conçus avec de bonnes intentions. Mais ils se transforment souvent en pipelines rigides. Un modèle qui pourrait adapter son approche à la taille et à la forme de la tâche est contraint d'accomplir chaque étape, même pour une simple requête. De multiples garde-fous peuvent également interagir et créer un blocage dont le modèle n'a aucun moyen de s'échapper.
J'ai commencé à utiliser un test plus simple pour décider ce qui relève du logiciel et ce qui doit rester au modèle : la règle contraint-elle uneméthode, ou uneconséquence ?
« Lire avant de modifier », « présenter une proposition avant l'implémentation » et « penser à vérifier à la fin » sont des méthodes. Elles ont leur place dans les prompts, les skills et le contexte, où le modèle peut les comprendre et les appliquer avec souplesse.
« Ne pas accéder aux données sans autorisation », « ne pas causer de perte irréversible sans confirmation » et « ne pas contourner une permission que l'utilisateur a explicitement désactivée » sont des limites sur les conséquences. Elles ne peuvent pas dépendre du fait que le modèle s'en souvienne. Le logiciel doit les faire respecter.
Les garde-fous mécaniques contre la répétition, l'inactivité et les boucles doivent eux aussi être utilisés avec parcimonie. Une première tentative doit être autorisée. Lorsque le système bloque une action, il doit en expliquer la raison. Et il doit offrir un chemin clair pour s'en remettre plutôt que d'emprisonner le modèle derrière le garde-fou.
Cela a changé ma façon de comprendre la répartition du travail entre les modèles et le logiciel :
Donnez les objectifs et les méthodes au modèle. Donnez les limites non négociables au logiciel.
Si le logiciel prescrit chaque étape du processus de raisonnement, nous n'avons fait que reconstruire un moteur de workflow avec de l'IA à l'intérieur. Si chaque limite ne vit que dans un prompt, nous avons remis une véritable responsabilité à la probabilité.
Un bon système évite les deux extrêmes.
5. Garder la réalité sans ambiguïté plutôt que de faire paraître le modèle certain
Le raisonnement d'un modèle peut rester ouvert. Son langage peut varier. Ses plans peuvent être révisés. Mais dès qu'il commence à agir sur le monde réel, certains faits ne peuvent pas être ambigus.
Une tâche a-t-elle réellement commencé ? Un changement a-t-il vraiment eu lieu ? Une permission est-elle activée ? Quelle session détient un résultat ? La réponse à ces questions ne devrait pas changer selon l'écran qui les observe.
Je considère de plus en plus le logiciel comme la couche de réalité d'un système d'IA. Un modèle peut proposer des interprétations du monde, mais le logiciel enregistre ce qui s'y est réellement produit.
Cela signifie qu'une interface peut exprimer un état mais ne peut pas devenir l'état lui-même. Les paramètres attachés à une requête peuvent exprimer une intention mais ne peuvent pas supplanter temporairement une autorité durable. Un cache peut améliorer les performances mais ne peut pas acquérir le même statut que sa source. Lorsque le système ne peut pas établir un fait, il doit s'arrêter honnêtement plutôt que de deviner la réponse la plus commode pour préserver une sensation de fluidité.
Le logiciel traditionnel a lui aussi besoin de cohérence, mais l'IA la rend encore plus importante. Les modèles sont exceptionnellement doués pour continuer à partir d'informations incomplètes et produire des explications qui sonnent plausibles. Lorsque les faits sous-jacents ont déjà divergé, l'intelligence au sommet peut faire paraître la contradiction plus cohérente et permettre au problème de rester caché plus longtemps.
Mon principe est donc simple : laissez le modèle être incertain, mais ne laissez pas les faits du système devenir ambigus.
Lorsque le système ne sait pas, il doit dire qu'il ne sait pas. Lorsque des données faisant autorité sont endommagées, il doit refuser d'agir. Lorsque deux sources sont en conflit, il doit exiger que le conflit soit résolu plutôt que de choisir silencieusement la plus facile.
L'intelligence peut travailler avec l'ambiguïté. Les institutions doivent préserver la réalité.
6. La réversibilité est un prérequis de l'autonomie
Nous définissons souvent l'autonomie de l'IA comme le fait de poser moins de questions à l'utilisateur. Mais ce qui détermine si un utilisateur est prêt à lâcher prise, ce n'est pas le nombre de boîtes de confirmation. C'est la possibilité pour le système de lui permettre de voir, d'arrêter, de corriger et de récupérer.
Si chaque étape exige une confirmation, l'IA devient un outil semi-automatique qui réclame des clics constants. Si rien n'exige de confirmation et qu'il n'existe ni trace ni chemin de retour, l'autonomie devient une perte de contrôle.
J'ai commencé à accorder plus d'attention à un autre ensemble de qualités : le processus est-il visible ? L'action peut-elle être interrompue ? Les changements peuvent-ils être examinés ? L'historique est-il traçable ? Après un échec, le système peut-il revenir à un état dont le sens est clair ?
La réversibilité n'est pas une couche d'assurance supplémentaire pour les cas où quelque chose tourne mal. C'est une condition préalable à l'octroi d'une autorité.
Lorsqu'une action est facile à annuler, le système peut laisser plus de marge d'essai au modèle. Lorsqu'une action est irréversible, le système doit resserrer les permissions et renvoyer la décision à l'utilisateur. Le degré d'autonomie ne doit pas être déterminé par notre optimisme à l'égard d'un modèle. Il doit être déterminé par la possibilité de récupérer après les conséquences.
Cela a aussi changé ma définition de « terminé ». L'achèvement n'est pas un statut vert dans l'interface. Cela signifie que le système a rendu clairs le résultat, le risque restant et le chemin de récupération. Lorsqu'une tâche de longue durée est interrompue, l'animation ne peut pas simplement disparaître. Lorsqu'un changement est rejeté, la moitié de l'état d'exécution ne peut pas rester en place. Lorsqu'une capacité est désactivée, la logique de récupération ne doit pas la réactiver discrètement au prochain démarrage de l'application.
Plus nous voulons que l'IA agisse de manière indépendante, plus nous devons concevoir soigneusement la façon dont elle s'arrête et revient.
7. Ne vous contentez pas de corriger des bugs. Faites en sorte que chaque échec laisse derrière lui un principe
Tout projet de longue durée rencontrera de nombreux échecs. Ce qui distingue les projets, ce n'est pas la survenue d'échecs, mais la forme sous laquelle ils subsistent ensuite.
La réponse la plus faible est de faire disparaître le symptôme immédiat. Une meilleure réponse est d'identifier et de corriger la cause racine. La réponse la plus précieuse va un cran plus loin : de quelle classe d'échec s'agissait-il ? Par quels autres chemins le même échec pourrait-il revenir ? Manque-t-il au système une règle, un propriétaire unique ou une structure capable d'empêcher le problème de repousser ?
J'ai pris l'habitude d'enregistrer plus que le symptôme et le correctif. J'enregistre aussi la cause racine, le propriétaire et la preuve qui empêcherait une régression. Un problème décrit uniquement par ses symptômes, sans frontière responsable ni preuve, n'est pas encore devenu un savoir.
Un correctif répare un échec. Un principe réduit toute une classe d'échecs.
Si un bug de synchronisation est réparé en ajoutant une notification de plus, il peut revenir plus tard sous un autre ordre d'événements. Si l'échec établit plutôt le principe que chaque élément d'état a un seul propriétaire, la conception future contiendra naturellement moins de chaînes de synchronisation. Si un ancien point d'entrée crée une ambiguïté de permission, un autre cas particulier ne fera qu'ajouter une branche supplémentaire. Établir qu'une intention a un point d'entrée canonique unique donne au système une règle de décision durable.
Je pense que cela compte encore plus maintenant que l'IA participe au développement logiciel. Le code est produit plus vite, et les correctifs locaux deviennent moins coûteux. Sans principes, un système peut accumuler des implémentations dupliquées plausibles à une vitesse sans précédent.
L'IA peut nous aider à écrire des correctifs plus vite. Les humains doivent encore décider ce que chaque échec doit enseigner au système.
La véritable accumulation de valeur dans un projet n'est ni son volume de code ni le nombre de tickets qu'il a fermés. C'est de savoir si le coût des échecs passés a été converti en principes qui empêchent de payer à nouveau le même coût.
8. Ne confondez pas l'apparence de l'achèvement avec une preuve
L'IA est très douée pour créer une impression de complétude. Elle produit des explications bien structurées, du code syntaxiquement valide et des résumés rassurants. Mais les parties les plus difficiles de l'ingénierie se trouvent souvent hors de cette surface.
Une interface n'affichant aucune erreur ne prouve pas que rien ne continue à s'exécuter en arrière-plan. Une capacité fonctionnant dans un environnement de développement ne prouve pas qu'elle existe dans l'artefact livré aux utilisateurs. Une validation sur une plateforme ne prouve pas un comportement identique sur une autre. Un code qui passe un vérificateur de types ne prouve pas qu'un ancien chemin faisant autorité a disparu du reste du système.
Avec le temps, j'ai posé moins souvent la question « Est-ce terminé ? » et plus souvent « Quelle preuve serait suffisante pour prouver que c'est terminé ? ».
Une preuve n'est pas un indicateur vert universel. Différentes couches répondent à différentes questions. Les vérifications de code établissent la structure locale. La validation à l'exécution établit le chemin d'exécution réel. L'inspection de l'artefact établit ce que les utilisateurs recevront réellement. Les tests de récupération établissent si le système préserve le sens dans des conditions non idéales.
À l'ère de l'IA, la validation ne sert pas seulement à empêcher les modèles d'écrire du code incorrect. Elle contre aussi un risque plus subtil : un résultat peut être si convaincant logiquement que tout le monde devient prêt à croire qu'il existe déjà dans la réalité.
L'une de mes règles les plus importantes est de ne pas inférer les faits sous-jacents à partir de l'état de surface, de ne pas inférer la justesse de tout le système à partir d'une réussite locale, et de ne pas présenter un avenir non vérifié comme une capacité livrée aujourd'hui.
Respecter la réalité signifie parfois admettre : « Ce n'est pas encore terminé. » Cette honnêteté n'affaiblit pas un produit. Elle donne au système un point fiable à partir duquel continuer à évoluer.
Le logiciel passe des instructions aux institutions
Avec le recul, ce que j'ai retenu de ce travail n'était pas une architecture particulière. C'était une vision différente du logiciel d'IA.
Auparavant, les logiciels s'efforçaient d'énumérer les chemins possibles. À l'avenir, une plus grande partie de ces chemins sera générée par des modèles au moment même. Le logiciel n'a plus besoin de prescrire chaque action à l'intelligence, mais il doit fournir un monde stable : des identités et une propriété claires, des frontières qui ne peuvent être contournées, un état qui préserve la réalité, des moyens de sortir et de récupérer, et des preuves qui établissent si un résultat est réel.
Cela s'apparente à la relation entre les institutions et les individus.
Les institutions ne pensent pas à la place de chaque personne, et elles ne devraient pas non plus transformer chaque action en procédure figée. Elles définissent une réalité partagée, des droits, des responsabilités et ce qui se passe lorsque quelque chose tourne mal. De bonnes institutions laissent de la place à l'action tout en empêchant que la liberté se fasse au détriment de l'ordre.
Le logiciel commence à jouer un rôle similaire à l'ère de l'IA.
Ce n’est pas le cerveau du modèle, et il ne doit pas rivaliser pour le poste du modèle. C’est la loi, le registre, la frontière et la mémoire du monde que le modèle habite. Il transforme une action astucieuse improvisée sur le moment en une capacité stable. Il transforme un succès accidentel en un produit reproductible. Il permet aux utilisateurs de confier à l’IA un travail plus important sans échanger une confiance aveugle contre de la commodité.
Je ne crois pas que l’IA rendra le logiciel moins important. Au contraire, plus les modèles deviennent capables, plus le logiciel doit répondre aux questions que les modèles ne devraient pas décider eux-mêmes.
Qu'est-ce qui est vrai ?
Qu'est-ce qui est autorisé ?
À qui cela appartient-il ?
Comment revenir après un échec ?
Quelles preuves démontrent l'achèvement ?
Les modèles explorent les réponses. Le logiciel protège les questions.
Nous passons d'une époque où les humains écrivaient chaque étape pour les machines à une époque où les humains établissent des principes et les machines explorent des chemins. Le rôle du logiciel évolue avec elle : de la description des actions à l'établissement de l'ordre.
Les modèles génèrent des possibilités.
Le logiciel rend ces possibilités dignes de confiance.