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:
| Normalisierungspunkt | Warum |
|---|---|
| Nach einem Generative AI Agent | Erzeugt generation-Verschachtelung |
| Nach einem JSON Merge Agent | Kann die Struktur verändern |
| An Abschnittsgrenzen (vor Source Agent) | Saubere Übergabe an den nächsten Workflow |
| Vor komplexer Weiterverarbeitung | Wenn 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 Normalisierung | Mit Normalisierung |
|---|---|
| Pfade werden mit jedem Agent tiefer | Pfade sind immer flach |
| Einfügen eines Agents bricht Folgepfade | Abschnitte sind voneinander unabhängig |
| Debugging erfordert Pfad-Archäologie | Daten sind jederzeit in der DB einsehbar |
| Gesamter Zustand nur in der Nachricht | Zustand ist persistent (überlebt Fehler) |
📹 Video: [Platzhalter — Screencast: Normalisierung Schritt für Schritt — Verschachtelte Nachricht → SQL Write → SQL Read → flache Nachricht]
Eine detaillierte Referenz zur Zustandsnormalisierung findest du in der Dokumentation unter Zustandsnormalisierung mit SQL-Persistenz.
Weiter: Übung — Teilnehmerliste abrufen, aufbereiten und abgleichen