L'interface utilisateur était autrefois une chose figée. Les designers la dessinaient. Les ingénieurs la construisaient. Les utilisateurs se contentaient de ce qui était livré.
C'est fini.
Les interfaces qui seront livrées en 2026 sont en partie dessinées par l'agent lui-même, en temps réel, à partir de ce que l'utilisateur a réellement demandé. Demandez un tableau, obtenez un tableau. Pas un paragraphe qui en décrit un.
La UI générative est la couche qui permet aux agents d'arrêter de décrire et de commencer à montrer. Trois modèles ont émergé pour la construire, et les différences entre eux sont plus importantes que la plupart des équipes ne le pensent.
Mais il n'y a pas qu'une seule façon de construire cela. Il y en a trois. Et la plupart des équipes en choisissent une sans savoir qu'elles ont fait un choix.
La pile de protocoles
Trois protocoles. Chacun fait un travail.
MCP connecte les agents aux outils. A2A connecte les agents entre eux. AG-UI connecte les agents aux utilisateurs.
AG-UI est la couche de streaming qui transporte tout ce que vous verrez ci-dessous : appels d'outils, schémas A2UI, événements MCP App, deltas d'état. Fonctionne sur SSE. L'état circule dans les deux sens sur le même flux. L'utilisateur édite, l'agent voit. L'agent modifie, l'utilisateur voit.
A2UI est la spécification de Google pour que les agents émettent de l'UI sous forme de schéma. Elle repose sur AG-UI. CopilotKit la livre en production.
Vous n'écrivez pas d'analyseur syntaxique pour tout cela. CopilotKit est un client AG-UI et décode le flux pour vous.
Les trois modèles que la plupart des équipes confondent
Demandez à dix développeurs ce qu'est la UI générative. Vous obtenez dix réponses. La plupart d'entre elles décrivent le modèle que leur framework actuel livre.
Il y en a juste trois. Le spectre va de plus de contrôle à plus de flexibilité.
- Contrôlé : Vous pré-construisez les composants. L'agent choisit lequel afficher.
- Déclaratif : L'agent émet un schéma. Votre application le mappe à des composants.
- Ouvert : L'agent écrit du HTML brut. Votre application le rend dans un bac à sable.

Chaque framework de UI générative en 2026 se situe quelque part sur cette ligne. Les différences sont architecturales, pas cosmétiques. Chaque modèle casse votre application d'une manière différente à grande échelle.
J'ai essayé différentes piles. La plupart couvrent bien un modèle. Je me suis arrêté sur CopilotKit parce qu'il supporte les trois sur le même runtime, en utilisant AG-UI. C'est la pile sur laquelle tout ce qui suit s'exécute.
Modèle 1 : Contrôlé, le frontend possède l'UI

C'est par là que la plupart des équipes commencent. C'est aussi là que la plupart des équipes se bloquent.
Vous pré-construisez un composant React. Vous le liez à un nom d'outil. L'agent choisit cet outil et le composant s'affiche en ligne dans le chat avec les arguments de l'agent comme props.
Un hook frontend. Zéro code agent. C'est tout.
1"use client";2import { z } from "zod";3import { useComponent } from "@copilotkit/react-core/v2";45const expenseChartSchema = z.object({6 title: z.string(),7 data: z.array(z.object({ label: z.string(), value: z.number() })),8});910function ExpenseChart({ title, data }: z.infer<typeof expenseChartSchema>) {11 return (12 <section className="rounded-xl border p-4">13 <h3 className="text-sm font-medium">{title}</h3>14 <ul className="mt-2 grid gap-1">15 {data.map((d) => (16 <li key={d.label} className="flex justify-between text-sm">17 <span>{d.label}</span>18 <span>${d.value}</span>19 </li>20 ))}21 </ul>22 </section>23 );24}2526export function ExpensesCopilot() {27 useComponent({28 name: "showExpenseChart",29 description: "Afficher une ventilation des dépenses par catégorie.",30 parameters: expenseChartSchema,31 render: ExpenseChart,32 });3334 return null;35}
Le hook enregistre l'outil auprès du runtime de CopilotKit. Le runtime le publicite auprès de l'agent via AG-UI. Lorsque l'agent l'appelle, les arguments arrivent en streaming et votre composant s'affiche en ligne. Pas d'outil Python à écrire, pas de schéma à câbler, pas de route API à ajouter.
Votre système de design reste aux commandes.
Ce graphique de dépenses n'est pas une maquette. L'Agent Coach Financier IA affiche des fiches comme celle-ci pour de vrais budgets, plans d'épargne et remboursements de dettes.
Vous voulez d'abord le hook nu ? C'est 'use-generative-ui-examples.tsx' dans le Projet de démarrage UI générative.
La taxe des tokens
Chaque composant que vous enregistrez se trouve dans la fenêtre de contexte de l'agent avant que l'utilisateur n'ait rien dit. Une description d'outil typique avec son schéma JSON consomme environ 400 tokens. 25 composants représentent 10 000 tokens à chaque tour. Vous payez cette taxe par requête.
L'agent choisit aussi le mauvais composant. Trop se ressemblent. Le diagramme circulaire et le donut « montrent tous deux des proportions ». Il devine.
Quand ajouter un état côté agent
L'état partagé est le seul cas où il vaut la peine d'écrire un outil Python. L'agent écrit dans l'état de session. D'autres parties de l'UI s'abonnent et se ré-affichent sans second appel LLM. Épinglez une métrique, le tableau de bord se met à jour. Ajoutez une ligne, le tableau se redessine.
1from google.adk.agents import LlmAgent2from google.adk.tools import ToolContext34def pin_metric(tool_context: ToolContext, label: str, value: float) -> dict:5 """Épingler une métrique au tableau de bord de l'utilisateur."""6 pinned = tool_context.state.get("pinnedMetrics", [])7 tool_context.state["pinnedMetrics"] = pinned + [{"label": label, "value": value}]8 return {"status": "pinned"}910agent = LlmAgent(name="dashboard_agent", model="gemini-3.5-flash", tools=[pin_metric])
Le frontend lit les métriques épinglées via le hook d'état partagé de CopilotKit. Le composant de chat s'affiche toujours en ligne car le même nom d'outil est câblé avec le hook frontend.
Épinglez une métrique dans le chat. Le panneau se redessine sans second appel de modèle. C'est l'Agent Tableau de Bord Canvas IA.
L'Agent de Recherche Approfondie IA va plus loin. Le plan, chaque recherche, chaque écriture de fichier, tout cela défile sous forme de fiches en direct. Pour tout le reste, le hook frontend est toute l'histoire.
Quand livrer le Contrôlé : Dix cas d'utilisation ou moins à forte valeur ajoutée. La précision du design compte. Vous connaissez exactement les UI dont vous avez besoin.
Quand ne pas le faire : Votre codebase croît linéairement avec les cas d'utilisation. 25 composants signifient 25 définitions d'outils présentes à chaque tour d'agent.
Ce qui casse : L'agent choisit le mauvais composant. Deux descriptions d'outils se chevauchent sémantiquement. Au-delà de 15 outils, deux d'entre eux ressemblent probablement à « affiche des données ». Correctif : réécrivez les descriptions pour nommer l'intention de l'utilisateur, pas le visuel. « Utiliser lorsque l'utilisateur demande à comparer des proportions d'un tout » bat « affiche un diagramme circulaire ».
Modèle 2 : Déclaratif (A2UI), l'agent émet un schéma

C'est le modèle dont la plupart des applications agent en production finissent par avoir besoin.
L'agent émet un schéma JSON décrivant l'UI. Votre application dispose d'un catalogue de composants qui mappe les nœuds du schéma à React (ou Svelte, Flutter, n'importe quoi). Un outil. Plusieurs UI.
A2UI est la spécification standard. CopilotKit livre le runtime. ADK exécute l'agent. AG-UI est le fil.
L'outil agent retourne trois opérations dans l'ordre : créer une surface, pousser l'arbre de composants, pousser les données.
1def search_flights(flights: list[Flight]) -> dict[str, Any]:2 """Rechercher des vols et les afficher sous forme de fiches enrichies."""3 return {4 "a2ui_operations": [5 {"type": "create_surface", "surfaceId": SURFACE_ID, "catalogId": CATALOG_ID},6 {"type": "update_components", "surfaceId": SURFACE_ID, "components": FLIGHT_SCHEMA},7 {"type": "update_data_model", "surfaceId": SURFACE_ID, "data": {"flights": flights}},8 ]9 }
C'est la fonction réelle, pas du pseudo-code. Le middleware du runtime voit le conteneur a2ui_operations dans le résultat de l'outil et transmet les surfaces au frontend. Vous ajoutez des hôtels ? Nouveau fichier de schéma. Une fonction de plus avec un identifiant de surface différent. Zéro travail frontend supplémentaire.
Schéma fixe vs schéma dynamique
L'arbre de composants ci-dessus vit dans flights.json. Vous l'avez écrit. L'agent ne fait que remplir les données. C'est un schéma fixe.
Le schéma dynamique inverse la situation : un LLM secondaire écrit l'arbre de composants à chaque tour à partir du contexte de la conversation. Même conteneur a2ui_operations à la fin. La vitrine Google ADK livre les deux.
Le catalogue est le contrat
Les définitions listent les composants que l'agent est autorisé à émettre, avec des schémas Zod pour les props. Les moteurs de rendu remplissent React. Les fautes de frappe deviennent des erreurs de build plutôt que des écrans blancs.
1const renderers: CatalogRenderers<TravelDefinitions> = {2 FlightCard: ({ props }) => (3 <article className="rounded-xl border p-4">4 <header className="flex justify-between">5 <span>{(props as any).airline}</span>6 <span>{(props as any).price}</span>7 </header>8 <div className="text-sm text-muted-foreground">9 {(props as any).origin} → {(props as any).destination} · {(props as any).departureTime}10 </div>11 </article>12 ),13};1415export const travelCatalog = createCatalog(travelDefinitions, renderers, {16 catalogId: "copilotkit://travel-catalog",17 includeBasicCatalog: true,18});
Les deux moitiés vivent dans le Projet de démarrage UI générative, câblées et appariées. search_flights dans 'a2ui_fixed_schema.py', le catalogue FlightCard dans 'renderers.tsx'. Demandez des vols. Regardez les fiches défiler dans le chat.
Les boutons et autres composants interactifs portent une action dans le schéma. Le catalogue de base la câble à onClick. Un clic déclenche un événement vers l'agent via AG-UI. L'agent décide quoi afficher ensuite. Zéro gestionnaire de clic.
Le calcul des tokens
50 types de fiches ou 500, l'agent voit une seule fonction. Les tokens par tour restent constants à mesure que votre bibliothèque de composants grandit.
Extensible à n'importe quel framework de rendu car ce n'est que du JSON. N'importe quel agent qui parle déjà AG-UI peut piloter A2UI dès le premier jour. Vous ne touchez pas au code agent pour câbler cela.
Compromis : Le LLM possède la mise en page. La sortie varie d'une exécution à l'autre au sein de votre catalogue. Si vous livrez des mentions légales, des surfaces marketing, ou tout ce où le placement exact des pixels compte, ce n'est pas votre catégorie.
Le Déclaratif est le modèle conçu pour la longue traîne. Tableaux de bord, résultats, formulaires, fiches, widgets.
Quand livrer le Déclaratif : Vous avez plus de cas d'utilisation que de temps pour pré-construire. Vous vous souciez de l'économie des tokens au-delà de la phase de prototype.
Ce qui casse : Vous avez construit un FlightCard personnalisé. Chaque vol s'affiche comme la fiche générique du catalogue de base. Pas d'erreur dans la console. Le CATALOG_ID sur l'agent et catalogId dans createCatalog sur le frontend ne correspondent pas. Le frontend ne reconnaît pas le catalogue que l'agent cible, il revient au catalogue de base. Faites correspondre exactement les chaînes des deux côtés.
Modèle 3 : Ouvert, pas de catalogue, pas de règles

Le troisième modèle est l'extrême opposé. Pas de catalogue. Pas de schéma. Juste une toile vierge.
Deux sous-modèles vivent dans ce seau.
Apps MCP
Un serveur MCP expose des surfaces UI que l'agent pilote. Excalidraw est l'exemple qui m'a marqué. L'agent obtient le contrôle total de la toile. Dessine des diagrammes à partir de votre contexte. Possède chaque pixel du tableau.

Implémenter le protocole client à partir de zéro est pénible, donc CopilotKit livre un MCPAppsMiddleware. Attachez-le à votre agent et pointez-le vers n'importe quel serveur MCP Apps.
1const agent = new BuiltInAgent({2 model: "openai/gpt-5.5",3 prompt: "Vous êtes un assistant utile.",4}).use(5 new MCPAppsMiddleware({6 mcpServers: [{ type: "http", url: "https://mcp.excalidraw.com/mcp", serverId: "my-server" }],7 }),8);
Lancez la Vitrine MCP Apps et vous réservez des vols et des hôtels dans la fenêtre de chat. Même middleware, de vrais serveurs MCP. Ou allez plus loin.
L'Constructeur d'App MCP IA permet à l'agent d'écrire une toute nouvelle application dans un bac à sable E2B, puis de la rendre en direct.
0:45
HTML en bac à sable
L'agent écrit du HTML brut. Votre application le rend dans un iframe en bac à sable pour qu'il ne puisse pas détourner la session.
Le runtime enregistre un outil de rendu HTML et le livre à l'agent via AG-UI. L'agent l'appelle avec le balisage qu'il souhaite. Il n'y a pas d'outil HTML à définir côté agent. Le runtime l'injecte.
L'instruction côté agent fait le vrai travail :
1canvas_agent = LlmAgent(2 name="canvas_agent",3 model="gemini-3.5-flash",4 instruction=(5 "Vous êtes un assistant de visualisation. Lorsque l'utilisateur demande à voir, "6 "dessiner ou visualiser quelque chose, générez une UI HTML interactive. "7 "Utilisez uniquement les classes Tailwind. Pas de polices externes. Tenez-vous en aux "8 "couleurs neutres sauf si l'utilisateur en nomme une."9 ),10)
Sans ces règles de style, le modèle se rabat sur l'esthétique la plus marquée dans ses données d'entraînement cette semaine-là. Avec elles, vous obtenez quelque chose de proche de votre marque la plupart du temps. Pas toujours.
Le problème d'incohérence de marque
J'ai essayé de livrer le modèle Ouvert comme UI principale d'un agent. Je l'ai retiré en une semaine.
« Néo-brutaliste » le mardi. « Clone iOS 4 » le mercredi. Les règles de style dans le prompt poussent l'agent vers votre marque. Elles ne la garantissent pas. La marque changeait constamment. Le produit semblait peu sérieux.

Le modèle Ouvert n'est pas inutile. Il est mal appliqué.
Le bon appel pour une chose : les interactions jetables où l'utilisateur se fiche de l'apparence de l'interface et ne la reverra jamais. « Montre-moi comment fonctionnent les électrons. » « Donne-moi un étrange diagramme à barres de mes 10 dernières requêtes. » « Visualise cette réponse API. » Le genre de choses que l'on voit dans les aperçus Google AI.
Quand livrer le Ouvert : Requêtes uniques. Visualisations jetables. Expériences en bac à sable. Jamais comme surface principale.
Ce qui casse : L'iframe s'affiche. Les boutons ne cliquent pas. Les formulaires ne se soumettent pas. Les indicateurs du bac à sable sont trop stricts, ou trop laxistes d'une manière que le navigateur refuse. Réglez le bac à sable de l'iframe sur allow-scripts et allow-forms. Rien d'autre. Jamais allow-same-origin.
Comment choisir
Parcourez l'arbre de décision avant d'écrire du code.
Le designer a des maquettes pixel-perfect pour ce flux ? Contrôlé.
Des dizaines de types de fiches ou de widgets à livrer ? Déclaratif.
Visualisation unique et jetable que l'utilisateur ne verra jamais deux fois ? Ouvert.
Vous ne pouvez pas décider ? Par défaut, choisissez Déclaratif. Passez en Contrôlé pour les 3 premiers flux. Jamais Ouvert par défaut.
Si vous livrez déjà et que vous n'êtes pas sûr de votre position, comptez les outils de rendu. Au-delà de 15, vous êtes en Contrôlé et le mur est proche. Commencez à câbler A2UI cette semaine.
Trois modèles. Trois paris.
Le Contrôlé parie sur vous. Composants pré-construits, pixel-perfect. Coûteux au-delà de 25.
Le Déclaratif parie sur le schéma. Le schéma est le contrat. L'agent le remplit. Il passe à l'échelle de manière linéaire.
Le Ouvert parie sur le modèle. Pas de catalogue, pas de schéma, HTML brut. Bon pour le jetable. Fragile pour tout ce qui est livré deux fois.
L'erreur n'est pas de choisir le mauvais modèle. C'est de ne pas savoir que vous en avez choisi un.
La plupart des équipes choisissent par défaut le Contrôlé parce que le framework par défaut est Contrôlé. Elles heurtent le mur à 25 composants et se tournent vers l'Ouvert parce qu'il a l'air convaincant dans les démos. Aucun des deux n'était une décision. Les deux étaient une dérive.
Choisissez intentionnellement. Faites correspondre le modèle au problème. Contrôlé pour les flux qui doivent être exacts. Déclaratif pour la longue traîne. Ouvert pour le jetable.
Modèles d'agent UI générative Open Source
La référence pour les trois se trouve dans la nouvelle section Agents UI générative de awesome-llm-apps. Clonez ce dont vous avez besoin. Supprimez ce dont vous n'avez pas besoin.
Je publierai davantage sur la livraison d'agents en production, AG-UI et les modèles qui passent à l'échelle. Suivez-moi @Saboo_Shubham_ pour rester à l'écoute.










