ADK 2 Orchestration: graf, przepływy pracy oparte na współpracy i dynamiczne

1. Przegląd

Nagłówek ADK 2 to 3 wzorce orkiestracji. W tym ćwiczeniu nauczysz się wszystkich 3 metod, tworząc po kolei aplikację Marathon Race Day Coach. Każdy poziom odpowiada na jedno pytanie, dodaje jeden pomysł i działa samodzielnie.

Czego się nauczysz

  • Przepływy pracy w formie wykresów (filary 1) – gdy możesz narysować przepływ przed nadejściem danych wejściowych.
  • Agenci współpracujący (Pillar 2) – gdy znasz zespół, ale prośba wybiera podzbiór, a wszystkie 3 tryby współpracy (chat / task / single_turn) działają na żywo.
  • Dynamiczne przepływy pracy (filar 3) – gdy kształt pracy zależy od danych wejściowych.
  • Jak wybrać – drzewo decyzyjne z 1 pytaniem i informacje o tym, jak powstają wzory.

Wspólny wątek

znana struktura → znany zespół lub podzbiór zmiennych → nieznany kształt → wybierz odpowiedni

Plan nauki

Co utworzysz

Jedna z aplikacji – Marathon Race Day Coach – tworzyła po jednym poziomie, który można było przebiec. Każdy poziom to zwykły moduł Pythona, który uruchamiasz w terminalu. Od poziomu 5 wszystkie poniższe elementy są Twoje.

Obraz jest generowany na podstawie działającego kodu: każda linia ciągła została odczytana z Workflow.graph.edges. Pierwsza lekcja: części, które można narysować z wyprzedzeniem, to właśnie filar 1, a części, których nie można, to powód istnienia filarów 2 i 3.

Cała aplikacja i to, czego nie pokazuje wykres

Czego potrzebujesz

  • Konto Google (w przypadku Colab) – nie wymaga konfiguracji lokalnej.
  • około 50 minut (poziomy L4 są długie – zaplanuj je);
  • Jeden z 2 sposobów na dostęp do modelu Gemini. Wybierz ścieżkę – wykonaj jeden krok konfiguracji i pomijaj pozostałe:

🎓 Warsztaty

🏠 Na wynos

Kto

Uczestniczysz w warsztatach na żywo, a prowadzący przekazał Ci link do wykorzystania środków.

Wszyscy pozostali, w tym uczestnicy warsztatów, po ich zakończeniu

Wymagania

link do roszczenia i konto Google, które może utworzyć projekt w chmurze

bezpłatny klucz interfejsu API AI Studio;

Działa na

Vertex AI w projekcie, za który opłaty są naliczane na podstawie środków na warsztaty

Google AI Studio

Koszt

Objęte środkami

Poziom bezpłatny

Krok konfiguracji

Konfiguracja warsztatu (następny krok)

Konfiguracja w domu (krok następny)

Wszystko od prologu jest identyczne w obu przypadkach – ścieżka decyduje tylko o tym, z którym punktem końcowym modelu rozmawia notatnik.

2 sposoby śledzenia

Każdy krok poniżej odpowiada jednej komórce w notatniku Colabjednemu folderowi w repozytorium GitHub. Wybierz jedną z tych opcji:

  • ▶ Colab (zalecane): otwórz notatnik → uruchom komórki od góry do dołu.
  • 💻 Lokalnie: git clone repozytorium, ./setup_venv.sh, a następnie uruchom każdy poziom jako moduł (python -m ...) lub przejrzyj je wszystkie za pomocą ./run.sh (adk web).

2. Konfiguracja warsztatów · Odbierz środki i przejdź na Vertex AI

Podczas warsztatów otrzymasz środki na Google Cloud. Odbierzesz go, utworzysz projekt, za który będziesz płacić, i skierujesz notatnik do Vertex AI zamiast do AI Studio. Jedna komórka wykonuje wszystkie czynności po zgłoszeniu roszczenia.

1. Odbierz środki (ok. 1 min)

  1. Otwórz link do odbioru udostępniony przez nauczyciela. Wygląda na to, że https://me.developers.google.com/benefits/claim/your-workshop-name.
  2. Zaloguj się i postępuj zgodnie z instrukcjami na stronie, aby zaakceptować środki.
  3. Zanotuj, którego konta Google używasz. Każdy z poniższych kroków musi zostać wykonany na tym samym koncie.

2. Otwórz notatnik i zainstaluj ADK 2 (~1 min)

Kliknij Otwórz w Colab ▶, a potem uruchom pierwszą komórkę kodu. Przypina dokładną wersję ADK 2, w której ten przewodnik został zweryfikowany, i wyświetla ✓ installed.

3. Uruchom komórkę „Workshop setup” (ok. 3 min)

To komórka o nazwie 🎓 Ścieżka A · Warsztaty. Uruchom go, a Colab poprosi Cię o autoryzację. Wybierz to samo konto Google, na którym właśnie odebrano środki, i zezwalaj na dostęp.

Wykonuje 4 czynności: tworzy projekt o nazwie adk-2-tutorial-XXXX na Twoim koncie, włącza w nim Vertex AI API, ustawia 4 zmienne środowiskowe, które odczytuje każda kolejna komórka, a następnie wykonuje połączenie testowe do Vertex i czeka na odpowiedź. Dzięki temu konfiguracja albo kończy się powodzeniem, albo informuje o przyczynie niepowodzenia, zamiast zawieść później na poziomie.

Oczekiwane dane wyjściowe – najważniejszy jest ostatni wiersz:

Signed in as: you@example.com
...
Successfully created GCP project 'adk-2-tutorial-4817'.
Successfully linked 'adk-2-tutorial-4817' to billing account '01ABCD-...'.
   waiting for Vertex AI to come up on the new project... (10s)
   waiting for Vertex AI to come up on the new project... (20s)

 Vertex AI on adk-2-tutorial-4817 · us-central1 · gemini-2.5-flash  answered a test call

4. Pomiń krok „Konfiguracja w domu”

Nie uruchamiaj komórki klucza AI Studio, ponieważ spowoduje to powrót do AI Studio i cofnięcie wprowadzonych zmian. (Komórka chroni przed tym i odmówi uruchomienia, ale lepszym rozwiązaniem jest po prostu jej pominięcie). Przejdź bezpośrednio do komórki Wspólne bloki.

5. Uruchom komórkę „Shared building blocks” (Wspólne elementy składowe).

Uruchom go raz. Definiuje schematy Pydantic i gotowe scenariusze maratonu, które są ponownie wykorzystywane na każdym poziomie od L2. Zobaczysz ✓ schemas + scenarios ready.

Po warsztatach

Środki i utworzony przez nie projekt nie będą dostępne bezterminowo. Aby po zakończeniu warsztatów nadal bezpłatnie uruchamiać te poziomy, wykonaj krok Konfiguracja do samodzielnego wykonania – bezpłatny klucz AI Studio, bez projektu w chmurze i bez rozliczeń. Zmienia się tylko ta jedna komórka.

Aby wcześniej usunąć projekt, otwórz konsolę Google Cloud, wybierz adk-2-tutorial-XXXX i usuń go. Żadne inne elementy tego laboratorium nie tworzą płatnych zasobów.

3. Konfiguracja w domu · klucz interfejsu API AI Studio

Wszystko na tej ścieżce działa na bezpłatnym kluczu interfejsu API Google AI Studio – nie wymaga projektu w chmurze Google Cloud, rozliczeń ani instalacji lokalnej. Cały ten krok zajmuje około 3 minut.

1. Otwórz notatnik.

Kliknij Otwórz w Colab ▶. Otworzy się notatnik z wprowadzeniem w formacie Markdown i jedną komórką do uruchomienia na każdym poziomie. Komórki są uruchamiane od góry do dołu, a każda z nich drukuje własne dane wyjściowe bezpośrednio pod nią.

2. Zainstaluj ADK 2 (~1 min)

Uruchom pierwszą komórkę z kodem. Określa dokładną wersję, w której te ćwiczenia zostały zweryfikowane:

%pip install -q "google-adk==2.3.0" python-dotenv pydantic nest_asyncio

Poczekaj na zakończenie procesu – zobaczysz ✓ installed. (Pierwsza instalacja trwa około 30–60 sekund, a potem jest zapisywana w pamięci podręcznej).

3. Uzyskaj klucz interfejsu Gemini API z AI Studio (ok. 1 min)

  1. Otwórz stronę aistudio.google.com/app/apikey w nowej karcie przeglądarki.
  2. Zaloguj się przy użyciu swojego konta Google.
  3. Kliknij Utwórz klucz interfejsu API (w prawym górnym rogu).
  4. Wybierz istniejący projekt Google lub pozwól mu utworzyć nowy.
  5. Skopiuj klucz – zaczyna się on od AIza... i ma około 40 znaków.

4. Dodaj klucz do Colab (ok. 1 min)

Opcja A – Colab Secrets (zalecana; klucz pozostaje ukryty):

  1. Na pasku bocznym po lewej stronie w Colab kliknij ikonę klucza 🔑.
  2. Kliknij + Dodaj nowy tajny klucz.
  3. Ustaw Name (Nazwa) na GOOGLE_API_KEY.
  4. Wklej klucz w polu Wartość.
  5. Ustaw przełącznik Dostęp do notatnika w pozycji WŁ.

Opcja B – wklejanie po wyświetleniu prośby (szybkie): pomiń klucz tajny. Gdy uruchomisz następną komórkę, pojawi się ukryta prośba 🔑 Enter your Google AI Studio API key: – wklej i naciśnij Enter.

5. Uruchom komórkę klucza

Odczytuje on klucz tajny (lub wraca do prompta wklejonego), a następnie kieruje ADK do AI Studio (nie do Vertex AI):

import os

# 🏠 TAKE-HOME ONLY — if you ran the Workshop setup cell, skip this one.
if os.environ.get("GOOGLE_GENAI_USE_VERTEXAI") == "True":
    raise SystemExit("✋ You're set up on the workshop path (Vertex AI). Skip this cell.")

# Google AI Studio API key — add GOOGLE_API_KEY in the 🔑 Secrets panel (or paste when prompted).
try:
    from google.colab import userdata
    key = userdata.get("GOOGLE_API_KEY")
except Exception:
    import getpass
    key = getpass.getpass("Enter your Google AI Studio API key: ")

os.environ["GOOGLE_API_KEY"] = "".join(key.split())    # drop any stray whitespace/newlines
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "False"      # use AI Studio, not Vertex AI
print("✅ API key set — using Google AI Studio.")

Oczekiwane dane wyjściowe: ✅ API key set — using Google AI Studio.

6. Uruchom komórkę „Shared building blocks” (Wspólne elementy składowe).

Uruchom komórkę Wspólne elementy składowe. Definiuje schematy Pydantic i gotowe scenariusze maratonu, które są ponownie wykorzystywane na każdym poziomie od L2. Zobaczysz ✓ schemas + scenarios ready.

Wszystko gotowe. 🎽 Zanim przejdziemy do L0, czyli wersji, którą wszyscy tworzą w pierwszej kolejności, zróbmy mały krok w bok.

4. Wprowadzenie · Dlaczego nie jeden duży prompt?

⚡ Zanim uruchomisz narzędzie, ustal JEDNĄ rzecz, na którą będziesz zwracać uwagę: skąd pochodzi każda konkretna liczba? To całe ćwiczenie – wszystko inne to tylko ozdoba.

Przed użyciem drabiny uruchom to, co ma ona zastąpić: jednego agenta, którego prompt obiecuje wszystko – pobieranie informacji o pogodzie, analizowanie trasy, odczytywanie dziennika treningowego, wyznaczanie trasy na podstawie warunków, generowanie planu.

Co zobaczysz: pewną, konkretną i dobrze sformatowaną strategię, której liczby są zmyślone. Podczas jednego z testów na żywo otworzył się z komunikatem „Pobrałem dzisiejsze dane pogodowe” i podał temperaturę 11°C, wiatr o prędkości 14,5 km/h oraz analizę dziennika treningowego, którego nigdy wcześniej nie widział. Nie ma tu interfejsu API pogodowego, danych o trasie ani logu – jedno nieprzejrzyste wywołanie modelu albo tworzy dane wejściowe, albo zabezpiecza je w taki sposób, że stają się bezużyteczne.

To choroba, która ma 4 objawy warte wymienienia:

  1. Nie możesz mu ufać – dane są wymyślone, ale brzmią naturalnie.
  2. Nie możesz go przetestować – routing w kroku 4 znajduje się w tekście, więc nie ma if, które można by przetestować jednostkowo.
  3. Nie możesz zamienić kroku – nie ma miejsca, w którym można by podłączyć prawdziwy interfejs API pogodowy.
  4. Za każdym razem płacisz za wszystko – 5 kroków, 1 duże wywołanie, bez buforowania części deterministycznej.

Utrzymaj to uczucie. Na kolejnych 9 poziomach te kroki są usuwane z promptu po kolei: funkcje pobierają (L1–L2a), instrukcja if kieruje (L2b), specjaliści dzielą pracę (L3a–L3b), a kod ogranicza kształt (L4a–L4b).

Trener mega-promptów – pewny siebie, bez żadnych informacji za wykresem

💻 Lokalne: python -m shared.prologue

5. L0 · Twój pierwszy agent ADK 2

Mapa – jesteś tutaj: L0

⚡ W skrócie: agent to model + instrukcja + narzędzia, z których może korzystać; Runner wykonuje te działania. Wszystko powyżej tego poziomu to po prostu więcej agentów ułożonych w lepsze kształty.

Pytanie: czy model może odpowiadać na pytania i sięgać po prawdziwy kod, gdy ważne są obliczenia arytmetyczne?

Jeden pomysł – trzy części:

  • Agent – element, który wnioskuje (model Gemini + instrukcja).
  • Runner – element, który wykonuje agenta w sesji i przesyła strumieniowo zdarzenia.
  • narzędzie – zwykła funkcja Pythona (pace_splits), którą model decyduje się wywołać; ADK odczytuje sygnaturę i ciąg dokumentujący i przekazuje modelowi deklarację. Nie trzeba pisać schematu.

Po prologu następuje pierwsza poprawka: duży model językowy wykonujący w pamięci obliczenia tempa z łatwością się pomyli – pace_splits to deterministyczny kod w Pythonie, więc liczby w odpowiedzi są obliczane, a nie improwizowane.

▶ Colab: uruchom L0komórkę · 📁 GitHub: L0_first_agent/ · 💻 lokalnie: python -m L0_first_agent.agent

Przepływ L0

def pace_splits(target_finish: str) -> dict:
    """Convert a goal time like '3:30:00' into exact per-mile / per-km paces."""
    ...                                  # deterministic Python — no LLM

pace_coach = Agent(
    name="pace_coach", model=MODEL,
    tools=[pace_splits],                 # the model may call it; ADK reads the signature
    instruction="You are a friendly, concise marathon coach. ... If the runner "
                "mentions a goal time, call pace_splits — never do arithmetic yourself.",
)
runner = Runner(node=pace_coach, session_service=InMemorySessionService(), auto_create_session=True)
async for event in runner.run_async(user_id="u1", session_id="s1", new_message=msg):
    ...  # events carry the model's text

🔍 Markery: Agent(...) · tools=[pace_splits] · Runner(...). W danych wyjściowych wiersz 🔧 oznacza, że model w trakcie udzielania odpowiedzi postanowił wywołać Twój kod.

Co zobaczysz:

   🔧 model called tool  pace_splits({'target_finish': '3:30:00'})
   🔧 tool returned      {'per_mile': '8:00', 'per_km': '4:58', ...}
🧠 Coach: To finish in 3:30:00, you need an average pace of 8:00 per mile...

Linie oznaczone 🔧 to lekcja: w trakcie odpowiedzi model zdecydował się wywołać Twoją funkcję, a dokładny 8:00/mile w odpowiedzi pochodzi z Twojego kodu, a nie ze statystyk tokenów.

Być może zastanawiasz się, czy model zawsze wywołuje narzędzie. Nie – decyduje o tym w przypadku każdego pytania. Zapytaj o coś, co nie zawiera liczb, a linie 🔧 znikną (w tym celu możesz skorzystać z platformy).

👀 Przeczytaj: pace_splits (zwykła funkcja) i wiersz tools=[pace_splits]. · ▶ Uruchom go. · ✏️ Zmiana: zadaj ogólne pytanie (bez czasu docelowego) – zauważ, że linie 🔧 znikają: model decyduje, kiedy warto wywołać narzędzie. Następnie przepisz instruction i uruchom ponownie – instrukcja to reszta programu.

6. L1 · Twój pierwszy przepływ pracy

Mapa drogowa – jesteś tutaj: L1

⚡ TL;DR: zwykła funkcja i agent LLM to ten sam rodzaj węzła. Przewidywalna praca → funkcja (0 LLM, deterministyczna); rozumowanie → agent.

Pytanie: jak połączyć zwykły kod i LLM w jednym przepływie bez płacenia za wywołanie modelu w przypadku części, które są tylko kodem?

Jedna idea:Workflow zarówno zwykła funkcja Pythona, jak i agent LLM są po prostu węzłami na tej samej liście edges.

START ──► fetch_conditions (function, 0 LLM) ──► advise (agent, 1 LLM)

▶ Colab: uruchom L1komórkę · 📁 GitHub: L1_graph_basics/ · 💻 lokalnie: python -m L1_graph_basics.workflow

Proces L1

Węzeł funkcji drukuje wygenerowane dane (bez wywołania modelu), a potem agent udziela porady, która odnosi się do otrzymanej temperatury i wiatru:

def fetch_conditions(node_input):                # function node — 0 LLM
    return Event(output=Conditions(temp_f=78, wind_mph=12, conditions="sunny").model_dump())

advise = Agent(name="advise", model=MODEL, mode="single_turn",
               input_schema=Conditions, instruction="...give pacing + gear advice...")

workflow = Workflow(edges=[(START, fetch_conditions, advise)])

🔍 Znaczniki: jedna krotka krawędzi – (START, fetch_conditions, advise) – z funkcją Pythona w środku i input_schema= weryfikująca przekazanie.

Nowości w porównaniu z L0: Workflow(edges=[...]), START (gdzie wprowadzane są dane), węzeł funkcji zwracający Event(output=...)input_schema=Conditions, dzięki czemu dane wyjściowe funkcji są weryfikowane pod kątem tego schematu, zanim zobaczy je agent (jako tekst JSON – input_schema weryfikuje granicę, nie przekazuje agentowi obiektu Pythona).

Być może zastanawiasz się, czy kolejność „funkcja, a potem agent” jest wymagana. Nie – dowolne zamówienie, dowolna kombinacja, dowolna liczba. advise jest uruchamiana jako druga, ponieważ potrzebuje danych z fetch_conditions. Lekcją jest szlachectwo, a nie kolejność.

👀 Przeczytaj: fetch_conditions zwraca dane bez wywołania modelu; advise ma input_schema=Conditions. · ▶ Uruchom go. · ✏️ Zmiana: ustaw temp_f=30 w funkcji i uruchom ją ponownie – porada zostanie odwrócona, a funkcja nadal będzie kosztować 0 wywołań LLM.

7. L2a · Parallel fan-out + JoinNode (Pillar 1a)

Mapa drogowa – jesteś tutaj: L2a

⚡ TL;DR: rozdziel zadanie równolegle (bezpłatnie), poczekaj na wszystkie odpowiedzi, połącz je w pakiet i przekaż agentowi pełny obraz.

Pytanie: możesz narysować schemat przed otrzymaniem danych wejściowych. Zacznij od szkieletu: zbieraj dane równolegle, grupuj je i przekazuj jednemu agentowi.

Kształt:

START ──► fetch_weather ──┐
START ──► analyze_course ─┼─► JoinNode ─► strategy (1 agent)
START ──► pull_fitness ───┘   (bundles)

▶ Colab: uruchom L2akomórkę · 📁 GitHub: L2a_parallel_join/ · 💻 lokalnie: python -m L2a_parallel_join.workflow

Przepływ L2a

🔍 Znaczniki: 3 krawędzie, które zaczynają się w punkcie START – to jest zwielokrotnienie wyjściowe – i punkt JoinNode, w którym się spotykają.

  • Te 3 pobrania to funkcje, które działają równolegle i nie wymagają wywołań LLM.
  • JoinNode czeka na wszystkie 3 odpowiedzi i łączy je w 1 ładunek z określonym typem (BundledRunData) z kluczem w postaci nazwy funkcji.
  • 1 agent strategy odczytuje pakiet i zapisuje RaceStrategy.

Co zobaczysz: każde pobranie powoduje wyświetlenie sygnatury czasowej started / finished. Wszystkie 3 rozpoczynają się o 0,0 s, a zwielokrotnienie wyjściowe kończy się o 2,0 s – jest to najwolniejsze pobieranie, a nie 4,5 s, czyli suma ich czasów trwania. Ten zakres nakładania się to równoległość. (Łączny czas rzeczywisty wydrukowany na końcu wynosi około 8 sekund, ponieważ zawiera również wywołanie LLM agenta strategii – odczytaj sygnatury czasowe pobierania dla równoległego zgłoszenia, a nie dla całości).

💡 Nawiązanie do prologu: mega-prompt wymyślił pogodę. Temperatura jest tu pobierana za pomocą funkcji pobierania – prawdziwy kod, prawdziwy szew. Zastąp gotowy dyktat rzeczywistym interfejsem Weather API i nie wprowadzaj żadnych innych zmian.

Być może zastanawiasz się, ile

JoinNode

muszę rozumieć? Jedno zdanie: czeka, aż wszystkie równoległe gałęzie zakończą działanie, pakuje wyniki w jeden słownik z kluczami w postaci nazw funkcji nadrzędnych i nie wykonuje żadnych obliczeń. Dzięki temu słownikowi router L2b może zapisywać node_input["fetch_weather"]["temp_f"].

👀 Odczyt: trzy krawędzie rozchodzą się od START; JoinNode łączy je w jeden wątek dla agenta. · ▶ Uruchom go i odczytaj sygnatury czasowe, a nie łączną liczbę. · ✏️ Zmiana: uśpij jedno pobieranie 3.0 – najpierw przewiduj nowy czas zakończenia zwielokrotnienia wyjściowego, a potem weryfikuj.

8. L2b · Dodaj deterministyczny router (Pillar 1b)

Mapa drogowa – jesteś tutaj: L2b

⚡ W skrócie: niezmieniony poziom 2a + zwykły if decyduje, który agent ma zostać uruchomiony. rozgałęzianie bez zadawania pytań modelowi.

Pytanie: plan powinien się różnić w zależności od tego, czy jest ciepło czy zimno. Jak rozgałęziać wątki bez proszenia modelu o podjęcie decyzji?

Kształt (L2a + router):

... JoinNode ─► route_by_weather ─► hot_strategy
               (if-statement)   ─► normal_strategy
                                ─► cold_strategy

▶ Colab: uruchom L2b komórkę – spróbuj run("NORMAL") / run("COLD") · 📁 GitHub: L2b_router/ · 💻 lokalnie: python -m L2b_router.workflow COLD

Przepływ L2b

def route_by_weather(node_input):                        # an if-statement, 0 LLM
    temp = node_input["fetch_weather"]["temp_f"]
    route = "HOT" if temp >= 70 else "COLD" if temp <= 40 else "NORMAL"
    return Event(output=node_input, route=route)

(route_by_weather, {"HOT": hot_strategy, "NORMAL": normal_strategy, "COLD": cold_strategy})

🔍 Markery: Event(output=..., route=...) węzeł funkcji określający ścieżkę i krawędź słownika {"HOT": ..., "NORMAL": ..., "COLD": ...}, która mapuje nazwy na węzły.

Podsumowanie – 3 rodzaje pracy, 3 domy:

  • Przewidywalna praca → funkcje (3 równoległe pobrania)
  • Jasna reguła → routing ze wcześniej ustaloną ścieżką (route_by_weather to if-statement, a nie decyzja modelu)
  • Uzasadnienie → model (dokładnie jeden agent strategii)

Co zobaczysz: temp=78F -> route=HOT, a potem ustrukturyzowany RaceStrategy. Koszt netto: 1 wywołanie LLM.

⚠️ Jeśli dodasz czwartą gałąź,dodaj też do słownika tras wpis DEFAULT_ROUTE. Trasa, która nie pasuje do słownika, nie jest błędem – gałąź po prostu się kończy, a program kończy działanie z kodem 0 bez danych wyjściowych, co jest mylącym ślepym zaułkiem do debugowania.

Być może zastanawiasz się: czy L2b to po prostu L2a z routerem? Tak – pobieranie i łączenie danych pozostają bez zmian i nadal jest to dokładnie 1 wywołanie LLM. Co się zmieniło: „zawsze ten sam agent” stało się „jeden z trzech, wybrany na podstawie danych”.

👀 Przeczytaj: route_by_weather – router jest if-statement, a nie agentem. · ▶ Uruchom run("COLD"). · ✏️ Zmiana: dodaj gałąź WINDY z czwartym agentem i przeczytaj DEFAULT_ROUTE ostrzeżenie powyżej zanim to zrobisz.

9. L3a · Collaborative agents: one flag, two worlds — Pillar 2

Mapa drogowa – jesteś tutaj: L3a

⚡ TL;DR: ten sam zespół, jedna flaga. chat przekazuje całą rozmowę jednemu specjaliście i nigdy nie wraca; single_turn zamienia każdego specjalistę w narzędzie – równoległy podzbiór, automatyczny powrót, jedna synteza.

Pytanie: znasz zespół, ale to prośba decyduje, którzy członkowie powinni odpowiedzieć. Jak pozwolić LLM wybrać podzbiór i uruchomić go jednocześnie?

Kształt: koordynator nadzorujący 6 specjalistów (medycznych, pogodowych, tempa, sprzętu, żywienia i zdrowia psychicznego). Na tym poziomie ten sam zespół wykonuje zadanie 2 razy – ten sam prompt koordynatora i tych samych 6 specjalistów. Jedyna różnica to jedna flaga w przypadku subagentów. Kontrast to lekcja.

▶ Colab: uruchom L3akomórkę · 📁 GitHub: L3a_collaborative/ · 💻 lokalnie: python -m L3a_collaborative.concierge --mode chat "What about fueling?"

Przepływ L3a

🔍 Markery: mode="single_turn" w fabryce i w danych wyjściowych, TRANSFER → (uderzenie 1) w porównaniu z serią linii DISPATCH → z tą samą sygnaturą czasową (uderzenie 2).

Krok 1. Uruchom domyślny kod i obserwuj, jak zadanie kończy się niepowodzeniem.

Brak mode= → subagenci domyślnie mają wartość chat. Co zobaczysz:

TRANSFER  nutrition_specialist   (transfer_to_agent  the only tool chat subagents provide)
Final speaker: nutrition_specialist

Koordynator nie ma narzędzi do przekazywania – podagenci na czacie przekazują mu tylko transfer_to_agent, czyli całą rozmowę do jednego specjalisty. Specjalista odpowiada użytkownikowi bezpośrednio i na tym kończy się działanie. Brak równoległego wysyłania. Bez możliwości zwrotu. Brak syntezy. Zadaj szerokie pytanie, a sytuacja się pogorszy: 6 specjalistów, 1 przekazanie.

To nie jest błąd – to tryb czatu wykonuje swoją pracę. Rozmowa należy do osoby, która ją prowadzi, dopóki nie zostanie wyraźnie przeniesiona na inną osobę. Prawidłowe w przypadku asystenta o otwartej architekturze, nieprawidłowe w przypadku kroku potoku.

Sekcja 2. Jedna flaga, dwa światy

Jedyna różnica: mode="single_turn" w przypadku każdego specjalisty. To samo pytanie, uruchom ponownie:

[t= 7.8s] DISPATCH  medical_specialist       same timestamp =
[t= 7.8s] DISPATCH  weather_specialist         one turn, many calls
[t=14.5s]    medical_specialist replied      replies land inside
[t=14.5s]    weather_specialist replied        one short window
🧠 Concierge (synthesized): <one answer>

ADK wstawia teraz 1 narzędzie do delegowania na specjalistę – nazwane na cześć podagenta i opisane przez jego description= (ten tekst jest odczytywany przez koordynatora podczas wybierania podzbioru; pomiń go, a będziesz kierować połączenia tylko na podstawie nazw). Koordynator wysyła kilka wywołań w jednej turze, ADK uruchamia je równolegle, każde z nich automatycznie zwraca wynik, a koordynator dokonuje syntezy.

Pytanie

Specjaliści, którzy strzelają

„A co z tankowaniem?”

tylko odżywianie,

„Boli mnie kolano na 18 mili”

tylko medyczne

„Should I race today?” (Czy powinienem dziś startować w zawodach?)

medyczne + pogoda + tempo

„Czy jest coś, czym powinienem się martwić?”

wszystkie 6

Dlaczego każdy specjalista otrzymuje pełne informacje: każdy single_turn podagent działa w osobnej, odizolowanej gałęzi sesji – nie widzi rozmowy ani innych podagentów. Nic nie jest otoczeniem: koordynator musi przekazywać cały SpecialistInput (pytanie + strategia + dane wykonawcy) oddzielnie do każdego wywołania równoległego.

💡 W ADK 2 jest to realizowane w ten sposób: model LLM wybiera podzbiór na potrzeby każdego żądania I uruchamia go równolegle – zadeklarowany za pomocą sub_agents + mode="single_turn". W wersji 1.x można było utworzyć ten sam kształt, umieszczając każdego specjalistę w AgentTool. Zmieniło się to, że teraz jest to deklaracja, a nie połączenie. (ParallelAgent to zawsze wszystkie, a transfer_to_agent to serial).

⚠️ Dwa ważne zastrzeżenia: (1) podzbiór jest wybierany przez model, więc jest on mniej deterministyczny niż router L2 z zakodowanymi na stałe regułami – dokładny podzbiór może się różnić w zależności od uruchomienia. (2) Czasami zobaczysz Error validating input: ... dla jednego specjalisty. Prawie nigdy nie jest to wynik specjalisty – output_schema wymusza to po stronie serwera. Jest to dane wejściowe: koordynator musi dosłownie odtworzyć całą zagnieżdżoną funkcję SpecialistInput w przypadku każdego wywołania równoległego, a czasami się myli. ADK zwraca błąd jako wynik działania tego narzędzia, koordynator odzyskuje dane, a synteza nadal jest wykonywana.

Być może zastanawiasz się, czy

chat

tylko delegowanie w stylu 1.x – jeden agent naraz? Tak, to domyślne działanie w wersji 1.x, tylko teraz ma nazwę. Różnica w stosunku do single_turn jest trójwymiarowa: co ma koordynator (1 transfer_to_agent w porównaniu z 1 narzędziem na specjalistę), ilu specjalistów może pracować (1, który prowadzi rozmowę, w porównaniu z N pracujących równolegle) i czy kontrola jest przywracana (nigdy w porównaniu z automatycznym przywracaniem z wynikami). A co do kodu: gałąź if mode == w fabryce istnieje tylko po to, aby jeden zespół mógł być budowany na 2 sposoby w celu uzyskania tego kontrastu – prawdziwa aplikacja ma zakodowany jeden tryb, a if znika.

👀 Odczytaj: _specialist fabryka – parametr mode to cały poziom. · ▶ Uruchom oba bity. · ✏️ Zmiana: zapytanie „Boli mnie kolano na 18 mili” – najpierw przewiduj podzbiór, a potem sprawdzaj wiersze DISPATCH.

# The factory's mode parameter is THE variable this level teaches:
def _specialist(name, domain, focus, mode):
    kwargs = {}
    if mode == "single_turn":   # the structured contract only makes sense for a TOOL
        kwargs = dict(mode="single_turn",
                      input_schema=SpecialistInput, output_schema=SpecialistResponse)
    return Agent(name=name, model=MODEL,
                 description=f"Marathon {domain} specialist. Consult for: {focus}.",
                 instruction=..., **kwargs)

race_concierge = Agent(name="race_concierge", model=MODEL,
                       sub_agents=[...six specialists...],   # NOTE: no `mode` on the coordinator
                       instruction="...DECIDE which specialists are relevant... call them IN PARALLEL... SYNTHESIZE...")

10. L3b · Tryb zadania: rozmowa z linią mety – filar 2

Mapa drogowa – jesteś tutaj: L3b

⚡ W skrócie: tryb środkowy – rozmawiaj z użytkownikiem do momentu zebrania pól, a następnie automatycznie zwracaj zweryfikowany obiekt.

Pytanie: L3a pozostawił lukę. chat prowadzi całą rozmowę; single_turn nigdy nie rozmawia z użytkownikiem. Prawdziwa praca polega jednak na tym, co dzieje się pomiędzy tymi etapami: „rozmawiaj z użytkownikiem, DOPÓKI nie zbierzesz X – a potem wróć z zweryfikowanym obiektem”. Który to tryb?

Kształt:

race_desk (coordinator)
  └─ gear_fitter (mode="task", output_schema=GearOrder)

▶ Colab: uruchom L3bkomórkę · 📁 GitHub: L3b_task_desk/ · 💻 lokalnie: python -m L3b_task_desk.desk

Przepływ L3b

gear_fitter trzymający zadanie otwarte – wstrzymane zadanie, nie zawieszone;

🔍 Markery: mode="task" + output_schema= na tym samym agencie, a w danych wyjściowych – ⏸ wstrzymanie i finish_task połączenie.

Co zobaczysz:

━━ TURN 1 ━━  user: 'I need shoes for the marathon.'
  race_desk  delegate: gear_fitter
  gear_fitter: What is your shoe size?
    The run ENDED  but nothing failed. This is a PAUSED task.

━━ TURN 2 ━━  user: 'Size 9, wide.'   (same session  resumes the task)
  gear_fitter  finish_task   (payload validates as GearOrder)
  race_desk: Your order ... in size 9 Wide has been confirmed.

Wystąpiły 3 sytuacje, których nie można osiągnąć w trybie L3a:

  1. Uruchomienie zostało zatrzymane w trakcie zadania – zadanie zostało wstrzymane, a nie zawieszone ani nie zakończone niepowodzeniem. Agent zadał pytanie doprecyzowujące i pozostawił zadanie otwarte. (W adk web wystarczy wpisać odpowiedź. Skrypty testowe potraktują ją jako drugą wiadomość w tej samej sesji).
  2. Kolejna wiadomość wznowiła TĘ SAMĄ aktywność agenta – bez przekierowania ani ponownego delegowania. Sesja wie, kto czekał.
  3. finish_task zakończył – narzędzie wstrzyknięte przez ADK z powodu mode="task". Aby zakończyć działanie, agent musi wywołać to narzędzie, a jego ładunek musi zostać zweryfikowany na podstawie output_schema. Rozmowa z wpisaną linią mety – sterowanie automatycznie wraca do koordynatora, a wynik jest dołączony.

Reguła jednego pytania dotycząca wyboru trybu

💡 „Czy użytkownik musi z nim rozmawiać i do kiedy?” czat = bezterminowo · zadanie = do momentu zebrania pól · pojedyncza tura = nigdy.

Tryb

Proces z udziałem człowieka

Równoległe?

Powrót do elementu nadrzędnego

chat (domyślny subagent) – asystent pomocy, otwarty kopilot

pełna rozmowa,

nie

ręcznie (przez przeniesienie)

task – przyjmowanie zgłoszeń, rezerwacje, rozwiązywanie problemów

tylko pytania wyjaśniające;

nie

automatycznie (za pomocą finish_task, z użyciem zweryfikowanego obiektu);

single_turn – klasyfikowanie, wyodrębnianie, ocenianie, generowanie

brak

yes

automatyczne (z wynikiem),

mode jest przekazywana tylko subagentom, a nigdy koordynatorowi. Węzły przepływu pracy mają domyślnie wartość single_turn (dlatego pracownicy poziomu 1–2b nigdy nie musieli tego robić), a podagenci mają domyślnie wartość chat (dlatego pracownicy poziomu 3a musieli to robić).

⚠️ Zanim zaczniesz korzystać z tej funkcji, zapoznaj się z 2 uwagami dotyczącymi wersji: (1) task

jako statyczny węzeł wykresu zależy od wersji – w przypadku wersji 2.0.0b1–2.3.0 (wersja przypięta w tym ćwiczeniu) Workflow(...) zgłasza błąd podczas tworzenia; użyj dokładnie tego, co robi ten poziom (koordynator czatu z podagentami zadań), lub wyślij za pomocą ctx.run_node. Usunięto w wersji 2.5.0. (2) „Agenci zadań muszą być agentami liści” (nie mogą mieć własnych subagentów) to udokumentowane ograniczenie ADK, ale umowa, a nie zabezpieczenie w czasie działania: ani wersja 2.3.0, ani 2.5.0 nie powstrzymają Cię przed jego naruszeniem. Brak błędu nie oznacza, że masz pozwolenie.

💡 Więcej informacji: task agent osadzony w przepływie pracy z wykorzystaniem grafu (kształt 2.5.0+), z routingiem, który może zapętlić rozmowę, aby ponowić próbę: repozytorium towarzyszące 22_agent_in_workflow · pełny przewodnik po trybie: docs/agent-modes.md.

❓ Możesz się zastanawiać: co oznacza

task

kupić mi tego, czego nie mogą kupić pozostałe 2 osoby? Trzy rzeczy: automatyczny powrót (czat przejmuje rozmowę) · wpisana linia mety (ładunek finish_task musi być zgodny ze schematem – otrzymujesz dane, a nie transkrypcję) · wstrzymanie/wznowienie (⏸ to wstrzymane zadanie oczekujące na człowieka, a nie zawieszenie).

👀 Przeczytaj: gear_fitter – mode="task" + output_schema to cała umowa. · ▶ Uruchom go. · ✏️ Zmiana: run_desk("I need a hydration vest", "2 liters, medium") – pytanie wyjaśniające dostosowuje się, a linia mety pozostaje wpisana.

11. L4a · Równoległe zwielokrotnienie wyjściowe o rozmiarze dostosowanym do czasu działania (Pillar 3a)

Mapa drogowa – jesteś tutaj: L4a

⚡ W skrócie: szkielet nadal składa się z 3 statycznych kroków – dynamiczny ukrywa się wewnątrz środkowego, w którym szerokość jest określana przez dane w czasie działania.

⚠️ Uwaga: to najbardziej stromy stopień drabiny. Poprzedni poziom miał 44 linie, a ten ma około 120 – 3 agenty i 2 węzły przepływu pracy, bez żadnego wypełnienia. Poświęć na to około 15 minut i skup się na wierszu „Przeczytaj/uruchom/zmień” na końcu. Nie musisz za pierwszym razem przyswajać wszystkich wierszy.

Pytanie: forma pracy zależy od danych wejściowych. Nie możesz narysować wykresu z wyprzedzeniem. Zacznij od szerokości środowiska wykonawczego: pozwól LLM zdecydować, ile podrzędnych pytań ma zostać zadanych.

Kształt (jeden poziom):

START ─► decompose ─► research_topic (parallel_worker) ─► synthesize
                                 
                             └──┴──┴─ (flat: no children yet)

Pytanie otwarte jest dzielone na N podpytań – liczbę N wybiera LLM w czasie działania (3–7) – z których każde jest wyszukiwane równolegle, a następnie syntetyzowane w jedno podsumowanie.

▶ Colab: uruchom L4akomórkę · 📁 GitHub: L4a_flat_research/ · 💻 lokalnie: python -m L4a_flat_research.deep_research

Przepływ L4a

🔍 Znaczniki – brak

dynamic=True

przełącznik. Dynamic to sposób pisania, a nie konfiguracja. Dwa markery i tylko dwa: @node(parallel_worker=True) (przyjmuje listę o rozmiarze określonym w czasie działania, uruchamia 1 proces roboczy na element) i ctx.run_node(...) (bezpośrednio planuje węzły kodu). Jeśli widzisz jedną z tych opcji, korzystasz z dynamicznego określania cen.

Co zobaczysz: dekompozytor drukuje np. 5 podpytań, które są badane równolegle, a następnie tworzone jest podsumowanie. Liczba różni się w każdym przebiegu – stały wykres nie mógłby tego zrobić.

Warto zwrócić uwagę na 2 flagi dotyczące pracownika:

  • rerun_on_resume=True jest wymagany w każdym węźle, który wywołuje ctx.run_node – bez niego ADK zgłasza ValueError. Po wznowieniu musi ponownie wykonać węzeł wysyłający, aby odbudować utworzone przez niego węzły podrzędne, ponieważ nie znajdują się one na statycznym wykresie.
  • retry_config= bounds how this FAILS. Pracownik równoległy anuluje wszystkie zadania równorzędne i ponownie zgłasza błąd, gdy jedno z zadań podrzędnych zakończy się niepowodzeniem. Oznacza to, że bez ponowienia próby pojedynczy przejściowy błąd 429 powoduje odrzucenie całego uruchomienia, w tym wszystkich wywołań, za które już zapłacono. Ponowna próba jest podejmowana w węźle wewnętrznym dla poszczególnych elementów, więc każda gałąź jest ponawiana niezależnie.

Możesz się zastanawiać: skąd ADK „wie”, że jest to dynamiczne? Nie musi tego robić – nigdzie nie jest zadeklarowana. Decomposer tworzy listę w czasie działania, a węzeł roboczy równoległy dostosowuje swój rozmiar do otrzymanych danych. Dynamiczność jest właściwością napisanego przez Ciebie przepływu danych, a nie trybem, który został włączony.

👀 Przeczytaj: 2 flagi na research_topic – parallel_workerrerun_on_resume. · ▶ Uruchom go. · ✏️ Zmiana: zastąpienie własnym pytaniem otwartym – N zmian, ponieważ dane wejściowe decydują o szerokości.

12. L4b · Add recursive spawning (Pillar 3b)

Mapa drogowa – jesteś tutaj: L4b

⚡ TL;DR: rekursja jest pisana, a nie podawana – proces roboczy wywołuje sam siebie za pomocą ctx.run_node, zwykłego kodu Pythona, więc hamulec też musi być napisany. To MAX_DEPTH.

Pytanie: jedno z odkryć badawczych może dotyczyć wąskiego podtematu, który warto zbadać osobno. Jak sprawić, aby gałąź generowała więcej równoległych zadań, i utrzymać ją w określonych granicach?

Kształt (obecnie rekurencyjny):

START ─► decompose ─► research_topic (parallel_worker, recursive) ─► synthesize
                                 
                                 └─ research(q3) ─► maybe spawn children
                               └─── research(q2) ─► maybe spawn children
                             └────── research(q1) ─► maybe spawn children

▶ Colab: uruchom L4bkomórkę · 📁 GitHub: L4b_recursion/ · 💻 lokalnie: python -m L4b_recursion.deep_research

Przepływ L4b

@node(parallel_worker=True, rerun_on_resume=True)
async def research_topic(ctx, node_input):
    finding = coerce(await ctx.run_node(research_agent, node_input=...), ResearchFinding)
    if finding.needs_deeper and finding.deeper_questions and depth < MAX_DEPTH:   # boundary in CODE
        children = await ctx.run_node(research_topic, node_input=deeper)          # recursive fan-out
    yield Event(output={..., "children": children})

🔍 Znaczniki: ctx.run_node(research_topic, ...) wewnątrzresearch_topic samego siebie – odwołanie do samego siebie jest rekurencją – i strażnik depth < MAX_DEPTH w wierszu powyżej.

Co zobaczysz: węzły wyszukiwania drukujące spawning N deeper – rekursja zachodząca na żywo – a następnie kształt drzewa czasu działania (np. 5 top-level + 10 recursive children). Drzewo różni się w każdym przebiegu.

⚠️ Zanim zwiększysz wartość: pułap rośnie szybko – MAX_DEPTH=3 w najgorszym przypadku zwiększa liczbę połączeń z ok. 30 do ok. 93. Na samym końcu uruchomienia możesz zobaczyć wiersz dziennika cancelling N leftover tasks: oznacza to, że ADK zamyka grupę zadań równoległych po zakończeniu działania. Nie jest szkodliwy i w zależności od konfiguracji logowania możesz go nigdy nie zobaczyć.

Być może zastanawiasz się, czy dynamiczne reklamy w wyszukiwarce nie są domyślnie rekursywne. Nie – L4a jest w pełni dynamiczny i ma zero rekursji. Dynamic tylko przekazuje zwykły przepływ sterowania w Pythonie; L4b wybiera, aby pisać z nim rekurencję. A ponieważ Ty piszesz rekurencję, Ty musisz napisać jej granicę – w tym miejscu hasło „pozwól LLM kształtować pracę, zachowaj granice w kodzie” przestaje być sloganem.

👀 Przeczytaj: strażnik: if finding.needs_deeper and depth < MAX_DEPTH. · ▶ Uruchom go. · ✏️ Zmiana: ustaw MAX_DEPTH = 1 i uruchom ponownie – drzewo zostanie spłaszczone (a wykonanie będzie tańsze). Granica jest TWOJA, w kodzie.

13. L5 · Którego wzorca użyć?

Mapa drogowa – jesteś tutaj: L5

⚡ W skrócie: wszystko zależy od jednego czynnika – kto wybiera następny krok: narysowany przez Ciebie wykres, LLM czy Twój kod.

Masz już wszystkie 3 rodzaje. To model, który sprawia, że są one przydatne: dopasuj wzorzec do kształtu problemu.

Oś: kto decyduje o tym, co będzie odtwarzane dalej?

Filar

Kto decyduje o tym, co będzie odtwarzane dalej

Wbudowane

1. Wykres

wykres, który został narysowany,

L2a / L2b

2. Współpraca

LLM

L3a / L3b

3. Dynamiczne

kod Pythona w czasie działania,

L4a / L4b

Krok 0. Czy w ogóle potrzebujesz wykresu?

ADK zawiera gotowe agenty przepływu pracy – SequentialAgent, ParallelAgent, LoopAgent. W przypadku zwykłego łańcucha agentów jest to najtańsza prawidłowa odpowiedź i nie ma grafu do złożenia. Możesz je pominąć, gdy potrzebujesz jawnego routingu (router L2b), złącza (JoinNode L2a) lub węzłów, które nie są agentami (zwykła funkcja, bez wywołań LLM) – zwykle to jest powód.

Would a prebuilt SequentialAgent / ParallelAgent / LoopAgent do?

├─ YES ──────────────────────────────► use it; stop here

└─ NO  I need routing, a join, or non-agent nodes
   
   Can you draw the workflow before the input arrives?
   
   ├─ YES ───────────────────────────► Pillar 1 · Graph workflow    (L2a/L2b)
   
   └─ NO
      ├─ Known team, request picks the subset? ─► Pillar 2 · Collaborative  (L3a/L3b)
      └─ Does the shape depend on the input?  ──► Pillar 3 · Dynamic        (L4a/L4b)

L5 · which pattern

Uczciwe porównanie wersji 1.x i 2

To nie jest tak, że „wersja 2.0 potrafi to, czego nie potrafiła wersja 1.x” – wersja 1.x też to potrafiła. Zmiana polega na tym, że wersja 2.0 przypisuje każdemu kształtowi bardziej bezpośrednie miejsce docelowe, dzięki czemu znany przepływ sterowania opuszcza prompt i staje się strukturą, którą można zobaczyć i przetestować.

Wzór

Koszt 1.x

Strona główna pakietu ADK 2

Wykres

4 wywołania LLM w przypadku typowej kompilacji; routing ukryty w prompcie

funkcja + węzły agenta jako węzły równorzędne → 1 wywołanie, router instrukcji if

Współpraca

możliwość tworzenia za pomocą AgentTool połączeń; ParallelAgent zawsze wszystkie, transfer_to_agent szeregowe

zadeklarowany zespół: sub_agents + mode="single_turn"

Dynamiczne

rekurencja powoduje wyjście z platformy.

parallel_worker + rekurencyjne ctx.run_node w ramach struktury

Cała aplikacja i to, czego nie widać na wykresie

Wszystkie elementy poniżej zostały już utworzone. Workflow udostępnia swoją strukturę pod adresem graph.edges, więc ten obraz jest generowany na podstawie kodu, a nie rysowany ręcznie. Wyniki introspekcji podsumowaniem tego modułu:

Filar

Co zawiera graph.edges

Dlaczego

1. Wykres (L2b)

10 krawędzi, trasy i wszystko inne

został narysowany przed otrzymaniem jakichkolwiek danych wejściowych;

2. Współpraca (L3a)

0 krawędzi – tylko sub_agents + mode

LLM wybiera podzbiór na podstawie żądania.

3. Dynamiczny (L4a/L4b)

3 krawędzie – identyczne w obu przypadkach

rekursja jest napisana w Pythonie, a nie w grafie.

Ostatni wiersz jest dowodem na odpowiedź na pytanie L4b: L4a i L4b mają ten sam wykres, a tylko jeden z nich jest rekurencyjny.

Co możesz teraz tworzyć

Każdy z wzorów, które właśnie zostały uruchomione, to rzeczywisty kształt produktu:

Udało Ci się przećwiczyć

Na wolności

Początek

Wykres + router (L2a/L2b)

potoki dokumentów, kroki ETL z LLM, łańcuchy weryfikacji/zatwierdzania, narzędzia do oceny

L2b tego repozytorium

Koordynator + zespół single_turn (L3a)

kopilot wsparcia z zespołami specjalistów, punktami triage i wielopłaszczyznową weryfikacją.

marathon demo mode 2

task agentów (L3b)

formularze wprowadzające, procesy rezerwacji, wprowadzanie nowych klientów, KYC – wszelkie procesy typu „zbierz, a potem działaj”;

22_agent_in_workflow

Dynamiczna szerokość/głębokość (L4a/L4b)

agenci badawczy, generatory raportów, skanowanie kontrolne danych wejściowych o nieznanej wielkości;

tryb maraton demo 3

Tworzą

Te 3 wzorce nie wykluczają się wzajemnie. Węzeł wykresu może wywołać koordynatora współpracy, a specjalista może uruchomić dynamiczny przepływ pracy. Wybierz odpowiedni wzorzec dla każdej części problemu – w ten sposób unikniesz przekształcenia każdego systemu agentów w jeden gigantyczny prompt.

Cała aplikacja i to, czego nie pokazuje wykres

💡 Wypróbuj go w swoim przepływie pracy: skrypt, który wygenerował ten wykres, to scripts/graph_dump.py. Skieruj go na dowolny Workflow, a wydrukuje prawdziwe krawędzie – bezpłatny schemat strukturalny dowolnej konstrukcji.

14. Gratulacje

9 agentów, 1 pałka, uporządkowane zakończenie

Utworzyliśmy trenera na dzień maratonu, a po drodze wszystkie 3 wzorce aranżacji ADK 2.

Czego się dowiedziałeś(-aś)

  • Prolog – mega-prompt, który sam wymyślił pogodę: dlaczego w ogóle istnieje struktura.
  • L0–L1 – Agent, Runner, prawdziwe narzędzie, które model wybiera do wywołania, i pierwszy Workflow (węzły funkcji + węzły agenta jako węzły równorzędne).
  • L2a / L2b – przepływy pracy z grafami: równoległe zwielokrotnienie wyjściowe + JoinNode, a potem deterministyczne przekierowywanie – 1 wywołanie LLM.
  • L3a – agenci współpracujący: ten sam zespół działa w chat (izolowany), a potem w single_turn (równoległy podzbiór + synteza) – 1 flaga, 2 światy.
  • L3btask tryb: wstrzymane pytanie doprecyzowujące, skryptowe wznowienie, finish_task zwracanie zweryfikowanego obiektu.
  • L4a / L4b – dynamiczne przepływy pracy: szerokość w czasie działania (zwielokrotnienie wyjściowe), a następnie głębokość w czasie działania (rekurencja) z granicami w kodzie.
  • L5 – drzewo decyzyjne i sposób tworzenia wzorców.

Wiersze, które warto zachować

Funkcje przygotowują kontekst. Krawędzie definiują przepływ pracy. Router wybiera ścieżkę. Model pisze odpowiedź.

Pozwól LLM kształtować pracę, ale zachowaj granice w kodzie.

Dopasuj wzór do kształtu problemu.

Dalsze kroki

  • Uruchom pełną aplikację, z której pochodzą te poziomy – Marathon Race Day Coach, czyli aplikację FastAPI + SSE z interfejsem przeglądarki, która na żywo pokazuje wszystkie 3 tryby: github.com/cuppibla/adk-2-marathon-demo.
  • Więcej informacji: adk-workflows-compared – wszystkie 23 oficjalne przykłady przepływów pracy ADK 2, każdy z wersją 1.x i wskazówkami dotyczącymi zastosowania. Zacznij od docs/three-pillars.md, a potem przejdź do pominiętych w tym ćwiczeniu elementów: 07_loop, 17_request_input, 22_agent_in_workflow.
  • Przenieś swój własny problem: które części mają znaną strukturę (L2), znany zespół (L3a/L3b), nieznany kształt (L4)?
  • Zapoznaj się z kodem: github.com/cuppibla/adk2-tutorial
  • Czy przeszedł przez warsztaty? Środki i utworzony dzięki nim projekt nie będą dostępne bezterminowo. Aby nadal bezpłatnie uruchamiać te poziomy, wykonaj krok Konfiguracja do samodzielnego wykonania: bezpłatny klucz AI Studio, bez projektu w chmurze i bez rozliczeń. Zamiana tej jednej komórki to jedyna zmiana.