Übung: Fehlersuche im laufenden Workflow
Was du in dieser Übung lernst: Du bekommst einen Workflow, der vier Fehler enthält — jeder davon typisch, jeder davon ohne Fehlermeldung. Du findest sie ausschließlich mit den MCP-Werkzeugen und reparierst sie in der richtigen Reihenfolge.
Alle vier Fehler haben eines gemeinsam: towelscript_check meldet valid: true. Genau das macht sie gefährlich.
Der Patient
module academy
flow kontaktanfrage_defekt {
node eingang: form {
secret: "academy-kurs6-debug"
form_fields: '[{"name":"freitext","label":"E-Mail-Text","type":"textarea","rows":8,"required":true}]'
}
node extrahieren: ai {
model: "gus:tav"
output_format: "json"
prompt_template: """Extrahiere den Absender aus dem folgenden E-Mail-Text.
Gib ausschliesslich ein JSON-Objekt zurueck, ohne Einleitung und ohne Markdown.
Zielschema:
{"name": "Vor- und Nachname", "company": "Firmenname", "email": "E-Mail-Adresse oder null"}
E-Mail-Text:
{{ freitext }}"""
}
node pruefen: filter {
expected_receive_period_in_days: 365
keep_message: true
rules: [
{ type: "field==value", value: "ja", path: "email_gueltig" }
]
}
node anlegen: http.post {
post_url: "https://69cde52533a09f831b7c9fa5.mockapi.io/api/v1/contacts"
method: "post"
content_type: "json"
payload {
name: "{{ last_message.name }}",
company: "{{ last_message.company }}",
email: "{{ last_message.email }}"
}
}
eingang -> extrahieren -> pruefen -> anlegen
}
Die Absicht dahinter: Freitext entgegennehmen, Absender per KI extrahieren, nur Kontakte mit gültiger E-Mail-Adresse durchlassen, Kontakt in der API anlegen.
Teil 1: Statische Analyse
Aufgabe 1 — Prüfen, ohne zu deployen
Lass den Quelltext durch alle drei Prüfwerkzeuge laufen. Was fällt auf?
Mögliche Lösung anzeigen
towelscript_check:
{ "valid": true, "errors": [], "warnings": [],
"stats": { "lines": 38, "nodes": 4, "flows": 1, "modules": 1 } }
Vier Nodes, keine Fehler, keine Warnungen. Sieht gut aus.
towelscript_graph:
{ "flows": [ { "name": "kontaktanfrage_defekt",
"nodes": [ … vier Stück … ],
"edges": [] } ] }
edges: [] — bei vier Nodes in einer Kette erwartest du drei Kanten. Das ist Fehler 1.
towelscript_compile bestätigt es: links: [], und beim Post-Node stehen name, company und email flach neben post_url statt unter payload.
Die Ursache ist die Blockschreibweise payload { statt payload: { — sie zieht die Schlüssel flach und verhindert, dass die Verbindungszeile ausgewertet wird.
Aufgabe 2 — Fehler 1 beheben und erneut prüfen
Mögliche Lösung anzeigen
payload: {
name: "{{ last_message.name }}",
company: "{{ last_message.company }}",
email: "{{ last_message.email }}"
}
Danach liefert graph:
"edges": [
{ "from": "eingang", "to": "extrahieren", "kind": "data" },
{ "from": "extrahieren", "to": "pruefen", "kind": "data" },
{ "from": "pruefen", "to": "anlegen", "kind": "data" }
]
Drei Kanten bei vier Nodes. Korrekt.
Merke: Fehler 1 musste zuerst weg. Solange die Agents nicht verbunden sind, produziert ein Testlauf keine Daten — und ohne Daten findest du die restlichen Fehler nicht.
Teil 2: Einzelne Agents prüfen
Aufgabe 3 — Den Post-Agent im Dry-Run testen
Teste die Post-Konfiguration virtuell, mit einer Payload, wie sie der KI-Agent liefern würde:
{
"name": "Dr. Anke Reinhardt",
"company": "Nordwerk GmbH",
"email": "a.reinhardt@nordwerk-pt.example",
"last_message": { "freitext": "Hallo zusammen, …" },
"workflow_run_id": "abc-123"
}
Was steht im Full request?
Mögliche Lösung anzeigen
body: "{\"name\":\"\",\"company\":\"\",\"email\":\"\",
\"last_message\":{\"freitext\":\"Hallo zusammen, …\"},
\"workflow_run_id\":\"abc-123\"}"
Zwei Probleme in einem Log.
Fehler 2 — falscher Liquid-Pfad. name, company und email sind leer. Der Generative AI Agent legt bei output_format: "json" die extrahierten Felder direkt auf oberster Ebene ab. Unter last_message steht die eingehende Payload — also nur freitext. {{ last_message.name }} greift ins Leere.
Richtig ist der direkte Pfad:
payload: {
name: "{{ name }}",
company: "{{ company }}",
email: "{{ email }}"
}
Fehler 3 — fehlendes no_merge. Im Body stehen last_message und workflow_run_id, die dort nichts zu suchen haben. Ohne no_merge: true hängt der Post Agent die komplette eingehende Payload an den Request an.
no_merge: true
Mit beiden Korrekturen:
body: "{\"name\":\"Dr. Anke Reinhardt\",
\"company\":\"Nordwerk GmbH\",
\"email\":\"a.reinhardt@nordwerk-pt.example\"}"
Im Log eines virtuellen Post Agents tauchen zusätzlich key, something und custom_field_key auf. Das sind die Standardoptionen des Agent-Typs, die bei virtuellen Läufen mit hineingemischt werden. Bei einem real deployten Agent kommen sie nicht vor — nicht davon irritieren lassen.
Teil 3: Den echten Lauf beobachten
Aufgabe 4 — Deployen und ausführen
Deploye den korrigierten Stand und führe ihn mit einem realistischen E-Mail-Text aus. Prüfe danach: Ist ein Kontakt in der API angekommen?
Was passieren sollte
Nichts kommt an. Die API hat keinen neuen Kontakt.
Aufgabe 5 — Herausfinden, wo die Kette abreißt
Mögliche Lösung anzeigen
workflow_messages zeigt Nachrichten von eingang und von extrahieren — aber keine von pruefen.
agent_messages auf den KI-Agent bestätigt, dass die Extraktion funktioniert hat:
{
"name": "Dr. Anke Reinhardt",
"company": "Nordwerk Präzisionstechnik GmbH",
"email": "a.reinhardt@nordwerk-pt.example",
"last_message": { "freitext": "…" }
}
Der Filter lässt also nichts durch. Ein Blick auf seine Regel:
rules: [
{ type: "field==value", value: "ja", path: "email_gueltig" }
]
Fehler 4 — die Filterregel prüft ein Feld, das es nicht gibt. Der Prompt fordert name, company und email an — von email_gueltig steht dort nichts. Das Feld existiert in keiner Nachricht, die Bedingung ist nie erfüllt, der Filter blockt alles.
Es gibt zwei saubere Lösungen. Entscheide dich bewusst:
A — Das Feld erzeugen. Erweitere Zielschema und Prompt um das Feld und passe die Regel an den tatsächlichen Datentyp an:
Zielschema:
{"name": "…", "company": "…", "email": "…",
"email_gueltig": "true wenn die E-Mail ein @ und eine Domain mit Punkt enthaelt, sonst false"}
B — Auf dem vorhandenen Feld prüfen. Statt eines abgeleiteten Kennzeichens direkt die Adresse prüfen:
rules: [
{ type: "regex", value: "@", path: "email" }
]
Variante B ist die robustere: ein Feld weniger, das die KI erfinden kann. Das ist genau die Abwägung aus Kurs 5, Einheit 7 — LLM oder Regel.
Aufgabe 6 — Reparieren und mit denselben Daten nachtesten
Korrigiere den Filter und teste erneut — ohne das Formular neu auszufüllen.
Mögliche Lösung anzeigen
workflow_updatemit dem korrigierten Quelltext — mitnameunddescription, sonst heißt der Workflow danach „Workflow"workflow_messagesaufrufen und die ID der ursprünglichen Eingangsnachricht heraussuchenmessage_reemitmit dieser ID
{ "success": true, "message": "Message 29213 re-emitted as 29215" }
Danach agent_logs auf den Post-Agent:
Received response status 201
Und in der API steht der Kontakt.
Der Vorteil gegenüber einem neuen Formulareintrag: identische Eingangsdaten. Du vergleichst die Reparatur gegen exakt denselben Fall, der vorher gescheitert ist.
Teil 4: Nachbereiten
Aufgabe 7 — Die vier Fehler einordnen
Trage zusammen, welches Werkzeug jeden Fehler gefunden hat.
Mögliche Lösung anzeigen
| # | Fehler | Gefunden durch | Symptom |
|---|---|---|---|
| 1 | payload { statt payload: { | towelscript_graph | edges: [] |
| 2 | Liquid-Pfad last_message.name | agent_test_run → Log | leere Felder im Body |
| 3 | no_merge fehlt | agent_test_run → Log | Fremdfelder im Body |
| 4 | Filterregel auf nicht existierendes Feld | workflow_messages + workflow_show | Kette bricht am Filter ab |
Zwei Beobachtungen:
towelscript_checkhat keinen einzigen der vier Fehler gefunden.valid: trueheißt: der Text lässt sich lesen. Mehr nicht.- Die Fehler mussten in dieser Reihenfolge behoben werden. Ohne Verbindungen keine Nachrichten, ohne Nachrichten kein Filter-Verhalten zu beobachten. Fehlersuche ist schichtweise: erst Struktur, dann einzelne Agents, dann der Fluss.
Aufgabe 8 — Aufräumen
Lösche deinen Testkontakt aus der API und exportiere den Workflow, bevor du ihn löschst.
Die vier Fehler sind keine Einzelfälle, sondern die vier häufigsten. Nimm sie in deine CLAUDE.md auf:
## TowelScript-Regeln
- Verschachtelte Optionen IMMER mit Doppelpunkt: `payload: { … }`
- Nach jeder Änderung `towelscript_graph` prüfen: edges = nodes − 1
- Bei `ai` mit `output_format: "json"` liegen die Felder OBEN, nicht unter `last_message`
- Post Agent: `no_merge: true`, wenn nur die Payload-Felder gesendet werden sollen
- Filterregeln nur auf Felder, die nachweislich in einer echten Nachricht vorkommen
- `workflow_update` immer mit `name` und `description`
Was du jetzt kannst
- Fehler finden, die keine Fehlermeldung erzeugen
- Statische Prüfung, Einzeltest und Laufbeobachtung in der richtigen Reihenfolge einsetzen
- Aus einem
Full request-Log auf die Ursache schließen - Eine Reparatur gegen exakt denselben Fall verifizieren, der vorher gescheitert ist