Potoki i automatyzacja
Potoki to deterministyczna połowa Control Center. Agent decyduje, co powiedzieć; potok decyduje, co stanie się dalej — za każdym razem tak samo. Potok to DAG kroków, które potrafią wysyłać agentów, uruchamiać skrypty, rozgałęziać się na warunku, rozdzielać pracę na elementy kolekcji i przekazywać dane między krokami — utrwalony, wznawialny i rozliczany pod względem kosztów.
Istnieją z trzech powodów: aby połączyć w łańcuch powtarzalny wieloetapowy proces, aby skoordynować kilku agentów po kolei lub równolegle oraz aby zareagować na coś, co się wydarzyło, bez obecności człowieka.
Szablony i uruchomienia
Dział zatytułowany „Szablony i uruchomienia”Szablon (PipelineDefinition) to deklaratywny DAG: kroki, zadeklarowane dane
wejściowe i konfiguracja. Mieszka we własnej bazie danych obszaru roboczego.
Uruchomienie (PipelineRun) to jedno wykonanie tego szablonu, niosące zmienny
worek stanu, sumy kosztów i tokenów oraz status każdego kroku.
Jeden szablon ma wiele uruchomień. Uruchomienia przeżywają restart: silnik wczytuje ponownie uruchomienia w toku i podejmuje je od miejsca zatrzymania.
Dwa zabezpieczenia celowo kończą awarią całe uruchomienie zamiast wznawiać krok, bo wznowienie byłoby gorsze niż zatrzymanie:
- Krok pozostający w stanie
suspendeddłużej niżsuspendedStepTimeout(domyślnie 24 godziny) czeka na zgłoszenia, które nigdy nie osiągną stanu końcowego, więc kończy się awarią, aby uwolnić uruchomienie. Wyjątkiem jestextras['awaitApproval'] == true— to brama częściowego zatwierdzeniaorchestration.awaitApproval.human.gatenie jest z niego zwolniony. - Krok zadeklarowany jako
extras: {'idempotent': false}, który był w trakcie działania, gdy serwer się zatrzymał, mógł już zastosować swój efekt uboczny (scalanie squash, czyszczenie worktree). Ponowne uruchomienie mogłoby zastosować go dwa razy, więc silnik kończy go awarią, a ponowienie pozostawia do wykonania ręcznie.
Rodzaj decyduje o harmonogramie, ciało wykonuje pracę
Dział zatytułowany „Rodzaj decyduje o harmonogramie, ciało wykonuje pracę”Krok ma dwie ortogonalne części i to ich rozdzielenie pozwala silnikowi pozostać małym.
Rodzaj (StepKind) mówi silnikowi, kiedy krok staje się gotowy do
uruchomienia względem kroków powyżej — to jedyna informacja, jakiej potrzebuje
scheduler. Jest ich sześć: trigger, listen, join, router, forEach i
terminal. Ciało (bodyKey) to praca, którą krok wykonuje po zaplanowaniu.
Ciała to domknięcia rejestrowane pod kluczem przy starcie serwera, więc wiele
kroków różnych rodzajów może współdzielić jedno ciało: na przykład kilka kroków
listen uruchamiających conversation.promptAgent.
Ponieważ semantyka planowania mieści się w sześciu rodzajach, katalog zachowań może rosnąć, a nikt nie musi przy tym ruszać schedulera. Pełny katalog rodzajów, ciał i ich konfiguracji znajdziesz w Rodzajach kroków potoku.
Dwie konsekwencje konstrukcji opartej na rejestrze warto znać, zanim napisze się szablon ręcznie:
- Krok, którego
bodyKeynie został zarejestrowany, generuje ostrzeżenie przy wczytywaniu szablonu, a potem nigdy się nie wykonuje. W czasie działania nic nie zgłasza głośno błędu. Krokitriggeriterminalsą z tego ostrzeżenia zwolnione: wyzwalacz nadal uruchamia puste ciałopipeline.trigger, aby zadziałały kroki nasłuchujące poniżej; krokterminalnie ma ciała i jest wartownikiem schedulera. - Klucze w mapie
extraswęzła liczą się tylko przy dokładnym dopasowaniu, a w innym wypadku są po cichu ignorowane. Tylkoextras['spaceId']umieszcza pracę kroku agenta w istniejącej rozmowie.extras['mode'](chat/plan/review) jest odczytywany na węźle, który otwiera przestrzeń (messaging.createSpace, domyślniereview) — późniejszy krok agenta nie może zmienić uprawnień rozmowy, do której dołączył. Każda inna pisownia któregokolwiek z tych kluczy jest ignorowana.
Stan i przepływ danych
Dział zatytułowany „Stan i przepływ danych”Kroki nie wywołują się nawzajem. Czytają ze wspólnego worka stanu uruchomienia i
do niego zapisują: krok odczytuje swoje dane wejściowe po kluczu, wykonuje się i
zapisuje wynik z powrotem pod własnym outputKey. StateReducer scala równoległe
zapisy, gdy równoległe gałęzie celują w ten sam klucz, a TemplateRenderer
podstawia w promptach i skryptach symbole zastępcze {{...}} z tego samego stanu.
Dlatego potok można obejrzeć po fakcie — worek stanu to cała historia uruchomienia.
Co rozpoczyna uruchomienie
Dział zatytułowany „Co rozpoczyna uruchomienie”Wiersze PipelineTrigger deklarują, kiedy szablon uruchamia się automatycznie.
Są cztery rodzaje:
| Wyzwalacz | Uruchamia potok, gdy |
|---|---|
| Zdarzenie domenowe | Zadziała pasujące zdarzenie domenowe — na przykład PullRequestPublished lub TicketAssigned |
| Harmonogram | Nadejdzie termin wyrażenia cron lub przedziału czasowego |
| Ręczny | Uruchamiasz go z ręki; to obecność włączonego wyzwalacza ręcznego sprawia, że szablon pojawia się w selektorze uruchomień |
| Webhook | Zewnętrzny system wyśle żądanie POST do /webhooks/<token> na porcie HTTP serwera |
Wyzwalacze zdarzeniowe to zbiór zamknięty: dispatcher zna 16 typów zdarzeń i
oferuje je w selektorze (EventPayloadMapper.knownEventTypes), a nic innego nie
może uruchomić potoku. Czternaście z tych 16 ma mapowanie ładunku. Dwa —
TicketCreated i TicketStatusChanged — nie mają go wcale, więc wyzwalacz na
którymkolwiek z nich pozostaje włączony i nigdy nie zadziała. Pełną listę
znajdziesz w Zdarzeniach domenowych.
Token webhooka służy też jako sekret HMAC: nagłówek żądania
X-Hub-Signature-256: sha256=<hex> jest weryfikowany względem
HMAC-SHA256(body, token), niezweryfikowane dostawy są odrzucane i nie można ich
odtworzyć, a duplikaty odrzuca dedupe_key. Ponieważ wiersze wyzwalaczy są
unikalne względem (obszar roboczy, typ zdarzenia, szablon), szablon może mieć
najwyżej jeden wyzwalacz webhooka i po jednym wyzwalaczu na typ zdarzenia
domenowego.
Wbudowane szablony zasiewają swoje wyzwalacze ręczne i zdarzeniowe jako włączone; wyzwalacze harmonogramu są zasiewane jako wyłączone, więc cykliczne przemiatanie nigdy nie włącza się samo.
Szablony dostarczane jako wyłączone
Dział zatytułowany „Szablony dostarczane jako wyłączone”Do każdego obszaru roboczego zasiewa się trzynaście szablonów potoków, a osiem z
nich jest domyślnie wyłączonych: external_pr_welcome, cross_review,
ticket_to_pr, pr_triage, pre_merge_gate, release_notes, dep_audit i
pr_digest.
Właśnie to zaskakuje ludzi. Ich wyzwalacze są zasiewane jako włączone, ale
PipelineEngine.start odrzuca wyłączony szablon i zwraca null — więc wyzwalacz
odpala w pustkę i żadne uruchomienie się nie pojawia. Przełącznikiem jest
szablon, nie wyzwalacz. Włącz go w Ustawienia → Obszar roboczy → Szablony
potoków, zanim zacznie się oczekiwać, że któreś z tych ośmiu cokolwiek zrobi.
Ponowne zasianie obszaru roboczego zachowuje wcześniejsze wybory: ustawienie
isEnabled i istniejące wiersze wyzwalaczy przetrwają. Jest dokładnie jeden
celowy wyjątek — dzienny harmonogram pr_merged_cleanup jest wymuszony jako
włączony, nawet jeśli wcześniej był wyłączony, bo pełni rolę odśmiecania. Bez
niego narastają wiersze worktree, partycje grafu kodu i kopie copy-on-write, a to
nigdy nie miało być wyborem użytkownika.
Jeszcze jedna asymetria warta poznania: wbudowany pr_review celowo nie publikuje
już na GitHubie. Opublikowanie recenzji to osobny krok wymagający zgody
użytkownika, a potok, który publikowałby bezpośrednio, publikowałby podwójnie.
Natomiast cross_review, ticket_to_pr i pr_triage nadal wysyłają komentarz
prosto na GitHuba, gdy się uruchamiają.
Co wie silnik
Dział zatytułowany „Co wie silnik”PipelineEngine tworzy rekord uruchomienia, planuje węzeł startowy pasujący do
typu zdarzenia tego uruchomienia, a następnie przechodzi DAG: gdy kroki się
kończą, ocenia wyzwalacze poniżej, rozwiązuje gałęzie routera, czeka na
złączenia, stosuje polityki ponawiania i kontynuację po błędzie oraz utrwala
stan, koszt i błędy przy każdym kroku. Krawędzie wychodzące z innego startu są dla
tego uruchomienia ignorowane, więc odpalenie z harmonogramu nie czeka na warunek
dostępny wyłącznie ręcznie.
Czego natomiast nie wie: niczego o zgłoszeniach, wysyłaniu, GitHubie czy
wiadomościach. Jedynym szwem rozszerzeń jest PipelineBodyRegistry. To właśnie
zarejestrowane tam ciała przechowują AgentDispatchPort, MessagingPort i im
pokrewne — dlatego dodanie potokom nowej możliwości nigdy nie oznacza zmiany
schedulera.
Obsługa błędów
Dział zatytułowany „Obsługa błędów”Krok, który zawiedzie, zapisuje swój komunikat błędu i przechodzi do stanu
failed. Jeśli krok deklaruje continueOnFail, kroki poniżej idą dalej, a błąd
trafia do stanu uruchomienia; w przeciwnym razie całe uruchomienie kończy się
awarią. Krok z StepRetryPolicy jest najpierw ponawiany, z liniowym lub
wykładniczym opóźnieniem, zanim zadziała którykolwiek z tych mechanizmów.
Zobacz też
Dział zatytułowany „Zobacz też”- Zdarzenia domenowe: na co nasłuchuje wyzwalacz zdarzeniowy
- Orkiestracja: plan, który zatwierdzasz, kompilowany do jednego z tych potoków
- Zgłoszenia i delegowanie: co tworzą i kończą kroki potoku
- Rodzaje kroków potoku: pełny katalog rodzajów, ciał i konfiguracji
- Zbuduj pierwszy potok
- Utwórz szablon potoku
- Uruchom potok ręcznie
- Monitoruj uruchomienia potoków
- Skonfiguruj wyzwalacze potoków