A2UI: Wie KI dynamische UIs zur Laufzeit erzeugt

  1. AG-UI verstehen: Der Standard für Agentic User Interfaces
  2. AG-UI in der Praxis: Das SDK für TypeScript
  3. AG-UI mit Angular umsetzen
  4. A2UI: Wie KI dynamische UIs zur Laufzeit erzeugt
  5. A2UI mit AG-UI in Angular integrieren
  6. Custom Catalogs in A2UI: Eigene Komponenten für KI-generierte UIs

Sprachmodelle antworten nicht mehr nur mit Text, sondern mit ganzen UI-Strukturen.

Sobald ein Sprachmodell auf eine Benutzeranfrage nicht nur einen Fließtext liefern, sondern auch eine Liste von Flügen anzeigen, eine Eingabemaske einblenden oder ein Diagramm rendern soll, wird die Integration zwischen Agent und Frontend schnell unübersichtlich. Mit A2UI hat Google einen Standard vorgelegt, der es einem Sprachmodell erlaubt, statt reinem Text auch Komponenten und deren Daten zurückzuliefern – das Frontend übernimmt dann die Darstellung.

Dieser erste Teil der dreiteiligen Serie führt in das A2UI-Protokoll ein und zeigt anhand einer Angular-Demo, wie der mitgelieferte Renderer die vom Modell beschriebenen UI-Strukturen darstellt.

📂 Source Code (siehe Branch a2ui-dynamic)

Was ist A2UI?

A2UI steht für Agent-to-UI und ist ein von Google vorgestellter Standard, der die Lücke zwischen einem Agent und einer dynamisch generierten Oberfläche schließt. Im Kern beschreibt A2UI ein Vokabular vordefinierter Komponenten samt deren Eigenschaften sowie ein Nachrichtenformat, mit dem der Agent dem Client mitteilt, welche Komponenten anzuzeigen sind und mit welchen Daten sie zu befüllen sind.

Statt nur Text zurückzugeben, wählt das Sprachmodell aus einem Katalog vordefinierter Komponenten und liefert die zugehörigen Daten gleich mit. Der Client muss diese Komponenten nur noch rendern – und bestimmt dabei die konkrete Darstellung selbst. Der Agent legt somit das „was" fest, der Client entscheidet über das „wie". So wirken die dynamisch erzeugten UI-Fragmente nicht wie Fremdkörper, sondern fügen sich in das bestehende Design der Anwendung ein.

Im Gegensatz zu Ansätzen wie MCP Apps, bei denen der Agent vollständige Komponenten ausliefert, verhindert die strikte Trennung zwischen Struktur und Darstellung bei A2UI auch, dass der Client zur Laufzeit fremden Code laden muss.

Als dieser Artikel verfasst wurde, lagen zwei Versionen von A2UI vor: die stabile Version 0.8 und die künftige Version 0.9 als Draft. In diesem Artikel gehe ich bereits auf Letztere ein.

A2UI in Aktion: Beispiele für dynamisch generierte UIs

Eine dynamische A2UI-Antwort sehen wir am Beispiel einer Demo-Anwendung, in der ein dynamisches Dashboard anhand einer textuellen Beschreibung aufgebaut wird:

A2UI-Beispiel: Vom Agent generiertes Dashboard

Die Anforderungen für das Dashboard werden in einem Textfeld hinterlegt:

Ein weiteres Beispiel ist ein Chatverlauf, indem sich das LLM entscheidet, über eine dynamische Frage weitere Informationen einzuholen:

A2UI-Beispiel: Vom Agent generierte Eingabemaske mit Bestätigungsfrage in einer Flugbuchungs-Anwendung

Welche Komponenten enthält der A2UI Basic Catalog?

Die zentrale Idee von A2UI ist ein sogenannter Komponenten-Katalog. Ein solcher Katalog beschreibt eine Menge von Komponenten, die sowohl der Agent als auch der Client kennen. Der von A2UI definierte Basic Catalog umfasst eine ganze Reihe gängiger UI-Bausteine, die sich in folgende Gruppen unterteilen lassen. Die folgende Übersicht ist der Spezifikation entnommen:

Kategorie Komponente Beispiel
Display Text Zeigt Text an. Unterstützt einfaches Markdown.
Image Zeigt ein Bild von einer URL an.
Icon Zeigt ein vom System bereitgestelltes Icon aus einer vordefinierten Liste an.
Video Zeigt ein Video von einer URL an.
AudioPlayer Ein Player für Audio-Inhalte von einer URL.
Layout Row Ein horizontaler Layout-Container.
Column Ein vertikaler Layout-Container.
List Eine scrollbare Liste von Komponenten.
Container Card Ein Container im Karten-Stil.
Tabs Eine Reihe von Tabs, jeweils mit Titel und untergeordneter Komponente.
Divider Eine horizontale oder vertikale Trennlinie.
Modal Ein Dialog, der über dem Hauptinhalt erscheint und durch einen Button im Hauptinhalt ausgelöst wird.
Input Button Ein anklickbarer Button, der eine Aktion auslöst. Unterstützt die Varianten „primary" und „borderless".
CheckBox Eine Checkbox mit Label und booleschem Wert.
TextField Ein Feld für Texteingaben durch den Benutzer.
DateTimeInput Eine Eingabe für Datum und/oder Uhrzeit.
ChoicePicker Eine Komponente zur Auswahl einer oder mehrerer Optionen.
Slider Ein Schieberegler zur Auswahl eines numerischen Wertes innerhalb eines Bereichs.

Welche Funktionen bietet A2UI?

Neben den eigentlichen Komponenten bietet A2UI auch Funktionen, die das LLM in den Komponentendefinitionen referenzieren kann. Auf diese Weise lassen sich Werte, die zur Laufzeit ermittelt oder aufbereitet werden müssen – beispielsweise formatierte Zahlen und Datumsangaben – deklarativ beschreiben, ohne dass das Sprachmodell die konkrete Logik selbst ausführen muss. Der Basic Catalog bringt eine Reihe häufig benötigter Funktionen wie formatNumber oder formatDate von Haus aus mit. Auch die folgende Übersicht stammt aus der Spezifikation:

Kategorie Funktion Beschreibung
Validierung required Prüft, dass der Wert nicht null, undefined oder leer ist.
regex Prüft, dass der Wert einem regulären Ausdruck entspricht.
length Prüft Einschränkungen für die Stringlänge.
numeric Prüft Einschränkungen für numerische Bereiche.
email Prüft, dass der Wert eine gültige E-Mail-Adresse ist.
Formatierung formatString Führt String-Interpolation von Datenmodellwerten und registrierten Funktionen durch.
formatNumber Formatiert eine Zahl mit Tausendertrennung und Nachkommastellen.
formatCurrency Formatiert eine Zahl als Währungszeichenkette.
formatDate Formatiert ein Datum/eine Uhrzeit anhand eines Musters.
Verknüpfung and Logische UND-Verknüpfung einer Liste boolescher Werte.
or Logische ODER-Verknüpfung einer Liste boolescher Werte.
not Logische NICHT-Verknüpfung eines booleschen Wertes.
Sonstige openUrl Öffnet eine URL in einem Browser.
pluralize Wählt eine lokalisierte Zeichenkette basierend auf einer numerischen Anzahl aus.

Wie funktionieren A2UI-Nachrichten und der Renderer?

Der Client stellt die Komponenten über einen Renderer zur Anzeige. Der Renderer ist die Instanz, die A2UI-Nachrichten entgegennimmt und die zugehörigen UI-Elemente erzeugt. Für die zwischen Agent und Client ausgetauschten Nachrichten definiert A2UI vier Nachrichtentypen:

Nachrichtentyp Beschreibung
createSurface Weist den Client an, eine neue Surface zu erstellen.
updateComponents Liefert eine Liste von Komponenten-Definitionen, die einer bestimmten Surface hinzugefügt oder dort aktualisiert werden.
updateDataModel Liefert neue Daten, die in das Datenmodell einer Surface eingefügt werden oder dieses ersetzen.
deleteSurface Entfernt eine Surface und deren Inhalte explizit aus der UI.

Ein Surface ist eine logische Anzeigefläche im Client, die der Agent mit createSurface anlegt, mit updateComponents befüllt und bei Bedarf wieder mit deleteSurface entfernt. Die in den Komponenten genutzten Daten verwaltet A2UI in einem eigenen Datenmodell pro Surface, das sich über updateDataModel aktualisieren lässt. Komponenten greifen über Datenbindung auf die Werte dieses Datenmodells zu, sodass Änderungen automatisch in der Anzeige übernommen werden.

A2UI-Renderer in Angular einbinden

Damit Entwickler:innen das Protokoll nicht selbst implementieren müssen, bietet das A2UI-Projekt bereits Renderer für verschiedene Sprachen sowie Adapter für verschiedene Frameworks. Der Renderer für JavaScript findet sich im Paket @a2ui/web_core und der darauf aufbauende Adapter für Angular in @a2ui/angular. Wie gewohnt können beide Pakete über npm bezogen werden:

npm install @a2ui/angular @a2ui/web_core

Beispiel: Eine Passagier-Karte mit A2UI in Angular

Um die Funktionsweise von A2UI greifbar zu machen, beginnen wir mit einem minimalen Beispiel, das sich im Beispielprojekt unter projects/a2ui-demo befindet. Statt eines echten Sprachmodells erzeugt unser Beispielcode die A2UI-Nachrichten zunächst hartcodiert. So können wir uns voll auf das Datenformat und die clientseitige Verarbeitung konzentrieren.

Die Demo präsentiert eine Karte mit den Daten eines Passagiers sowie einen Button, mit dem sich die erworbenen Bonusmeilen erhöhen lassen:

A2UI-Demo in Angular: Passagier-Karte mit Name, Bonusmeilen und Button zum Erhöhen der Meilen

Die Passagier-Karte wird mit der Funktion createSimpleCard definiert, die ein Array mit A2UI-Nachrichten erzeugt:

import type { A2uiMessage } from '@a2ui/web_core/v0_9';

export function createSimpleCard(
  surfaceId: string,
  catalogId: string,
  passenger: Passenger,
): A2uiMessage[] {
  return [
    {
      version: 'v0.9',
      createSurface: {
        surfaceId,
        catalogId,
      },
    },
    {
      version: 'v0.9',
      updateComponents: {
        surfaceId,
        components: [
          { id: 'root', component: 'Card', child: 'content' },
          {
            id: 'content',
            component: 'Column',
            children: ['headline', 'name-row', 'miles-row', 'button'],
          },
          {
            id: 'headline',
            component: 'Text',
            text: 'Passenger',
            variant: 'h2',
          },
          {
            id: 'name-row',
            component: 'Row',
            children: ['first-name', 'last-name'],
          },
          {
            id: 'first-name',
            component: 'Text',
            text: { path: '/passenger/firstName' },
            variant: 'body',
          },
          {
            id: 'last-name',
            component: 'Text',
            text: { path: '/passenger/lastName' },
            variant: 'body',
          },
          {
            id: 'miles-row',
            component: 'Row',
            children: ['miles-label', 'miles-value'],
          },
          {
            id: 'miles-label',
            component: 'Text',
            text: 'Miles:',
            variant: 'caption',
          },
          {
            id: 'miles-value',
            component: 'Text',
            text: {
              call: 'formatNumber',
              args: {
                value: { path: '/passenger/bonusMiles' },
                decimals: 0,
              },
              returnType: 'string',
            },
            variant: 'body',
          },
          {
            id: 'button',
            component: 'Button',
            child: 'button-label',
            action: {
              event: {
                name: 'increaseMiles',
                context: {
                  passenger: { path: '/passenger' },
                },
              },
            },
          },
          {
            id: 'button-label',
            component: 'Text',
            text: 'Increase Miles',
            variant: 'body',
          },
        ],
      },
    },
    {
      version: 'v0.9',
      updateDataModel: {
        surfaceId,
        path: '/passenger',
        value: passenger,
      },
    },
  ];
}

Die erste Nachricht vom Typ createSurface legt ein neues Surface an. Die zweite Nachricht beschreibt mittels updateComponents die anzuzeigenden Komponenten: eine Card als Wurzel, die eine Spalte mit Überschrift, Namensreihe, Meilenanzeige und Button enthält. Die einzelnen Ausgaben werden teilweise über die Eigenschaft path an Werte im Datenmodell gebunden. Dieses Datenmodell definiert das Beispiel in der dritten Nachricht, die den Typ updateDataModel aufweist.

Jede Komponente erhält eine eigene ID; geschachtelte Komponenten werden über child bzw. children anhand dieser IDs referenziert. Durch diese Art der Referenzierung befinden sich sämtliche Knoten des Komponentenbaums auf derselben Ebene – eine Schachtelung im JSON-Dokument umgeht A2UI damit ganz bewusst, da sie für Sprachmodelle eine Herausforderung darstellt.

Der Button beschreibt rein deklarativ, dass beim Klick ein Event increaseMiles mit den Passagierdaten als Kontext ausgelöst werden soll. Darauf zu reagieren ist die Aufgabe des Clients.

Zur Anzeige der beschriebenen Struktur nutzt das Beispiel den vom Angular-Adapter bereitgestellten A2uiRendererService:

import {
  A2UI_RENDERER_CONFIG,
  A2uiRendererService,
  SurfaceComponent,
} from '@a2ui/angular/v0_9';

[...]

@Component({
  selector: 'app-root',
  imports: [SurfaceComponent],
  template: `
    <a2ui-v09-surface [surfaceId]="surfaceId" />
  `,
  styleUrl: './app.css',
})
export class App {
  private readonly renderer = inject(A2uiRendererService);
  protected readonly config = inject(A2UI_RENDERER_CONFIG);
  private readonly destroyRef = inject(DestroyRef);
  protected readonly surfaceId = 'passenger-card-surface';

  constructor() {
    this.render();
    this.registerHandler();
  }

  private render(): void {
    const passenger: Passenger = {
      id: 42,
      firstName: 'Anna',
      lastName: 'Miller',
      bonusMiles: 1200,
    };

    const messages = createSimpleCard(
      this.surfaceId,
      this.config.catalogs[0].id,
      passenger,
    );

    this.renderer.processMessages(messages);
  }

  private registerHandler(): void {
    [...]
  }
}

Der A2uiRendererService verarbeitet die erhaltenen A2UI-Nachrichten mit seiner Methode processMessages. Die eigentliche Anzeige übernimmt die Komponente SurfaceComponent, die die gewünschte surfaceId entgegennimmt.

NOTE

Neu: Agentic UI with Angular

Wenn du A2UI nicht nur integrieren, sondern sauber in größere Architekturen einbetten willst:
In meinem Buch Agentic UI mit Angular gehe ich genau auf diese Patterns und Trade-offs im Detail ein.

Cover des eBooks Agentic UI with Angular

Mehr zum eBook →

Da der Button in unserer Karte ein Event increaseMiles auslöst, registriert das Beispiel dafür einen Handler bei der Eigenschaft onAction:

import type { A2uiClientAction } from '@a2ui/web_core/v0_9';

[...]

private registerHandler(): void {
  const subscription = this.renderer.surfaceGroup.onAction.subscribe(
    (action: A2uiClientAction) => {
      console.log('[A2UI Event]', action);

      if (action.name !== 'increaseMiles') {
        return;
      }

      const passenger = action.context['passenger'] as Passenger;

      this.renderer.processMessages([
        {
          version: 'v0.9',
          updateDataModel: {
            surfaceId: this.surfaceId,
            path: '/passenger',
            value: {
              ...passenger,
              bonusMiles: passenger.bonusMiles + 300,
            },
          },
        },
      ]);
    },
  );

  this.destroyRef.onDestroy(() => {
    subscription.unsubscribe();
  });
}

Der Handler für increaseMiles nimmt die im Kontext mitgegebenen Passagierdaten entgegen und aktualisiert die Bonusmeilen im gebundenen Datenmodell mit einer updateDataModel-Nachricht. Der Renderer erkennt die Änderung im Datenmodell und aktualisiert die gebundenen Anzeigen automatisch.

Leider handelt es sich bei onAction nicht um ein RxJS-Observable. Deswegen erfolgt hier das Unsubscribe programmatisch über eine DestroyRef.

Damit der Renderer weiß, welche Kataloge zur Verfügung stehen und wie etwaige Markdown-Inhalte umgewandelt werden sollen, sieht der Angular-Adapter eine zentrale Konfiguration vor, die über Provider bereitgestellt wird:

import {
  A2UI_RENDERER_CONFIG,
  A2uiRendererService,
  BasicCatalog,
  provideMarkdownRenderer,
} from '@a2ui/angular/v0_9';
import {
  ApplicationConfig,
  inject,
  provideBrowserGlobalErrorListeners,
} from '@angular/core';
import { marked } from 'marked';

export const appConfig: ApplicationConfig = {
  providers: [
    provideBrowserGlobalErrorListeners(),
    {
      provide: A2UI_RENDERER_CONFIG,
      useFactory: () => ({
        catalogs: [inject(BasicCatalog)],
      }),
    },
    provideMarkdownRenderer(async (markdown) =>
      marked.parse(String(markdown ?? '')),
    ),
    A2uiRendererService,
  ],
};

Das gezeigte Beispiel konfiguriert den von A2UI mitgelieferten Basic Catalog, den der Adapter als Service bereitstellt. Dazu wird der Katalog für das Token A2UI_RENDERER_CONFIG registriert. Außerdem benötigt die vom A2UI-Team gelieferte Umsetzung des Basic-Kataloges einen Markdown-Renderer, der über provideMarkdownRenderer einzubinden ist.

Zusammenfassung

A2UI ermöglicht es einem Sprachmodell, auf Anfragen nicht nur mit Text, sondern mit konkreten UI-Strukturen zu antworten, die der Client zur Laufzeit direkt rendern kann. Das Modell kombiniert dabei vorhandene Komponenten zu passenden Oberflächen – abgestimmt auf den jeweiligen Kontext und die aktuelle Interaktion.

Ein zentraler Vorteil liegt in der Trennung von Struktur und Darstellung: Der Client entscheidet über die konkrete Darstellung und führt keinen fremden Code aus. Dadurch bleiben die dynamisch erzeugten UI-Fragmente konsistent mit der Anwendung und wirken nicht wie Fremdkörper.

Der nächste Schritt

Wir wissen jetzt, wie sich A2UI in Angular rendern lässt. Im nächsten Teil geht es darum, die UI von einem Agenten generieren zu lassen und über AG-UI anzubinden.

Nächster Artikel →

Interesse an produktionsreifen Agentic-UI-Architekturen?

In meinem Workshop beschäftigen wir uns mit AG-UI, A2UI, MCP Apps, HITL-Patterns und modernen Angular-Architekturen für reale agentische Systeme.

Alle Details


FAQ

Was ist A2UI?

A2UI (Agent-to-UI) ist ein von Google vorgestellter Standard, der die Lücke zwischen einem Agent und einer dynamisch generierten Oberfläche schließt. Er definiert ein Vokabular vordefinierter Komponenten samt Eigenschaften sowie ein Nachrichtenformat, mit dem ein Agent dem Client mitteilt, welche Komponenten anzuzeigen und mit welchen Daten sie zu befüllen sind.

Wofür braucht man A2UI?

A2UI ermöglicht es Sprachmodellen, auf Anfragen nicht nur mit Text, sondern mit konkreten UI-Strukturen wie Listen, Karten, Diagrammen oder Eingabeformularen zu antworten. Damit lassen sich agentische Sidecars und Assistenten weit über klassische Chatverläufe hinaus erweitern, ohne dass der Client zur Laufzeit fremden Code laden muss.

Was ist der Basic Catalog in A2UI?

Der Basic Catalog ist die von A2UI mitgelieferte Sammlung gängiger UI-Bausteine. Er umfasst Display-, Layout-, Container- und Input-Komponenten wie Text, Image, Card, Column, Row, Button oder TextField und enthält dazu Standardfunktionen wie formatNumber, formatDate oder typische Validierungen.

Wie integriert man A2UI in eine Angular-Anwendung?

Für Angular bietet A2UI das Paket @a2ui/angular, das auf dem JavaScript-Renderer @a2ui/web_core aufbaut. Der A2uiRendererService verarbeitet die A2UI-Nachrichten, die Komponente SurfaceComponent zeigt das gerenderte Surface an, und der gewünschte Katalog wird über das Token A2UI_RENDERER_CONFIG registriert.

Agentic UI with Angular

Architecting Agentic AI with Open Standards

Integriere AI-Agents in Angular mit offenen Standards.

Mehr zum Buch