Węzły workflow¶
Każdy krok, jaki może zawierać workflow: czym się go konfiguruje, co czyta, co produkuje i co znaczy jego błąd. Paleta w edytorze pokazuje te same węzły z tego samego rejestru. Dokumentacja pól poniżej jest generowana ze źródła, dlatego zostaje po angielsku.
Kilka zasad obowiązuje każdy węzeł:
- Krawędź ustala kolejność, a binding niesie wartość. Pola wejściowe węzła są wiązane z wcześniejszymi wyjściami, z literałem albo z referencją do pliku lub tabeli. Zobacz Konfigurowanie węzła.
- Ścieżka w głąb wartości o dowolnym kształcie jest sprawdzana, gdy węzeł
działa. Ładunek triggera, zmapowany rekord i ustrukturyzowana odpowiedź
agenta nie mają stałego kształtu, więc binding taki jak
payload.emailjest przyjmowany przy publikacji i sprawdzany względem celu, gdy węzeł zostaje wysłany. Wartość, która nie pasuje, kończy run błędemINVALID_BINDING, a handler nigdy jej nie widzi. - Zasoby są sprawdzane dwa razy. Publikacja odrzuca kolekcję, wersję agenta, poświadczenie albo odbiorcę, do których autor grafu nie ma dostępu. Każdy run sprawdza je ponownie względem własnego principala, bo dostęp może zostać odebrany w międzyczasie.
- Rodzaj efektu i ponowienia decydują, co silnik może zrobić po błędzie.
Krok
purealboidempotentjest ponawiany. Krok, który mógł już zadziałać i nie daje żadnej gwarancji, zatrzymuje się dla człowieka. Własna polityka węzła ustala, jak często jest ponawiany, jak długo może trwać wywołanie i dokąd trafia błąd.
Wyzwalacze¶
Workflow startuje od jednego wyzwalacza, węzła, od którego zaczyna się jego graf.
Publikacja wersji włącza jej wyzwalacz; zobacz
Uruchamianie workflow spoza konsoli.
Każdy wyzwalacz przekazuje kolejnym krokom to, od czego jego powierzchnia uruchomiła
run, zamrożone przy przyjęciu runa i najwyżej WORKFLOW_RUN_MAX_INPUT_BYTES. Run
testowy uruchomiony z wejściem innego kształtu kończy wyzwalacz błędem
TRIGGER_INPUT_INVALID. Drugi wyzwalacz albo wyzwalacz, od którego graf się nie
zaczyna, jest odrzucany przy publikacji.
core.input¶
API request. Uruchamiany żądaniem HTTP albo przez WebSocket. Jego id pozostaje core.input, więc graf zapisany, zanim Manual i API się rozdzieliły, dalej startuje z API. Przekazuje
grafowi wejście runa jako payload, cokolwiek wysłał wywołujący, i nazywa
powierzchnię w 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.
Jego pola wejścia robią z wejścia kontrakt. Pole ma nazwę, typ - tekst, liczba,
liczba całkowita, tak lub nie, data albo wybór - i to, czy jest wymagane. Bez pól run
przyjmuje dowolny obiekt JSON. Z polami Start a run prosi o każde po nazwie, run,
którego wejściu brakuje pola, który wysyła pole złego typu albo niezadeklarowane,
jest odrzucany przed startem błędem WORKFLOW_RUN_INPUT_INVALID, a binding do
payload.<pole> jest sprawdzany pod kątem typu przy publikacji.
app.workflows.contracts.fields.InputField
¶
Bases: BaseModel
One value a run starts with, asked for by name in the Run dialog.
trigger.manual¶
Manual. Uruchamia go osoba - Test draft w edytorze albo Start a run na stronie runów. Przekazuje grafowi to samo, co API request, i przyjmuje te same pola wejścia, o które edytor prosi przed startem runa.
trigger.chat¶
Chat message. Uruchamiany wiadomością na czacie, gdy ten workflow wybrano do
odpowiedzi. Tekst jego core.output trafia z powrotem do tej rozmowy.
app.workflows.nodes._triggers.ChatTriggerOutput
¶
Bases: _Frozen
The message a member sent in the chat, and where the answer goes.
trigger.webhook¶
Webhook. Uruchamiany podpisanym dostarczeniem na własny adres workflow, który pierwsza publikacja węzła tworzy razem z sekretem do podpisu.
Przed publikacją Listen for test event w panelu Output wyzwalacza otwiera testowy URL dla szkicu na dwie minuty. Jedno wysłane na niego wywołanie, obiekt JSON, zostaje przypięte jako wynik wyzwalacza, więc każdy kolejny krok da się przetestować na prawdziwym dostarczeniu. Testowy URL nie sprawdza podpisu i nigdy nie uruchamia runu.
app.workflows.nodes._triggers.WebhookTriggerOutput
¶
Bases: _Frozen
One signed delivery: its JSON body and the id the sender gave it.
trigger.schedule¶
Schedule. Uruchamiany według zegara, w strefie czasowej workflow i najczęściej raz na minutę.
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. Uruchamiany dodaniem, zmianą lub usunięciem rekordu jego tabeli -
tym, co wymienia events - gdy rekord pasuje do każdego filtra. Zmiana uruchamia go
tylko wtedy, gdy zmieniła się kolumna wybrana dla tabeli, o ile kolumny są wybrane.
Publikacja wymaga dostępu do odczytu tabeli.
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. Uruchamiany przez krok workflow.run innego workflow, z
polami, które deklaruje - tymi samymi co Manual - i nigdy ręcznie ani przez API.
Dane wejściowe wywołującego są sprawdzane względem nich, zanim przebieg się zacznie.
trigger.form¶
Form. Startuje, gdy ktoś wypełni publiczną stronę workflow pod linkiem, który
tworzy pierwsza publikacja węzła. Strona pokazuje tytuł, opis i jedno pole na każde
pole - te same pola co Manual - a zgłoszenie, które do nich pasuje, uruchamia run
jako członek, który go opublikował. triggered_by to form.
app.workflows.nodes.trigger_form._handler.FormTriggerConfig
¶
Bases: TriggerInputConfig
The page a form shows, and the fields it asks for.
trigger.github¶
GitHub. Startuje od dostawy podpisanej przez GitHub dla repozytorium, jednego ze
zdarzeń i - jeśli węzeł je wymienia - jednej z akcji, które wskazuje węzeł. Publikacja
rejestruje hook przez podłączone konto GitHub organizacji, gdy może, albo raz pokazuje
sekret do wklejenia. triggered_by to 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. Startuje od nowej wiadomości w podłączonej skrzynce Gmail organizacji,
pasującej do wszystkich ustawionych filtrów, czytanej w odstępie Check for new mail every skrzynki (domyślnie minuta). Publikacja jest
odrzucana, dopóki skrzynka nie jest podłączona. triggered_by to gmail. Sender contains dopasowuje adres nadawcy, nie nazwę
wyświetlaną: @acme.com to dokładnie ta domena, bez poddomen. Gdy jest ustawiony,
wiadomość musi też przejść sprawdzenie uwierzytelnienia nadawcy (DMARC albo podpis
DKIM domeny nadawcy). Wiadomości z etykietą SENT, DRAFT, SPAM lub TRASH są
pomijane, chyba że Label je wskazuje.
Wyjście niesie wątek - thread_id, rfc_message_id, in_reply_to, references,
received_at - oraz treść html, czytaną do 20 000 znaków. Pola nagłówkowe pisze
nadawca, więc wiążą odpowiedź z wątkiem, ale nic nie dowodzą o tym, kto wysłał
wiadomość.
Run uruchomiony przez skrzynkę zapisuje załączniki wiadomości jako pliki runa, ale tylko
gdy workflow je czyta: graf, który nie wiąże ani attachments triggera, ani całego jego
wyjścia, zostawia każdy załącznik jako not_fetched. Każdy zapisany plik wlicza się do
limitu plików organizacji, a do triggera bez Sender contains może napisać każdy, więc
wiąż attachments tylko wtedy, gdy krok potrzebuje plików.
Skrzynka podaje najwyżej 20 na wiadomość, a brane jest pod uwagę tylko pierwsze 10 z
wymienionych, do 25 MiB łącznie, każdy w limicie rozmiaru uploadu czatu, na liście
dozwolonych typów plików i w limicie miejsca organizacji. Pominięty załącznik
zostaje w attachments z polem reason: too_many, too_large,
type_not_allowed, quota, unreadable lub not_fetched. Run, którego skrzynka
nie uruchomiła - testowy - niczego nie czyta, więc każdy załącznik jest
not_fetched.
unreadable oznacza odpowiedź 400, 401, 403 lub 404 dla załącznika albo grant
skrzynki, który jest brakujący, wyłączony lub nie da się go odnowić. Każda inna
awaria dostawcy kończy krok błędem MAIL_ATTACHMENT_UNREACHABLE, który jest
ponawiany. Oba wyzwalacze poczty przyjmują dane z zewnątrz.
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. Startuje od nowej wiadomości w folderze podłączonej skrzynki Microsoft
365 organizacji, pasującej do wszystkich ustawionych filtrów, czytanej w odstępie Check for new mail every skrzynki (domyślnie minuta). Publikacja jest odrzucana, dopóki skrzynka nie jest podłączona.
triggered_by to outlook. Microsoft Graph jest czytany folder po folderze, więc
Folder i Shared mailbox są miejscem, w którym poller szuka, a zarazem
filtrem, a pierwszy odczyt folderu zaczyna się od chwili opublikowania lub ostatniego włączenia workflow. Wskazanie skrzynki
współdzielonej, którą otwiera grant Microsoft organizacji, wymaga uprawnienia do
zarządzania połączeniami, tak jak w triggerach agentów Outlook; workflow, który już
nasłuchuje takiej skrzynki, zachowuje ją przy kolejnych publikacjach, a jej zmiana znowu
wymaga tego uprawnienia.
Sender contains dopasowuje adres nadawcy, jak w Gmailu, i wymaga poczty,
którą uwierzytelniła domena nadawcy albo która przyszła z wnętrza tenanta. Folder
lub skrzynka współdzielona, które według Outlooka nie dają się odczytać,
wyłączają workflow, z mailbox_folder_unreadable w dzienniku audytu. Zapisana konfiguracja,
która przestała przechodzić walidację, jest pomijana przez poller. Załączniki są
zapisywane jak w trigger.gmail, a labels zawiera kategorie
wiadomości. Jej wyjście jest takie samo jak 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. Startuje od wiadomości do jednego z botów organizacji na
Slacku, Mattermost lub Telegramie, na którą bot odpowiedziałby własnym agentem i
która pasuje do filtra węzła. Publikacja wymaga channels:manage, a wiadomości
jednego bota uruchamiają najwyżej jeden workflow. triggered_by to channel_message.
Bot Teams jest odrzucany przy publikacji i w przebiegu testowym; nasłuch Teams opublikowany
wcześniej oddaje swoje wiadomości agentowi bota.
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. Uruchamiany raz dla każdego nieudanego prawdziwego przebiegu workflow, którego ustawienia wskazują ten jako workflow błędów, jako członek, który go wybrał. Przebieg uruchomiony tym wyzwalaczem sam nigdy nie uruchamia workflow błędów.
app.workflows.nodes._triggers.WorkflowFailedTriggerOutput
¶
Bases: _Frozen
The run that failed: which workflow, which step, and with what error.
core.output¶
To, czym workflow odpowiada. Jego zbindowane pola stają się output runa, który
zwraca API i dostarcza wywołująca powierzchnia. Ma te same pola co wyjście
agent.run, więc odpowiedź agenta binduje się wprost. Wyjście bez żadnego
bindingu to pusta odpowiedź. Run odpowiada raz, więc publikacja odrzuca graf z dwoma
takimi krokami, które mogą oba działać w jednym runie.
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. Odpowiada na dostarczenie, które uruchomiło run, statusem
(od 200 do 599), nagłówkami i JSON-owym body powiązanym z wcześniejszego kroku.
Dostarczenie do grafu z tym krokiem czeka na niego zamiast dostać 202, najwyżej
przez WORKFLOW_WEBHOOK_RESPONSE_TIMEOUT_SECONDS. Odpowiedzią jest pierwszy
ukończony krok respond, ponowienie tego samego dostarczenia dostaje ją jeszcze
raz, a run toczy się dalej. Run, który kończy się bez dojścia do kroku, odpowiada
202, jeśli się powiódł, i 500, jeśli nie. Publikacja odrzuca graf
z dwoma takimi krokami, które mogą oba działać w jednym runie. Nagłówki należące do własnej
odpowiedzi API są odrzucane przy publikacji: ramkowanie, Content-Type,
Set-Cookie oraz nagłówki CORS i polityk przeglądarki.
app.workflows.nodes.webhook_respond.WebhookRespondConfig
¶
Bases: BaseModel
The status and headers the delivery is answered with.
data.map¶
Buduje mały, typowany rekord z wcześniejszych wyjść. Każde mapowanie czyta jedną
wartość wyrażeniem JMESPath i zamienia ją na string, number, integer,
boolean, json, file_ref albo table_ref. Wartość, której nie da się
zamienić, kończy się błędem MAPPING_COERCION_FAILED z nazwą pola.
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 i data.combine¶
Filter a list zostawia te elementy powiązanej listy, dla których spełniony jest
warunek JMESPath nad item i index, i mówi, ile odrzucił. Combine lists tworzy
jedną listę z first i second: append stawia jedną po drugiej, by_position
łączy obiekty na tej samej pozycji, a by_key dołącza do każdego obiektu pierwszej
obiekt drugiej o tym samym key. Gdy oba mają pole, wygrywa druga; łączenie element
po elemencie wymaga obiektów, inaczej kończy się 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 ocenia, jak bardzo podobne są dwa teksty, a i b: 1, gdy są takie
same po pominięciu ignorowanych różnic, 0, gdy nie mają nic wspólnego. Nie wywołuje
modelu i nic nie kosztuje, a te same dwa teksty zawsze dają te same liczby (wyniki
są zaokrąglane do sześciu miejsc). Metoda określa, co jest liczone:
char_ratio(domyślna) to udział znaków, których edytor nie musiałby ruszać, liczony z dokładnej odległości edycyjnej po znakach.token_f1to udział słów wspólnych dla obu tekstów, bez względu na kolejność.line_diffto udział linii, które się nie zmieniły.
Złożone i rozłożone akcenty nigdy nie są edycją, podobnie jak zakończenia linii.
Resztę ustawia krok. Ignore whitespace jest domyślnie włączone: zwija odstępy
w obrębie linii, przycina każdą linię i usuwa puste linie. Podziały linii nadal się
liczą, więc miejsca końca linii ignoruje tylko token_f1, który liczy słowa.
Ignore case i Ignore punctuation są domyślnie wyłączone. Słowa w token_f1
to ciągi liter, cyfr i podkreślników: dzieli na apostrofach i nie dzieli na słowa
tekstu pisanego bez spacji.
Leave out quoted mail and signatures pomija każdą linię, której pierwszy
niebiały znak to > (także cytaty z wcięciem), wiersz z atrybucją nad cytatem oraz
wszystko po separatorze podpisu -- lub samym -- albo po linii
----- Original Message -----. To heurystyka dla poczty w czystym tekście: nie
wykrywa cytatów HTML, odpowiedzi, w której cytat nie jest oznaczony >, atrybucji
zawiniętej na dwie linie (zostaje) ani podpisu bez separatora.
Każdy tekst ma co najwyżej 100 000 znaków, liczonych przed jakąkolwiek normalizacją;
ocena dwóch takich tekstów trwa kilka sekund. Dłuższy tekst kończy run błędem
INVALID_BINDING jeszcze przed uruchomieniem kroku, więc error.handle nie może go
przekierować. Krok, w którym nic nie jest powiązane z a i b, kończy się błędem
TEXT_COMPARE_NOT_CONFIGURED; gdy jeden jest powiązany, a drugi nie, run kończy się
błędem INVALID_BINDING. Puste teksty są dozwolone: dwa puste dają wynik 1, jeden
pusty daje 0. Paleta rysuje krok z ikoną różnic, wśród kroków danych.
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 zamienia HTML na jego tekst, linki i elementy wybrane selektorem CSS.
Służy do HTML-a, który spotyka przepływ: treści maila, body z http.request,
strony zapisanej przez http.download. text.extract czyta dokumenty, nie
znaczniki - oddałby tagi jako tekst. Powiąż albo html (najwyżej 2 000 000 znaków),
albo file (najwyżej 5 000 000 bajtów, sprawdzane przed odczytem pliku), nie oba
naraz. Plik jest dekodowany ze znacznika kolejności bajtów, z <meta charset> lub z
samych bajtów, w razie potrzeby stratnie, a jego zadeklarowany typ nie jest
sprawdzany: PDF czyta się jako zniekształcony tekst i nie jest odrzucany. Znak NUL
czyta się jako U+FFFD.
Krok ma trzy wyjścia, każde ograniczone:
textto czytelny tekst jako Markdown, obcięty na Most characters of text (domyślnie 100 000;text_truncatedmówi, że go obcięto). Nagłówki, listy i kod zostają; skrypty, style, menu (nav), stopki i<head>nie. Tak samo czyta stronę konektor webowy kolekcji wiedzy.titleto<title>strony, obcięty do 500 znaków, albonull, gdy go nie ma (także pusty<title>).linkszawiera każdea[href]raz, w kolejności dokumentu, z tekstem jego pierwszej kotwicy (obciętym do 500 znaków). Link jest rozwiązywany względem Base address, gdy jest ustawiony, a bez niego zostaje tak, jak napisano. Linki do adresówjavascript:,vbscript:idata:są pomijane, żeby kolejny krok, który żąda każdego linku, nigdy nie dostał skryptu; pomijany jest teżhrefdłuższy niż 2048 znaków i taki, którego nie da się odczytać jako adresu. Krok zachowuje najwyżej 500 linków i sprawdza pierwsze 5000 kotwic;links_truncatedmówi, że strona miała więcej.matcheszawiera elementy wybrane przez CSS selector, w kolejności dokumentu, najwyżej Most matches (domyślnie 20, do 200);match_countmówi, ile wybrał selektor przed obcięciem. Każde dopasowanie matexti zewnętrznyhtml(oba obcięte do 10 000 znaków) oraz pierwszych 50attributes, przy czym atrybut wielowartościowy, np.class, jest łączony spacjami, a każda wartość obcięta do 2000 znaków. Bez selektora nie ma dopasowań. Selektor, który nie jest poprawnym CSS-em, jest odrzucany przy publikacji z podaniem pola.
Nic nie jest pobierane i nic się nie uruchamia. Nie jest żądany żaden obraz, arkusz stylów, Base address ani link, skrypt jest tylko tekstem, a parser pochodzi z biblioteki standardowej - to nie parser XML, więc żadna encja spoza strony nie jest rozwijana.
Parsowanie działa w procesie potomnym z 20 sekundami czasu zegarowego, limitem CPU i
limitem pamięci, bo selektor taki jak p div div na głęboko zagnieżdżonym znaczniku
może trwać minuty. Po 20 sekundach proces jest zabijany, a krok kończy się błędem
HTML_TOO_COMPLEX, bez ponawiania. Praca na każdą kotwicę i każde dopasowanie jest
również ograniczona, więc jedna strona nie spowolni kroku ani nie rozdmie jego
wyjścia.
| Co się stało | Wynik |
|---|---|
| Brak ustawień lub wejścia | HTML_PARSE_NOT_CONFIGURED |
html ponad 2 000 000 znaków albo powiązane naraz html i file |
INVALID_BINDING, jeszcze przed uruchomieniem kroku |
| Plik nie istnieje albo ma ponad 5 000 000 bajtów | FILE_NOT_FOUND, FILE_TOO_LARGE |
| Żaden dekoder nie czyta bajtów albo proces czytający pada | HTML_NOT_READABLE, szczegół reason mówi który przypadek |
| Strona zajmuje ponad 20 sekund | HTML_TOO_COMPLEX |
INVALID_BINDING kończy run, zanim krok się uruchomi, więc error.handle nie może
go przekierować; za duży plik to FILE_TOO_LARGE, którym krok kończy się błędem i
który da się przekierować.
Paleta rysuje krok z ikoną kodu, wśród kroków danych.
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¶
Kroki Transform przekształcają listę obiektów bez kroku z kodem. Każdy przyjmuje
items, listę powiązaną z wcześniejszego kroku, a większość przekazuje dalej
items, więc da się je łączyć w łańcuch.
| Krok | Robi |
|---|---|
transform.edit_fields |
Ustawia pola z wyrażeń JMESPath nad każdym item, usuwa pola albo zostawia tylko ustawione |
transform.sort |
Sortuje po kolejnych polach, rosnąco lub malejąco |
transform.limit |
Zostawia pierwsze albo ostatnie elementy |
transform.remove_duplicates |
Zostawia pierwszy z każdej grupy elementów równych na wskazanych polach albo w całości |
transform.aggregate |
Zbiera wartości każdego pola ze wszystkich elementów w jedną listę na pole, jako values |
transform.split_out |
Zamienia listę wewnątrz każdego elementu w osobne elementy |
transform.summarize |
Liczy, sumuje, uśrednia, znajduje najmniejszą lub największą albo liczy różne wartości, w grupach |
transform.date_time |
Teraz albo powiązana value przesunięta o wartość, zapisana w strefie czasowej - workflow, chyba że krok wskazuje własną |
transform.crypto |
Liczy skrót lub koduje base64 powiązany text albo tworzy UUID lub losowy hex |
Pole to ścieżka z kropkami, customer.email, a element bez niego nigdy nie jest
błędem. Sort stawia go na końcu, Remove duplicates traktuje „brak” jako osobną
wartość, Aggregate i Summarize go pomijają, Split out zostawia element bez zmian, a
Edit fields ustawia null, gdy wyrażenie nic nie znajdzie. Crypto nie służy do
sekretów: nic w nim nie używa klucza.
Połączone w łańcuch odpowiadają na częste pytania bez kroku z kodem:
- Najlepsze leady, każdy raz - Remove duplicates po
email, Sort poscoremalejąco, Limit do 10, potem Edit fields zostawiające tylkonameiscore, gotowe do wiadomości. - Sumy według regionu - Split out
lines, aby każda pozycja zamówienia była elementem, potem Summarize zsumpolaamountgrupowane poregion: jeden element na region zsum_amount. - Jedna lista adresów - Aggregate
emailprzekazujevalues.email, wszystkie adresy na jednej liście, dla każdego ustawienia, które przyjmuje listę.
Każdy krok czyta items z poprzedniego. tests/integration/test_workflow_transform_composed.py
uruchamia dwa pierwsze dokładnie tak.
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 i logic.merge¶
logic.if wylicza warunek JMESPath na zbindowanej wartości value i prowadzi
dalej portem true albo false. Każdy węzeł na nieobranej gałęzi zostaje
zapisany jako skipped. value musi być zbindowane: publikacja i przebieg
testowy wersji roboczej odrzucają krok bez niego; wersja opublikowana wcześniej
działa dalej i czyta null.
logic.merge łączy gałęzie z powrotem. Domyślnie czeka na gałąź, która się
wykonała: działa, gdy ta gałąź do niego dotrze, i przekazuje jej wyjście dalej
jako value. Publikacja sprawdza, czy to bezpieczne, i odrzuca merge, którego
wejścia mogą działać w tym samym runie. Nie mogą tego wejścia wychodzące z jednego
kroku różnymi portami, jeśli ten krok w każdym przebiegu wybiera dokładnie jeden
port - If, Switch, decyzja, ai.classify, sprawdzenie tabeli, zatwierdzenie albo
krok kierujący swoje błędy. Nie mogą też gałęzie różnych takich kroków, które się
wzajemnie wykluczają, na przykład zagnieżdżony If ze wspólnym else.
Merge może przyjąć dwa lub więcej portów tego kroku bezpośrednio, bez kroku
pomiędzy, na przykład needs_reply i unsure z ai.classify: uruchamia się raz,
dla portu, który krok wybrał. Dwie krawędzie z tego samego portu nadal są
odrzucane.
Gałęzie działające obok siebie, takie jak odnogi Split, wymagają
ustawienia Wait for na All branches (wait_for: all). Merge uruchamia się
wtedy, gdy każde wejście się zakończy, i przekazuje wyjście każdej gałęzi w
branches, obiekcie według nazw gałęzi, zamiast w value. Nazwę gałęzi daje wpis
w branch_keys: edytor uzupełnia go dla każdego połączenia, nazwą kroku albo
gałęzi Split, a krok dalej czyta branches.<nazwa>. Publikacja odrzuca nazwę,
której nic nie ma, oraz dwie gałęzie o tej samej nazwie.
Publikacja odrzuca też All branches, gdy jakieś wejście nie ma pewności
dotarcia, ilekroć merge działa - z jego nazwą - oraz gdy może działać tylko jedno
wejście, bo wtedy potrzebne jest The branch that ran. branches jest
przechowywane jak wynik każdego kroku: wynik ponad limit inline kończy krok błędem
NODE_OUTPUT_TOO_LARGE, więc pliki przekazuj dalej jako referencje.
Starszy zapisany graf, który czeka na wszystkie gałęzie, a dociera do niego tylko
część z nich, kończy run błędem JOIN_INCOMPLETE ze wskazaniem merge'a. Publikacja
takiego grafu jest odrzucana; kod zostaje jako zabezpieczenie. wait_for i
branch_keys mają wartości domyślne, więc merge zapisany bez konfiguracji nadal
czeka na gałąź, która się wykonała.
Każdy krok z kilkoma krawędziami wejściowymi jest złączeniem (join) i nie potrzebuje merge'a. Uruchamia się, gdy każdy krok prowadzący do niego się zakończy, o ile choć jedna krawędź jest aktywna, a pomijany jest, gdy żadna nie jest. Jest też pomijany, gdy źródło zawiodło bez ścieżki błędu: ta porażka należy już do runa. Bindingi złączenia mogą czytać tylko kroki, które na pewno wykonały się wcześniej, więc złączenie może czytać każdą odnogę grupy równoległej, ale nie jedną gałąź If.
Reguła, kategoria, gałąź albo wybór, któremu nadasz nazwę, staje się portem
wyjściowym, więc nie może powtarzać portu, który krok już ma: in, otherwise,
other, default, approved i rejected w krokach, które je mają, unsure w
klasyfikatorze z minimalną pewnością oraz error w kroku, który kieruje swoje
błędy. Edytor oznacza taką nazwę, a publikacja ją odrzuca, wskazując, która nazwa
koliduje i że trzeba ją zmienić.
Wyrażenia mogą wybierać, filtrować i porównywać oraz wywoływać stały zestaw
czystych funkcji: 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 i values.
Null, false oraz pusty napis, lista albo obiekt są fałszem, a wszystko inne
prawdą, także 0. Wyrażenia, które się nie parsuje albo które wywołuje coś
innego, nie da się opublikować.
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. Kieruje przebieg każdą ze swoich gałęzi naraz. Każda gałąź ma nazwę,
będącą jej portem wyjściowym, a gałęzi jest od dwóch do dwudziestu. Nic nie jest
wybierane i żaden warunek nie jest czytany: każda gałąź jest zawsze obierana, a
kroki po nich działają obok siebie. Krok nie przekazuje niczego dalej, więc krok w
gałęzi binduje się do tego, co działo się przed Split. Nazwa gałęzi nie może być
in ani powtarzać nazwy, którą krok już ma.
Split nie może kierować swoich błędów, bo Split, który zawiódł, nie uruchomił
żadnej gałęzi: publikacja odrzuca na nim on_error: route, a kroki każdej gałęzi
kierują własne. Gałęzie spotykają się znów w
logic.merge, który czeka na wszystkie, albo w
dowolnym kroku mającym je jako wejścia. Także każdy inny krok może wysłać jeden
port do kilku kroków, które działają naraz tak samo; Split nadaje gałęziom nazwy i
trzyma je na widoku.
app.workflows.nodes.logic_split._handler.LogicSplitConfig
¶
Bases: BaseModel
The branches the run is sent down together.
logic.switch¶
Switch. Kieruje przebieg pierwszą z wielu gałęzi, której reguła jest spełniona.
Każda reguła ma nazwę, będącą portem jej gałęzi, i warunek JMESPath nad powiązaną
value, sprawdzane po kolei; otherwise przejmuje przebieg, gdy żadna nie jest
spełniona. value musi być zbindowane: publikacja i przebieg testowy wersji
roboczej odrzucają krok bez niego; wersja opublikowana wcześniej działa dalej i
czyta null. Gałęzie łączą się w logic.merge czekającym na gałąź, która się wykonała;
publikacja go przyjmuje, bo zawsze biegnie dokładnie jedna. Reguła, która zawodzi na swoich danych, kończy krok błędem
CONDITION_FAILED i wskazuje regułę.
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¶
Przeszukuje kolekcje wiedzy według zbindowanego query i zwraca fragmenty jako
typowane źródła, od najlepszego. Pusty wynik to udane wyszukiwanie. Kolekcja,
której już nie ma albo której principal runa nie może już czytać, kończy krok
błędem COLLECTION_NOT_ACCESSIBLE. Krok nigdy nie przeszukuje mniej kolekcji,
niż wskazuje graf.
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¶
Dodaje zbindowany tekst z title albo plik runu do jednej do pięciu kolekcji
wiedzy, tym samym uploadem co strona kolekcji: z jej własnymi ustawieniami
parsowania i dzielenia na fragmenty, sprawdzeniem budżetu organizacji i ingestią
kolejkowaną, a nie oczekiwaną. Tekst jest zapisywany jako <title>.md, a
dokument o tej samej nazwie jest zastępowany, a nie dublowany, więc ponowienie go
aktualizuje. Kolekcja, do której principal nie może dodawać, kończy krok błędem
KNOWLEDGE_ADD_REFUSED.
app.workflows.nodes.knowledge_add._handler.KnowledgeAddConfig
¶
Bases: BaseModel
Which collections the document goes into.
Kroki modelu¶
Cztery kroki pytają jeden z modeli organizacji o jedną rzecz, bez narzędzi.
Każdy działa jako własny agent kroku - tworzony przy pierwszym runie,
publikowany ponownie, gdy zmienią się jego ustawienia - przez ten sam runner co
agent.run, więc max_cost_usd kroku, budżet workflow i limit organizacji są
sprawdzane przed zapytaniem, koszt trafia do runu, a run widać w Activity.
Publikacja sprawdza, czy autor może używać modelu.
| Krok | Robi | Przekazuje dalej |
|---|---|---|
ai.prompt |
Odpowiada na zbindowany prompt według instructions kroku |
text, a structured, gdy structured_output_schema prosi o obiekt |
ai.classify |
Przypisuje zbindowany text do jednej z categories kroku albo do other |
Wychodzi portem kategorii, z category, jednozdaniowym reason, text i confidence; poniżej min_confidence - portem unsure |
ai.extract |
Wyciąga ze zbindowanego text pola nazwane w output_schema, pomijając to, czego tekst nie mówi |
data, obiekt tego kształtu |
ai.rewrite |
Streszcza (short, medium, long), tłumaczy na language albo przepisuje zbindowany text w danym style |
text |
Kategorie klasyfikatora są jego portami, w kolejności, a model może odpowiedzieć
tylko nazwą jednej z nich albo other. Odpowiedź, która nie jest obiektem, o
który prosił krok, kończy go błędem STRUCTURED_OUTPUT_MISMATCH; wyczerpany
limit - błędem AGENT_BUDGET_EXCEEDED.
Gdy ustawiono min_confidence, model podaje też własną pewność co do kategorii,
od 0 do 1. To jego deklaracja, nie prawdopodobieństwo, i nie jest skalibrowana:
zanim zaufasz progowi, sprawdź go na własnych przypadkach za pomocą
ewaluacji kroku. Minimum dodaje port
unsure po other: poniżej niego krok wychodzi tym portem, niosąc to samo
wyjście, więc category nadal jest do dyspozycji workflow.
Bez minimum model nie jest pytany o liczbę, prompt się nie zmienia, confidence
jest null, a kategoria zawsze wygrywa. Odpowiedź bez liczby nigdy nie wychodzi
portem unsure, a gdy ten port istnieje, unsure nie może być nazwą kategorii.
Krok wychodzi dokładnie jednym portem, więc logic.merge może połączyć jego
gałęzie: poprowadzić unsure i jedną kategorię tą samą ścieżką albo dwie kategorie
do jednego kroku zapisu.
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.
Decyzje¶
Trzy kroki zadają Jev od TypeSafe typowane pytanie o powiązany text, z kluczem
API TypeSafe z vaulta. Jev nie pisze tekstu: odpowiada na pytanie z pewnością od 0
do 1, w jednym żądaniu, i może odpowiedzieć tylko jedną z odpowiedzi, na które krok
pozwala. Poniżej min_confidence kroku wychodzi on portem unsure, więc to
workflow decyduje, co człowiek albo agent zrobi z wątpliwym przypadkiem.
decide.* wywołuje wyłącznie hostowanego Jev od TypeSafe. Wątpliwe przypadki na
modelu hostowanym u siebie kieruj przez ai.classify z min_confidence, który
wychodzi portem unsure tak samo.
| Krok | Pyta | Wychodzi portem |
|---|---|---|
decide.yes_no |
o tak albo nie | yes, no albo unsure |
decide.choose |
która z maksymalnie 255 opcji pasuje | out z choice albo unsure |
decide.score |
gdzie tekst leży na skali od 2 do 10 poziomów | out ze score albo unsure |
Klucz jest sprawdzany przy publikacji i odczytywany ponownie przy każdym runie.
Klucz, którego już nie ma albo nie jest udostępniony, kończy krok błędem
SECRET_NOT_USABLE, model, który nie odpowiada, błędem DECISION_FAILED,
ponawianym zgodnie z polityką kroku, a wdrożenie zbudowane bez extra browser
błędem DECISION_MODEL_UNAVAILABLE. Merge może połączyć gałęzie decyzji tak jak
gałęzie kroku 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.
Kanały¶
Slack, Mattermost i Telegram mają każdy własną grupę kroków, które działają jako jeden z botów organizacji na tej platformie, przez ten sam adapter, którym idą jego odpowiedzi, więc wiadomość wysłana przez workflow przychodzi od tego bota, a krok może odczytać to, co bot może czytać. Platforma ma tylko te kroki, które jej boty mogą wykonać.
| Krok | Slack | Mattermost | Telegram | Przekazuje dalej |
|---|---|---|---|---|
Send a message (<platform>.message.send) |
tak | tak | tak | gdzie ją wysłano |
Read messages (<platform>.messages.read) |
tak | tak | - | messages, od najstarszej |
List members (<platform>.members.list) |
tak | tak | administratorzy | members, z ich id na platformie |
Find channels (<platform>.channels.find) |
tak | tak | - | channels |
Krok przyjmuje tylko bota swojej platformy, a bot mówi w imieniu całej
organizacji, więc działanie jako on wymaga channels:manage: od autora grafu przy
publikacji i od podmiotu runa przy każdym runie. Bot usunięty, wyłączony albo z innej
platformy kończy krok błędem CHANNEL_NOT_USABLE. Platforma, która odmawia wywołania
w trakcie runa, kończy go błędem CHANNEL_UNSUPPORTED, a taka, która nie odpowiada,
błędem CHANNEL_CALL_FAILED. Wysyłanie nigdy nie jest powtarzane samo.
Send a message odpowiada w wątku, gdy ustawiono thread_id: znacznik czasu
wątku na Slacku, id posta głównego na Mattermost albo numer tematu forum na Telegramie.
Wiadomość trafia do wątku, nigdy jako cytowana odpowiedź. Pusty thread_id wysyła na
sam kanał. Id kanału lub wątku zawierające : albo wątek Telegrama, który nie jest
liczbą, kończy krok błędem CHANNEL_TARGET_INVALID przed wysłaniem czegokolwiek.
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¶
Pyta opublikowanego agenta, dokładnie w wersji przypiętej w kroku, przez ten sam
runner co chat i API, z budżetem, zatwierdzeniami, guardrailami i historią runów
agenta. Run jest zapisywany z powierzchnią workflow. Zbindowane sources są
dopisywane do promptu jako numerowany kontekst. Wywołanie narzędzia wymagające
zatwierdzenia zatrzymuje krok, a decyzja wznawia ten sam run agenta.
inline konfiguruje agenta w kroku zamiast agent i ustawione jest dokładnie jedno
z nich. Spec należy do kroku i jest wersjonowany razem z grafem: pierwszy run
tworzy agenta należącego do workflow, ukrytego w katalogu i osiągalnego przez
dostęp do workflow, a run, którego spec różni się od najnowszej wersji tego
agenta, najpierw publikuje nową, sprawdzaną tak jak publikacja agenta. Publikacja
workflow sprawdza spec względem dostępu autora. max_cost_usd ogranicza jedno
wykonanie: agent zatrzymuje się przed zapytaniem do modelu, które by je przekroczyło,
a krok kończy się błędem AGENT_BUDGET_EXCEEDED.
Agent z własnym formatem odpowiedzi przekazuje swój obiekt jako structured.
structured_output_schema prosi w zamian o inny kształt: agent jest uruchamiany
z tym schematem, a odpowiedź, która go łamie, wraca do modelu do poprawki. Obiekt
jest sprawdzany jeszcze raz, zanim cokolwiek dalej się wykona. Agent, który nigdy
nie da pasującego obiektu, kończy krok błędem AGENT_RUN_FAILED, a odpowiedź bez
obiektu tam, gdzie o niego proszono, błędem STRUCTURED_OUTPUT_MISMATCH.
| Jak zakończył się run agenta | Wynik kroku |
|---|---|
| Completed | Completed |
| Czeka na zatwierdzenie | Czeka, potem wznawia ten sam run |
| Przekroczony budżet | AGENT_BUDGET_EXCEEDED |
| Zablokowany przez guardrail | AGENT_GUARDRAIL_BLOCKED |
| Inaczej | AGENT_RUN_FAILED |
Powiązane attachments to obrazy z plików runa - pobrany plik, wyrenderowana strona
PDF, przekształcone zdjęcie - pokazywane agentowi jako obrazy, a nie jako link,
którego nie może otworzyć. Pokazywane są tylko PNG, JPEG, WebP i GIF. Każdy inny plik
kończy się UNSUPPORTED_ATTACHMENT_TYPE, więc tekst dokumentu najpierw odczytaj
przez text.extract.
Nigdy nie jest ponawiany automatycznie, bo agent mógł wywołać narzędzia z efektami ubocznymi.
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¶
Wywołuje API HTTP. Każde żądanie i każde przekierowanie przechodzi kontrolę SSRF
wdrożenia i jest wysyłane pod adres, który ta kontrola zatwierdziła. Prywatny host, który operator wpisał do
OUTBOUND_PRIVATE_ALLOWLIST, też jest dopuszczany, ale tylko dla URL-a, który od niego zaczyna: przekierowanie z URL-a publicznego nigdy do niego nie dociera.
URL podpięty z publicznego wyzwalacza (webhooka, formularza, e-maila) albo z argumentów narzędzia agenta może wskazać dowolny host z listy, więc wpisuj tylko hosty, które same sprawdzają swoje poświadczenie.
Poświadczenie to HTTP credential z vaulta i jest wysyłane
tylko do originów, na które pozwala sekret. Odpowiedź jest czytana w granicy
max_response_bytes i oddawana bez Set-Cookie, nagłówków uwierzytelniania i
samego tokena. Odpowiedź w HTML wraca jako tekst w body; powiąż ją z
html.parse, by odczytać jej tekst, linki i elementy.
Dane uwierzytelniające idą jako token bearer, uwierzytelnianie Basic, nagłówek o
podanej nazwie albo, z query, parametr URL o podanej nazwie. Import cURL w
edytorze wczytuje polecenie wklejone z dokumentacji API do metody, URL-a,
nagłówków i treści JSON. Dane uwierzytelniające z polecenia nigdy nie zostają w
kroku: krok zostaje ustawiony, by wysyłał je tak samo, a formularz vault otwiera
się z nimi wypełniony.
GET może przechodzić listę stronami dzięki pagination: idąc za następnym URL-em,
który podaje odpowiedź, odsyłając kursor albo zwiększając parametr strony.
Elementy items_path każdej strony zbierane są do items, po kolei.
Stronicowanie kończy się, gdy nie ma następnej strony, gdy strona nie ma
elementów, albo na max_pages, kiedy complete jest fałszem. Wszystkie strony
razem czytają nie więcej niż max_response_bytes, a następna strona na innym
originie nie dostaje danych uwierzytelniających.
| Co się stało | Wynik |
|---|---|
URL to loopback, metadata, nie http(s) albo prywatny i spoza OUTBOUND_PRIVATE_ALLOWLIST operatora |
URL_REFUSED, nic nie wysłano |
| URL spoza originów poświadczenia | SECRET_ORIGIN_DENIED, nic nie wysłano |
| Połączenie nigdy się nie otworzyło | HTTP_UNREACHABLE, ponawiane |
Wysłane, brak odpowiedzi, GET albo nagłówek idempotencji |
HTTP_NO_RESPONSE, ponawiane |
| Wysłane, brak odpowiedzi, inny zapis | Niepewne: run zatrzymuje się dla człowieka |
| Status inny niż 2xx | HTTP_ERROR_STATUS albo odpowiedź jako wyjście przy on_error_status: complete |
| Większa niż limit | RESPONSE_TOO_LARGE |
items_path strony znajduje coś, co nie jest listą |
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 wywołuje jedno narzędzie na jednym z
serwerów MCP organizacji, z arguments zbindowanymi z wcześniejszych
kroków. Edytor pokazuje serwery ze znakiem ich usługi, a potem narzędzia, które
serwer wypisał przy ostatnim teście; Refresh tools testuje go ponownie.
Każdy argument narzędzia jest wtedy osobnym polem, wpisanym albo zbindowanym
jako arguments/<name>.
Kwalifikuje się tylko serwer należący do organizacji, nigdy własny serwer
członka, a publikacja odrzuca serwer, który nie jest organizacji, jest wyłączony
albo nie wypisał tego narzędzia. Poświadczenie pochodzi z vaultu, tak jak dla
agenta.
approval decyduje, kiedy osoba najpierw zatwierdza wywołanie: writes,
domyślnie, przed każdym narzędziem, którego serwer nie oznacza jako tylko do
odczytu; all przed każdym wywołaniem; none nigdy. Prośba trafia do zakładki
Approvals, tak jak przy human.approval, do approvers albo
do każdego z approvals:decide. Zatwierdzone - narzędzie jest wywoływane;
odrzucone - krok kończy się błędem MCP_CALL_REJECTED. Oznaczenie „tylko do
odczytu” to deklaracja samego serwera, więc dla serwera, któremu tego nie
powierzasz, wybierz all.
Wyjście to text odpowiedzi, jej structured dane, gdy narzędzie je zwraca, i
is_error. Błąd, którym odpowiada narzędzie, kończy krok błędem
MCP_TOOL_ERROR, chyba że on_tool_error to complete.
MCP nie ma własnego klucza idempotencji. Serwer, który przyjmuje taki klucz jako
argument narzędzia, można mimo to uczynić bezpiecznym przy ponowieniu: ustaw
Idempotency key argument (idempotency_key_argument) na nazwę tego argumentu,
a krok wyśle tam swój stały klucz operacji - taki sam przy każdej próbie kroku i
inny w każdej iteracji pętli - bez względu na to, jaką wartość do niego podpięto.
Narzędzie musi deklarować ten argument jako tekst.
Publikacja odrzuca argument,
którego brakuje w zapisanym schemacie narzędzia, a narzędzie bez zapisanego
schematu przechodzi, więc najpierw przetestuj serwer. W czasie runu krok przed
wywołaniem wypisuje narzędzia serwera i kończy się błędem
MCP_IDEMPOTENCY_ARGUMENT_UNDECLARED, niczego nie wywołując, gdy aktualny schemat
nie ma takiego argumentu. Krok z kluczem liczy się jako idempotentny: silnik może
go uruchomić ponownie.
Gdy wywołanie się urwie, dalszy przebieg zależy od tego, czy mogło dotrzeć do serwera:
- Przed wywołaniem - nie udało się połączyć z serwerem albo nie odpowiedział
na połączenie lub listę narzędzi - nic nie wysłano, a krok kończy się
ponawianym
MCP_CALL_FAILED. - Po wywołaniu, wywołanie z kluczem albo narzędzie oznaczone przez serwer jako tylko do odczytu (w liście, którą krok właśnie odczytał, a w razie braku w tej zapisanej przy teście serwera) - ten sam ponawiany błąd, bo powtórzenie jest bezpieczne.
- Po wywołaniu, każde inne narzędzie, bez klucza - narzędzie mogło zadziałać, a odpowiedź nie nadeszła. Krok kończy się jako Uncertain z komunikatem The tool was called and no answer came back; it may have acted, a run czeka na człowieka, który sprawdza drugi system, zanim uruchomi krok ponownie.
| Co się stało | Wynik |
|---|---|
| Serwera nie ma albo jest wyłączony | MCP_SERVER_UNAVAILABLE, nic nie wysłano |
| Jego poświadczenie trzeba ponownie autoryzować | MCP_SERVER_UNAUTHORIZED, nic nie wysłano |
| Serwer nie oferuje narzędzia | MCP_TOOL_NOT_FOUND |
| Nie udało się połączyć z serwerem albo połączenie zerwało się przed bezpiecznym wywołaniem lub po nim | MCP_CALL_FAILED, ponawiany |
| Połączenie zerwało się po wywołaniu bez klucza narzędzia nieznanego jako tylko do odczytu | Uncertain: run zatrzymuje się dla człowieka |
| Narzędzie nie deklaruje Idempotency key argument | MCP_IDEMPOTENCY_ARGUMENT_UNDECLARED, nic nie wysłano |
| Narzędzie odpowiedziało błędem | MCP_TOOL_ERROR albo odpowiedź jako wyjście |
| Ustrukturyzowana odpowiedź ma ponad 1 MiB | MCP_RESULT_TOO_LARGE |
| Ustrukturyzowana odpowiedź nie jest JSON-em | MCP_RESULT_INVALID |
| Krok nie wskazuje serwera ani narzędzia | MCP_CALL_NOT_CONFIGURED, nic nie wysłano |
| Narzędzie nie jest dozwolone w tym połączeniu | MCP_TOOL_NOT_ALLOWED, nic nie wysłano |
| Wskazany zatwierdzający nie może już podejmować decyzji | APPROVER_UNAVAILABLE, nic nie wysłano |
| Wywołanie nie zostało zatwierdzone | MCP_CALL_REJECTED, nic nie wysłano |
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¶
Powiadamia członków organizacji w aplikacji, mailem albo na oba sposoby, przez
centrum powiadomień. Odbiorcy to członkowie wskazani po id. W chwili działania
każdy musi nadal być aktywnym członkiem, który widzi workflow, a pozostali są
pomijani. Jeśli nie zostanie nikt, krok kończy się błędem
NO_PERMITTED_RECIPIENTS. Preferencje każdej osoby dla Workflow
notifications nadal obowiązują. Krok kończy się, gdy powiadomienie zostanie
zapisane, a mail wychodzi później. Ponowiony krok nie zapisuje drugiego
powiadomienia.
app.workflows.nodes.notification_send._handler.NotificationSendConfig
¶
Bases: BaseModel
Who to tell, the headline, and which channels it may use.
email.send¶
Dostawcy wysyłający wiadomości rezerwują każdego unikalnego odbiorcę we współdzielonych limitach Redis przed wysłaniem całej paczki: WORKFLOW_EMAIL_MAX_PER_PRINCIPAL_PER_HOUR (100), WORKFLOW_EMAIL_MAX_PER_ORG_PER_HOUR (500) i WORKFLOW_EMAIL_MAX_PER_DEPLOYMENT_PER_HOUR (1000). Limit użytkownika obejmuje jego klucze i organizacje. Stałe okna godzinne obejmują różne przebiegi i workery; na granicy okien można wykorzystać podwójny limit. Wyczerpanie daje EMAIL_DELIVERY_LIMIT, niedostępność liczników EMAIL_ALLOWANCE_UNAVAILABLE, bez wysyłki. Rezerwacje są konserwatywne i nie są zwracane po częściowej rezerwacji, błędzie dostawcy ani anulowaniu. Dostawca tylko logujący nie zużywa limitu. Limit deploymentu ustaw poniżej kwoty SMTP, zachowując zapas na pocztę transakcyjną.
Send an email wysyła maila na maksymalnie dziesięć adresów z nadawcy i
dostawcy poczty wdrożenia, osobną kopię do
każdego, więc żaden odbiorca nie widzi adresu innego. Jest dla osób spoza
organizacji, takich jak lead czy klient: członków dosięga
notification.send zgodnie z ich preferencjami. to,
subject i zwykły tekst body wpisuje się albo binduje z wcześniejszych
kroków, a reply_to kieruje odpowiedzi gdzie indziej niż na adres odpowiedzi
wdrożenia.
Wyjście wymienia adresy, które dostawca przyjął. delivered jest fałszywe, gdy
dostawca tylko loguje pocztę, co jest domyślne w środowisku deweloperskim, więc
run nigdy nie twierdzi, że dotarł do kogoś, do kogo nie dotarł. Kopia odrzucona
przez dostawcę kończy krok błędem EMAIL_NOT_SENT, wymieniając, co już wysłano.
Poczta nie ma klucza idempotencji, więc ponowiony krok wysyła ponownie.
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 zatrzymuje run, dopóki osoba nie zatwierdzi albo nie odrzuci
tego, co run ma zrobić, a potem idzie dalej przez approved albo rejected.
Zatwierdzający czyta title kroku i powiązane z nim details w zakładce
Approvals w Activity albo przez GET /api/v1/workflow-approvals.
approvers wskazuje, kto może zdecydować, i ci ludzie dostają powiadomienie w
aplikacji; gdy lista jest pusta, może każdy z approvals:decide. Po
timeout_hours prośba wygasa, a krok wychodzi przez rejected z
decision: "expired". Anulowanie runa anuluje jego prośby. Każdy przebieg kroku
pyta raz, więc ponowienie ani iteracja pętli nigdy nie pytają dwa razy o to samo.
Wskazani zatwierdzający muszą być aktywnymi członkami z approvals:decide. Publikacja to sprawdza, a krok sprawdza ponownie przed wysłaniem prośby; utrata tego uprawnienia kończy krok kodem APPROVER_UNAVAILABLE zamiast zostawiać prośbę, której nikt nie może rozstrzygnąć.
Krok może nieść propozycję: tekst powiązany z proposal, na przykład
przygotowaną odpowiedź. Zatwierdzający czyta ją obok prośby. Po włączeniu
editable kolejka pokazuje ją w edytorze, jako text albo markdown
(proposal_format), pod etykietą proposal_label („Propozycja (Proposal)”, gdy pusta), a
zatwierdzający może zatwierdzić zmienioną wersję. Krok bez powiązanego tekstu
otwiera pusty edytor, na wypadek gdy przygotowanie niczego nie dało. details się
nie zmienia i ma pozostać krótkie; propozycja może mieć do 100 000 znaków.
Zatwierdzone wyjście niesie trzy pola obok decyzji:
finalto wersja, którą zatwierdzający zatwierdził, a gdy niczego nie zmienił, sama propozycja.originalto propozycja w chwili prośby,"", gdy edytowalny krok nie miał nic powiązanego.editedjest prawdą, gdyfinalróżni się odoriginal. Końce linii, spacje na końcu linii i puste miejsce wokół tekstu nie są różnicą.
Edytowana wersja jest zapisywana bez białych znaków na początku i na końcu, więc edycja zaczynająca się od wciętego bloku kodu traci to pierwsze wcięcie.
Zatwierdzona prośba kroku editable zapisuje też swój współczynnik zmiany
(edit_ratio), czyli to, jak bardzo zatwierdzający zmienił propozycję, od 0 do 1.
Statystyki workflow podają go razem z pozostałymi
wynikami zatwierdzeń.
Krok, który nie jest editable, pokazuje propozycję tylko do odczytu, a po
zatwierdzeniu przekazuje ją bez zmian jako final z edited równym false, więc
kolejne kroki mogą wiązać final niezależnie od trybu kroku. Odrzucenie albo
wygaśnięcie nie niesie final. Wysłanie edytowanej wersji do kroku, który nie jest
editable, albo razem z odrzuceniem, kończy się odmową 422. Decydujący, którego
krok nie wskazuje, widzi propozycję tylko do odczytu.
Krok może zamiast zwykłego zatwierdzenia oferować wybory (choices), z których
każdy jest sposobem zatwierdzenia: na przykład zostawić szkic albo wysłać go od razu.
Można podać do pięciu, każdy z id (małe litery, cyfry i podkreślenia, nie in,
approved ani rejected) i label do 40 znaków. Kolejka pokazuje jeden przycisk na
wybór zamiast Approve, z etykietą dokładnie tak, jak ją zapisano, bez tłumaczenia,
a Reject zostaje. Krok ma wtedy po jednym porcie wyjściowym na wybór, w kolejności,
potem rejected, i nie ma portu approved: krawędź z approved jest odrzucana przy
publikacji workflow.
Wybór zatwierdzającego jest portem, którym krok wychodzi, a jego id to choice w
wyjściu. Jest null, gdy krok nie ma wyborów, przy odrzuceniu i przy wygaśnięciu. Krok
bez wyborów działa jak dotąd: ma approved i rejected. Zatwierdzenie kroku z wyborami
wymaga jednego z nich jako choice, a podanie choice przy każdej innej decyzji kończy
się odmową 422. Statystyki nie dzielą się według wyboru.
Jeśli workflow wysyła zatwierdzone final przez mcp.call, ustaw
approval tego kroku na none, bo inaczej osoba zostanie zapytana dwa razy.
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. Zatrzymuje przebieg na kroku przez seconds od dotarcia do niego albo do
powiązanego until, po czym idzie dalej; najwyżej trzydzieści dni. Krok jest
zaparkowany na zegarze, którego wiersz dyspozycji przypada na ten moment, więc
oczekiwanie przetrwa restart workera i nie zajmuje go, a inne gałęzie w tym czasie
działają. Moment, który już minął, przechodzi od razu, a termin przebiegu nadal
obowiązuje.
Powiązany until późniejszy niż trzydzieści dni od dojścia do kroku kończy krok błędem WAIT_TOO_LONG.
Z Wait for a call to the run's resume link krok czeka zamiast tego, aż zostanie
wywołany adres, który dał krok Resume link, najwyżej seconds (trzydzieści dni bez
ustawienia). Wyślij na ten adres POST z obiektem JSON albo bez treści: krok przekazuje
treść dalej jako body, z called równym true, i przebieg idzie dalej. Gdy nikt nie
wywoła go na czas, idzie dalej z called równym false. Wywołanie, gdy nic nie czeka -
przed dotarciem do kroku, po tym, jak poszedł dalej, albo po zakończeniu przebiegu -
jest odrzucane z WORKFLOW_NOT_WAITING (409), a link, który nie należy do przebiegu,
odpowiada 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. Przekazuje url, adres, który wznawia kroki Wait tego przebiegu
czekające na wywołanie - dla wiadomości albo żądania przed Wait, aby ten, kto
odpowiada, mógł kontynuować przebieg. Każdy przebieg ma własny, zrobiony z jego id pod
sekretem wdrożenia i nikomu innemu niepokazywany: to jego posiadanie pozwala wznowić
przebieg, więc wysyła się go tylko tam, skąd ma przyjść odpowiedź.
app.workflows.nodes.flow_resume_link._handler.ResumeLinkOutput
¶
Bases: BaseModel
The run's resume link.
Obsługa błędów¶
Każdy węzeł przyjmuje opcjonalną policy obok swojej konfiguracji.
| Pole | Domyślnie | Skutek |
|---|---|---|
timeout_seconds |
brak | Wywołanie, które trwa dłużej, zostaje przerwane. Krok bez zewnętrznego zapisu albo z idempotentnym wywołaniem kończy się błędem NODE_TIMEOUT i może zostać ponowiony. Zapis, który mógł już dojść, staje się niepewny i zatrzymuje się dla człowieka |
retry.max_attempts |
WORKFLOW_RETRY_CEILING |
Łączna liczba prób, łącznie z pierwszą. Ponawiany jest tylko błąd, który węzeł oznacza jako retryable, a publikacja odrzuca więcej niż jedną próbę dla kroku, którego wywołania nie da się bezpiecznie powtórzyć |
retry.backoff, base_delay_seconds, max_delay_seconds |
exponential, 2, 60 |
Odstęp między próbami: stały albo podwajany aż do sufitu |
on_error |
fail_run |
route wysyła błąd, którego ponowienia nie rozwiązały, portem error węzła, zamiast kończyć run błędem |
Węzeł, którego polityka kieruje błędy, ma dodatkowy port wyjściowy error.
Niesie on WorkflowError: code, message, details i retryable. Zwykłe
wyjście węzła istnieje tylko na pozostałych portach, więc publikacja odrzuca
binding, który czyta wyjście na ścieżce błędu albo błąd na ścieżce sukcesu. Port
błędu musi dokądś prowadzić, a obie ścieżki mogą się połączyć w logic.merge.
Gdy kroki działają równolegle, każda gałąź ma własny handler. Gałąź, która zawodzi
bez ścieżki błędu, kończy run błędem, a kroki obok niej nie zaczynają niczego
więcej: ich oczekujące zatwierdzenia i timery są wycofywane, tak samo jak runy
agentów, na których są zaparkowane. Krok, którego praca już trwała, kończy się i
jest zapisany jako zakończony po zatrzymaniu runa, z zachowanym wyjściem. Ponowienie
runa używa go zamiast robić to jeszcze raz, a użyty ponownie krok Output albo Respond
to webhook zgłasza swoją odpowiedź na nowo. Publikacja odrzuca error.handle
zasilany przez dwa kroki, które mogą zawieść w tym samym runie.
Krok wymagający uwagi można rozstrzygnąć na runie, w którym jest. Uznanie go za
nieudany kończy go kodem STEP_MARKED_FAILED, który wychodzi portem error kroku,
jeśli ten kieruje swoje błędy, albo trafia do polityki błędów elementu jego pętli.
Zakończenie runa błędem kończy run tym błędem bez względu na połączenia grafu.
Anulowanie runa albo jego porażka zamyka też krok wymagający uwagi.
error.handle przyjmuje ten błąd na porcie in i wychodzi pierwszą gałęzią,
której code i retryable pasują, albo gałęzią default, która musi być
podłączona. error.raise kończy swoją gałąź błędem o kodzie, komunikacie i
szczegółach ustalonych przez autora.
Niektórych błędów nigdy się nie kieruje. Odebrany dostęp, wyczerpany budżet (runa albo agenta), anulowany run, miniony termin, limit węzłów na run i efekt o nieznanym wyniku kończą run bez względu na to, jak połączono graf. Konflikt rewizji albo błąd walidacji można skierować, ale nigdy nie jest ponawiany na ślepo, bo te same dane wejściowe zawiodą tak samo.
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.
Pętle¶
control.foreach uruchamia swoje ciało raz dla każdego elementu zbindowanej
listy items, po jednym elemencie i po kolei. Potem idzie dalej portem done z
results w kolejności wejścia, errors i count. Ciało zaczyna się od
loop.item, podłączonego z portu body pętli, który udostępnia item, index
i count. Kończy się na loop.yield, którego zbindowane value jest wynikiem
iteracji. Żadna krawędź nie prowadzi z powrotem do pętli. Krok w ciele może
bindować do wszystkiego, co działało przed pętlą, a nic spoza ciała nie może
bindować do jego wnętrza.
Lista zostaje zamrożona, gdy pętla startuje, więc iteracja nigdy nie widzi
źródła, które zmieniło się w trakcie runa. Lista dłuższa niż
WORKFLOW_FOREACH_MAX_ITEMS albo większa niż
WORKFLOW_FOREACH_MAX_MANIFEST_BYTES jest odrzucana, a nie przycinana. Pusta
lista daje results: [] bez uruchamiania ciała. Kroki każdej iteracji działają
we własnym zakresie, z własnymi próbami, kluczami idempotencji i kosztami.
Następna iteracja jest planowana w transakcji, która kończy poprzednią, więc
restart wznawia od właściwego indeksu i nigdy nie powtarza potwierdzonego zapisu.
Zatwierdzenie wewnątrz iteracji wznawia tę samą iterację.
Przy item_error_policy: stop, domyślnym, pętla kończy się błędem przy
pierwszej nieudanej iteracji, a szczegóły błędu niosą scope_path tej
iteracji. Przy collect wynik elementu to null, błąd trafia do errors, a
pętla działa dalej. Pętle zagnieżdżają się najwyżej na
WORKFLOW_FOREACH_MAX_DEPTH poziomów, a każdy przebieg węzła, który tworzy run,
liczy się do WORKFLOW_RUN_MAX_NODE_RUNS. Nie ma pętli while ani
równoległego map.
Ciało może rozgałęziać się na równoległe odnogi, które działają obok siebie w
obrębie iteracji, a iteracja kończy się, gdy żaden jej krok nie jest aktywny. Gdy
krok w iteracji zawodzi bez ścieżki błędu, collect pozwala jego sąsiadom w tej
iteracji dokończyć, po czym przechodzi do następnego elementu. stop anuluje ich,
wycofując ich zatwierdzenia, timery i zaparkowane runy agentów, i kończy pętlę
błędem. Następna iteracja nigdy nie startuje, dopóki poprzednia ma jeszcze
działający krok.
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¶
Siedem węzłów czyta i zapisuje Virtual Tables przez ten sam serwis, którego używają konsola, API i narzędzia tabel agenta. Walidacja, konflikty rewizji, limity, historia, paragony i audyt są takie same na każdej powierzchni.
| Węzeł | Co robi | Efekt |
|---|---|---|
table.record.create |
Dodaje rekord | write |
table.record.upsert |
Tworzy albo aktualizuje rekord z external id | write |
table.record.update |
Zmienia część komórek rekordu | write |
table.record.delete |
Usuwa rekord, zachowując jego historię | write |
table.record.get |
Znajduje jeden rekord po id albo external id | read |
table.record.query |
Czyta jedną stronę rekordów, z filtrem i sortowaniem | read |
table.create |
Tworzy nową tabelę z typowanym schematem | write |
Każdy węzeł rekordów przypina swoją tabelę w konfiguracji. Przy publikacji jest ona
sprawdzana względem autora grafu, a przy każdym runie ponownie jako principal runa.
Wartości są bindowane i podawane po id albo etykiecie kolumny. Rekord wraca z
wartościami dwa razy: values po id kolumny, do bindingów, i fields po etykiecie,
do czytania. Klucz, który nie wskazuje żywej kolumny, kończy się błędem
UNKNOWN_COLUMN.
Zapis niesie klucz operacji kroku, więc ponowiony krok odtwarza swój pierwszy zapis.
Update, upsert albo delete bez zbindowanej rewizji zapisuje przy bieżącej rewizji
rekordu, odczytanej pod blokadą rekordu, a odtworzenie działa nawet wtedy, gdy pierwszy
zapis już przesunął tę rewizję. Rewizja, która się przesunęła, to REVISION_CONFLICT, który nie jest ponawiany: ta sama rewizja
skonfliktowałaby się znowu, więc skieruj go przez error.handle do świeżego odczytu.
Brakujący rekord to found: false z table.record.get, a nie błąd.
table.record.query czyta najwyżej 100 rekordów na stronę i podaje has_more.
Nigdy sam nie czyta całej dużej tabeli.
table.create to osobny węzeł i wymaga tables:create. Jego wyjście niesie nową
tabelę jako referencję, do której można zbindować table kolejnego węzła, oraz id
każdej kolumny po etykiecie. Tabeli, którą czyta albo zapisuje żywy workflow, ani
kolumny, którą przypina, nie da się zarchiwizować, dopóki używa jej bieżąca wersja
tego workflow.
Cztery kolejne kroki odczytują, jakie tabele ma organizacja, i niczego nie zmieniają.
| Krok | Robi | Wychodzi przez |
|---|---|---|
table.list |
wymienia tabele widoczne dla podmiotu, szukane po nazwie | out, z tables i total |
table.describe |
czyta nazwę i kolumny jednej tabeli | out, z columns |
table.exists |
czy istnieje tabela o dokładnie tej nazwie | yes, z jej id, albo no |
table.record.exists |
czy jakikolwiek rekord pasuje do filtrów | yes, z id pierwszego, albo no |
table.exists porównuje całą nazwę bez względu na wielkość liter, więc workflow
może utworzyć swoją tabelę przy pierwszym uruchomieniu, a potem jej używać. Oba
pytania wychodzą dokładnie jednym portem, a logic.merge może z powrotem
połączyć gałęzie.
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.
Pliki¶
Plik, który tworzy krok, jest zapisywany jako plik jego runa i przekazywany dalej
jako FileRef: id, typ, jakim okazały się jego bajty, i rozmiar. Krok czyta plik
tylko wtedy, gdy utworzył go jego własny run albo run został z nim uruchomiony -
FileRef powiązany w grafie, sprawdzany przy publikacji wobec runa, który autor
widzi. Każdy inny plik, innej organizacji albo innego runa, to FILE_NOT_FOUND, więc
znajomość id niczego nie daje. Pliki runa są wymienione i do pobrania na jego stronie.
| Węzeł | Robi | Efekt |
|---|---|---|
http.download |
Pobiera plik przez HTTP, strumieniowo, i go zapisuje | write |
http.upload |
Wysyła plik do endpointu HTTP, strumieniowo z magazynu | write |
file.read |
Czyta plik jako tekst, wartość JSON albo wiersze CSV | read |
file.write |
Zapisuje tekst, wartość JSON albo wiersze jako plik | write |
text.extract |
Tekst pliku TXT, JSON, CSV, tekstowego PDF albo DOCX | read |
convert.csv_to_json |
Plik CSV jako plik JSON z wierszami | write |
convert.json_to_csv |
Lista płaskich obiektów JSON jako plik CSV | write |
convert.text_to_file |
Tekst jako plik TXT | write |
convert.pdf_to_png |
Wybrane strony PDF jako obrazy PNG | write |
image.transform |
Przycina, skaluje, obraca albo konwertuje obraz | write |
Pobieranie stosuje te same reguły SSRF i poświadczeń co http.request, do pięciu
przekierowań. Jego treść jest liczona w trakcie napływu i odrzucana powyżej
max_bytes, a typ jest rozpoznawany po bajtach, więc nagłówek nie oszuka
expected_content_types. text.extract nie robi OCR: zeskanowana strona kończy
krok błędem TEXT_EXTRACTION_NEEDS_OCR i wymienia strony. Uszkodzony dokument to
DOCUMENT_CORRUPT, dokument Word, który rozpakowuje się ponad limity archiwum
obowiązujące upload na czacie, to DOCUMENT_TOO_LARGE, a PDF chroniony hasłem
DOCUMENT_ENCRYPTED.
Obraz jest mierzony, zanim zostanie zdekodowany. Jego szerokość razy wysokość, obszar
przycięcia i żądany rozmiar są każdy sprawdzane wobec CHAT_IMAGE_MAX_PIXELS, a
wynik nie niesie żadnych metadanych źródła. Każdy krok, który zapisuje plik, zapisuje
nowy przy każdej próbie, więc jest at_least_once.
Pliki runa mogą łącznie zająć WORKFLOW_RUN_MAX_FILE_BYTES, a pliki organizacji WORKFLOW_ORG_MAX_FILE_BYTES. Krok, którego plik przekroczyłby któryś z tych limitów, kończy się błędem FILE_QUOTA_EXCEEDED i niczego nie zapisuje. Aby zwolnić miejsce, usuń stare runy albo ustaw retencję runów w workflow, które tworzą pliki.
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
Konwersje CSV dopuszczają najwyżej 10 000 wierszy, 1000 kolumn i 1 000 000 komórek. Oba kierunki ograniczają zakodowany wynik przez max_bytes kroku przed zapisaniem pliku. Ekstrakcja tekstu PDF odrzuca ponad 2000 stron i przestaje czytać strony po przekroczeniu max_chars. Pliki wskazane bezpośrednio są ponownie sprawdzane przy przyjęciu runa względem bieżącego dostępu jego osoby do źródłowego workflowu.
Wyniki sandboxa są ograniczane także podczas strumieniowego pobierania z hosta. Plik rosnący po pomiarze nie omija pozostałego wspólnego limitu bajtów; JSON wyniku też do niego wchodzi. Zakończone wywołane runy zostają do końca runa wywołującego, żeby retencja nie powodowała powtórzenia ich skutków przez oczekujący krok.
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¶
Dwa węzły uruchamiają Pythona, do dwóch rodzajów pracy.
code.python.simple uruchamia krótki skrypt w sandboxie Monty, który nie ma systemu
plików, sieci i ma małą bibliotekę standardową. Skrypt czyta powiązane wartości jako
args, a jego ostatnie wyrażenie jest result kroku, które musi być wartością JSON.
Tylko liczy, więc jest pure i wymaga code:execute.
W edytorze skrypt kroku z kodem pisze się w edytorze kodu: Python albo
JavaScript podświetlony w obu motywach, Tab i Shift+Tab do wcięć, Enter
zachowujący wcięcie, nawiasy i cudzysłowy domykane podczas pisania, nawias przy
kursorze obrysowany razem z pasującym oraz klucze
powiązanego args podpowiadane po wpisaniu args[" albo, w JavaScript, args..
Esc, a potem Tab, opuszcza edytor. Test step uruchamia sam skrypt na tym, co
przekazały kroki przed nim.
code.python.sandbox uruchamia pełnego Pythona z pakietami i plikami runa na
połączeniu sandboxd organizacji i wymaga sandbox:execute. Skrypt znajduje pliki
wejściowe w inputs, zapisuje pliki do outputs i ustawia result. To trwałe
zadanie: pierwsze wysłanie uruchamia je w tle, a każde kolejne sprawdza je w tej samej
sesji, więc zrestartowany worker łączy się ponownie, zamiast uruchamiać je od nowa. Do
sandboxa nigdy nie trafia żadne poświadczenie platformy, a to, do czego skrypt sięga
poza swoimi plikami, zależy od konfiguracji runtime'u hosta - do niezaufanej pracy
wybierz runtime bez sieci.
| Co się stało | Wynik |
|---|---|
| Wynik nie jest JSON-em | PYTHON_OUTPUT_NOT_JSON |
| Skrypt rzucił wyjątek albo przekroczył limit | PYTHON_ERROR |
Zadanie przekroczyło timeout_seconds |
PYTHON_SANDBOX_TIMEOUT, sesja wyczyszczona |
Brak używalnego połączenia sandboxd |
SANDBOX_UNAVAILABLE |
| Zapisał więcej niż 20 plików albo 100 MB albo wypisał więcej niż 10 MB | PYTHON_OUTPUT_TOO_LARGE, zmierzone w sandboksie, zanim cokolwiek zostanie pobrane, a sesja wyczyszczona |
| Nie udało się połączyć z hostem | SANDBOX_UNREACHABLE, ponawiane |
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 uruchamia JavaScript na Node jako to samo trwałe
zadanie, na tym samym połączeniu sandboxd, i wymaga sandbox:execute. Skrypt
jest ciałem funkcji asynchronicznej: czyta powiązane wartości jako args, swoje
pliki wejściowe w inputs, zapisuje pliki do outputs, może używać await, a
to, co zwróci przez return, jest result kroku - null, gdy nie zwraca
niczego. require ładuje moduły samego Node i to, co runtime ma zainstalowane.
Wybierz runtime z Node.
Jego błędy są błędami sandboxa Pythona, nazwanymi dla JavaScriptu: rzucony błąd
to JAVASCRIPT_ERROR, wynik, który nie jest wartością JSON - funkcja, BigInt -
to JAVASCRIPT_OUTPUT_NOT_JSON, a limit czasu i limity wyjścia to
JAVASCRIPT_SANDBOX_TIMEOUT i 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. Uruchamia opublikowaną wersję innego workflow, zaczynającą się od
Called by a workflow, z powiązanym input, tak jak działa ten przebieg. Krok
czeka, aż wywołany przebieg się skończy, i przekazuje jego output albo kończy się
błędem CALLED_WORKFLOW_FAILED z błędem wywołanego przebiegu; przy wyłączonym Wait
for it to finish od razu przekazuje uruchomiony przebieg. Wywołany przebieg jest
powiązany z krokiem i z łańcuchem tego przebiegu. Wywołanie workflow, który już działa
wyżej w łańcuchu, kończy się WORKFLOW_CALL_LOOP, a głębsze niż pięć wywołań -
WORKFLOW_CALL_TOO_DEEP. Przebieg testowy wersji roboczej naprawdę wywołuje
opublikowany workflow.
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.
Dodawanie węzła¶
Węzeł to paczka w backend/app/workflows/nodes/: __init__.py rejestruje
NodeDefinition, _handler.py go implementuje, a README.md wyjaśnia, po co
istnieje. load_builtins importuje paczkę.
tests/test_workflow_node_layout.py pilnuje tego układu. Handler zwraca
Completed, Waiting, Failed albo Uncertain i nigdy nie rzuca wyjątku. Run,
dla którego działa, czyta z app.services.workflow_execution.context.current().
Typowany węzeł deklaruje trzy modele Pydantic: konfigurację, ustawianą w edytorze i
zamrażaną przy publikacji; wejście, którego pola wypełniają bindingi; oraz wyjście, do
którego bindują się późniejsze kroki. extra="forbid" na każdym z nich trzyma literówkę
z dala od opublikowanego grafu. retry_guarantee wybierz według tego, co zrobiłoby
powtórzenie wywołania: idempotent, gdy handler sam czyni powtórzenie nieszkodliwym -
zapis do tabeli przekazuje operation_key() - at_least_once, gdy powtórzenie jest
dopuszczalne, i none, gdy nie jest. Węzeł, który czyta albo zapisuje zasób, deklaruje
check_resources, które publikacja uruchamia względem autora, a każdy run względem
swojego principala.
Konsola rysuje węzeł z jego definicji, z ikoną i odcieniem w
frontend/src/components/workflows/node-visuals.ts. Przetestuj handler bezpośrednio pod
kątem odmów i przeprowadź go raz przez tests/integration/workflow_run_support.py, żeby
jego konfiguracja, bindingi i wyjście zostały dowiedzione w prawdziwym runie.
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.