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.emailbeim Veröffentlichen angenommen und beim Ausführen des Knotens gegen sein Ziel geprüft. Ein Wert, der nicht passt, lässt den Run mitINVALID_BINDINGfehlschlagen, 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- oderidempotent-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
¶
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
¶
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_f1ist der Anteil der Wörter, die beide Texte gemeinsam haben, unabhängig von der Reihenfolge.line_diffist 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:
textist der lesbare Text als Markdown, gekürzt auf Most characters of text (standardmäßig 100.000;text_truncatedsagt, 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.titleist der<title>der Seite, auf 500 Zeichen gekürzt, oder null, wenn die Seite keinen hat (auch bei einem leeren<title>).linksenthält jedesa[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 aufjavascript:-,vbscript:- unddata:-Adressen werden weggelassen, damit ein späterer Schritt, der jeden Link abruft, nie ein Skript bekommt; ebenso einhrefü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_truncatedsagt, dass die Seite mehr hatte.matchesenthält die Elemente, die CSS selector auswählt, in Dokumentreihenfolge, höchstens Most matches (standardmäßig 20, bis 200);match_countsagt, wie viele der Selektor vor dem Kürzen ausgewählt hat. Jeder Treffer hat seinentextund sein äußereshtml(beide auf 10.000 Zeichen gekürzt) und seine ersten 50attributes, wobei ein mehrwertiges wieclassmit 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 nachscoreabsteigend, Limit auf 10, dann Edit fields, das nurnameundscorebehält, bereit für eine Nachricht. - Summen je Region - Split out
lines, sodass jede Bestellposition ein Element ist, dann Summarize diesumvonamount, gruppiert nachregion: ein Element je Region mitsum_amount. - Eine Liste von Adressen - Aggregate
emailgibtvalues.emailweiter, 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.
knowledge.search¶
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:
finalist die Fassung, die freigegeben wurde, bei unveränderter Übernahme der Vorschlag selbst.originalist der Vorschlag zum Zeitpunkt der Anfrage,"", wenn ein editierbarer Schritt nichts gebunden hatte.editedist wahr, wenn sichfinalvonoriginalunterscheidet. 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.
flow.resume_link¶
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.
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
¶
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
¶
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
¶
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
¶
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
¶
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.