Systèmes de design pour usage agentique
Cette compétence éclaire les décisions de conception ; elle ne les impose pas. Chaque recommandation peut céder face à un objectif légitime du projet (expressivité experte, besoins de marque, limites de plateforme, conventions existantes). Nommez le compromis et reportez-vous à un choix délibéré contraire. Les exemples sont en React et TypeScript parce que c'est là que les priors des modèles sont les plus nets ; le raisonnement s'applique à toute pile technique.
L'objectif
Un modèle générateur de code est un échantillonneur. Il émet le programme qu'il juge le plus plausible compte tenu de ce qu'il a appris lors de l'entraînement (le prior) et de ce qui se trouve dans la session (le contexte), puis conserve ce qui survit aux vérifications qu'il peut exécuter. Une API pensée pour l'humain demande : « la bonne chose peut-elle être exprimée ? » Une API pensée pour l'agent demande : « parmi tout ce qui semble correct, combien est réellement correct ? » L'objectif a donc deux faces : faire en sorte que le programme le plus probable soit le bon, et faire échouer les programmes improbables avant qu'ils ne soient livrés.
C'est une conception d'API ordinaire (le « facile à utiliser correctement, difficile à utiliser incorrectement » de Bloch, le « rendre les états illégaux non représentables » de Minsky, le pit of success) sous deux hypothèses plus strictes : la familiarité du consommateur est une distribution mesurable sur le code public, et le consommateur ne vérifie que ce qu'il peut exécuter.
Trois propriétés du consommateur
Tout ce qui suit en découle. Lisez le Registre des preuves lorsqu'une affirmation ci-dessous importe pour une décision ou que vous devez mesurer localement ; il contient les sources, les limites de portée et le protocole de mesure.
-
Le prior est façonné par la fréquence, mélangé par version et non modifiable. Un modèle connaît un motif proportionnellement à sa fréquence d'apparition dans l'entraînement ; la précision augmente de façon log-linéaire avec la fréquence dans le corpus. Chaque version d'une bibliothèque qui a jamais existé est superposée en une seule distribution, ce qui explique pourquoi les modèles émettent des API dépréciées 25 à 38 % du temps et obtiennent environ 50 % sur des tâches verrouillées par version. Rien de ce qu'une bibliothèque publie ne peut modifier cela. Cela ne peut qu'être apparié ou remplacé.
-
Le contexte remplace le prior, mais seulement le contexte réellement présent. Les invites tirées de code obsolète ont produit 70 à 90 % d'utilisation dépréciée ; les invites tirées de code actuel ont produit 9 à 18 %. Le code frère dans le fichier oriente la sortie plus que tout ce qu'une bibliothèque publie. La documentation récupérée aide ; une simple étiquette de version aide un peu. La documentation publiée que l'agent ne charge jamais ne fait rien.
-
Seul le retour exécutable est une vérification. Le vérificateur de types est déterministe, peu coûteux et toujours dans la boucle ; 94 % des erreurs de compilation dans le TypeScript généré par LLM sont des erreurs de type, donc il en attrape beaucoup. Le rendu est coûteux, nécessite une configuration, et le jugement vision-langage de la mise en page est faible même avec une capture d'écran en main. Ce que les types ne codent pas est livré si le modèle se trompe.
Deux conséquences. Le coût de l'erreur est asymétrique : un humain voit un mauvais espace et le corrige en quelques secondes, le mauvais espace d'un agent est livré, donc « facile à corriger » vaut bien moins que « difficile à obtenir faux ». Et l'évolution doit être additive : un renommage ou un changement de sens coûte aux agents jusqu'au prochain entraînement et l'ancienne forme ne quitte jamais complètement la distribution, donc quand vous devez casser, mettez la migration dans les types et la documentation colocalisée, pas seulement dans un changelog.
Trois leviers
Chaque levier de conception est l'un de ceux-ci. Lors de l'évaluation d'une conception, demandez quel levier elle actionne et lequel elle sape.
Levier 1 : faire du défaut la bonne devinette
La première devinette de l'agent est le prior plus le contexte visible. Deux choses la façonnent.
Les noms sont des paris sur les distributions. La valeur d'un nom est la netteté fois l'accord : à quel point son usage public est monosémique, fois si ce sens est le vôtre. La netteté suit la taille du corpus, l'âge et la stabilité, donc le vocabulaire des spécifications HTML et CSS bat les noms de motifs ARIA, qui battent les mots transversaux aux bibliothèques (sm/md/lg, primary/danger, solid/outline/ghost), qui battent les idiomes d'une seule bibliothèque ; ancrez aussi profondément que la sémantique le permet. L'accord est binaire, et un nom familier avec un comportement différent est le pire des cas : le modèle applique le sens appris avec confiance et le code compile. Lorsqu'aucun nom net n'est en accord avec votre sémantique, composez un nom nouveau à partir d'une grammaire apprise. Page/PageHeader/PageTitle n'existe dans aucune grande bibliothèque, pourtant la conjugaison X/XHeader/XTitle porte la hiérarchie et l'ordre des enfants gratuitement et reste découvrable dans l'autocomplétion et dans le propre plan de l'agent. Une prop dont le sens dépend d'une autre prop (align sous un commutateur direction) est deux paris couplés ; découplez-les en un composant par axe ou des noms explicites. Le gain est un degré de liberté en moins, pas un prior sauvé ; les modèles gèrent bien le changement d'axe du CSS lui-même.
La base de code visible est l'invite. Les usages frères, le catalogue et l'exemple canonique sont ce sur quoi l'agent se conditionne réellement. La régularité compresse cette invite : si chaque composant conjugue les mêmes axes avec le même vocabulaire, deux composants en contexte enseignent tout le catalogue, et chaque synonyme ou exception par composant est un fait séparé qui doit être enseigné et ne l'est généralement pas. Le catalogue partitionne la sortie : les agents planifient en unités comme « en-tête de page » et « barre d'outils » avant de lire le code et forcent le plan à travers ce qui existe, alors nommez les composants d'après ces unités. Ce que les exemples montrent devient le workflow, y compris les surcharges className ; montrez le chemin prévu et gardez les exemples appariés par version et colocalisés avec le code que l'agent doit toucher.
La force du prior justifie la conformité dans la syntaxe que l'agent doit écrire. Elle ne justifie jamais d'exposer une décision que l'agent ne devrait pas prendre ; c'est le Levier 3.
Levier 2 : faire échouer la sortie erronée avant livraison
Tout ce qu'un rendu ou un runtime aurait attrapé doit échouer plus tôt, sinon c'est livré.
Là où les types peuvent l'exprimer, mettez-le dans le type. Unions fermées au lieu de combinaisons booléennes que personne n'a conçues. Unions discriminées pour les props dépendantes du mode. Omit ce qu'une partie ne supporte pas au lieu de documenter « n'utilisez pas ceci ici ». Props obligatoires pour les couches de comportement que le consommateur doit fournir (titres de dialogue, étiquettes de boutons d'icônes, texte d'erreur ; Radix et React Aria n'avertissent sur celles-ci que dans une console que l'agent ne voit jamais). Erreurs de compilation pour les cas non supportés de asChild/as, ou des variantes explicitement non polymorphes comme ButtonLink. Le test est le même à chaque fois : un agent remplissant la prop voit le vocabulaire légal, et le programme illégal ne compile pas.
Là où les types ne peuvent pas l'exprimer, rendez la divergence visible là où le code est écrit. Les noms portent des attentes de mise en page qu'aucun type ne code (un conteneur possède son padding, un bloc remplit son parent, l'espacement vient des gaps du parent ; l'étude de cas les liste). Un composant qui brise l'attente que son nom crée compile et rend silencieusement faux dans la seule dimension que l'agent ne peut pas voir. Signalez la rupture au site d'appel (une prop obligatoire, un nom non conflictuel) ou dans le contexte que l'agent charge de manière fiable (une règle à côté du code). La documentation que l'agent ne rencontrera pas ne compte pas.
Levier 3 : supprimer la décision
Une décision dont la correction n'est visible qu'au rendu appartient à la bibliothèque, pas au site d'appel. Un principe à quatre granularités :
- Valeurs. Quantifiez chaque magnitude et résolvez l'apparence via des rôles sémantiques :
gap="3"ettone="danger"au site d'appel, des px et hex dans le thème. Les modèles connaissent les mécanismes de flex et grid ; ils ne peuvent pas choisir 12 contre 16 à l'aveugle, et les choix indépendants dérivent entre fichiers en un rythme que personne ne voit. Les mots-clés de spécification (justify="space-between") maintiennent la sémantique libre tandis que l'échelle supprime la devinette. - Composition. Possédez la mise en page, pas seulement les widgets. La règle de Braid est la version propre : les composants ne publient pas d'espacement externe, et les composants de mise en page possèdent tout l'espacement. Un système qui s'arrête à la frontière des widgets fait improviser chaque site d'appel.
- Motifs. Là où une composition se répète (
FormField,EmptyState,PageHeader,ConfirmDialog), publiez-la. Chaque re-dérivation à partir de primitives est une chance de diverger. - Adaptation. Gérez la fenêtre d'affichage, la longueur du contenu, la direction d'écriture, les préférences de mouvement et de contraste, et les états de chargement/vide/erreur par construction, ou exigez-les dans le type. La logique média au site d'appel est une décision aveugle.
Les trappes de sortie (className, style, variables CSS) devraient exister et se situer en dessous du chemin par défaut en probabilité : disponibles, classées, jamais mises en avant. Si elles sont le workflow démontré, le Levier 1 en a fait la devinette par défaut.
Le régime décide lisibilité contre contrat
Deux philosophies cohérentes existent ; shadcn est l'énoncé le plus clair de la première.
- Lisibilité. Publiez le code source dans le dépôt. L'agent le lit et le modifie, et le style est visible là où il est écrit. Correct lorsque l'agent possède les composants : il ouvrira le fichier, et une implémentation lisible (style colocalisé, indirection minimale) est ce dont il a besoin.
- Contrat. Exposez une API typée et cachez l'implémentation. Correct lorsque l'agent consomme les composants depuis le code applicatif : il voit les imports, les types et quelques usages frères, et ouvre rarement l'implémentation. Un style lisible est un style modifiable, et le style au site d'appel est l'entropie que le Levier 3 supprime.
La plupart des systèmes ont besoin des deux, répartis par public : lisibilité pour quiconque maintient components/, contrat pour quiconque importe depuis celui-ci. Décidez dans quel régime se trouve un fichier avant d'appliquer les leviers. Lisez l'étude de cas shadcn lorsque le système est construit sur shadcn ou comparé à lui ; il détaille quoi garder, quoi changer et pourquoi.
Tensions
Deux, chacune avec plusieurs visages. Nommez le compromis ; ne prétendez pas qu'il est gratuit.
- Probabilité concentrée contre expressivité. Chaque degré de liberté supprimé est une capacité perdue pour quelqu'un. Cela se manifeste par composants d'intention contre primitives, axes fermés contre props orthogonales, et étirement d'un axe contre sa division (étirez lorsque chaque valeur répond à la même question ; la variante
Buttonde shadcn, fusionnant l'intention, le traitement et l'identité de l'élément, est l'axe fusionné canonique). La trappe de sortie classée est la réponse habituelle. - Alignement sur le prior contre propriété. Correspondre à la convention achète une correction gratuite et importe les défauts et le tumulte de la convention. Cela se manifeste par noms familiers contre distincts (distincts coûtent en découvrabilité ; les noms nouveaux conformes à la grammaire récupèrent la majeure partie) et par plus amical pour le prior contre mieux conçu. Diverge coûte un alignement une fois ; un défaut conservé coûte une correction pour toujours.
Mesurez au lieu de faire confiance
Les trois propriétés sont mesurées au niveau du modèle. Les leviers sont des mécanismes cohérents avec ces mesures, non mesurés sur une bibliothèque d'interface utilisateur ; le fichier de preuves indique ce qui est quoi. Avant d'adopter une règle, exécutez-la : 20 à 40 tâches UI réalistes, échantillonnées plusieurs fois par régime (pas de contexte, types plus deux usages frères, code source complet) ; scorez la compilation du premier essai, l'utilisation abusive attrapée par les types contre silencieuse, la variance de tokens entre sites d'appel frères, le lint d'accessibilité, et un court passage visuel ; changez une décision de conception ; réexécutez. Gardez ce qui fait bouger les chiffres.
Liste de vérification pour la revue
- Levier 1 : un modèle familier avec des bibliothèques comparables devinerait-il cela du premier coup, et si le nom est emprunté, se comporte-t-il de manière identique ? L'exemple canonique montre-t-il le chemin prévu ?
- Levier 2 : toute utilisation abusive réaliste échoue-t-elle à compiler ? Lorsque l'attente visuelle d'un nom est brisée, la rupture est-elle visible là où le code est écrit ?
- Levier 3 : une valeur, composition, motif ou adaptation est-il laissé au site d'appel pour décider à l'aveugle ?
- Régime : ce fichier est-il possédé ou consommé par l'agent, et le choix lisibilité/contrat est-il délibéré ?
- Évolution : ce changement casserait-il du code écrit à partir du prior de l'année dernière, et la migration est-elle visible dans les types et la documentation colocalisée ?