Konfigurowanie triggera zdarzeniowego¶
Trigger zdarzeniowy uruchamia agenta, gdy coś wydarzy się gdzie indziej.
Są dwie drogi, którymi to do nas dociera, a to, której używa dane źródło, jest sprawą tego źródła, a nie twoją:
- Wypychane. Provider wysyła POST-em podpisany payload — issue z GitHuba albo cokolwiek, co potrafi wysłać podpisany JSON (źródło API).
- Odpytywane. Platforma czyta podłączone konto według harmonogramu. Gmail jest właśnie taki: nic nie jest do nas wysyłane, więc nie ma URL-a do skonfigurowania ani sekretu do przechowania. Podłączasz skrzynkę i to cała konfiguracja.
Koncepcje opisują, czym trigger jest i jak zachowuje się uruchomiony run; Nadzór opisuje, co on wydaje i jak obsługiwana jest odmowa.
Ta strona jest o tym, co robisz dalej: jak skierować prawdziwego providera na webhook, co musi zawierać dostarczenie i jak przetestować całość z laptopa.
Jeśli potrzebujesz tylko zegara, chodzi ci o harmonogram
Żadnego webhooka, żadnego sekretu, żadnego providera do skonfigurowania.
Każdy z tych dwóch rodzajów może zacząć od zasianego szablonu
(GET /trigger-templates). Szablon harmonogramu — „podsumuj moje otwarte
pull requesty każdego ranka w dzień roboczy” — wypełnia z góry prompt i
sensowną kadencję. Szablon zdarzeniowy — „posegreguj nowe issue”, „napisz
szkic odpowiedzi na maila” — wypełnia z góry prompt na kroku wiadomości
swojego własnego źródła. Żaden nie zaczyna od pustego pola.
Wszystko poniżej dotyczy przypadku zdarzeniowego.
Gdzie żyją w produkcie i jak je nazywać¶
Routines to parasol - nawigacja, strona, własny panel agenta, boczny pasek czatu i kafelek na dashboardzie używają tego jednego słowa, więc człowiek spotyka ten sam rzeczownik, gdziekolwiek trafi. Dwie rodziny pod tym parasolem pozostają rozróżnione, bo zachowują się inaczej: harmonogram odpala się z zegara, trigger odpala się na przyjściu czegoś. Tym, czemu nie wolno było się różnić, był parasol — i tak właśnie nawigacja zaczęła mówić „Routines” nad panelem zatytułowanym „Schedules & triggers” (#594).
Cztery powierzchnie, jedna lista:
| Gdzie | Do czego służy |
|---|---|
Routines (/routines) |
Każda rutyna w organizacji i dwa sposoby, żeby zacząć nową |
| Zakładka Availability agenta | Tylko rutyny tego agenta, obok miejsca, gdzie konfiguruje się jego ekspozycję |
| Sekcja Routines w bocznym pasku czatu | To, co agent, z którym rozmawiasz, robi sam z siebie |
| Kafelek Routines na dashboardzie | Najbliższe na górze, wraz z tym, jak poszło ostatnie odpalenie - rutyna psująca się co godzinę jest niewidoczna gdziekolwiek indziej na tej stronie |
Kafelek dashboardu można dodać z Customize i jest na domyślnym układzie pod
Needs attention. Czyta tę samą listę obejmującą całą organizację, więc ten,
kto może widzieć agentów, widzi ich rutyny; wynik i koszt w każdym wierszu
wymagają runs:view i po prostu ich nie ma bez tego uprawnienia.
Mechanizm, raz¶
Trigger zdarzeniowy daje ci dwie rzeczy: URL webhooka i sekret podpisujący. Provider wysyła POST-em swój payload na ten URL i podpisuje żądanie; platforma przelicza podpis i odpala agenta tylko wtedy, gdy oba się zgadzają.
flowchart TD
P[A provider, or your own script] -->|POST + signature header| W["/api/v1/webhooks/triggers/{source}/{id}"]
W --> V{signature verifies?}
V -->|no| R403["403 - refused before the runner"]
V -->|yes| J{a JSON object?}
J -->|no| R400["400"]
J -->|yes| F{trigger active,<br/>filter matches?}
F -->|no| R202["202 - nothing to do"]
F -->|yes| SUB["submit a capped Prefect flow"]
SUB --> R202b["202 - accepted, not finished"]
SUB -.->|later, in the worker| RUN[the agent runs, spending the org's budget]
- URL budowany jest na jednym publicznym adresie deploymentu
(
PUBLIC_BASE_URL), a nie na originie dashboardu - webhook serwowany jest przez host API, który zwykle jest innym originem niż UI. Jego kształt to:
source to github albo webhook (nazwa źródła API na drucie); trigger_id
to nieodgadywalny UUID. Dialog wypełnia to za ciebie - skopiuj, nie buduj tego
ręcznie. gmail nie ma URL-a: odpytywane źródło nie ma drzwi wejściowych, a
POST nazywający je dostaje odpowiedź jak każde dostarczenie, przy którym nie ma
nic do zrobienia.
- Podpis to
HMAC-SHA256po dokładnych surowych bajtach żądania, kluczowany sekretem podpisującym, zakodowany szesnastkowo i poprzedzonysha256=. Jedzie w nagłówku, który zależy od źródła:
| Źródło | Nagłówek |
|---|---|
github |
X-Hub-Signature-256 |
webhook |
X-Signature-256 |
GitHub podpisuje swoje dostarczenia natywnie, pod własnym nagłówkiem
X-Hub-Signature-256, więc dajesz GitHubowi sekret, a on robi podpisywanie.
Źródło API używa identycznego schematu pod X-Signature-256, który to, co
skierujesz na ten URL, musi ustawić samo. Źródło odpytywane nic nie
podpisuje i nie trzyma sekretu: nie było adresowane, tylko odczytane, a
autoryzacją odczytu jest własna zgoda OAuth tego konta.
Podpis nie jest ozdobą
Bez niego URL jest jedyną rzeczą między obcym a budżetem modelowym twojej organizacji - a URL-e wyciekają: do logów, do historii dostarczeń providera, na zrzut ekranu w zgłoszeniu do supportu. Ktokolwiek ma ten URL, mógłby odpalać agenta do woli i wydawać z twoich limitów. Sekret jest tym, co czyni dostarczenie autentycznym, a nie tylko poprawnie zaadresowanym.
Żądanie, którego podpis się nie weryfikuje, jest odrzucane z 403, zanim
runner zostanie w ogóle osiągnięty; sekret jest zapieczętowany w
vault i nigdy nie pojawia się w odczycie, w listingu ani w URL-u.
202 znaczy przyjęte, a nie zakończone
Dopasowane dostarczenie jest zgłaszane jako własny flow
run-scheduled-trigger, a agent działa w workerze, więc provider dostaje
swoją odpowiedź w jednym szybkim wywołaniu Prefect, zamiast czekać na model.
Nie czytaj 202 jako „agent odpowiedział” - od tego jest przeczytanie runa w
Activity.
Zweryfikowane dostarczenie, przy którym nie ma nic do zrobienia - nieaktywny
trigger albo payload, którego filtr nie dopasowuje - odpowiada 202 dokładnie
tak samo jak odpalone, więc posiadanie sekretu nie mówi ci nic o tym, które
triggery istnieją. Body, które nie jest obiektem JSON, to 400.
Rotowanie sekretu i edytowanie filtra¶
URL jest tożsamością triggera i nigdy się nie zmienia. Sekret jest poświadczeniem i jak każdy inny klucz w tym produkcie może być rotowany — ponowne zapieczętowanie i świeży tekst jawny pokazany dokładnie raz.
Bije nowy sekret, pieczętuje go i zwraca trigger z reveal_secret ustawionym raz
— tym samym polem, którego używa tworzenie. Rotuj w chwili, gdy sekret mógł
wyciec; stary natychmiast przestaje się weryfikować.
Dla hooka, który platforma zarejestrowała sama (auto_webhook), rotacja
rejestruje go ponownie z nowym sekretem, więc jego dostarczenia dalej się
weryfikują i nie ma czego ujawniać. Chyba że konto nie może go już zarejestrować
— wtedy trigger spada do manual, a ujawniony sekret jest tym, co wklejasz
ponownie.
Harmonogram nie ma sekretu, więc rotowanie go jest odrzucane.
To, które akcje na issue odpalają trigger, jest filtrem, a nie innym
triggerem, więc daje się edytować w miejscu. Zrób PATCH na triggerze z nowym
event_config, a zostanie on ponownie zwalidowany wobec reguł źródła dokładnie
tak, jak waliduje tworzenie — nieznany klucz jest odrzucany, a nie zapisywany po
to, żeby nic nie dopasowywać.
Źródło i sekret nie są tą drogą edytowalne. Przekierowanie triggera zdarzeniowego na inne źródło to nowy trigger: usuń ten, utwórz tamten.
Gmail (~1 minuta i żadnego sekretu nigdzie)¶
Gmail jest odpytywany, więc konfiguracja to ekran zgody i nic poza tym.
- Podłącz konto. Routines → New event trigger → Gmail → Connect account.
To wymaga
mcp:manage, tego samego uprawnienia, którego wymaga każde inne podłączone konto. - Wybierz, co go odpala: dowolna nowa wiadomość, tylko skrzynka odbiorcza albo oznaczone jako ważne. Zawęź dalej fragmentem nadawcy lub tematu albo etykietą Gmaila.
- Napisz prompt albo zacznij od szablonu „draft a reply”.
Nie ma URL-a do wklejenia ani sekretu do zapisania, bo nic do nas nie wysyła. Co warto wiedzieć o tym, jak to czyta:
- Raz na minutę. Heartbeat pyta Gmaila, co przyszło od ostatniego zajrzenia,
więc najgorsze opóźnienie to minuta. To celowe: alternatywa -
users.watchdo tematu Google Cloud Pub/Sub - jest w czasie rzeczywistym i kosztuje temat oraz subskrypcję jako wymagania deploymentu, plus rejestrację, która wygasa co siedem dni i potrzebuje czegoś, co ją odnowi. - Podłączenie nic nie odpala - i nic nie gubi. Pozycja skrzynki jest brana w chwili zakończenia zgody, więc podłączenie nie odpala agenta raz na każdą wiadomość, która już tam leży, a poczta przychodząca między zgodą a pierwszym heartbeatem wciąż ląduje za tą pozycją i odpala.
- Nawał jest ograniczony. Jedno tyknięcie czyta najwyżej 25 nowych wiadomości w całości. Zrzut z listy mailingowej nie staje się 400 runami agenta; pozycja i tak idzie do przodu, więc zaległość nie jest czytana w kółko.
- Jedna wiadomość może odpalić kilka triggerów. W odróżnieniu od webhooka, którego URL nazywa dokładnie jeden - „dowolna wiadomość” i „oznaczone jako ważne” na tej samej skrzynce odpalają oba.
- Przegapiony tydzień naprawia się sam. Google trzyma około tygodnia historii. Kursor starszy niż to resynchronizuje się do teraz, zamiast zaparkować skrzynkę na zawsze.
Deployment potrzebuje klienta Google OAuth (GOOGLE_CLIENT_ID /
GOOGLE_CLIENT_SECRET - tej samej pary, której używa logowanie przez Google) z
włączonym Gmail API. Bez niego kafelek mówi to wprost, zamiast oferować przycisk
Connect, który mógłby tylko zawieść. W odróżnieniu od GitHuba klient należy do
deploymentu, a nie do każdej organizacji: ekran zgody Google dla zakresu
skrzynki pocztowej wymaga zweryfikowanego projektu, który operator rejestruje raz
i którego żaden z jego tenantów nie może zarejestrować w ogóle.
Przepis na GitHuba (~5 minut)¶
GitHub podpisuje własne dostarczenia, więc to najszybsze źródło do podłączenia. Najpierw utwórz trigger ze źródłem GitHub, skopiuj jego URL webhooka i sekret podpisujący, a potem:
- W repozytorium, które chcesz obserwować, wejdź w Settings → Webhooks → Add webhook.
- Payload URL - wklej URL webhooka z dialogu triggera.
- Content type - wybierz
application/json. Nieapplication/x-www-form-urlencoded: podpis obejmuje dokładne bajty, które GitHub wysyła, a kodowanie formularzowe je zmienia, więc dostarczenie zakodowane formularzowo weryfikuje się z niczym i wraca403. - Secret - wklej sekret podpisujący.
- Which events? - wybierz Let me select individual events, zaznacz Issues i
odznacz całą resztę. Tylko webhooki
issuesdocierają w ogóle do ścieżki odpalania (typ zdarzenia czytany jest z nagłówkaX-GitHub-Event); wszystko inne jest odrzucane. Zawęź to, które akcje na issue odpalają trigger, jego filtrem - domyślnie jest to utworzenie issue (opened). - Add webhook. GitHub wysyła
ping, który nie jest zdarzeniemissues, więc nie odpali agenta - i tak ma być.
Kiedy dostarczenie zostaje odrzucone, zdiagnozuj to w zakładce Recent Deliveries
przy webhooku w GitHubie: pokazuje ona dokładne żądanie i odpowiedź. 403 tam to
niezgodność podpisu - prawie zawsze sekret jest zły albo content type nie jest
application/json.
Kontrakt payloadu dla źródeł dostarczanych przez przekaźnik¶
GitHub jest właścicielem kształtu swojego payloadu, a payload źródła odpytywanego czyta adapter, który go czyta - filtry triggera Gmaila dopasowywane są do samej wiadomości, więc nie ma kontraktu, który miałbyś spełnić.
Tym, który należy do ciebie, jest uniwersalne źródło webhook - API w
dialogach. Nie ma filtra: zweryfikowane dostarczenie odpala, a całe body JSON
jest doklejane do promptu. Użyj go do wszystkiego, czego nie obejmuje żaden
portal - obserwowania kanału, dla którego żaden provider nie wystawia API (strona
na LinkedInie, ogłoszenie na marketplace) albo dowolnego narzędzia, które potrafi
wysłać POST - przy czym obserwowanie robi przekaźnik, który napiszesz.
Było tu kiedyś źródło email i było to właśnie to źródło pod inną nazwą:
zmieniało nazwy dwóch pól filtra i prosiło cię o uruchomienie przekaźnika - kroku
kodu w Zapierze albo Make, małego skryptu - który podpisywał i wysyłał do nas
JSON, bo nic w tym produkcie nie potrafiło odbierać poczty. Zostało usunięte z
tego samego powodu co linkedin: pozycja w liście rozwijanej, której nazwa
obiecuje integrację, której nie ma. Gmail zastąpił je jako prawdziwe podłączone
konto (powyżej), a skrzynka karmiona przekaźnikiem to źródło API z
udokumentowanym przykładem.
E-mail karmiony przekaźnikiem, jako źródło API:
Nic już nie filtruje po tych nazwach, więc całe body dociera do promptu i agent je czyta. Jeśli chcesz filtrowania, podłącz skrzynkę zamiast tego.
Samodzielne podpisanie dostarczenia¶
Dla uniwersalnego źródła webhook (i żeby przetestować dowolne źródło ręcznie)
podpisujesz żądanie sam. O tym, czy podpis się zweryfikuje, decydują dwie pułapki,
bo obie zmieniają bajty:
- Podpisuj te bajty, które wysyłasz, i tylko te.
echodokleja kończący znak nowej linii, który zostaje podpisany, ale może nie zostać wysłany, albo wysłany, ale nie podpisany; użyjprintf '%s'i przekaż body przezcurl --data-raw, żeby nic nie zostało dodane ani zinterpretowane. - Nie serializuj ponownie. Podpisanie słownika, a potem pozwolenie klientowi HTTP zakodować go na nowo, daje inne bajty (przestawione klucze, inne odstępy). Podpisz string i wyślij ten sam string.
SECRET='your-signing-secret'
URL='https://api.example.com/api/v1/webhooks/triggers/webhook/<trigger_id>'
BODY='{"hello":"world"}'
SIG="sha256=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')"
curl -sS -X POST "$URL" \
-H 'Content-Type: application/json' \
-H "X-Signature-256: $SIG" \
--data-raw "$BODY"
import hashlib
import hmac
import httpx
secret = b"your-signing-secret"
url = "https://api.example.com/api/v1/webhooks/triggers/webhook/<trigger_id>"
body = b'{"hello":"world"}'
signature = "sha256=" + hmac.new(secret, body, hashlib.sha256).hexdigest()
# content=body sends these exact bytes. json=... would re-serialize and sign nothing.
httpx.post(
url,
content=body,
headers={"Content-Type": "application/json", "X-Signature-256": signature},
)
Dla źródła github algorytm jest identyczny; zmienia się tylko nazwa nagłówka na
X-Hub-Signature-256.
Zapier i Make nie zrobią tego bez kroku kodu¶
Żaden z nich nie ma akcji HMAC
Ich standardowe kroki „POST na webhooka” wysyłają body, ale nie potrafią go
podpisać, więc każde dostarczenie przychodzi niepodpisane i jest odrzucane
403. Zaplanuj godzinę z krokiem kodu, a nie pięć minut klikania.
Kusi, żeby sięgnąć po no-code'ową akcję webhooka w Zapierze albo Make. Musisz
dodać ich krok kodu (Code by Zapier w Zapierze, moduł Custom JS / functions w Make), policzyć
HMAC sha256=<hex> po dokładnym body, które zaraz wyślesz, i ustawić z niego
nagłówek X-Signature-256.
To działa, ale bądź szczery co do kosztu: to mniej więcej godzina z krokiem kodu, a nie pięć minut klikania. Jeśli tylko sprawdzasz trigger od początku do końca, podpisz najpierw żądanie ręcznie snippetem powyżej.
Testowanie lokalnie¶
Spróbuj Run now, zanim skonfigurujesz providera
Odpala jednorazowo trigger dowolnego rodzaju, na żądanie, bez podpisu i bez udziału webhooka - to najszybszy sposób, żeby potwierdzić, że agent, jego prompt i jego budżet zachowują się jak trzeba.
Na laptopie PUBLIC_BASE_URL domyślnie ma wartość http://localhost:8000, więc URL,
który daje ci dialog, jest nieosiągalny z GitHuba ani z żadnego hostowanego
przekaźnika - nie widzą twojej maszyny. Dwa sposoby, żeby to obejść:
-
Po prostu użyj Run now. Run now odpala trigger dowolnego rodzaju raz, na żądanie - harmonogram odpala się jeden dodatkowy raz przy nietkniętej kadencji, a trigger zdarzeniowy też się odpala, jako ręczne odpalenie testowe: agent uruchamia swój bazowy prompt bez kontekstu dostarczenia, bez podpisu i bez udziału webhooka. To najszybszy sposób, żeby potwierdzić, że agent, jego prompt i jego budżet zachowują się jak trzeba, bez konfigurowania jakiegokolwiek providera. Nieaktywny (wstrzymany) trigger jest respektowany - Run now nic mu nie robi. Jego jedyną luką jest to, że nie ćwiczy ścieżki podpisu ani prawdziwego payloadu, więc nie wyłapie złego sekretu ani źle nazwanego pola.
-
Wystaw port tunelem, kiedy rzeczywiście chcesz przetestować prawdziwą ścieżkę webhooka. Skieruj tunel na API, ustaw
PUBLIC_BASE_URLna publiczny adres tunelu i utwórz trigger dopiero potem - URL budowany jest zPUBLIC_BASE_URLw momencie odczytu, więc trigger utworzony przed tą zmianą wciąż wydawałby URL zlocalhost.
cloudflared tunnel --url http://localhost:8000
# then set PUBLIC_BASE_URL to the printed https URL, restart the API,
# and create the trigger
Skieruj providera (albo swój skrypt podpisujący) na URL tunelu, a dostarczenie dotrze do twojej maszyny jak każde hostowane.
Podsumowanie¶
- Trigger daje ci URL i sekret podpisujący. URL jest jego tożsamością i nigdy się nie zmienia; sekret jest poświadczeniem i może być rotowany.
- Podpis to
HMAC-SHA256po dokładnych surowych bajtach i to on czyni dostarczenie autentycznym, a nie tylko poprawnie zaadresowanym. 202znaczy przyjęte, a nie zakończone. Przeczytaj run w Activity.- Gmail jest odpytywany, więc nie ma URL-a ani sekretu w ogóle — podłącz skrzynkę i to cała konfiguracja.
- Na laptopie sięgnij po Run now, zanim sięgniesz po tunel.