Il n’est pas fiable jusqu’à sa validation.
La couche de validation est l’endroit où l’application transforme la sortie du modèle probabiliste en quelque chose de suffisamment déterministe pour le rendu. Qu’une équipe utilise JSON Schema, Zod, Valibot ou une autre bibliothèque de validation, l’application doit recevoir la réponse du modèle sous forme de données inconnues et la valider avant que quoi que ce soit n’atteigne l’écran. Cette étape de validation doit rejeter les types de composants inconnus, les accessoires mal formés, les actions non prises en charge et toute structure que l’application ne comprend pas explicitement. Ce n’est qu’une fois que la réponse a dépassé cette limite qu’elle doit être restituée via le registre des composants.
Avec une bibliothèque de schémas, la limite de validation pourrait ressembler à ceci :
const CostSummarySchema = z.object({
type: z.literal('cost-summary'),
props: z.object({
period: z.enum(('current-week', 'current-month')),
comparisonPeriod: z.enum(('previous-week', 'previous-month'))
})
});
const AnomalyListSchema = z.object({
type: z.literal('anomaly-list'),
props: z.object({
severity: z.enum(('medium', 'high'))
})
});
const UIBlockSchema = z.discriminatedUnion('type', (
CostSummarySchema,
AnomalyListSchema
));
function parseUIResponse(response: unknown): UIBlock() {
const result = z.array(UIBlockSchema).safeParse(response);
if (!result.success) {
return ();
}
return result.data;
}
Dans une application réelle, le schéma couvrirait probablement les règles de présentation, les limites des composants, l’imbrication autorisée, les références d’action et la gestion des versions. Le point n’est pas la bibliothèque spécifique. Le point est la frontière.
Le modèle ne décide pas si sa sortie est sûre. L’application le fait.
Une voie de repli est également essentielle. Si la validation échoue, l’application ne doit pas tenter d’improviser. Il doit afficher une solution de secours sûre, demander à l’utilisateur de reformuler ou renvoyer une réponse textuelle conventionnelle. Les interfaces basées sur l’IA nécessitent un échec progressif. Une description d’interface utilisateur mal formée ne doit jamais devenir un écran cassé ou dangereux.
Séparer le rendu des actions
La limite la plus importante de l’interface utilisateur générative n’est pas le rendu. C’est l’exécution.
Une interface dynamique peut inclure des boutons, des formulaires, des confirmations ou des étapes de flux de travail. Ces contrôles peuvent nécessiter des opérations réelles : arrêter une instance, redimensionner une base de données, ouvrir un ticket de support, approuver un déploiement, mettre à jour une politique ou modifier les paramètres d’un compte.
Le modèle ne doit pas exécuter ces actions. Il ne doit pas décider qu’une opération est autorisée simplement parce que l’utilisateur l’a demandé. Au lieu de cela, l’exécution des actions doit passer par un registre d’actions appartenant à l’application.
Par exemple, un modèle peut demander un composant de confirmation :
{
"type": "confirmation",
"props": {
"message": "Do you want to open a remediation task for the unused compute instances?",
"actionId": "create-remediation-task"
}
}
Mais l’action elle-même doit être définie et exécutée par l’application :
type UIAction =
| {
type: 'create-remediation-task';
resourceIds: string();
}
| {
type: 'open-support-ticket';
category: 'billing' | 'performance' | 'security';
};
const actionRegistry = {
'create-remediation-task': createRemediationTask,
'open-support-ticket': openSupportTicket
};
async function executeAction(action: UIAction, user: CurrentUser) {
if (!isActionAllowed(action, user)) {
throw new Error('Action not allowed');
}
return actionRegistry(action.type)(action);
}
Avant l’exécution d’une opération, l’application doit prendre des décisions déterministes que le modèle ne doit pas contrôler. L’action doit exister dans le registre de l’application, l’utilisateur actuel doit être autorisé à l’exécuter, les ressources cibles doivent appartenir à un contexte auquel l’utilisateur peut accéder et l’opération doit toujours être valide dans l’état actuel. Certaines actions peuvent nécessiter une confirmation, un audit, un routage d’approbation ou une vérification finale des autorisations côté serveur avant que quoi que ce soit ne change.
Ces questions ne peuvent pas être déléguées au modèle. Ils appartiennent à l’application et, en fin de compte, aux systèmes back-end qui appliquent les règles métier.
Le modèle peut aider à générer le chemin. Il ne peut pas devenir l’autorité.
L’État appartient toujours à la demande
L’interface utilisateur générative crée également un problème subtil de gestion de l’état.
Dans une application traditionnelle, le front-end sait où réside l’État. Les données de facturation, les autorisations des utilisateurs, les métadonnées des ressources, l’état des anomalies, les tâches de correction et la progression du flux de travail sont chargés, mis en cache, invalidés et mis à jour via des chemins d’application connus.
Une interface basée sur l’IA peut brouiller cette frontière. Le modèle peut résumer l’état, déduire l’état, mémoriser le contexte de la conversation ou décrire un écran basé sur des messages précédents. Si les équipes n’y prennent pas garde, l’interface générée devient un deuxième système à états cachés.
C’est dangereux.
L’interface utilisateur peut indiquer qu’une instance de calcul est inutilisée même si son statut a changé. Il peut afficher une option de correction basée sur des données de facturation obsolètes. Cela peut produire un message de confirmation qui ne correspond plus au flux de travail actuel. Il se peut qu’il se souvienne de quelque chose de la conversation que l’application elle-même n’a pas vérifié.
La demande doit toujours rester l’autorité de l’État.
Le modèle peut aider à décider quels composants afficher, mais ces composants doivent lire l’état réel de l’application et de ses API. UN CostSummary Le composant doit récupérer ou recevoir des données de facturation via le même chemin approuvé que toute autre partie du produit. Une action corrective doit mettre à jour l’état via le flux d’application normal. Un élément de confirmation ne doit pas devenir la source de vérité quant à la possibilité d’une opération.
L’interface utilisateur générative doit être une projection de l’état de l’application, et non de son propriétaire.
Cette distinction devient encore plus importante dans les applications agentiques, où les interfaces peuvent changer au cours de plusieurs tours de conversation. Un utilisateur peut poser une question, inspecter un résultat, demander une action, changer d’avis et revenir plus tard. L’application a besoin d’un modèle fiable pour ce qui s’est passé, ce qui est en attente, ce qui a échoué et ce qui nécessite encore une approbation humaine.
Cela ne peut pas vivre uniquement dans la fenêtre contextuelle du modèle.
Conception pour une composition contrôlée
L’avenir de l’interface utilisateur générative n’est pas la génération arbitraire de code d’exécution. C’est une composition contrôlée.
Le modèle doit être capable d’assembler des expériences à partir de fonctionnalités fiables : composants, mises en page, actions, règles de validation et transitions d’état que l’application expose intentionnellement.
Cela offre aux développeurs le meilleur des deux mondes.
L’interface peut s’adapter au but de l’utilisateur, mais le système reste testable. Le modèle peut choisir les bons blocs d’interface utilisateur, mais le système de conception reste intact. L’utilisateur peut évoluer dans des flux de travail dynamiques, mais les autorisations et les règles métier restent déterministes. L’application peut sembler intelligente sans devenir imprévisible.
C’est également un meilleur modèle mental pour les équipes front-end. L’interface utilisateur générative ne remplace pas l’architecture frontale. Cela augmente le besoin d’une architecture frontale.
Les équipes ont toujours besoin de systèmes de composants. Ils ont encore besoin d’une validation d’exécution. Ils ont encore besoin d’être propriété de l’État. Ils ont encore besoin de normes d’accessibilité. Ils ont encore besoin de limites d’action. Ils ont toujours besoin d’une autorisation côté serveur. L’IA ne supprime pas ces préoccupations. Cela rend les limites faibles plus faciles à exposer.
Dans ce modèle, l’utilisateur exprime son intention et le modèle répond avec une intention d’interface utilisateur structurée. L’application valide cette réponse, la restitue via un registre de composants et achemine tout comportement demandé via un registre d’actions. L’état de l’application reste la source de vérité, tandis que le serveur reste responsable de l’autorisation finale.
C’est la frontière dont les systèmes de production ont besoin.
L’IA peut aider les développeurs à générer des fonctionnalités complètes pendant le développement. Ce code peut et doit être révisé, testé et livré normalement. Mais lorsque l’IA participe à une application en cours d’exécution, le contrat d’exécution doit être beaucoup plus restreint. Le modèle dynamique doit décrire ce que l’interface utilisateur doit exprimer, et non générer du code non contrôlé que le produit exécute.
La meilleure approche consiste à doter l’IA d’un système de composants.
Laissez le modèle composer. Laissez l’application contrôler. Laissez l’expérience utilisateur devenir plus dynamique sans sacrifier l’architecture qui garantit la fiabilité des logiciels.
