Zum Inhalt

Workflow-Knoten

Jeder Schritt, den ein Workflow enthalten kann: womit er konfiguriert wird, was er liest, was er erzeugt und was ein Fehler bedeutet. Die Palette im Editor zeigt dieselben Knoten aus derselben Registry. Die Felddokumentation unten wird aus dem Quellcode erzeugt und bleibt deshalb auf Englisch.

Einige Regeln gelten für jeden Knoten:

  • Eine Kante legt die Reihenfolge fest, ein Binding trägt einen Wert. Die Eingabefelder eines Knotens werden an frühere Ausgaben, an ein Literal oder an eine Datei- oder Tabellenreferenz gebunden. Siehe Einen Knoten konfigurieren.
  • Ein Pfad in einen frei geformten Wert wird geprüft, wenn der Knoten läuft. Die Nutzlast eines Triggers, ein gemappter Datensatz und die strukturierte Antwort eines Agents haben keine feste Form, daher wird ein Binding wie payload.email beim Veröffentlichen angenommen und beim Ausführen des Knotens gegen sein Ziel geprüft. Ein Wert, der nicht passt, lässt den Run mit INVALID_BINDING fehlschlagen, und der Handler sieht ihn nie.
  • Ressourcen werden zweimal geprüft. Das Veröffentlichen lehnt eine Collection, eine Agent-Version, ein Credential oder einen Empfänger ab, die der Autor des Graphen nicht erreichen kann. Jeder Run prüft sie erneut gegen seinen eigenen Principal, weil der Zugriff dazwischen entzogen werden kann.
  • Effektart und Wiederholungen entscheiden, was die Engine nach einem Fehler tun darf. Ein pure- oder idempotent-Schritt wird wiederholt. Ein Schritt, der bereits gewirkt haben kann und keine Garantie gibt, hält für einen Menschen an. Die eigene Policy eines Knotens legt fest, wie oft er wiederholt wird, wie lange ein Aufruf dauern darf und wohin ein Fehler geht.

Trigger

Ein Workflow startet mit einem Trigger, dem Knoten, mit dem sein Graph beginnt. Das Veröffentlichen einer Version schaltet ihren Trigger ein; siehe Einen Workflow von außerhalb der Konsole starten. Jeder Trigger gibt den folgenden Schritten das weiter, womit seine Oberfläche den Run gestartet hat, beim Annehmen des Runs eingefroren und höchstens WORKFLOW_RUN_MAX_INPUT_BYTES. Ein Test-Run, der mit einer Eingabe anderer Form gestartet wird, lässt den Trigger mit TRIGGER_INPUT_INVALID fehlschlagen. Ein zweiter Trigger oder ein Trigger, mit dem der Graph nicht beginnt, wird beim Veröffentlichen abgelehnt.

core.input

API request. Über eine HTTP-Anfrage oder einen WebSocket gestartet. Seine ID bleibt core.input, sodass ein Graph, der geschrieben wurde, bevor Manual und API getrennt waren, weiter über die API startet. Er gibt dem Graphen die Eingabe des Runs als payload, was auch immer der Aufrufer gesendet hat, und nennt die Oberfläche in triggered_by.

app.workflows.contracts.io.WorkflowInputPayload

Bases: BaseModel

What a run was started with, as core.input hands it to the graph.

payload is whatever the invoking surface supplied - an API body, a chat message, a webhook's JSON, a table record's snapshot - untyped here because the graph, not the node, decides its shape: a binding reads a field out of it by path and is checked against the target when the run dispatches.

Seine Eingabefelder machen die Eingabe zu einem Vertrag. Ein Feld hat einen Namen, einen Typ - Text, Zahl, ganze Zahl, ja oder nein, Datum oder Auswahl - und ob es Pflicht ist. Ohne Felder nimmt ein Run jedes JSON-Objekt. Mit Feldern fragt Start a run jedes beim Namen ab, ein Run, dessen Eingabe eines fehlt, eines vom falschen Typ oder ein nicht deklariertes sendet, wird vor dem Start mit WORKFLOW_RUN_INPUT_INVALID abgelehnt, und ein Binding an payload.<feld> wird beim Veröffentlichen auf seinen Typ geprüft.

app.workflows.contracts.fields.InputField

Bases: BaseModel

One value a run starts with, asked for by name in the Run dialog.

trigger.manual

Manual. Von einer Person gestartet - Test draft im Editor oder Start a run auf seiner Run-Seite. Er gibt dem Graphen dasselbe wie API request und nimmt dieselben Eingabefelder, nach denen der Editor vor dem Start fragt.

trigger.chat

Chat message. Von einer Nachricht im Chat gestartet, in dem dieser Workflow zum Antworten gewählt ist. Der Text seines core.output wird in diese Unterhaltung zurückgeschrieben.

app.workflows.nodes._triggers.ChatTriggerOutput

Bases: _Frozen

The message a member sent in the chat, and where the answer goes.

trigger.webhook

Webhook. Von einer signierten Zustellung an die eigene Adresse des Workflows gestartet, die die erste Veröffentlichung des Knotens zusammen mit seinem Signatur-Secret anlegt.

Vor der Veröffentlichung öffnet Listen for test event im Output-Bereich des Triggers für zwei Minuten eine Test-URL für den Entwurf. Der eine Aufruf an sie, ein JSON-Objekt, wird als Ausgabe des Triggers angeheftet, sodass jeder Schritt danach mit einer echten Zustellung getestet werden kann. Die Test-URL prüft keine Signatur und startet nie einen Run.

app.workflows.nodes._triggers.WebhookTriggerOutput

Bases: _Frozen

One signed delivery: its JSON body and the id the sender gave it.

trigger.schedule

Schedule. Nach der Uhr gestartet, in der Zeitzone des Workflows und höchstens einmal pro Minute.

app.workflows.nodes._triggers.ScheduleTriggerConfig

Bases: _Frozen

How often the workflow runs, and what every run starts with.

app.workflows.nodes._triggers.ScheduleTriggerOutput

Bases: _Frozen

When the tick fired, and the input the schedule starts every run with.

trigger.table_record

Table record. Gestartet, wenn ein Datensatz seiner Tabelle hinzugefügt, geändert oder gelöscht wird - je nachdem, was events nennt - und jedem Filter entspricht. Eine Änderung startet ihn nur, wenn sich eine für die Tabelle gewählte Spalte geändert hat, sofern Spalten gewählt sind. Das Veröffentlichen braucht Lesezugriff auf die Tabelle.

app.workflows.nodes._triggers.TableRecordTriggerConfig

Bases: _Frozen

Which table, which of its records' events, and which records, start the workflow.

app.workflows.nodes._triggers.TableRecordTriggerOutput

Bases: _Frozen

The record the event is about: its values by column id and by label, who did it, and - for a change or a deletion - what it held before.

trigger.workflow_call

Called by a workflow. Vom Schritt workflow.run eines anderen Workflows gestartet, mit den Feldern, die er deklariert - denselben wie bei Manual - und nie von Hand oder über die API. Die Eingabe des Aufrufers wird vor dem Start dagegen geprüft.

trigger.form

Form. Gestartet, wenn jemand die öffentliche Seite des Workflows ausfüllt, unter dem Link, den die erste Veröffentlichung des Knotens anlegt. Die Seite zeigt Titel, Beschreibung und ein Feld je Feld - dieselben Felder wie Manual -, und eine Einsendung, die zu ihnen passt, startet einen Lauf als das Mitglied, das ihn veröffentlicht hat. triggered_by ist form.

app.workflows.nodes.trigger_form._handler.FormTriggerConfig

Bases: TriggerInputConfig

The page a form shows, and the fields it asks for.

trigger.github

GitHub. Gestartet von einer von GitHub signierten Zustellung für das Repository, eines der Ereignisse und - sofern er welche nennt - eine der Aktionen, die der Knoten nennt. Die Veröffentlichung registriert den Hook über das verbundene GitHub-Konto der Organisation, wenn sie kann, oder zeigt das Geheimnis einmal zum Einfügen. triggered_by ist github.

app.workflows.nodes.trigger_github._handler.GithubTriggerConfig

Bases: BaseModel

Which repository, and which of its events, start the workflow.

takes

takes(*, event, action, repository)

Whether a delivery of event and action on repository starts the workflow.

app.workflows.nodes._triggers.GithubTriggerOutput

Bases: _Frozen

One GitHub delivery: which event and action, where, by whom, and all of it.

trigger.gmail

Gmail. Gestartet von einer neuen Nachricht im verbundenen Gmail-Postfach der Organisation, die zu allen gesetzten Filtern passt, im Prüfintervall Check for new mail every des Postfachs (standardmäßig eine Minute) gelesen. Die Veröffentlichung wird abgelehnt, bis ein Postfach verbunden ist. triggered_by ist gmail. Sender contains prüft die Adresse des Absenders, nicht den Anzeigenamen: @acme.com ist genau diese Domain, ohne Subdomains. Ist es gesetzt, muss die Nachricht außerdem die Prüfung des authentifizierten Absenders bestehen (DMARC oder eine DKIM-Signatur der Absenderdomain). Nachrichten mit dem Label SENT, DRAFT, SPAM oder TRASH werden übersprungen, es sei denn, Label nennt sie.

Die Ausgabe trägt den Thread - thread_id, rfc_message_id, in_reply_to, references, received_at - und den html-Inhalt, bis zu 20.000 Zeichen gelesen. Die Header-Felder schreibt der Absender; sie ordnen eine Antwort in den Thread ein und beweisen nichts darüber, wer die Nachricht gesendet hat.

Ein Lauf, den das Postfach gestartet hat, speichert die Anhänge der Nachricht als Dateien des Runs, aber nur, wenn der Workflow sie liest: Ein Graph, der weder die attachments des Triggers noch seine gesamte Ausgabe bindet, lässt jeden Anhang not_fetched. Jede gespeicherte Datei zählt zum Dateikontingent der Organisation, und an einen Trigger ohne Sender contains kann jeder schreiben; binde attachments daher nur, wenn ein Schritt die Dateien braucht.

Ein Postfach liefert höchstens 20 pro Nachricht, und nur die ersten 10 davon werden berücksichtigt, zusammen bis 25 MiB, jeder im Größenlimit für Chat-Uploads, in der Liste erlaubter Dateitypen und im Speicherkontingent der Organisation. Ein ausgelassener Anhang bleibt mit einem reason in attachments: too_many, too_large, type_not_allowed, quota, unreadable oder not_fetched. Ein Lauf, den das Postfach nicht gestartet hat - ein Testlauf - liest nichts, daher ist jeder Anhang not_fetched.

unreadable ist eine Antwort mit 400, 401, 403 oder 404 für den Anhang oder ein Postfach-Grant, der fehlt, ausgeschaltet ist oder sich nicht erneuern lässt. Jeder andere Ausfall des Anbieters lässt den Schritt mit MAIL_ATTACHMENT_UNREACHABLE fehlschlagen, was wiederholt wird. Beide Mail-Trigger nehmen Eingaben von außen an.

app.workflows.nodes.trigger_gmail._handler.GmailTriggerConfig

Bases: BaseModel

Which messages start the workflow; every new one when nothing is set.

The same filter an agent's Gmail trigger applies, read by the same matcher. A sender filter also needs the message to pass the authenticated-sender check (DMARC, or a DKIM signature from the sender's domain).

app.workflows.nodes._triggers.EmailTriggerOutput

Bases: _Frozen

One message that arrived in the connected mailbox.

app.workflows.nodes._triggers.MailAttachment

Bases: _Frozen

One attachment the message listed, and the stored file when the trigger staged it.

trigger.outlook

Outlook. Gestartet von einer neuen Nachricht in einem Ordner des verbundenen Microsoft-365-Postfachs der Organisation, die zu allen gesetzten Filtern passt, im Prüfintervall Check for new mail every des Postfachs (standardmäßig eine Minute) gelesen. Die Veröffentlichung wird abgelehnt, bis ein Postfach verbunden ist. triggered_by ist outlook. Microsoft Graph wird Ordner für Ordner gelesen, deshalb sind Folder und Shared mailbox zugleich der Ort, an dem der Poller sucht, und ein Filter; das erste Lesen eines Ordners beginnt mit dem Zeitpunkt der Veröffentlichung oder des letzten Einschaltens des Workflows. Ein gemeinsames Postfach, das der Microsoft-Grant der Organisation öffnen kann, zu nennen, erfordert die Berechtigung, Verbindungen zu verwalten, wie bei Outlook-Agent-Triggern; ein Workflow, der schon eines liest, behält es bei späteren Veröffentlichungen, und es zu ändern erfordert die Berechtigung erneut. Sender contains prüft die Adresse des Absenders wie bei Gmail und verlangt Post, die die Absenderdomain authentifiziert hat oder die aus dem Tenant stammt. Ein Ordner oder gemeinsames Postfach, das Outlook als nicht lesbar meldet, schaltet den Workflow aus, mit mailbox_folder_unreadable im Audit-Log. Eine gespeicherte Konfiguration, die nicht mehr validiert, überspringt der Poller. Die Anhänge werden wie bei trigger.gmail gespeichert, und labels enthält die Kategorien der Nachricht. Die Ausgabe ist dieselbe wie bei trigger.gmail.

app.workflows.nodes.trigger_outlook._handler.OutlookTriggerConfig

Bases: BaseModel

Which folder is read, and which of its messages start the workflow.

The same filter an agent's Outlook trigger applies, read by the same matcher: every key here is one that trigger accepts. A sender filter also needs the message to pass the authenticated-sender check (DMARC, a DKIM signature from the sender's domain, or mail from inside the tenant).

trigger.channel_message

Channel message. Gestartet von einer Nachricht an einen der Slack-, Mattermost- oder Telegram-Bots der Organisation, die der Bot mit seinem eigenen Agenten beantwortet hätte und die zum Filter des Knotens passt. Das Veröffentlichen braucht channels:manage, und die Nachrichten eines Bots starten höchstens einen Workflow. triggered_by ist channel_message. Ein Teams-Bot wird beim Veröffentlichen und im Testlauf abgelehnt; ein früher veröffentlichter Teams-Listener gibt seine Nachrichten an den Agenten des Bots zurück.

app.workflows.nodes.trigger_channel_message._handler.ChannelMessageTriggerConfig

Which bot's messages start the workflow, and which of them.

takes

takes(*, chat_type, text)

Whether a message in a chat_type chat saying text starts the workflow.

app.workflows.nodes._triggers.ChannelMessageTriggerOutput

Bases: _Frozen

One message to the bot: what it said, who said it, and where to answer it.

channel_id and thread_id are what a Send a message step of the same platform takes, so a reply goes back into the thread the message came from.

trigger.workflow_failed

On failure of a workflow. Einmal für jeden fehlgeschlagenen echten Lauf eines Workflows gestartet, dessen Einstellungen diesen als Fehler-Workflow nennen, als das Mitglied, das ihn gewählt hat. Ein von diesem Auslöser gestarteter Lauf startet selbst nie einen Fehler-Workflow.

app.workflows.nodes._triggers.WorkflowFailedTriggerOutput

Bases: _Frozen

The run that failed: which workflow, which step, and with what error.

core.output

Womit der Workflow antwortet. Seine gebundenen Felder werden zum output des Runs, den die API zurückgibt und die aufrufende Oberfläche zustellt. Er hat dieselben Felder wie die Ausgabe von agent.run, sodass die Antwort eines Agents direkt gebunden werden kann. Eine Ausgabe ohne Bindings ist eine leere Antwort. Ein Run antwortet einmal, daher lehnt das Veröffentlichen einen Graphen mit zwei solchen Schritten ab, die in einem Run beide laufen können.

app.workflows.contracts.io.WorkflowOutputPayload

Bases: BaseModel

What a run answers with, as core.output records it.

The same fields agent.run produces, so the common case - an agent feeding the workflow's answer - type-checks with nothing in between. Every field is optional: a workflow that only writes to a table still ends, with nothing to say.

webhook.respond

Respond to webhook. Beantwortet die Zustellung, die den Run gestartet hat, mit einem Status (200 bis 599), Headern und einem JSON-body, der aus einem früheren Schritt gebunden ist. Eine Zustellung an einen Graphen mit diesem Schritt wartet auf ihn, statt 202 zu bekommen, höchstens WORKFLOW_WEBHOOK_RESPONSE_TIMEOUT_SECONDS lang. Der erste abgeschlossene Respond-Schritt ist die Antwort, eine Wiederholung derselben Zustellung bekommt sie erneut, und der Run läuft danach weiter. Ein Run, der endet, ohne den Schritt zu erreichen, antwortet 202, wenn er erfolgreich war, und sonst 500. Das Veröffentlichen lehnt einen Graphen mit zwei solchen Schritten ab, die in einem Run beide laufen können. Header, die der eigenen Antwort der API gehören, werden beim Veröffentlichen abgelehnt: Framing, Content-Type, Set-Cookie sowie die CORS- und Browser-Policy-Header.

app.workflows.nodes.webhook_respond.WebhookRespondConfig

Bases: BaseModel

The status and headers the delivery is answered with.

data.map

Baut aus früheren Ausgaben einen kleinen, typisierten Datensatz. Jedes Mapping liest einen Wert mit einem JMESPath-Ausdruck und wandelt ihn in string, number, integer, boolean, json, file_ref oder table_ref um. Ein Wert, der sich nicht umwandeln lässt, schlägt mit MAPPING_COERCION_FAILED fehl und nennt das Feld.

app.workflows.nodes.data_map._handler.DataMapConfig

Bases: BaseModel

The fields to build, in order.

app.workflows.nodes.data_map._handler.FieldMapping

Bases: BaseModel

One output field: where its value comes from, and what type it becomes.

data.filter und data.combine

Filter a list behält die Elemente einer gebundenen Liste, für die eine JMESPath-Bedingung über item und index gilt, und sagt, wie viele es verworfen hat. Combine lists macht aus first und second eine Liste: append hängt die zweite an, by_position vereint die Objekte an derselben Position, und by_key vereint jedes Objekt der ersten mit dem Objekt der zweiten mit demselben key. Hat ein Feld beide, gewinnt die zweite; elementweises Vereinen braucht Objekte und scheitert sonst mit COMBINE_NEEDS_OBJECTS.

app.workflows.nodes.data_filter._handler.DataFilterConfig

Bases: BaseModel

What an item must be to be kept.

app.workflows.nodes.data_combine._handler.DataCombineConfig

Bases: BaseModel

How the two lists become one.

text.compare

Compare texts bewertet, wie ähnlich sich zwei Texte, a und b, sind: 1, wenn sie nach Abzug der ignorierten Unterschiede gleich sind, 0, wenn sie nichts gemeinsam haben. Es ruft kein Modell auf und kostet nichts, und dieselben zwei Texte ergeben immer dieselben Zahlen (Werte werden auf sechs Stellen gerundet). Die Methode legt fest, was gezählt wird:

  • char_ratio (Standard) ist der Anteil der Zeichen, die ein Bearbeiter nicht anfassen müsste, aus der exakten Editierdistanz über Zeichen.
  • token_f1 ist der Anteil der Wörter, die beide Texte gemeinsam haben, unabhängig von der Reihenfolge.
  • line_diff ist der Anteil der unveränderten Zeilen.

Zusammengesetzte und zerlegte Akzente sind nie eine Änderung, ebenso wenig Zeilenenden. Den Rest bestimmt der Schritt. Ignore whitespace ist standardmäßig an: Es fasst Leerraum innerhalb einer Zeile zusammen, kürzt jede Zeile und lässt leere Zeilen weg. Zeilenumbrüche zählen weiter, daher ignoriert nur token_f1, das Wörter zählt, wo eine Zeile endet. Ignore case und Ignore punctuation sind standardmäßig aus. Die Wörter von token_f1 sind Folgen aus Buchstaben, Ziffern und Unterstrichen: Es trennt an Apostrophen und zerlegt Text ohne Leerzeichen nicht in Wörter.

Leave out quoted mail and signatures lässt jede Zeile weg, deren erstes Nicht-Leerzeichen ein > ist (auch eingerückte Zitate), die Einleitungszeile über einem Zitat und alles nach einem Signaturtrenner -- oder einem bloßen -- oder nach einer Zeile ----- Original Message -----. Das ist eine Heuristik für Nur-Text-Mails: HTML-Zitate, eine Antwort, deren Zitat nicht mit > markiert ist, eine auf zwei Zeilen umbrochene Einleitung (sie bleibt stehen) und eine Signatur ohne Trenner erkennt sie nicht.

Jeder Text hat höchstens 100.000 Zeichen, gezählt vor jeder Normalisierung; zwei Texte dieser Größe zu bewerten dauert einige Sekunden. Ein längerer Text beendet den Run mit INVALID_BINDING, bevor der Schritt läuft, sodass error.handle ihn nicht weiterleiten kann. Ein Schritt, bei dem weder a noch b gebunden ist, scheitert mit TEXT_COMPARE_NOT_CONFIGURED; ist nur eines gebunden, endet der Run mit INVALID_BINDING. Leere Texte sind erlaubt: zwei leere ergeben 1, ein leerer 0. Die Palette zeigt den Schritt mit einem Diff-Symbol bei den Datenschritten.

app.workflows.nodes.text_compare._handler.TextCompareConfig

Bases: BaseModel

How to score the two texts and what to ignore while doing it.

app.workflows.nodes.text_compare._handler.TextCompareOutput

Bases: BaseModel

The score, how much changed, and what was counted.

html.parse

Parse HTML liest HTML als Text, als Links und als die Elemente, die ein CSS-Selektor auswählt. Es ist für das HTML gedacht, dem ein Ablauf begegnet: ein Mail-Text, ein http.request-Body, eine von http.download gespeicherte Seite. text.extract liest Dokumente, kein Markup - es würde die Tags als Text zurückgeben. Binden Sie entweder html (höchstens 2.000.000 Zeichen) oder file (höchstens 5.000.000 Bytes, geprüft, bevor die Datei gelesen wird), nicht beides. Eine Datei wird anhand ihrer Byte-Order-Mark, ihres <meta charset> oder ihrer Bytes dekodiert, nötigenfalls verlustbehaftet, und ihr deklarierter Typ wird nicht geprüft: Ein PDF wird als verstümmelter Text gelesen und nicht abgelehnt. Ein NUL-Zeichen wird als U+FFFD gelesen.

Der Schritt hat drei Ausgaben, alle begrenzt:

  • text ist der lesbare Text als Markdown, gekürzt auf Most characters of text (standardmäßig 100.000; text_truncated sagt, dass gekürzt wurde). Überschriften, Listen und Code bleiben; Skripte, Stile, Menüs (nav), Fußzeilen und der <head> nicht. Der Web-Konnektor einer Wissenssammlung liest eine Seite genauso. title ist der <title> der Seite, auf 500 Zeichen gekürzt, oder null, wenn die Seite keinen hat (auch bei einem leeren <title>).
  • links enthält jedes a[href] einmal, in Dokumentreihenfolge, mit dem Text seines ersten Ankers (auf 500 Zeichen gekürzt). Ein Link wird gegen Base address aufgelöst, wenn eine gesetzt ist, sonst bleibt er, wie er geschrieben ist. Links auf javascript:-, vbscript:- und data:-Adressen werden weggelassen, damit ein späterer Schritt, der jeden Link abruft, nie ein Skript bekommt; ebenso ein href über 2.048 Zeichen und einer, der sich nicht als Adresse lesen lässt. Höchstens 500 Links werden behalten und die ersten 5.000 Anker angesehen; links_truncated sagt, dass die Seite mehr hatte.
  • matches enthält die Elemente, die CSS selector auswählt, in Dokumentreihenfolge, höchstens Most matches (standardmäßig 20, bis 200); match_count sagt, wie viele der Selektor vor dem Kürzen ausgewählt hat. Jeder Treffer hat seinen text und sein äußeres html (beide auf 10.000 Zeichen gekürzt) und seine ersten 50 attributes, wobei ein mehrwertiges wie class mit Leerzeichen verbunden und jeder Wert auf 2.000 Zeichen gekürzt wird. Ohne Selektor gibt es keine Treffer. Ein Selektor, der kein gültiges CSS ist, wird beim Veröffentlichen unter Nennung des Felds abgelehnt.

Nichts wird abgerufen und nichts ausgeführt. Kein Bild, kein Stylesheet, keine Base address und kein Link wird angefragt, ein Skript ist nur Text, und der Parser stammt aus der Standardbibliothek, ist kein XML-Parser, sodass keine Entität von außerhalb der Seite aufgelöst wird.

Das Parsen läuft in einem Kindprozess mit 20 Sekunden Wanduhrzeit, einem CPU-Limit und einem Speicherlimit, weil ein Selektor wie p div div über tief verschachteltem Markup Minuten brauchen kann. Nach den 20 Sekunden wird der Kindprozess beendet und der Schritt scheitert mit HTML_TOO_COMPLEX, ohne erneuten Versuch. Auch der Aufwand je Anker und je Treffer ist begrenzt, sodass eine einzelne Seite den Schritt weder langsam noch seine Ausgabe groß machen kann.

Was passiert ist Ergebnis
Keine Einstellungen oder keine Eingabe HTML_PARSE_NOT_CONFIGURED
html über 2.000.000 Zeichen, oder html und file beide gebunden INVALID_BINDING, bevor der Schritt läuft
Die Datei fehlt oder ist größer als 5.000.000 Bytes FILE_NOT_FOUND, FILE_TOO_LARGE
Kein Decoder kann die Bytes lesen, oder der Leseprozess stirbt HTML_NOT_READABLE, das Detail reason sagt welches
Die Seite braucht über 20 Sekunden HTML_TOO_COMPLEX

INVALID_BINDING beendet den Run, bevor der Schritt läuft, sodass error.handle ihn nicht weiterleiten kann; eine zu große Datei ist das FILE_TOO_LARGE, mit dem der Schritt scheitert und das sich weiterleiten lässt. Die Palette zeigt den Schritt mit einem Code-Symbol bei den Datenschritten.

app.workflows.nodes.html_parse._handler.HtmlParseConfig

Bases: BaseModel

What to pick out of the page and how to resolve its links.

app.workflows.nodes.html_parse._handler.HtmlParseOutput

Bases: BaseModel

The page's title and text, its links and what the selector picked.

Transform

Die Transform-Schritte formen eine Liste von Objekten ohne Code-Schritt um. Jeder nimmt items, eine aus einem früheren Schritt gebundene Liste, und die meisten geben items weiter, sodass sie sich verketten lassen.

Schritt Tut
transform.edit_fields Setzt Felder aus JMESPath-Ausdrücken über jedes item, entfernt Felder oder behält nur die gesetzten
transform.sort Sortiert der Reihe nach nach Feldern, auf- oder absteigend
transform.limit Behält die ersten oder die letzten Elemente
transform.remove_duplicates Behält das erste jeder Gruppe von Elementen, die auf den genannten Feldern oder ganz gleich sind
transform.aggregate Sammelt die Werte jedes Felds über alle Elemente in einer Liste je Feld, als values
transform.split_out Macht aus einer Liste in jedem Element eigene Elemente
transform.summarize Zählt, summiert, mittelt, findet das Kleinste oder Größte oder zählt verschiedene Werte, je Gruppe
transform.date_time Jetzt oder ein gebundener value, um eine Menge verschoben und in einer Zeitzone geschrieben - der des Workflows, sofern der Schritt keine nennt
transform.crypto Hasht oder base64-kodiert den gebundenen text oder erzeugt eine UUID oder zufälliges Hex

Ein Feld ist ein Pfad mit Punkten, customer.email, und ein Element ohne es ist nie ein Fehler. Sort stellt es ans Ende, Remove duplicates behandelt „fehlt“ als eigenen Wert, Aggregate und Summarize lassen es aus, Split out behält das Element unverändert, und Edit fields setzt null, wo sein Ausdruck nichts findet. Crypto ist nicht für Geheimnisse gedacht: Nichts darin verwendet einen Schlüssel.

Verkettet beantworten sie häufige Fragen ohne Code-Schritt:

  • Die besten Leads, jeder einmal - Remove duplicates nach email, Sort nach score absteigend, Limit auf 10, dann Edit fields, das nur name und score behält, bereit für eine Nachricht.
  • Summen je Region - Split out lines, sodass jede Bestellposition ein Element ist, dann Summarize die sum von amount, gruppiert nach region: ein Element je Region mit sum_amount.
  • Eine Liste von Adressen - Aggregate email gibt values.email weiter, alle Adressen in einer Liste, für jede Einstellung, die eine Liste nimmt.

Jeder Schritt liest items aus dem vorigen. tests/integration/test_workflow_transform_composed.py führt die ersten beiden genau so aus.

app.workflows.nodes.transform._handler.EditFieldsConfig

Bases: _Config

The fields to set and the fields to remove, on every item.

app.workflows.nodes.transform._handler.SummarizeConfig

Bases: _Config

app.workflows.nodes.transform._handler.DateTimeConfig

Bases: _Config

logic.if und logic.merge

logic.if wertet eine JMESPath-Bedingung über seinem gebundenen value aus und geht über den Port true oder false weiter. Jeder Knoten auf dem nicht genommenen Zweig wird als skipped festgehalten. value muss gebunden sein: Das Veröffentlichen und ein Testlauf des Entwurfs lehnen den Schritt ohne es ab; eine früher veröffentlichte Version läuft weiter und liest null.

logic.merge führt Zweige wieder zusammen. Standardmäßig wartet er auf den Zweig, der lief: Er läuft, sobald dieser Zweig ihn erreicht, und gibt dessen Ausgabe als value weiter. Das Veröffentlichen prüft, ob das sicher ist, und lehnt einen Merge ab, dessen Eingänge im selben Run laufen können. Das können Eingänge nicht, die einen Schritt über verschiedene Ports verlassen, wenn dieser Schritt pro Run genau einen Port nimmt - ein If, ein Switch, eine Entscheidung, ai.classify, eine Tabellenprüfung, eine Freigabe oder ein Schritt, der seine Fehler weiterleitet. Ebenso wenig können es Zweige verschiedener solcher Schritte, die einander ausschließen, etwa ein verschachteltes If mit gemeinsamem Else.

Ein Merge kann zwei oder mehr Ports dieses Schritts direkt aufnehmen, ohne Schritt dazwischen, etwa needs_reply und unsure von ai.classify: Er läuft einmal, für den Port, den der Schritt genommen hat. Zwei Kanten vom selben Port werden weiterhin abgelehnt.

Zweige, die nebeneinander laufen, etwa die Beine eines Split, brauchen Wait for auf All branches (wait_for: all). Der Merge läuft dann, sobald jeder Eingang abgeschlossen ist, und gibt die Ausgabe jedes Zweigs unter branches weiter, einem Objekt nach Zweignamen, statt unter value. Den Namen eines Zweigs liefert sein Eintrag in branch_keys: Der Editor legt für jede Verbindung einen an, aus dem Namen des Schritts oder dem Zweig des Split, und ein nachfolgender Schritt liest branches.<Name>. Das Veröffentlichen lehnt einen Namen ab, den nichts hat, und zwei Zweige mit demselben Namen.

Es lehnt außerdem All branches ab, wenn ein Eingang nicht sicher ankommt, sooft der Merge läuft - mit seinem Namen -, und wenn nur ein Eingang je laufen kann, denn dann braucht es The branch that ran. branches wird wie das Ergebnis jedes Schritts gespeichert: Ein Ergebnis über dem Inline-Limit lässt den Schritt mit NODE_OUTPUT_TOO_LARGE scheitern, reichen Sie Dateien also als Referenzen weiter.

Ein älterer gespeicherter Graph, der auf alle Zweige wartet und den nur einige davon erreichen, lässt den Run mit JOIN_INCOMPLETE fehlschlagen und nennt den Merge. Das Veröffentlichen lehnt einen solchen Graphen ab; der Code bleibt als Sicherheitsnetz. wait_for und branch_keys haben Standardwerte, ein ohne Konfiguration gespeicherter Merge wartet also weiter auf den Zweig, der lief.

Jeder Schritt mit mehreren eingehenden Kanten ist ein Join und braucht keinen Merge. Er läuft, sobald jeder Schritt, der zu ihm führt, abgeschlossen ist, wenn mindestens eine Kante aktiv ist, und wird übersprungen, wenn keine es ist. Er wird auch übersprungen, wenn eine Quelle ohne Fehlerpfad fehlgeschlagen ist: Dieser Fehler ist schon Sache des Runs. Die Bindings eines Joins dürfen nur Schritte lesen, die sicher vorher gelaufen sind; ein Join darf also jedes Bein einer parallelen Gruppe lesen, aber keinen Zweig eines If.

Eine Regel, Kategorie, ein Zweig oder eine Auswahl, die Sie benennen, wird zu einem Ausgangsport und darf deshalb keinen Port wiederverwenden, den der Schritt schon hat: in, otherwise, other, default, approved und rejected bei den Schritten, die sie haben, unsure bei einem Klassifizierer mit Mindestsicherheit und error bei einem Schritt, der seine Fehler weiterleitet. Der Editor markiert den Namen, und das Veröffentlichen lehnt ihn ab und nennt den kollidierenden Namen mit der Bitte, ihn umzubenennen.

Ausdrücke können auswählen, filtern und vergleichen und eine feste Menge reiner Funktionen aufrufen: abs, avg, ceil, contains, ends_with, floor, join, keys, length, max, merge, min, not_null, reverse, sort, starts_with, sum, to_array, to_number, to_string, type und values. Null, false sowie ein leerer String, eine leere Liste oder ein leeres Objekt sind falsch, alles andere ist wahr, auch 0. Ein Ausdruck, der sich nicht parsen lässt oder etwas anderes aufruft, kann nicht veröffentlicht werden.

app.workflows.nodes.logic_if._handler.LogicIfConfig

Bases: BaseModel

The decision, as a JMESPath expression over {"value": ...}.

app.workflows.nodes.logic_merge._handler.LogicMergeConfig

Bases: BaseModel

How the merge waits, and what each branch is called when it waits for all.

app.workflows.nodes.logic_merge._handler.MergeBranchKey

Bases: BaseModel

The name one incoming branch goes by in branches.

app.workflows.nodes.logic_merge._handler.LogicMergeOutput

Bases: BaseModel

What reached the merge: the branch that ran, or every branch by name.

logic.split

Split. Schickt den Run gleichzeitig in jeden seiner Zweige. Jeder Zweig hat einen Namen, der sein Ausgangsport ist, und es gibt zwei bis zwanzig. Nichts wird gewählt und keine Bedingung gelesen: Jeder Zweig wird immer genommen, und die Schritte danach laufen nebeneinander. Der Schritt gibt nichts weiter, ein Schritt in einem Zweig bindet also an das, was vor dem Split lief. Ein Zweigname darf nicht in sein und keinen Namen wiederverwenden, den der Schritt schon hat.

Ein Split kann seine Fehler nicht weiterleiten, denn ein Split, der fehlschlug, hat keinen Zweig gestartet: Das Veröffentlichen lehnt on_error: route an ihm ab, und die Schritte jedes Zweigs leiten ihre eigenen weiter. Die Zweige treffen sich in einem logic.merge wieder, der auf alle wartet, oder in jedem Schritt, der sie als Eingänge hat. Auch jeder andere Schritt kann einen Port an mehrere Schritte schicken, die dann ebenso gleichzeitig laufen; ein Split benennt die Zweige und hält sie im Blick.

app.workflows.nodes.logic_split._handler.LogicSplitConfig

Bases: BaseModel

The branches the run is sent down together.

logic.switch

Switch. Schickt den Lauf in den ersten von vielen Zweigen, dessen Regel gilt. Jede Regel hat einen Namen, der Port ihres Zweigs ist, und eine JMESPath-Bedingung über den gebundenen value, der Reihe nach geprüft; otherwise übernimmt, wenn keine gilt. value muss gebunden sein: Das Veröffentlichen und ein Testlauf des Entwurfs lehnen den Schritt ohne es ab; eine früher veröffentlichte Version läuft weiter und liest null. Die Zweige laufen in einem logic.merge zusammen, der auf den Zweig wartet, der lief; das Veröffentlichen nimmt ihn an, weil stets genau einer genommen wird. Eine Regel, die an ihren Daten scheitert, lässt den Schritt mit CONDITION_FAILED scheitern und nennt die Regel.

app.workflows.nodes.logic_switch._handler.LogicSwitchConfig

Bases: BaseModel

The rules, tried in order; the first that holds wins.

app.workflows.nodes.logic_switch._handler.LogicSwitchOutput

Bases: BaseModel

Which branch was taken, and the value that decided it, passed on unchanged.

Durchsucht Wissens-Collections nach einer gebundenen query und gibt die Passagen als typisierte Quellen zurück, die beste zuerst. Ein leeres Ergebnis ist eine erfolgreiche Suche. Eine Collection, die nicht mehr existiert oder die der Principal des Runs nicht mehr lesen darf, lässt den Schritt mit COLLECTION_NOT_ACCESSIBLE fehlschlagen. Der Schritt durchsucht nie weniger Collections, als der Graph nennt.

app.workflows.nodes.knowledge_search._handler.KnowledgeSearchConfig

Bases: BaseModel

Which collections to search, and how many passages to return.

app.workflows.contracts.io.SourceRef

Bases: BaseModel

One retrieved passage, as a node hands it on rather than as a model reads it.

What knowledge.search produces and agent.run/core.output carry: typed fields a later node can bind to, not a formatted citation string. content is the matched chunk; the page and chunk locate it.

knowledge.add

Fügt einen gebundenen Text mit title oder eine Run-Datei einer bis fünf Wissenssammlungen hinzu, über denselben Upload wie die Seite einer Sammlung: mit ihren eigenen Einstellungen für Parsing und Chunking, der Budgetprüfung der Organisation und einer Ingestion, die eingereiht statt abgewartet wird. Ein Text wird als <title>.md gespeichert, und ein Dokument gleichen Namens wird ersetzt statt doppelt angelegt, sodass eine Wiederholung es aktualisiert. Eine Sammlung, der der Principal nichts hinzufügen darf, lässt den Schritt mit KNOWLEDGE_ADD_REFUSED fehlschlagen.

app.workflows.nodes.knowledge_add._handler.KnowledgeAddConfig

Bases: BaseModel

Which collections the document goes into.

Modellschritte

Vier Schritte fragen eines der Modelle der Organisation nach einer Sache, ohne Werkzeuge. Jeder läuft als eigener Agent des Schritts - beim ersten Run angelegt, neu veröffentlicht, wenn sich seine Einstellungen ändern - über denselben Runner wie agent.run, sodass max_cost_usd des Schritts, das Budget des Workflows und das Limit der Organisation vor der Anfrage geprüft werden, die Kosten auf den Run gebucht werden und er in Activity erscheint. Das Veröffentlichen prüft, ob der Autor das Modell verwenden darf.

Schritt Tut Gibt weiter
ai.prompt Beantwortet einen gebundenen prompt nach den instructions des Schritts text, und structured, wenn structured_output_schema ein Objekt verlangt
ai.classify Ordnet einen gebundenen text einer der categories des Schritts zu, oder other Verlässt den Schritt über den Port der Kategorie, mit category, einem einsätzigen reason, dem text und confidence; unter min_confidence über unsure
ai.extract Zieht die in output_schema genannten Felder aus einem gebundenen text und lässt weg, was er nicht sagt data, ein Objekt dieser Form
ai.rewrite Fasst zusammen (short, medium, long), übersetzt in language oder schreibt einen gebundenen text in einem style um text

Die Kategorien eines Klassifikators sind seine Ports, in Reihenfolge, und das Modell kann nur mit einem ihrer Namen oder other antworten. Eine Antwort, die nicht das verlangte Objekt ist, lässt den Schritt mit STRUCTURED_OUTPUT_MISMATCH fehlschlagen; ein ausgeschöpftes Limit mit AGENT_BUDGET_EXCEEDED.

Ist min_confidence gesetzt, meldet das Modell zusätzlich seine eigene Konfidenz für die Kategorie, von 0 bis 1. Das ist seine Selbstauskunft, keine Wahrscheinlichkeit, und sie ist nicht kalibriert: Prüfen Sie einen Schwellenwert mit den Evals des Schritts an Ihren eigenen Fällen, bevor Sie ihm trauen. Das Minimum fügt nach other einen Port unsure hinzu: Unter dem Minimum geht es über diesen Port weiter, mit derselben Ausgabe, sodass die category dem Workflow weiter zur Verfügung steht.

Ohne Minimum wird das Modell nicht nach einer Zahl gefragt, der Prompt bleibt unverändert, confidence ist null und die Kategorie gewinnt immer. Eine Antwort ohne Zahl verlässt den Schritt nie über unsure, und solange der Port existiert, darf unsure kein Kategoriename sein.

Der Schritt geht über genau einen Port weiter, also kann ein logic.merge seine Zweige wieder zusammenführen: unsure und eine Kategorie auf denselben Weg schicken oder zwei Kategorien zu einem Protokollschritt.

app.workflows.nodes.ai_classify._handler.AiClassifyConfig

Bases: ModelStepConfig

The categories, in order, and anything else the model should know.

app.workflows.nodes.ai_classify._handler.AiClassifyOutput

Bases: BaseModel

The category chosen, why, and the text passed on unchanged.

confidence is filled in only when the step has a min_confidence, and unsure is true when it fell below that minimum, so the step left by its unsure port.

Entscheidungen

Drei Schritte stellen TypeSafes Jev eine typisierte Frage zu einem gebundenen text, mit einem TypeSafe-API-Key aus dem Vault. Jev schreibt keinen Text: Er beantwortet die Frage mit einer Konfidenz von 0 bis 1, in einer Anfrage, und kann nur mit einer der Antworten antworten, die der Schritt zulässt. Unter dem min_confidence des Schritts verlässt er ihn stattdessen über seinen Port unsure, sodass ein Workflow dort entscheidet, was ein Mensch oder ein Agent mit einem zweifelhaften Fall tut.

decide.* ruft nur TypeSafes gehostetes Jev auf. Um zweifelhafte Fälle mit einem selbst gehosteten Modell zu routen, nehmen Sie ai.classify mit min_confidence, das den Schritt genauso über unsure verlässt.

Schritt Fragt Verlässt über
decide.yes_no eine Ja-oder-Nein-Frage yes, no oder unsure
decide.choose welche von bis zu 255 Optionen passt out mit der choice, oder unsure
decide.score wo der Text auf einer Skala von 2 bis 10 Stufen liegt out mit dem score, oder unsure

Der Key wird beim Veröffentlichen geprüft und bei jedem Run neu gelesen. Ein Key, der weg oder nicht mehr geteilt ist, lässt den Schritt mit SECRET_NOT_USABLE fehlschlagen, ein Modell, das nicht antwortet, mit DECISION_FAILED, das nach der Policy des Schritts wiederholt wird, und eine Bereitstellung ohne das Extra browser mit DECISION_MODEL_UNAVAILABLE. Ein Merge darf die Zweige einer Entscheidung wieder zusammenführen, wie die eines If.

app.workflows.nodes._decide.DecisionConfig

Bases: BaseModel

The question, the model that answers it, and how sure it must be.

app.workflows.nodes.decide_choose._handler.ChooseConfig

The question, and the options it is answered from.

app.workflows.nodes.decide_score._handler.ScoreConfig

The question, and what each level of the rubric means, lowest first.

Kanäle

Slack, Mattermost und Telegram haben jeweils eine eigene Gruppe von Schritten, die als einer der Bots der Organisation auf dieser Plattform handeln, über denselben Adapter, über den seine Antworten gehen. Eine Nachricht, die ein Workflow sendet, kommt also von diesem Bot, und ein Schritt kann lesen, was der Bot lesen darf. Eine Plattform hat nur die Schritte, die ihre Bots ausführen können.

Schritt Slack Mattermost Telegram Gibt weiter
Send a message (<platform>.message.send) ja ja ja wohin sie ging
Read messages (<platform>.messages.read) ja ja - messages, älteste zuerst
List members (<platform>.members.list) ja ja Administratoren members, mit ihren Plattform-IDs
Find channels (<platform>.channels.find) ja ja - channels

Ein Schritt nimmt nur einen Bot seiner eigenen Plattform, und ein Bot spricht für die ganze Organisation, darum braucht das Handeln als Bot channels:manage: der Autor des Graphen zum Veröffentlichen, der Principal des Runs bei jedem Run. Ein gelöschter, abgeschalteter oder zu einer anderen Plattform gehörender Bot lässt den Schritt mit CHANNEL_NOT_USABLE fehlschlagen. Eine Plattform, die einen Aufruf zur Laufzeit ablehnt, mit CHANNEL_UNSUPPORTED, und eine, die nicht antwortet, mit CHANNEL_CALL_FAILED. Senden wird nie von selbst wiederholt.

Send a message antwortet in einem Thread, wenn thread_id gesetzt ist: der Thread-Zeitstempel bei Slack, die ID des Root-Posts bei Mattermost oder die Nummer eines Telegram-Forenthemas. Die Nachricht wird in den Thread gepostet, nie als zitierte Antwort. Eine leere thread_id postet in den Kanal selbst. Eine Kanal- oder Thread-ID mit einem : darin oder ein Telegram-Thread, der keine Zahl ist, lässt den Schritt mit CHANNEL_TARGET_INVALID scheitern, bevor etwas gesendet wird.

app.workflows.nodes._channels.ChannelBotConfig

Bases: BaseModel

Which bot the step acts as.

app.workflows.nodes.channel_read._handler.ChannelReadConfig

The bot, and how many messages to read.

app.workflows.nodes.channel_read._handler.ChannelReadOutput

Bases: BaseModel

The messages, oldest first.

app.workflows.nodes.channel_members._handler.ChannelMembersOutput

Bases: BaseModel

The people in the channel.

app.workflows.nodes.channel_find._handler.ChannelFindOutput

Bases: BaseModel

The channels that match.

agent.run

Fragt einen veröffentlichten Agent in genau der Version, die der Schritt festlegt, über denselben Runner wie Chat und API, mit Budget, Freigaben, Guardrails und Run-Historie des Agents. Der Run wird mit der Oberfläche workflow festgehalten. Gebundene sources werden als nummerierter Kontext an den Prompt angehängt. Ein freigabepflichtiger Tool-Aufruf hält den Schritt an, und die Entscheidung setzt denselben Agent-Run fort.

inline konfiguriert den Agent im Schritt statt agent, und genau eines von beiden ist gesetzt. Die Spec gehört dem Schritt und wird mit dem Graphen versioniert: Der erste Run legt einen Agent an, der dem Workflow gehört, nicht im Katalog erscheint und über den Zugriff auf den Workflow erreicht wird, und ein Run, dessen Spec von der neuesten Version dieses Agents abweicht, veröffentlicht zuerst eine neue - geprüft wie jede Veröffentlichung eines Agents. Das Veröffentlichen des Workflows prüft die Spec gegen den Zugriff seines Autors. max_cost_usd begrenzt eine Ausführung: Der Agent hält vor einer Modellanfrage darüber an, und der Schritt schlägt mit AGENT_BUDGET_EXCEEDED fehl.

Ein Agent mit eigenem Antwortformat gibt sein Objekt als structured weiter. structured_output_schema verlangt stattdessen eine andere Form: Der Agent läuft mit diesem Schema, und eine Antwort, die es verletzt, geht zum Korrigieren an das Modell zurück. Das Objekt wird noch einmal geprüft, bevor irgendetwas danach läuft. Ein Agent, der nie ein passendes liefert, lässt den Schritt mit AGENT_RUN_FAILED fehlschlagen, und eine Antwort ohne Objekt, wo eines verlangt war, mit STRUCTURED_OUTPUT_MISMATCH.

Wie der Agent-Run endete Ergebnis des Schritts
Completed Completed
Wartet auf Freigabe Wartet, setzt dann denselben Run fort
Budget überschritten AGENT_BUDGET_EXCEEDED
Von einem Guardrail blockiert AGENT_GUARDRAIL_BLOCKED
Sonst AGENT_RUN_FAILED

Gebundene attachments sind Bilder aus den Dateien des Runs - ein Download, eine gerenderte PDF-Seite, ein umgewandeltes Foto -, die dem Agenten als Bilder gezeigt werden statt als Link, den er nicht öffnen kann. Nur PNG, JPEG, WebP und GIF werden gezeigt. Jede andere Datei scheitert mit UNSUPPORTED_ATTACHMENT_TYPE, also lies den Text eines Dokuments zuerst mit text.extract.

Er wird nie automatisch wiederholt, weil ein Agent Tools mit Nebenwirkungen aufgerufen haben kann.

app.workflows.nodes.agent_run._handler.AgentRunConfig

Bases: BaseModel

Which agent answers - pinned from the catalog or configured here - and how.

app.workflows.nodes.agent_run._handler.AgentRunOutput

Bases: BaseModel

The agent's answer, the sources it was given, and its structured value.

http.request

Ruft eine HTTP-API auf. Jede Anfrage und jede Weiterleitung durchläuft die SSRF-Prüfung des Deployments und wird an die Adresse gesendet, die diese Prüfung freigegeben hat. Ein privater Host, den der Betreiber in OUTBOUND_PRIVATE_ALLOWLIST eingetragen hat, wird ebenfalls zugelassen, aber nur für eine URL, die dort beginnt: Eine Weiterleitung von einer öffentlichen URL erreicht ihn nie.

Eine URL, die aus einem öffentlichen Trigger (Webhook, Formular, E-Mail) oder aus den Tool-Argumenten eines Agenten gebunden wird, kann jeden gelisteten Host nennen, listen Sie also nur Hosts, die ihre eigenen Zugangsdaten prüfen.

Ein Credential ist ein HTTP credential aus dem Vault und wird nur an die Origins gesendet, die das Secret erlaubt. Die Antwort wird innerhalb von max_response_bytes gelesen und ohne Set-Cookie, die Authentifizierungs-Header und das Token zurückgegeben. Eine HTML-Antwort kommt als Text in body zurück; binden Sie sie an html.parse, um ihren Text, ihre Links und Elemente zu lesen.

Die Zugangsdaten gehen als Bearer-Token, Basic-Authentifizierung, ein Header mit einem Namen deiner Wahl oder, mit query, ein URL-Parameter mit einem Namen deiner Wahl. Import cURL im Editor liest einen aus der Doku einer API eingefügten Befehl in Methode, URL, Header und JSON-Body ein. Zugangsdaten im Befehl bleiben nie im Schritt: Der Schritt wird so eingestellt, dass er sie genauso sendet, und das Vault-Formular öffnet sich mit ihnen ausgefüllt.

Ein GET kann mit pagination seitenweise durch eine Liste gehen: einer nächsten URL folgen, die die Antwort nennt, einen Cursor zurücksenden oder einen Seitenparameter hochzählen. Die items_path-Elemente jeder Seite werden der Reihe nach in items gesammelt. Das Blättern endet, wenn es keine nächste Seite gibt, wenn eine Seite keine Elemente hat, oder bei max_pages, wobei complete dann falsch ist. Alle Seiten zusammen lesen nicht mehr als max_response_bytes, und eine nächste Seite auf einem anderen Origin bekommt keine Zugangsdaten.

Was passiert ist Ergebnis
Die URL ist Loopback, Metadata, nicht http(s) oder privat und nicht in der OUTBOUND_PRIVATE_ALLOWLIST des Betreibers URL_REFUSED, nichts gesendet
Die URL liegt außerhalb der Origins des Credentials SECRET_ORIGIN_DENIED, nichts gesendet
Die Verbindung wurde nie geöffnet HTTP_UNREACHABLE, wird wiederholt
Gesendet, keine Antwort, GET oder Idempotenz-Header HTTP_NO_RESPONSE, wird wiederholt
Gesendet, keine Antwort, jeder andere Schreibzugriff Unsicher: Der Run hält für einen Menschen an
Nicht 2xx HTTP_ERROR_STATUS oder die Antwort als Ausgabe mit on_error_status: complete
Größer als das Limit RESPONSE_TOO_LARGE
items_path einer Seite findet etwas, das keine Liste ist PAGE_ITEMS_NOT_A_LIST

app.workflows.nodes.http_request._handler.HttpRequestConfig

Bases: BaseModel

Where to send the request, how, and what to accept back.

app.workflows.nodes.http_request._handler.HttpAuth

Bases: BaseModel

How the request authenticates, with a vault secret rather than a value.

app.workflows.nodes.http_request._handler.HttpPagination

Bases: BaseModel

How a GET fetches its next page, and when it stops.

next_url follows a URL the response names, cursor sends back a cursor it names in the param query parameter, and page counts param up from first_page. Each is read with a JMESPath expression over the page's body. The items items_path finds on every page are collected, in order; paging stops when there is no next page, when a page has no items, or at max_pages.

app.workflows.nodes.http_request._handler.HttpRequestOutput

Bases: HttpResponseOutput

What came back - the last page's, when it paged - and every page's items.

mcp.call

Call an MCP tool ruft ein Tool auf einem der MCP-Server der Organisation auf, mit arguments, die aus früheren Schritten gebunden sind. Der Editor bietet die Server mit dem Zeichen ihres Dienstes an, dann die Tools, die der Server beim letzten Test gelistet hat; Refresh tools testet ihn erneut. Jedes Argument des Tools ist dann ein eigenes Feld, eingetippt oder gebunden als arguments/<name>. Nur ein Server der Organisation kommt in Frage, nie der eigene eines Mitglieds, und die Veröffentlichung lehnt einen Server ab, der nicht der Organisation gehört, ausgeschaltet ist oder das Tool nicht gelistet hat. Die Zugangsdaten kommen aus dem Vault, wie bei einem Agent.

approval bestimmt, wann eine Person den Aufruf vorher freigibt: writes, der Standard, vor jedem Tool, das der Server nicht als nur lesend markiert; all vor jedem Aufruf; none nie. Die Anfrage landet im Tab Approvals, wie bei human.approval, bei approvers oder bei allen mit approvals:decide. Freigegeben, wird das Tool aufgerufen; abgelehnt, scheitert der Schritt mit MCP_CALL_REJECTED. Eine Nur-lesen-Markierung ist die eigene Angabe des Servers, wählen Sie also all für einen Server, dem Sie diese Angabe nicht zutrauen.

Die Ausgabe ist der text der Antwort, ihre structured Daten, wenn das Tool welche liefert, und is_error. Ein Fehler, mit dem das Tool antwortet, lässt den Schritt mit MCP_TOOL_ERROR scheitern, außer on_tool_error ist complete.

MCP hat keinen eigenen Idempotenzschlüssel. Ein Server, der einen solchen Schlüssel als Tool-Argument annimmt, lässt sich trotzdem wiederholungssicher machen: Setzen Sie Idempotency key argument (idempotency_key_argument) auf den Namen dieses Arguments, und der Schritt sendet dort seinen stabilen Operationsschlüssel - bei jedem Versuch des Schritts derselbe, in jeder Iteration einer Schleife ein anderer -, gleich welcher Wert daran gebunden war. Das Tool muss das Argument als Text deklarieren.

Das Veröffentlichen lehnt ein Argument ab, das im aufgezeichneten Schema des Tools fehlt; ein Tool ohne aufgezeichnetes Schema geht durch, testen Sie den Server also zuerst. Zur Laufzeit listet der Schritt vor dem Aufruf die Tools des Servers und scheitert mit MCP_IDEMPOTENCY_ARGUMENT_UNDECLARED, ohne etwas aufzurufen, wenn das aktuelle Schema kein solches Argument hat. Ein Schritt mit Schlüssel gilt als idempotent: Die Engine darf ihn erneut ausführen.

Bricht der Aufruf ab, hängt das Weitere davon ab, ob er den Server erreicht haben kann:

  • Vor dem Aufruf - der Server war nicht erreichbar oder antwortete nicht auf die Verbindung oder die Tool-Liste - wurde nichts gesendet, und der Schritt scheitert mit einem wiederholten MCP_CALL_FAILED.
  • Nach dem Aufruf, mit Schlüssel oder bei einem Tool, das der Server als nur lesend markiert (in der Liste, die der Schritt gerade gelesen hat, sonst in der beim Test des Servers aufgezeichneten) - derselbe wiederholte Fehler, weil eine Wiederholung sicher ist.
  • Nach dem Aufruf, jedes andere Tool, ohne Schlüssel - das Tool kann gehandelt haben, und es kam keine Antwort. Der Schritt endet als Uncertain mit The tool was called and no answer came back; it may have acted, und der Run wartet auf eine Person, die im anderen System nachsieht, bevor sie den Schritt erneut ausführt.
Was passiert ist Ergebnis
Der Server ist weg oder ausgeschaltet MCP_SERVER_UNAVAILABLE, nichts gesendet
Seine Zugangsdaten müssen neu autorisiert werden MCP_SERVER_UNAUTHORIZED, nichts gesendet
Der Server bietet das Tool nicht an MCP_TOOL_NOT_FOUND
Der Server war nicht erreichbar oder brach vor einem sicheren Aufruf oder danach ab MCP_CALL_FAILED, wiederholt
Er brach nach einem Aufruf ohne Schlüssel an einem Tool ab, das nicht als nur lesend bekannt ist Uncertain: Der Run hält für eine Person an
Das Tool deklariert das Idempotency key argument nicht MCP_IDEMPOTENCY_ARGUMENT_UNDECLARED, nichts gesendet
Das Tool antwortete mit einem Fehler MCP_TOOL_ERROR oder die Antwort als Ausgabe
Die strukturierte Antwort ist größer als 1 MiB MCP_RESULT_TOO_LARGE
Die strukturierte Antwort ist kein JSON MCP_RESULT_INVALID
Der Schritt nennt weder Server noch Tool MCP_CALL_NOT_CONFIGURED, nichts gesendet
Das Tool ist auf dieser Verbindung nicht erlaubt MCP_TOOL_NOT_ALLOWED, nichts gesendet
Ein genannter Genehmiger kann Freigaben nicht mehr entscheiden APPROVER_UNAVAILABLE, nichts gesendet
Der Aufruf wurde nicht genehmigt MCP_CALL_REJECTED, nichts gesendet

app.workflows.nodes.mcp_call._handler.McpCallConfig

Bases: BaseModel

Which server, which tool, and when a person approves the call first.

app.workflows.nodes.mcp_call._handler.McpCallOutput

Bases: BaseModel

What the tool answered.

notification.send

Benachrichtigt Mitglieder der Organisation in der App, per E-Mail oder beides, über das Benachrichtigungszentrum. Empfänger sind Mitglieder, die per ID angegeben werden. Zur Laufzeit muss jeder noch ein aktives Mitglied sein, das den Workflow sehen kann, alle anderen werden ausgelassen. Bleibt niemand übrig, schlägt der Schritt mit NO_PERMITTED_RECIPIENTS fehl. Die Einstellungen jeder Person für Workflow notifications gelten weiterhin. Der Schritt ist abgeschlossen, wenn die Benachrichtigung geschrieben ist, die E-Mail wird danach zugestellt. Ein wiederholter Schritt schreibt keine zweite Benachrichtigung.

app.workflows.nodes.notification_send._handler.NotificationSendConfig

Bases: BaseModel

Who to tell, the headline, and which channels it may use.

email.send

Versendende Anbieter reservieren vor dem gesamten Versand jeden eindeutigen Empfänger über gemeinsame Redis-Limits: WORKFLOW_EMAIL_MAX_PER_PRINCIPAL_PER_HOUR (100), WORKFLOW_EMAIL_MAX_PER_ORG_PER_HOUR (500) und WORKFLOW_EMAIL_MAX_PER_DEPLOYMENT_PER_HOUR (1000). Das Benutzerlimit umfasst Schlüssel und Organisationen desselben Ausstellers. Feste Stundenfenster gelten über Ausführungen und Worker hinweg; am Fensterübergang ist die doppelte Menge möglich. Erschöpfung ergibt EMAIL_DELIVERY_LIMIT, nicht verfügbare Zähler EMAIL_ALLOWANCE_UNAVAILABLE, ohne Versand. Reservierungen werden nach Teilreservierung, Anbieterfehler oder Abbruch nicht erstattet. Nur protokollierende Anbieter verbrauchen kein Versandlimit. Die Deployment-Grenze sollte unter dem SMTP-Kontingent liegen und Spielraum für Transaktionsmails lassen.

Send an email schickt eine Mail an bis zu zehn Adressen, vom eigenen Absender und E-Mail-Anbieter des Deployments, je eine Kopie, sodass kein Empfänger die Adresse eines anderen sieht. Der Schritt ist für Personen außerhalb der Organisation gedacht, etwa einen Lead oder einen Kunden: Mitglieder erreicht notification.send über ihre eigenen Einstellungen. to, subject und der Klartext body werden eingetippt oder aus früheren Schritten gebunden, und reply_to leitet Antworten an eine andere Adresse als die Antwortadresse des Deployments.

Die Ausgabe nennt die Adressen, die der Anbieter angenommen hat. delivered ist false, wenn der Anbieter Mail nur protokolliert, was in der Entwicklung der Standard ist, damit ein Run nie behauptet, jemanden erreicht zu haben, den er nicht erreicht hat. Eine Kopie, die der Anbieter ablehnt, lässt den Schritt mit EMAIL_NOT_SENT scheitern und nennt, was schon gesendet wurde. Mail hat keinen Idempotenzschlüssel, daher sendet ein wiederholter Schritt erneut.

app.workflows.nodes.email_send._handler.EmailSendConfig

Bases: BaseModel

Which SMTP account sends it, and where replies go.

app.workflows.nodes.email_send._handler.EmailSendInput

Bases: BaseModel

Who it goes to, and what it says - typed in or bound from earlier steps.

app.workflows.nodes.email_send._handler.EmailSendOutput

Bases: BaseModel

Who the provider accepted the mail for, and whether it actually leaves.

human.approval

Ask for approval hält den Run an, bis eine Person freigibt oder ablehnt, was er gleich tun wird, und geht dann über approved oder rejected weiter. Wer freigibt, liest den title des Schritts und die daran gebundenen details im Tab Approvals unter Activity oder über GET /api/v1/workflow-approvals. approvers nennt, wer entscheiden darf, und diese Personen werden in der App benachrichtigt; leer darf jeder mit approvals:decide. Nach timeout_hours läuft die Anfrage ab, und der Schritt geht über rejected mit decision: "expired" weiter. Einen Run abzubrechen bricht seine Anfragen ab. Jeder Lauf des Schritts fragt einmal, sodass eine Wiederholung oder eine Schleifeniteration nie zweimal nach demselben fragt.

Benannte Genehmigende müssen aktive Mitglieder mit approvals:decide sein. Die Veröffentlichung prüft dies, der Schritt erneut vor der Anfrage. Fehlt diese Berechtigung inzwischen, endet der Schritt mit APPROVER_UNAVAILABLE, statt eine unentscheidbare Anfrage zu hinterlassen.

Ein Schritt kann einen Vorschlag tragen: an proposal gebundener Text, etwa eine vorbereitete Antwort. Wer freigibt, liest ihn neben der Anfrage. Ist editable eingeschaltet, zeigt die Warteschlange ihn in einem Editor, als text oder markdown (proposal_format), unter proposal_label („Vorschlag (Proposal)“, wenn leer), und die freigebende Person darf eine geänderte Fassung freigeben. Ein Schritt ohne gebundenen Text öffnet einen leeren Editor, für den Fall, dass die Vorbereitung nichts ergab. details bleibt unverändert und kurz; der Vorschlag darf bis zu 100.000 Zeichen lang sein.

Die freigegebene Ausgabe trägt neben der Entscheidung drei Felder:

  • final ist die Fassung, die freigegeben wurde, bei unveränderter Übernahme der Vorschlag selbst.
  • original ist der Vorschlag zum Zeitpunkt der Anfrage, "", wenn ein editierbarer Schritt nichts gebunden hatte.
  • edited ist wahr, wenn sich final von original unterscheidet. Zeilenenden, Leerzeichen am Zeilenende und Leerraum um den Text sind kein Unterschied.

Eine bearbeitete Fassung wird ohne führende und nachgestellte Leerzeichen gespeichert; eine Änderung, die mit einem eingerückten Codeblock beginnt, verliert diese erste Einrückung.

Eine freigegebene Anfrage eines editable-Schritts hält außerdem ihr Änderungsmaß (edit_ratio) fest, also wie viel vom Vorschlag die Person geändert hat, von 0 bis 1. Die Statistik des Workflows meldet es mit den übrigen Freigabe-Ergebnissen.

Ein Schritt, der nicht editable ist, zeigt seinen Vorschlag schreibgeschützt und gibt ihn nach der Freigabe unverändert als final weiter, mit edited auf false, sodass spätere Schritte final binden können, in welchem Modus der Schritt auch ist. Eine Ablehnung oder ein Ablauf trägt kein final. Eine bearbeitete Fassung an einen Schritt zu senden, der nicht editable ist, oder zusammen mit einer Ablehnung, wird mit 422 abgelehnt. Wer entscheiden darf, vom Schritt aber nicht genannt wird, sieht den Vorschlag schreibgeschützt.

Ein Schritt kann statt einer schlichten Freigabe Auswahlmöglichkeiten (choices) anbieten, jede ein Weg der Freigabe: etwa einen Entwurf behalten oder ihn sofort senden. Bis zu fünf sind möglich, jede mit einer id (Kleinbuchstaben, Ziffern und Unterstriche, nicht in, approved oder rejected) und einem label mit bis zu 40 Zeichen. Die Warteschlange zeigt statt Approve eine Schaltfläche je Auswahl, mit der Beschriftung genau so, wie sie geschrieben wurde, ohne Übersetzung; Reject bleibt. Der Schritt hat dann einen Ausgangsport je Auswahl, in dieser Reihenfolge, danach rejected, und keinen Port approved: Eine Kante von approved wird beim Veröffentlichen des Workflows abgelehnt.

Die Auswahl der freigebenden Person ist der Port, über den der Schritt weitergeht, und ihre id ist choice in der Ausgabe. Sie ist null, wenn der Schritt keine Auswahl hat, bei einer Ablehnung und bei einem Ablauf. Ein Schritt ohne Auswahl bleibt unverändert: Er hat approved und rejected. Das Freigeben eines Schritts mit Auswahl verlangt eine davon als choice, und choice bei jeder anderen Entscheidung wird mit 422 abgelehnt. Die Statistiken teilen nicht nach Auswahl auf.

Sendet der Workflow das freigegebene final über mcp.call, setzen Sie approval dieses Schritts auf none, sonst wird die Person zweimal gefragt.

app.workflows.nodes.human_approval._handler.HumanApprovalConfig

Bases: BaseModel

What the approver is asked, who may decide, and for how long the run waits.

app.workflows.nodes.human_approval._handler.HumanApprovalInput

Bases: BaseModel

What the approver reads before deciding, bound from an earlier step.

app.workflows.nodes.human_approval._handler.HumanApprovalOutput

Bases: BaseModel

The decision, and who made it.

flow.wait

Wait. Hält den Lauf am Schritt für seconds nach dem Erreichen oder bis zu einem gebundenen until an und geht dann weiter; höchstens dreißig Tage. Der Schritt wird auf einer Uhr geparkt, deren Dispatch-Zeile dann fällig wird, sodass das Warten einen Worker-Neustart übersteht und keinen Worker belegt; andere Zweige laufen derweil weiter. Ein bereits vergangener Zeitpunkt geht sofort weiter, und die Frist des Laufs gilt weiterhin. Ein gebundenes until, das mehr als dreißig Tage nach Erreichen des Schritts liegt, lässt den Schritt mit WAIT_TOO_LONG scheitern.

Mit Wait for a call to the run's resume link wartet der Schritt stattdessen, bis die Adresse aufgerufen wird, die ein Resume link-Schritt gab, höchstens seconds (dreißig Tage ohne Angabe). Sende an diese Adresse POST mit einem JSON-Objekt oder ohne Inhalt: Der Schritt gibt den Inhalt als body weiter, mit called true, und der Lauf geht weiter. Ruft niemand rechtzeitig auf, geht er mit called false weiter. Ein Aufruf, während nichts wartet - bevor der Schritt erreicht ist, nachdem er weiterging oder wenn der Lauf endete -, wird mit WORKFLOW_NOT_WAITING (409) abgelehnt, und ein Link, der nicht zum Lauf gehört, antwortet 404.

app.workflows.nodes.flow_wait._handler.FlowWaitConfig

Bases: BaseModel

How long to wait, when no time to wait until is bound, and whether for a call.

app.workflows.nodes.flow_wait._handler.FlowWaitOutput

Bases: BaseModel

When the wait ended, and what a call to the resume link sent.

Resume link. Gibt url weiter, die Adresse, die die Wait-Schritte dieses Laufs fortsetzt, die auf einen Aufruf warten - für eine Nachricht oder eine Anfrage vor dem Wait, damit wer antwortet den Lauf fortsetzen kann. Jeder Lauf hat seine eigene, aus seiner Id unter dem Geheimnis des Deployments gebildet und niemandem sonst gezeigt: Wer sie hat, kann den Lauf fortsetzen, daher wird sie nur dorthin geschickt, woher die Antwort kommen soll.

app.workflows.nodes.flow_resume_link._handler.ResumeLinkOutput

Bases: BaseModel

The run's resume link.

Fehlerbehandlung

Jeder Knoten nimmt neben seiner Konfiguration eine optionale policy an.

Feld Standard Wirkung
timeout_seconds keiner Ein Aufruf, der länger läuft, wird abgebrochen. Ein Schritt ohne externen Schreibzugriff oder mit idempotentem Aufruf schlägt mit NODE_TIMEOUT fehl und darf wiederholt werden. Ein Schreibzugriff, der schon angekommen sein kann, wird unsicher und hält für einen Menschen an
retry.max_attempts WORKFLOW_RETRY_CEILING Versuche insgesamt, der erste eingeschlossen. Wiederholt wird nur ein Fehler, den der Knoten als retryable markiert, und das Veröffentlichen lehnt mehr als einen Versuch für einen Schritt ab, dessen Aufruf nicht sicher wiederholbar ist
retry.backoff, base_delay_seconds, max_delay_seconds exponential, 2, 60 Die Wartezeit zwischen Versuchen: fest oder verdoppelt bis zur Obergrenze
on_error fail_run route schickt einen Fehler, den die Wiederholungen nicht erledigt haben, über den error-Port des Knotens, statt den Run fehlschlagen zu lassen

Ein Knoten, dessen Policy Fehler umleitet, hat einen zusätzlichen Ausgangsport error. Er trägt den WorkflowError: code, message, details und retryable. Die normale Ausgabe des Knotens gibt es nur auf seinen anderen Ports, daher lehnt das Veröffentlichen ein Binding ab, das auf dem Fehlerpfad die Ausgabe oder auf dem Erfolgspfad den Fehler liest. Der Fehlerport muss irgendwohin führen, und beide Pfade dürfen sich in einem logic.merge wieder treffen.

Laufen Schritte parallel, hat jeder Zweig seinen eigenen Handler. Ein Zweig, der ohne Fehlerpfad fehlschlägt, lässt den Run fehlschlagen, und die Schritte neben ihm starten nichts mehr: Ihre offenen Freigaben und Timer werden zurückgezogen, ebenso die Agent-Runs, auf die sie geparkt sind. Ein Schritt, dessen Arbeit schon lief, wird fertig und als nach dem Ende des Runs beendet festgehalten, seine Ausgabe bleibt erhalten. Eine Wiederholung des Runs verwendet ihn weiter, statt es noch einmal zu tun, und ein wiederverwendeter Output- oder Respond-to-webhook-Schritt meldet seine Antwort erneut. Das Veröffentlichen lehnt einen error.handle ab, den zwei Schritte speisen, die im selben Run fehlschlagen können.

Ein Schritt, der Aufmerksamkeit braucht, lässt sich in dem Run klären, in dem er steht. Ihn als fehlgeschlagen zu behandeln beendet ihn mit STEP_MARKED_FAILED, der über seinen Port error weitergeht, wenn der Schritt seine Fehler weiterleitet, oder an die Fehlerrichtlinie der Schleife geht. Den Run fehlschlagen zu lassen beendet ihn mit diesem Fehler, wie auch immer der Graph verbunden ist. Ein Run, der abgebrochen wird oder fehlschlägt, schließt auch einen Schritt, der Aufmerksamkeit braucht.

error.handle nimmt diesen Fehler an seinem Port in an und verlässt ihn über den ersten Zweig, dessen code und retryable beide passen, oder über default, der verbunden sein muss. error.raise lässt seinen Zweig mit Code, Meldung und Details fehlschlagen, die der Autor festlegt.

Manche Fehler werden nie umgeleitet. Entzogener Zugriff, ein aufgebrauchtes Budget (des Runs oder eines Agenten), ein abgebrochener Run, eine verstrichene Deadline, die Knotengrenze pro Run und ein Effekt mit unbekanntem Ausgang beenden den Run, wie auch immer der Graph verdrahtet ist. Ein Revisionskonflikt oder ein Validierungsfehler kann umgeleitet werden, wird aber nie blind wiederholt, weil dieselbe Eingabe genauso scheitert.

app.workflows.contracts.policy.NodePolicy

Bases: BaseModel

What happens around one node's call: its time limit, its retries, its failure.

app.workflows.contracts.policy.RetryPolicy

Bases: BaseModel

How many tries a failing step gets, and how long it waits between them.

delay_seconds

delay_seconds(failures)

The wait after the failures-th failed try.

app.workflows.nodes.error_handle._handler.ErrorHandleConfig

Bases: BaseModel

The branches, tried in order; the first match wins.

app.workflows.nodes.error_handle._handler.HandledError

Bases: BaseModel

The error that arrived, and the branch it was sent down.

app.workflows.nodes.error_raise._handler.ErrorRaiseConfig

Bases: BaseModel

The error this step fails with.

Schleifen

control.foreach führt seinen Rumpf einmal für jedes Element einer gebundenen Liste items aus, ein Element nach dem anderen und in Reihenfolge. Danach geht es über seinen Port done weiter, mit results in Eingabereihenfolge, errors und count. Der Rumpf beginnt bei loop.item, verbunden vom Port body der Schleife, das item, index und count bereitstellt. Er endet bei loop.yield, dessen gebundenes value das Ergebnis der Iteration ist. Keine Kante führt zur Schleife zurück. Ein Schritt im Rumpf darf an alles binden, was vor der Schleife lief, und nichts außerhalb des Rumpfs darf in ihn hinein binden.

Die Liste wird beim Start der Schleife eingefroren, daher sieht eine Iteration nie eine Quelle, die sich während des Runs geändert hat. Eine Liste, die länger als WORKFLOW_FOREACH_MAX_ITEMS oder größer als WORKFLOW_FOREACH_MAX_MANIFEST_BYTES ist, wird abgelehnt statt gekürzt. Eine leere Liste ergibt results: [], ohne den Rumpf auszuführen. Die Schritte jeder Iteration laufen in einem eigenen Scope, mit eigenen Versuchen, Idempotenzschlüsseln und Kosten. Die nächste Iteration wird in der Transaktion geplant, die die vorige beendet, daher setzt ein Neustart beim richtigen Index fort und wiederholt nie einen bestätigten Schreibzugriff. Eine Freigabe innerhalb einer Iteration setzt diese Iteration fort.

Mit item_error_policy: stop, dem Standard, schlägt die Schleife bei der ersten fehlgeschlagenen Iteration fehl, und die Details des Fehlers tragen den scope_path dieser Iteration. Mit collect ist das Ergebnis des Elements null, der Fehler kommt zu errors, und die Schleife läuft weiter. Schleifen verschachteln sich höchstens WORKFLOW_FOREACH_MAX_DEPTH tief, und jeder Knotenlauf, den ein Run anlegt, zählt gegen WORKFLOW_RUN_MAX_NODE_RUNS. Es gibt keine while-Schleife und kein paralleles Map.

Ein Rumpf darf sich in parallele Beine verzweigen, die innerhalb ihrer Iteration nebeneinander laufen, und eine Iteration endet, sobald keiner ihrer Schritte mehr aktiv ist. Schlägt ein Schritt in einer Iteration ohne Fehlerpfad fehl, lässt collect seine Nachbarn in dieser Iteration zu Ende laufen und geht dann zum nächsten Element. stop bricht sie ab, zieht ihre Freigaben, Timer und geparkten Agent-Runs zurück und lässt die Schleife fehlschlagen. Die nächste Iteration startet nie, solange eine frühere noch einen laufenden Schritt hat.

app.workflows.nodes.control_foreach._handler.ForeachConfig

Bases: BaseModel

What a failed iteration does to the loop.

app.workflows.nodes.control_foreach._handler.ForeachOutput

Bases: BaseModel

Every iteration's result, in input order, and the ones that failed.

app.workflows.nodes.loop_item._handler.LoopItemOutput

Bases: BaseModel

The current element, where it sits in the list, and how long the list is.

Virtual Tables

Sieben Knoten lesen und schreiben Virtual Tables über denselben Service, den die Konsole, die API und die Tabellen-Tools eines Agents nutzen. Validierung, Revisionskonflikte, Kontingente, Historie, Receipts und Audit sind auf jeder Oberfläche dieselben.

Knoten Tut Effekt
table.record.create Fügt einen Datensatz hinzu write
table.record.upsert Legt den Datensatz mit einer External ID an oder ändert ihn write
table.record.update Ändert einige Zellen eines Datensatzes write
table.record.delete Löscht einen Datensatz und behält seine Historie write
table.record.get Findet einen Datensatz per ID oder External ID read
table.record.query Liest eine Seite Datensätze, gefiltert und sortiert read
table.create Legt eine neue Tabelle mit typisiertem Schema an write

Jeder Datensatz-Knoten legt seine Tabelle in der Konfiguration fest. Beim Veröffentlichen wird sie gegen den Autor des Graphen geprüft und bei jedem Run erneut als Principal des Runs. Werte werden gebunden und per Spalten-ID oder Spalten-Label angegeben. Ein Datensatz kommt mit seinen Werten zweimal zurück: values nach Spalten-ID für Bindings und fields nach Label zum Lesen. Ein Schlüssel, der keine lebende Spalte nennt, schlägt mit UNKNOWN_COLUMN fehl.

Ein Schreibzugriff trägt den Operationsschlüssel des Schritts, daher spielt ein wiederholter Schritt seinen ersten Schreibzugriff erneut ab. Ein Update, Upsert oder Delete ohne gebundene Revision schreibt bei der aktuellen Revision des Datensatzes, unter der Sperre des Datensatzes gelesen, und das erneute Abspielen hält auch dann, wenn der erste Schreibzugriff diese Revision schon weitergesetzt hat. Eine verschobene Revision ist REVISION_CONFLICT, der nicht wiederholt wird: Dieselbe Revision würde erneut kollidieren, also leite ihn mit error.handle zu einem frischen Lesen um. Ein fehlender Datensatz ist found: false aus table.record.get, kein Fehler. table.record.query liest höchstens 100 Datensätze pro Seite und meldet has_more. Es liest nie von sich aus eine ganze große Tabelle.

table.create ist ein eigener Knoten und braucht tables:create. Seine Ausgabe trägt die neue Tabelle als Referenz, an die das table eines späteren Knotens gebunden werden kann, und die ID jeder Spalte nach Label. Eine Tabelle, die ein lebender Workflow liest oder schreibt, oder eine Spalte, die er festlegt, kann nicht archiviert werden, solange die aktuelle Version dieses Workflows sie nutzt.

Vier weitere Schritte lesen, welche Tabellen die Organisation hat, und ändern nichts.

Schritt Tut Verlässt über
table.list listet die Tabellen, die der Principal sieht, nach Namen gesucht out, mit tables und total
table.describe liest Namen und Spalten einer Tabelle out, mit columns
table.exists ob eine Tabelle genau dieses Namens existiert yes, mit ihrer ID, oder no
table.record.exists ob ein Datensatz zu den Filtern passt yes, mit der ID des ersten, oder no

table.exists vergleicht den ganzen Namen ohne Rücksicht auf Groß- und Kleinschreibung, sodass ein Workflow seine Tabelle beim ersten Lauf anlegen und danach wiederverwenden kann. Beide Fragen verlassen den Schritt über genau einen Port, und ein logic.merge kann die Zweige wieder zusammenführen.

app.workflows.nodes.table_list._handler.TableListOutput

Bases: BaseModel

The tables found, and how many match in all.

app.workflows.nodes.table_describe._handler.TableDescribeOutput

Bases: BaseModel

The table and its live columns, in order.

app.workflows.nodes.table_record_exists._handler.TableRecordExistsConfig

Bases: BaseModel

Which table, and what a record must match.

app.workflows.nodes._tables.TableRecordOutput

Bases: BaseModel

One record as a step hands it on: by column id for bindings, by label to read.

app.workflows.nodes.table_record_get._handler.TableRecordLookup

Bases: BaseModel

Whether the record exists, and it when it does.

app.workflows.nodes.table_record_query._handler.TableRecordQueryConfig

Bases: BaseModel

Which records, in what order, and how many.

app.workflows.nodes.table_create._handler.TableCreateConfig

Bases: BaseModel

The new table: its name, what it is for, its columns, who sees it.

app.workflows.nodes.table_create._handler.TableCreatedOutput

Bases: BaseModel

The table made, as a reference later steps bind to, and its columns by label.

Dateien

Eine Datei, die ein Schritt erzeugt, wird als Datei seines Runs gespeichert und als FileRef weitergegeben: eine ID, der Typ, als der sich ihre Bytes erwiesen haben, und eine Größe. Ein Schritt liest eine Datei nur, wenn sein eigener Run sie erzeugt hat oder der Run mit ihr gestartet wurde - ein im Graphen gebundener FileRef, beim Veröffentlichen gegen einen Run geprüft, den der Autor sehen kann. Jede andere Datei, die einer anderen Organisation oder eines anderen Runs, ist FILE_NOT_FOUND, sodass eine bekannte ID nichts gewährt. Die Dateien eines Runs stehen auf seiner Seite zum Herunterladen bereit.

Knoten Macht Effekt
http.download Holt eine Datei per HTTP, gestreamt, und speichert sie write
http.upload Sendet eine Datei an einen HTTP-Endpunkt, gestreamt aus dem Speicher write
file.read Liest eine Datei als Text, JSON-Wert oder CSV-Zeilen read
file.write Speichert Text, einen JSON-Wert oder Zeilen als Datei write
text.extract Der Text einer TXT-, JSON-, CSV-, Text-PDF- oder DOCX-Datei read
convert.csv_to_json Eine CSV-Datei als JSON-Datei mit Zeilen write
convert.json_to_csv Eine JSON-Liste flacher Objekte als CSV-Datei write
convert.text_to_file Text als TXT-Datei write
convert.pdf_to_png Ausgewählte PDF-Seiten als PNG-Bilder write
image.transform Schneidet ein Bild zu, skaliert, dreht oder konvertiert es write

Ein Download folgt denselben SSRF- und Credential-Regeln wie http.request, bis zu fünf Weiterleitungen. Sein Body wird beim Eintreffen gezählt und über max_bytes abgelehnt, und sein Typ wird an den Bytes erkannt, sodass ein Header expected_content_types nicht täuschen kann. text.extract macht kein OCR: Eine gescannte Seite lässt den Schritt mit TEXT_EXTRACTION_NEEDS_OCR scheitern und nennt die Seiten. Ein beschädigtes Dokument ist DOCUMENT_CORRUPT, ein Word-Dokument, das sich über die Archivgrenzen eines Chat-Uploads hinaus entpackt, DOCUMENT_TOO_LARGE, ein passwortgeschütztes PDF DOCUMENT_ENCRYPTED.

Ein Bild wird vermessen, bevor es dekodiert wird. Seine Breite mal Höhe, ein Zuschnittsbereich und eine angeforderte Größe werden jeweils gegen CHAT_IMAGE_MAX_PIXELS geprüft, und das Ergebnis trägt keine Metadaten der Quelle. Jeder Schritt, der eine Datei speichert, speichert bei jedem Versuch eine neue, also ist er at_least_once. Die Dateien eines Runs dürfen zusammen WORKFLOW_RUN_MAX_FILE_BYTES belegen, die einer Organisation WORKFLOW_ORG_MAX_FILE_BYTES. Ein Schritt, dessen Datei eines davon überschreiten würde, scheitert mit FILE_QUOTA_EXCEEDED und speichert nichts. Um Platz zu schaffen, lösche alte Runs oder lege bei den Workflows, die Dateien erzeugen, eine Aufbewahrungsdauer für Runs fest.

app.workflows.nodes.http_download._handler.HttpDownloadConfig

Bases: BaseModel

What to fetch, and what to accept.

app.workflows.nodes.http_upload._handler.HttpUploadConfig

Bases: BaseModel

Where to send the file, and how.

app.workflows.nodes.file_read._handler.FileReadConfig

Bases: BaseModel

CSV-Konvertierungen erlauben höchstens 10.000 Zeilen, 1.000 Spalten und 1.000.000 Zellen. Beide Richtungen begrenzen die kodierte Ausgabe vor dem Speichern auf max_bytes des Schritts. Die PDF-Textextraktion verweigert mehr als 2.000 Seiten und liest nach Überschreiten von max_chars keine weiteren Seiten. Direkt angegebene Dateien werden bei der Zulassung erneut gegen den aktuellen Zugriff der ausführenden Person auf den Quell-Workflow geprüft.

Sandbox-Ausgaben werden auch beim Streaming vom Host begrenzt. Eine nach der Messung wachsende Datei umgeht das verbleibende gemeinsame Byte-Limit nicht; das Ergebnis-JSON zählt mit. Beendete aufgerufene Runs bleiben bis zum Ende ihres Aufrufers erhalten, damit die Aufbewahrung keine Wiederholung ihrer Seiteneffekte auslöst.

app.workflows.nodes.text_extract._handler.TextExtractOutput

Bases: BaseModel

app.workflows.nodes.convert_pdf_to_png._handler.ConvertPdfToPngConfig

Bases: BaseModel

app.workflows.nodes.image_transform._handler.ImageTransformConfig

Bases: BaseModel

Python

Zwei Knoten führen Python aus, für zwei Arten von Arbeit.

code.python.simple führt ein kurzes Skript in der Monty-Sandbox aus, die kein Dateisystem, kein Netzwerk und nur eine kleine Standardbibliothek hat. Das Skript liest die gebundenen Werte als args, und sein letzter Ausdruck ist das result des Schritts, das ein JSON-Wert sein muss. Es rechnet und tut sonst nichts, also ist es pure und braucht code:execute.

Im Editor wird das Skript eines Code-Schritts in einem Code-Editor geschrieben: Python oder JavaScript, in beiden Themes hervorgehoben, Tab und Shift+Tab zum Einrücken, Enter behält die Einrückung, Klammern und Anführungszeichen schließen sich beim Tippen, die Klammer am Cursor wird mit ihrem Gegenstück umrahmt, und die Schlüssel des gebundenen args werden angeboten, sobald args[" oder, in JavaScript, args. getippt wird. Esc und dann Tab verlässt den Editor. Test step führt das Skript allein mit dem aus, was die Schritte davor weitergegeben haben.

code.python.sandbox führt vollständiges Python mit Paketen und den Dateien des Runs auf der sandboxd-Verbindung der Organisation aus und braucht sandbox:execute. Das Skript findet seine Eingabedateien in inputs, schreibt Dateien nach outputs und setzt result. Es ist ein dauerhafter Job: Der erste Dispatch startet ihn im Hintergrund, und jeder weitere prüft ihn in derselben Sitzung, sodass ein neu gestarteter Worker sich wieder verbindet, statt ihn erneut zu starten. Kein Credential der Plattform gelangt je in die Sandbox, und was das Skript über seine Dateien hinaus erreicht, ist die eigene Konfiguration der Runtime des Hosts - wähle für nicht vertrauenswürdige Arbeit eine Runtime ohne Netzwerk.

Was passiert ist Ergebnis
Das Ergebnis ist kein JSON PYTHON_OUTPUT_NOT_JSON
Das Skript hat eine Ausnahme ausgelöst oder ein Limit überschritten PYTHON_ERROR
Der Job lief länger als timeout_seconds PYTHON_SANDBOX_TIMEOUT, Sitzung gelöscht
Keine nutzbare sandboxd-Verbindung SANDBOX_UNAVAILABLE
Es schrieb mehr als 20 Dateien oder 100 MB oder gab mehr als 10 MB aus PYTHON_OUTPUT_TOO_LARGE, in der Sandbox gemessen, bevor etwas abgeholt wird, und die Session gelöscht
Der Host war nicht erreichbar SANDBOX_UNREACHABLE, wiederholt

app.workflows.nodes.code_python_simple._handler.PythonSimpleConfig

Bases: BaseModel

app.workflows.nodes.code_python_sandbox._handler.PythonSandboxConfig

Bases: BaseModel

app.workflows.nodes.code_python_sandbox._handler.PythonSandboxOutput

Bases: BaseModel

JavaScript

code.javascript.sandbox führt JavaScript auf Node als denselben dauerhaften Job aus, über dieselbe sandboxd-Verbindung, und braucht sandbox:execute. Das Skript ist der Rumpf einer async-Funktion: Es liest die gebundenen Werte als args, seine Eingabedateien in inputs, schreibt Dateien nach outputs, darf await verwenden, und was es per return zurückgibt, ist das result des Schritts - null, wenn es nichts zurückgibt. require lädt Nodes eigene Module und was die Runtime installiert hat. Wähle eine Runtime mit Node.

Seine Fehler sind die der Python-Sandbox, für JavaScript benannt: ein geworfener Fehler ist JAVASCRIPT_ERROR, ein Ergebnis, das kein JSON-Wert ist - eine Funktion, ein BigInt - ist JAVASCRIPT_OUTPUT_NOT_JSON, und Zeitlimit und Ausgabegrenzen sind JAVASCRIPT_SANDBOX_TIMEOUT und JAVASCRIPT_OUTPUT_TOO_LARGE.

app.workflows.nodes.code_javascript_sandbox._handler.JavaScriptSandboxConfig

Bases: BaseModel

app.workflows.nodes.code_javascript_sandbox._handler.JavaScriptSandboxOutput

Bases: BaseModel

workflow.run

Run a workflow. Führt die veröffentlichte Version eines anderen Workflows aus, die mit Called by a workflow beginnt, mit dem gebundenen input, so wie dieser Lauf handelt. Der Schritt wartet, bis der aufgerufene Lauf endet, und gibt dessen output weiter oder scheitert mit CALLED_WORKFLOW_FAILED und dem Fehler des aufgerufenen Laufs; ist Wait for it to finish aus, gibt er den gestarteten Lauf sofort weiter. Der aufgerufene Lauf ist mit dem Schritt und der Kette dieses Laufs verknüpft. Ein Aufruf eines Workflows, der weiter oben in der Kette schon läuft, scheitert mit WORKFLOW_CALL_LOOP, einer tiefer als fünf Aufrufe mit WORKFLOW_CALL_TOO_DEEP. Ein Testlauf des Entwurfs ruft den veröffentlichten Workflow wirklich auf.

app.workflows.nodes.workflow_run._handler.WorkflowRunConfig

Bases: BaseModel

Which workflow to run, and whether to wait for it.

app.workflows.nodes.workflow_run._handler.WorkflowRunOutput

Bases: BaseModel

The run it started: how it stands, and what it answered once it has.

Einen Knoten hinzufügen

Ein Knoten ist ein Paket unter backend/app/workflows/nodes/: __init__.py registriert eine NodeDefinition, _handler.py implementiert sie, und README.md erklärt, warum es sie gibt. load_builtins importiert das Paket. tests/test_workflow_node_layout.py erzwingt diesen Aufbau. Ein Handler gibt Completed, Waiting, Failed oder Uncertain zurück und wirft nie. Den Run, für den er läuft, liest er aus app.services.workflow_execution.context.current().

Ein typisierter Knoten deklariert drei Pydantic-Modelle: seine Konfiguration, im Editor gesetzt und bei der Veröffentlichung eingefroren; seine Eingabe, deren Felder die Bindungen füllen; und seine Ausgabe, an die sich spätere Schritte binden. extra="forbid" auf jedem hält einen Tippfehler aus einem veröffentlichten Graphen. Wählen Sie retry_guarantee danach, was eine Wiederholung des Aufrufs täte: idempotent, wenn der Handler eine Wiederholung selbst harmlos macht - ein Tabellen-Schreibzugriff übergibt operation_key() -, at_least_once, wenn eine Wiederholung vertretbar ist, und none, wenn nicht. Ein Knoten, der eine Ressource liest oder schreibt, deklariert check_resources, das die Veröffentlichung gegen den Autor und jeder Run gegen seinen Principal ausführt.

Die Konsole zeichnet einen Knoten aus seiner Definition, mit Icon und Tönung in frontend/src/components/workflows/node-visuals.ts. Testen Sie den Handler direkt auf seine Ablehnungen, und führen Sie ihn einmal über tests/integration/workflow_run_support.py aus, sodass Konfiguration, Bindungen und Ausgabe in einem echten Run belegt sind.

app.workflows.contracts.definition.NodeDefinition dataclass

A node type, as _registry.get() returns it and the catalog serializes it.

id is permanent, like a capability id: it is what a graph stores and what a client's exported workflow names. version is bumped on a breaking config/IO change; a graph pins (id, version), so an old published graph may still reference a version this deployment has since superseded - _registry.REGISTRY keeps every version it has ever seen, never only the latest.

retry_guarantee_for class-attribute instance-attribute

retry_guarantee_for = None

The guarantee one configured call gives, when it is not the kind's own.

retry_guarantee above is the conservative default for the kind; http.request is at_least_once as a kind, but a GET - or a write sent with an idempotency header the far side honours - is idempotent. Called with the resolved config when an attempt is created, before the handler.

ports_for class-attribute instance-attribute

ports_for = None

This instance's ports, when its config declares some of them.

error.handle's branches are the author's, one output port each, so they cannot live in ports, which is fixed per kind. Called with the validated config (None if it does not validate); ports is what the catalog shows.

loop_body_only class-attribute instance-attribute

loop_body_only = False

Whether the node exists only inside a control.foreach body - loop.item and loop.yield, which mean nothing outside an iteration.

spends_budget class-attribute instance-attribute

spends_budget = False

Whether an attempt may spend money out of the run's budget - a model call, a knowledge search, an agent, a called workflow. Such an attempt is handed the most it may spend when it begins, and waits when parallel steps already hold all that is left; see app.services.workflow_execution.budget.

exclusive_branches class-attribute instance-attribute

exclusive_branches = False

Whether routes always answers with exactly one output port, so a logic.merge may rejoin the node's branches - logic.if, a decision, a classification. control.foreach routes too, but leaves by body and done in one run, so it is not exclusive.