Zum Hauptinhalt springen

Einheit 4 — Die Syntax des Workflow-Quelltexts

Was du nach dieser Einheit weißt: Du kannst einen Workflow vollständig als Text schreiben — Module, Flows, Nodes, Optionen, Verbindungen — und kennst die Stellen, an denen die Syntax sich anders verhält, als man erwartet.

Der Quelltext ist klein. Es gibt vier Bausteine, und du hast sie in zehn Minuten gesehen. Die Tücke liegt nicht im Umfang, sondern in einer Handvoll Sonderfälle, die ohne Fehlermeldung das Falsche tun. Die stehen deshalb alle in dieser Einheit.

Das Grundgerüst

module academy

flow kontaktanfrage {
node eingang: form {
secret: "academy-kurs6"
}

node extrahieren: ai {
model: "gus:tav"
}

eingang -> extrahieren
}

module

Jede Datei beginnt mit einer Modulzeile. Fehlt sie, bricht der Compiler mit missing module declaration ab. Der Name ist ein Bezeichner — Buchstaben, Ziffern, Unterstriche, keine Anführungszeichen.

flow

Ein flow-Block umfasst einen Workflow. Auch der Flow-Name ist ein Bezeichner, kein String:

flow kontaktanfrage { … }      // richtig
flow "Kontaktanfrage" { … } // Syntaxfehler
Der Flow-Name ist nicht der Anzeigename

Der Name im Quelltext taucht in 42°flow nirgends auf. Den sichtbaren Workflow-Namen setzt du über den Parameter name beim Deployen.

Rufst du workflow_update ohne name auf, wird der Workflow in der Oberfläche in „Workflow" umbenannt — und eine nicht mitgegebene description wird geleert. Gib beide bei jedem Update mit an.

node

node <name>: <typ> {
option: wert
}

Der Node-Name wird zum Agent-Namen in 42°flow. Gib ihm also einen Namen, den ein Kollege im Canvas versteht — die Regeln aus Kurs 5, Einheit 5 gelten unverändert.

Die gültigen Typen liefert towelscript_catalog (75 Stück, siehe Einheit 5).

Verbindungen

eingang -> extrahieren -> anlegen

-> und => sind gleichwertig. Verzweigungen schreibst du als mehrere Zeilen:

eingang -> pruefen
pruefen -> anlegen
pruefen -> benachrichtigen

Optionen schreiben

Einfache Werte

node a: ai {
model: "gus:tav" // String
timeout: 120 // Zahl
emit_on_error: true // Boolean
}

key: wert und key = wert werden beide akzeptiert — außer bei Objekten, siehe unten. Bleib der Einheitlichkeit halber überall bei :.

Kommentare

Nur //. #, -- und /* */ sind Syntaxfehler.

Strings mit JSON darin

Viele Agents erwarten JSON als String — etwa form_fields beim Web Form Agent. Nutze dafür einfache Anführungszeichen, sonst wird doppelt maskiert und im Agent landet \\\":

node eingang: form {
form_fields: '[{"name":"freitext","label":"E-Mail-Text","type":"textarea","rows":8}]'
}

Mehrzeilige Texte

Für Prompts und Templates gibt es dreifache Anführungszeichen:

node extrahieren: ai {
prompt_template: """Extrahiere den Absender aus dem folgenden Text.
Gib ausschliesslich ein JSON-Objekt zurueck.

Text:
{{ freitext }}"""
}

Backticks funktionieren ebenfalls. '''…''' liefert einen leeren String — sieht aus wie ein Textblock, ist aber keiner.

In einem normalen "…"-String schreibst du echte Zeilenumbrüche direkt hinein; ein \n bleibt literal stehen.

Objekte — hier wird es gefährlich

Für verschachtelte Optionen wie payload oder headers gibt es drei Schreibweisen, die sich sehr ähnlich sehen. Nur eine davon ist richtig.

// ✅ RICHTIG - Doppelpunkt vor der Klammer
payload: {
name: "{{ name }}",
company: "{{ company }}"
}
// ❌ FALSCH - Block ohne Doppelpunkt
payload {
name: "{{ name }}"
}
// ❌ FALSCH - Gleichheitszeichen
payload = { "name": "{{ name }}" }

Was die falschen Varianten anrichten:

SchreibweiseWas passiertMeldung?
payload: { … }Korrektes verschachteltes Objekt
payload { … }Die Schlüssel landen flach auf oberster Optionsebene — es gibt gar kein payload. Und: alle Verbindungen des Flows verschwindenkeine
payload = { … }Die Option wird komplett verworfenkeine
zwei { … }-Blöcke im selben NodeSyntaxfehler beim zweiten Blockja
Die Blockschreibweise verschluckt deine Verbindungen

Das ist der teuerste Fehler im Quelltext. Ein Node mit payload { … } lässt towelscript_check weiterhin valid: true melden — aber die Zeile eingang -> extrahieren -> anlegen wird nicht mehr ausgewertet.

Deployst du das, bekommst du drei Agents, die nebeneinander im Canvas stehen und nichts miteinander zu tun haben. Der Workflow läuft, tut aber nichts.

Deshalb: nach jeder Änderung towelscript_graph aufrufen und prüfen, dass edges nicht leer ist. Mehr dazu in Einheit 6.

Arrays

Arrays von Objekten funktionieren erwartungsgemäß:

node pruefen: filter {
expected_receive_period_in_days: 365
keep_message: true
rules: [
{ type: "regex", value: "@", path: "email" }
]
}

Schlüssel mit Sonderzeichen

Objektschlüssel mit Bindestrich müssen in Anführungszeichen:

headers: {
"x-api-token": "…",
"Content-Type": "application/json"
}

Zugangsdaten

Zugangsdaten gehören nie in den Quelltext. Referenziere sie über @secret(…):

node anlegen: http.post {
post_url: "https://api.example.com/deals"
token: @secret(Pipedrive_token)
}

Der Name ist der, den credential_list liefert — ohne Anführungszeichen. Das ist dieselbe Regel wie in Kurs 5, Einheit 2, nur in Textform.

Ein vollständiges Beispiel

Das ist der Workflow, den du in der nächsten Übung baust:

module academy

flow kontaktanfrage {
node eingang: form {
secret: "academy-kurs6"
form_fields: '[{"name":"freitext","label":"E-Mail-Text einfuegen","type":"textarea","rows":8,"required":true}]'
response_text: "Danke - die Anfrage wird verarbeitet."
}

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 anlegen: http.post {
post_url: "https://69cde52533a09f831b7c9fa5.mockapi.io/api/v1/contacts"
method: "post"
content_type: "json"
no_merge: true
payload: {
name: "{{ name }}",
company: "{{ company }}",
email: "{{ email }}"
}
}

eingang -> extrahieren -> anlegen
}

Beachte output_format: "json" beim KI-Node: Damit legt der Generative AI Agent die extrahierten Felder direkt auf oberster Payload-Ebene ab — deshalb kann der nächste Node einfach {{ name }} schreiben. Ohne diese Option steht die Antwort als Text unter generation. Die eingehende Payload findest du in beiden Fällen unter last_message.

Zusammengefasst

RegelMerksatz
module zuerstSonst kein Compile
Flow-Name = BezeichnerAnzeigename über name beim Deploy
Nur // als Kommentar# und /* */ sind Fehler
Einfache Quotes für JSON-StringsSonst doppelte Maskierung
"""…""" für Mehrzeiler'''…''' ergibt leeren String
payload: { … } mit DoppelpunktOhne Doppelpunkt sind die Verbindungen weg
@secret(name) für ZugangsdatenNie Klartext im Quelltext
Nach jeder Änderung graph prüfenedges darf nicht leer sein

Weiter: Einheit 5 — Nachschlagen statt raten