Zum Hauptinhalt springen

Einheit 8 — Zustandsnormalisierung

Was du nach dieser Einheit weißt: Du verstehst warum Generative AI Agents verschachtelte Nachrichtenstrukturen erzeugen, warum das ein Problem ist und wie du mit SQL-Persistenz für saubere Zustände sorgst.


Das Problem: Verschachtelte Nachrichten

In 42°OS fließt eine Nachricht von Agent zu Agent. Jeder Agent fügt seine Ausgabe hinzu. Das funktioniert gut — solange die Struktur flach bleibt:

{
"dateiname": "RE-2026-04523.pdf",
"kategorie": "Rechnung",
"rechnungsnummer": "RE-2026-04523"
}

Aber: Wenn ein Generative AI Agent oder ein JSON Merge Agent seine Ausgabe hinzufügt, entsteht oft eine verschachtelte Struktur:

{
"last_message": {
"last_message": {
"dateiname": "RE-2026-04523.pdf"
},
"generation": {
"kategorie": "Rechnung"
}
},
"generation": {
"rechnungsnummer": "RE-2026-04523",
"betrag": 640.00
}
}

Jetzt ist dateiname nicht mehr unter dateiname zu finden, sondern unter last_message.last_message.dateiname. Und nach dem nächsten Agent wird es last_message.last_message.last_message.dateiname.

📸 Screenshot: [Platzhalter — Run Inspector: Nachrichtenstruktur nach drei Generative AI Agents — tiefe Verschachtelung mit last_message.last_message.last_message]

Das hat Konsequenzen:

  • Pfade werden unvorhersehbar — Folge-Agents müssen wissen, in welcher Tiefe ein Feld liegt
  • Änderungen am Workflow brechen Pfade — fügst du einen Agent davor ein, verschiebt sich die gesamte Struktur
  • Debugging wird schwierig — die Nachricht ist kaum noch lesbar

📹 Video: [Platzhalter — Screencast: Verschachtelungsproblem in der Praxis — ein neuer Agent wird eingefügt und plötzlich stimmen die Pfade in allen folgenden Agents nicht mehr]


Die Lösung: Normalisierungspunkte

Die Idee ist einfach: An definierten Stellen im Workflow schreibst du die relevanten Ergebnisse in eine Datenbank (SQLite über Internal Storage) — und der nächste Abschnitt liest nur die sauberen, flachen Daten aus der Datenbank. Die verschachtelte Nachrichtenstruktur wird damit irrelevant.

Generative AI Agent → SQL Write Agent → [Abschnittsgrenze] → SQL Read Agent → nächster Abschnitt

Vorher (ohne Normalisierung):

{
"last_message": {
"last_message": { "dateiname": "RE-2026-04523.pdf" },
"generation": { "kategorie": "Rechnung" }
},
"generation": {
"rechnungsnummer": "RE-2026-04523",
"betrag": 640.00,
"kreditor": "Müller GmbH"
}
}

Nachher (nach Normalisierung in SQL und Rücklesen):

{
"dateiname": "RE-2026-04523.pdf",
"kategorie": "Rechnung",
"rechnungsnummer": "RE-2026-04523",
"betrag": 640.00,
"kreditor": "Müller GmbH"
}

Flach, lesbar, vorhersehbar. Egal wie viele Agents vorher gelaufen sind — die Datenbank liefert immer dieselbe Struktur.

📸 Screenshot: [Platzhalter — Workflow-Designer: SQL Write Agent nach einem Generative AI Agent, gefolgt von einem SQL Read Agent am Anfang des nächsten Abschnitts]

📸 Screenshot: [Platzhalter — Run Inspector: Saubere, flache Nachricht nach dem Lesen aus der Datenbank]


Wann normalisieren?

Nicht nach jedem Agent — das wäre übertrieben. Aber an diesen Stellen:

NormalisierungspunktWarum
Nach einem Generative AI AgentErzeugt generation-Verschachtelung
Nach einem JSON Merge AgentKann die Struktur verändern
An Abschnittsgrenzen (vor Source Agent)Saubere Übergabe an den nächsten Workflow
Vor komplexer WeiterverarbeitungWenn Folge-Agents auf stabile Pfade angewiesen sind

SQL Write: Was wird gespeichert?

Du schreibst nicht die gesamte Nachricht in die Datenbank — sondern nur die Felder die du im weiteren Verlauf brauchst. Das ist gleichzeitig eine gute Übung um dir klar zu werden, welche Daten tatsächlich relevant sind.

Beispiel — SQL INSERT mit Liquid Templating:

INSERT INTO rechnungen (dateiname, kategorie, rechnungsnummer, betrag, kreditor)
VALUES (
'{{ last_message.last_message.dateiname }}',
'{{ last_message.generation.kategorie }}',
'{{ generation.rechnungsnummer }}',
{{ generation.betrag }},
'{{ generation.kreditor }}'
);

📸 Screenshot: [Platzhalter — SQL Agent Konfiguration: INSERT-Statement mit Liquid-Variablen die auf die verschachtelte Nachricht zugreifen]

Ja, die Pfade im INSERT sind verschachtelt — aber das ist das letzte Mal dass du dich mit der Verschachtelung auseinandersetzen musst. Ab dem SELECT sind die Daten flach.


SQL Read: Saubere Daten zurückholen

Der nächste Abschnitt startet mit einem SQL SELECT:

SELECT dateiname, kategorie, rechnungsnummer, betrag, kreditor
FROM rechnungen
WHERE rechnungsnummer = '{{ rechnungsnummer }}';

Das Ergebnis ist ein flaches JSON-Objekt — unabhängig davon wie verschachtelt die Nachricht vorher war.


Vorteile auf einen Blick

Ohne NormalisierungMit Normalisierung
Pfade werden mit jedem Agent tieferPfade sind immer flach
Einfügen eines Agents bricht FolgepfadeAbschnitte sind voneinander unabhängig
Debugging erfordert Pfad-ArchäologieDaten sind jederzeit in der DB einsehbar
Gesamter Zustand nur in der NachrichtZustand ist persistent (überlebt Fehler)

📹 Video: [Platzhalter — Screencast: Normalisierung Schritt für Schritt — Verschachtelte Nachricht → SQL Write → SQL Read → flache Nachricht]

Mehr dazu im Nachschlagewerk

Eine detaillierte Referenz zur Zustandsnormalisierung findest du in der Dokumentation unter Zustandsnormalisierung mit SQL-Persistenz.


Weiter: Übung — Teilnehmerliste abrufen, aufbereiten und abgleichen