Tworzenie agentów AI z użyciem pakietu ADK: koordynowanie przepływów pracy grafów

1. Przegląd

Aby spełnić prośbę o zaplanowanie podróży z wieloma ograniczeniami (np. zaplanowanie jednodniowej wycieczki do Kioto za 100 USD, podczas której można zwiedzić zabytkowe świątynie, zjeść lokalny ramen i nie odwiedzać muzeów sztuki nowoczesnej), trzeba wyodrębnić uporządkowane preferencje, jednocześnie wyszukać miejsca, przygotować szczegółowy projekt i sprawdzić łączny koszt przed udzieleniem odpowiedzi. W pakiecie ADK 1.x koordynowanie tych etapów wymagało zagnieżdżania obiektów opakowujących SequentialAgent, ParallelAgent i LoopAgent, zapisywania wszystkich danych wyjściowych pośrednich w stanie sesji i wywoływania dodatkowego wywołania narzędzia LLM tylko po to, aby wyjść z pętli ulepszania.

W tym ćwiczeniu utworzysz wieloagentowy planer podróży na platformie agentów, korzystając z przepływów pracy grafów (Workflow) pakietu Agent Development Kit (ADK) 2. W 3 kolejnych etapach połączysz specjalistyczne węzły Agent (obsługiwane przez gemini-3.8-flash) w sekwencyjny łańcuch krawędzi (node_input), rozproszysz równoczesne procesy badawcze google_search zsynchronizowane z JoinNode oraz wymusisz stosowanie reguł budżetowych i reguł wykluczających za pomocą deterministycznego routera Pythona (Event(route=...)), który pomija ulepszanie, gdy wstępna wersja robocza przejdzie kontrolę.

Wymagania wstępne

Czego się nauczysz

  • Jak zadeklarować potok wieloagentowy w ADK 2 przy użyciu krotek krawędzi Workflow
  • Jak ADK 2 przekazuje dane wyjściowe bezpośrednio wzdłuż krawędzi jako node_input i wiąże klucze stanu sesji jako parametry funkcji
  • Jak rozgałęziać równoległe gałęzie badań i scalać ich wyniki za pomocą JoinNode
  • Jak deterministycznie kierować ograniczone cykle maker-checker w Pythonie za pomocą Event(route=...)
  • jak sprawdzać skompilowane wykresy przepływu pracy, osie czasu wykonywania równoległego i delty stanu w interfejsie internetowym ADK.

Wymagania

  • konto Google (jeśli chcesz otrzymać punkty za warsztaty, użyj osobistego konta @gmail.com);
  • link do środków na warsztaty od instruktora (oraz aparat w telefonie do zeskanowania kodu QR) lub aktywne konto rozliczeniowe Cloud.
  • Google Chrome (zalecamy używanie okna incognito, aby uniknąć mieszania kont osobistych i służbowych).

2. Odbierz środki na Google Cloud (opcjonalnie)

Jeśli uczestniczysz w warsztatach z linkiem do środków na wydarzenie, odbierz środki w ramach programu Google Developer Program, aby utworzyć konto rozliczeniowe bez karty kredytowej.

Odbierz środki na wydarzenie

  1. Otwórz link do środków na warsztaty w oknie incognito i zaloguj się na konto @gmail.com.
  2. W sekcji Odbierz korzyści kliknij Odbierz środki:

Karta „Odbierz korzyści” w Google Builders Hub z przyciskiem „Odbierz środki”

  1. Jeśli pojawi się prośba o utworzenie profilu w programie Google Developer Program, wypełnij formularz.
  2. Gdy pojawi się komunikat Zeskanuj, aby potwierdzić, że jesteś człowiekiem, zeskanuj kod QR telefonem i dokończ weryfikację (jeśli kod QR się nie pojawi, odśwież stronę i ponownie kliknij Odbierz środki):

Okno kodu QR „Zeskanuj, aby potwierdzić, że jesteś człowiekiem” w programie Google Developer Program

  1. Sprawdź, czy pojawił się komunikat Udało Ci się odebrać środki na wydarzenie, a następnie kliknij Otwórz konto rozliczeniowe:

Google Builders Hub z informacją o odebraniu środków na wydarzenie i przyciskiem Otwórz konto rozliczeniowe

🔍 Sprawdź swoją pracę

Konfigurowanie konta rozliczeniowego zajmuje do 3 minut:

  1. Otwórz Rozliczenia usługi Google Cloud (lub skopiuj adres URL do okna incognito) i wybierz Twoje konta rozliczeniowe:
https://console.cloud.google.com/billing
  1. Odświeżaj stronę, dopóki konto GDP Credit nie będzie miało stanu Aktywne:

Zarządzanie kontem rozliczeniowym Google Cloud z aktywnym kontem rozliczeniowym środków Google Cloud

3. Tworzenie projektu Google Cloud (opcjonalnie)

Utwórz dedykowany projekt Google Cloud, aby wszystkie zasoby z tego ćwiczenia były odizolowane i łatwe do usunięcia.

  1. Otwórz stronę Utwórz projekt (lub skopiuj adres URL do okna incognito):
https://console.cloud.google.com/projectcreate
  1. Wpisz szczegóły projektu:
    • Nazwa projektu: na przykład google-cloud-workshop.
    • Konto rozliczeniowe (jeśli jest widoczne): wybierz GDP Credit (lub aktywne konto rozliczeniowe).
    • Zasób nadrzędny: Brak organizacji (lub wybierz organizację, jeśli jest to wymagane)
  2. Zanotuj wygenerowany identyfikator projektu pod nazwą projektu, a następnie kliknij Utwórz:

Ekran tworzenia projektu Google Cloud z nazwą projektu, identyfikatorem projektu, zasobem nadrzędnym i przyciskiem Utwórz

🔍 Sprawdź swoją pracę

Na pasku narzędzi w prawym górnym rogu kliknij Powiadomienia, a po zakończeniu tworzenia projektu kliknij Wybierz projekt:

Menu Powiadomienia w konsoli Google Cloud z opcją Utwórz projekt i linkiem Wybierz projekt

4. Konfigurowanie środowiska

Skonfiguruj Google Cloud Shell, włącz interfejs Agent Platform API i utwórz odizolowane środowisko wirtualne Python 3.12 za pomocą uv.

Konfigurowanie Cloud Shell

  1. Kliknij Aktywuj Cloud Shell (>_) na pasku narzędzi konsoli Google Cloud lub otwórz Google Cloud Shell bezpośrednio (albo skopiuj adres URL do okna incognito):
https://shell.cloud.google.com/?show=ide,terminal

Przycisk Aktywuj Cloud Shell na pasku narzędzi konsoli Google Cloud

  1. Jeśli pojawi się prośba o autoryzację, kliknij Autoryzuj:

Okno dialogowe autoryzacji Cloud Shell

  1. Wybierz aktywny identyfikator projektu Google Cloud z menu numerycznego w terminalu:
unset CLOUDSDK_CORE_PROJECT
PS3="Select your project (e.g. 1): "
select PROJECT_ID in $(gcloud projects list --format="value(projectId)"); do
  [ -n "$PROJECT_ID" ] && gcloud config set project "$PROJECT_ID" && break
done

Dane wyjściowe powinny być podobne do tych (projekty bez tagów zawierają też wiersz informacyjny [environment: untagged]):

1) google-cloud-workshop-4210
2) my-cloud-project-8812
Select your project (e.g. 1): 1
Updated property [core/project].

Sprawdź, czy wybrany identyfikator projektu jest podświetlony na żółto w promptu Cloud Shell:

Terminal Cloud Shell z wybranym identyfikatorem projektu zaznaczonym na żółto

Włącz wymagane interfejsy API

Włącz Agent Platform API, aby agenty ADK mogły wywoływać modele Gemini:

gcloud services enable aiplatform.googleapis.com

Dane wyjściowe powinny być podobne do tych:

Operation "operations/acat.p2-..." finished successfully.

Tworzenie środowiska wirtualnego Pythona i instalowanie pakietu ADK

Utwórz obszar roboczy ~/adk-graph-agents i aktywuj środowisko wirtualne Pythona 3.12:

cd ~
mkdir -p adk-graph-agents && cd ~/adk-graph-agents

which uv >/dev/null 2>&1 || curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"

uv venv .venv --python 3.12
source .venv/bin/activate

Sprawdź, czy na początku promptu w terminalu pojawia się prefiks (.venv):

Terminal Cloud Shell z prefiksem .venv w aktywnym środowisku wirtualnym Pythona

Zainstaluj ADK 2 w aktywnym środowisku wirtualnym:

uv pip install --no-cache "google-adk==2.14.0"

🔍 Sprawdź swoją pracę

Sprawdź zainstalowaną wersję interfejsu wiersza poleceń adk:

adk --version

Dane wyjściowe powinny być podobne do tych:

adk, version 2.14.0

5. Tworzenie szkieletu planera podróży

Wygeneruj pakiet startowy travel_planner za pomocą adk create i przetestuj domyślny punkt odniesienia z jednym agentem w adk web.

Wygeneruj pakiet startowy

Uruchom adk create z ~/adk-graph-agents:

cd ~/adk-graph-agents
adk create travel_planner

Gdy pojawi się prośba, wybierz te opcje (jeśli przy pierwszym uruchomieniu pojawi się prośba o Enable telemetry? [Y/n]:, naciśnij Enter lub wpisz n i naciśnij Enter):

  1. Wybierz model agenta głównego: wpisz 2, aby wybrać gemini-3.8-flash (opcja 1 wybiera gemini-3.5-flash), i naciśnij Enter.
  2. Wybierz backend: wpisz 2, aby wybrać backend Agent Platform Google Cloud (opcja 2 w menu interfejsu wiersza poleceń poniżej), i naciśnij Enter.
  3. Enter Google Cloud project ID (Wpisz identyfikator projektu Google Cloud): naciśnij Enter, aby zaakceptować wykryty identyfikator projektu w nawiasach.
  4. Enter Google Cloud region (Wpisz region Google Cloud): wpisz global (aby użyć globalnego punktu końcowego Gemini) i naciśnij Enter.

adk create zapisuje GOOGLE_GENAI_USE_ENTERPRISE=1, identyfikator projektu i GOOGLE_CLOUD_LOCATION=global w travel_planner/.env, dzięki czemu dane logowania pozostają nienaruszone, gdy później zastąpisz agent.py.

Dane wyjściowe powinny być podobne do tych:

Choose a model for the root agent:
1. gemini-3.5-flash
2. gemini-3.8-flash
3. Other models (fill later)
Choose model (1, 2, 3): 2
1. Google AI
2. Vertex AI
3. Login with Google
Choose a backend (1, 2, 3): 2

You need an existing Google Cloud account and project, check out this link for details:
https://google.github.io/adk-docs/get-started/quickstart/#gemini---google-cloud-vertex-ai

Enter Google Cloud project ID [google-cloud-workshop-4210]:
Enter Google Cloud region [us-central1]: global

Agent created in /home/your_username/adk-graph-agents/travel_planner:
- .env
- .gitignore
- __init__.py
- agent.py

⚠️  WARNING: Secrets (like GOOGLE_API_KEY) are stored in .env.

Next steps:
- Chat with your agent in the terminal: adk run travel_planner
- Or in the browser: adk web .
- Have your coding agent evaluate and deploy it: uvx google-agents-cli setup
  (other install options: https://adk.dev/get-started/agents-cli/)

Otwórz plik ~/adk-graph-agents w edytorze kodu Cloud Shell:

which cloudshell >/dev/null 2>&1 && cloudshell workspace ~/adk-graph-agents || true

Ponownie aktywuj środowisko wirtualne w ponownie załadowanym terminalu:

cd ~/adk-graph-agents
source .venv/bin/activate

Testowanie początkowego pojedynczego agenta

Uruchom interfejs internetowy ADK, aby zobaczyć, jak pojedynczy, niezarządzany agent początkowy odpowiada na prompta dotyczącego podróży:

cd ~/adk-graph-agents
adk web --allow_origins "*"

Kliknij ikonę Podgląd w przeglądarce na pasku narzędzi Cloud Shell w prawym górnym rogu, wybierz Zmień port, wpisz 8000 i kliknij Zmień i wyświetl podgląd:

Okno konfiguracji portu podglądu Cloud Shell w przeglądarce ustawione na 8000

🔍 Sprawdź swoją pracę

Wybierz travel_planner w menu w lewym górnym rogu adk web i wyślij "Help me plan a trip to Japan":

Interfejs internetowy ADK Development pokazujący podstawową rozmowę z jednym agentem

W panelu czatu powinna pojawić się odpowiedź konwersacyjna (z jednym wywołaniem root_agent w sekcji Zdarzenia):

root_agent -> responds in one pass without structured preference extraction, live search, or budget verification

Zanim przejdziesz do edytowania plików w następnym kroku, naciśnij Ctrl + C w terminalu Cloud Shell, aby zatrzymać adk web.

6. Tworzenie przepływu pracy z wykresem liniowym

Utwórz pierwszy ADK 2 Workflow: sekwencyjny łańcuch łączący zarezerwowany punkt wejścia "START" z user_profiler_agent i itinerary_synthesizer_agent (każdy węzeł bez krawędzi wychodzących działa jako węzeł końcowy). Każda krawędź (a, b) automatycznie uruchamia węzły Agent (alias dla LlmAgent) w odizolowanym trybie single_turn i dostarcza dane wyjściowe węzła a jako node_input (przychodząca wiadomość użytkownika) do węzła b, podczas gdy {user_preferences} w ciągu instrukcji interpoluje pasujący klucz stanu sesji zapisany za pomocą output_key.

Schemat architektury przedstawiający travel_workflow (przepływ pracy) łączący START z user_profiler_agent i itinerary_synthesizer_agent

Tworzenie początkowych specjalistów

Upewnij się, że usługa adk web jest zatrzymana (Ctrl + C), a następnie otwórz nowy plik o nazwie travel_planner/specialists.py:

cloudshell edit ~/adk-graph-agents/travel_planner/specialists.py

Wklej ten kod do pliku travel_planner/specialists.py, aby zdefiniować user_profiler_agent i itinerary_synthesizer_agent (importując google_search na potrzeby następnego kroku):

from google.adk import Agent
from google.adk.tools import google_search

MODEL = "gemini-3.8-flash"

# --- Specialist 1: The User Profiler ---
user_profiler_agent = Agent(
    model=MODEL,
    name="user_profiler_agent",
    description="Extracts travel preferences and constraints from user input.",
    instruction="""
        Extract destination, duration (default 1 day), budget, interests, and exclusions.
        Return a single JSON object with keys: "destination", "duration", "budget", "interests", "exclusions".
    """,
    output_key="user_preferences",  # Saved for multi-hop constraints downstream
)

# --- Specialist 2: The Synthesizer ---
itinerary_synthesizer_agent = Agent(
    model=MODEL,
    name="itinerary_synthesizer_agent",
    description="Compiles preferences and research into a final Markdown itinerary.",
    instruction="""
        You are an elite Travel Concierge. Using the incoming input and
        User Preferences ({user_preferences}), write a Markdown itinerary
        with an itemized USD and local currency cost breakdown.
    """,
    output_key="final_itinerary",
)

Definiowanie sekwencyjnego łańcucha krawędzi

Otwórz nowy plik o nazwie travel_planner/workflows.py:

cloudshell edit ~/adk-graph-agents/travel_planner/workflows.py

Wklej tę definicję Workflow (gdzie krotka składająca się z co najmniej 3 węzłów kompiluje się w łańcuch sekwencyjny z "START"):

from google.adk import Workflow
from .specialists import (
    itinerary_synthesizer_agent,
    user_profiler_agent,
)

# 1. SEQUENTIAL GRAPH: START -> user_profiler_agent -> itinerary_synthesizer_agent
travel_workflow = Workflow(
    name="travel_workflow",
    description="Orchestrates the end-to-end travel planning process as a graph workflow.",
    edges=[
        ("START", user_profiler_agent, itinerary_synthesizer_agent),
    ],
)

Udostępnij przepływ pracy jako root_agent

Otwórz travel_planner/agent.py:

cloudshell edit ~/adk-graph-agents/travel_planner/agent.py

Zastąp całą zawartość pliku travel_planner/agent.py tymi 2 wierszami:

from .workflows import travel_workflow

root_agent = travel_workflow

🔍 Sprawdź swoją pracę

Uruchom adk web z ~/adk-graph-agents:

cd ~/adk-graph-agents
adk web --allow_origins "*"

Otwórz podgląd w przeglądarce na porcie 8000, kliknij + Nowa sesja i wyślij:

Help me plan a trip to Kyoto

W widoku Zdarzenia powinna być widoczna sekwencja zdarzeń podobna do tej:

1. user_profiler_agent -> outputs JSON {"destination": "Kyoto", "duration": "1 day", ...} and writes state["user_preferences"]
2. itinerary_synthesizer_agent -> receives preferences via node_input and outputs the Markdown itinerary

Interfejs internetowy ADK 2.14.0 pokazujący wykonanie grafu od START do user_profiler_agent i itinerary_synthesizer_agent oraz wygenerowany plan podróży do Kioto

Kliknij dowolne zdarzenie w sekcji Zdarzenia, a następnie kliknij ikonę Wykres (account_tree na pasku narzędzi szczegółów zdarzenia), aby sprawdzić skompilowaną topologię START -> user_profiler_agent -> itinerary_synthesizer_agent. Następnie sprawdź kartę Stan, aby upewnić się, że pola user_preferences i final_itinerary są wypełnione.

Zanim przejdziesz dalej, naciśnij Ctrl + C w terminalu, aby zatrzymać adk web.

7. Rozgałęzianie i łączenie za pomocą węzła JoinNode

Oprzyj plan podróży na danych z internetu, dodając 2 specjalistów google_search (activity_finder_agent i restaurant_finder_agent) i synchronizując ich z JoinNode:

  • Krotka rozsyłania: umieszczenie (activity_finder_agent, restaurant_finder_agent) w krotce krawędzi powoduje, że dane wyjściowe user_profiler_agent są dostarczane jako node_input do obu badaczy jednocześnie.
  • Bariera fan-in (JoinNode): umieszczenie JoinNode(name="research_join") zaraz po krotce powoduje, że system czeka, aż każda gałąź przychodząca zostanie ukończona, i przekazuje jedną scaloną wartość dict ({"activity_finder_agent": ..., "restaurant_finder_agent": ...}) jako node_input do itinerary_synthesizer_agent zamiast dwukrotnego uruchamiania syntezy.

Schemat architektury przedstawiający przepływ travel_workflow, który rozgałęzia się od agenta user_profiler_agent przez node_input do agentów activity_finder_agent i restaurant_finder_agent równolegle, łączy się w research_join (JoinNode) i przekazuje node_input (dict) do agenta itinerary_synthesizer_agent

Dołącz specjalistów ds. badań

Sprawdź, czy usługa adk web jest zatrzymana (Ctrl + C), a następnie otwórz travel_planner/specialists.py:

cloudshell edit ~/adk-graph-agents/travel_planner/specialists.py

Dołącz activity_finder_agent i restaurant_finder_agent na dole pliku travel_planner/specialists.py (żaden z nich nie potrzebuje output_key, ponieważ research_join przekazuje oba wyjścia bezpośrednio przez node_input):

# --- Specialist 3: The Activity Finder ---
activity_finder_agent = Agent(
    model=MODEL,
    name="activity_finder_agent",
    description="Finds 3 attractions matching the user's destination and interests.",
    instruction="""
        Using the travel preferences in the input, call google_search to find 3 specific attractions
        (including budget-friendly options).
        Return a JSON list with keys: "attraction_name", "estimated_cost_local", "estimated_cost_usd", "notes".
    """,
    tools=[google_search],
)

# --- Specialist 4: The Restaurant Finder ---
restaurant_finder_agent = Agent(
    model=MODEL,
    name="restaurant_finder_agent",
    description="Finds 3 restaurants matching the destination and budget.",
    instruction="""
        Using the travel preferences in the input, call google_search to find 3 highly-rated restaurants
        across price points.
        Return a JSON list with keys: "restaurant_name", "cuisine", "estimated_cost_local", "estimated_cost_usd".
    """,
    tools=[google_search],
)

Dodawanie rozgałęzienia i węzła JoinNode do workflows.py

Otwórz travel_planner/workflows.py:

cloudshell edit ~/adk-graph-agents/travel_planner/workflows.py

Zastąp całą zawartość pliku travel_planner/workflows.py wykresem fan-out i JoinNode:

from google.adk import Workflow
from google.adk.workflow import JoinNode
from .specialists import (
    activity_finder_agent,
    itinerary_synthesizer_agent,
    restaurant_finder_agent,
    user_profiler_agent,
)

research_join = JoinNode(name="research_join")

# 2. FAN-OUT / FAN-IN GRAPH: Profile -> (Activities || Restaurants) -> Join -> Synthesize
travel_workflow = Workflow(
    name="travel_workflow",
    description="Orchestrates the end-to-end travel planning process as a graph workflow.",
    edges=[
        (
            "START",
            user_profiler_agent,
            (activity_finder_agent, restaurant_finder_agent),
            research_join,
            itinerary_synthesizer_agent,
        ),
    ],
)

🔍 Sprawdź swoją pracę

Uruchom adk web, aby przetestować wyszukiwanie równoczesne i synchronizację JoinNode:

cd ~/adk-graph-agents
adk web --allow_origins "*"

Otwórz podgląd w przeglądarce na porcie 8000, kliknij + Nowa sesja i wyślij:

Help me plan a trip to Kyoto focused on historic temples and ramen

W widoku Zdarzenia powinna być widoczna sekwencja zdarzeń podobna do tej:

1. user_profiler_agent -> emits JSON preferences via node_input
2. activity_finder_agent & restaurant_finder_agent -> invoke google_search concurrently
3. research_join -> merges both researcher outputs into one dict node_input
4. itinerary_synthesizer_agent -> drafts final_itinerary from the merged dict + {user_preferences}

Zanim przejdziesz dalej, naciśnij Ctrl + C w terminalu, aby zatrzymać adk web.

8. Cykle sprawdzania tras

Wymuś ograniczenia budżetu i ograniczenia ujemne, dodając węzeł itinerary_critic_agent, węzeł itinerary_refiner_agent i 2 węzły deterministycznej funkcji Pythona (route_critique i finalize_itinerary), które zwracają obiekty ADK Event:

  • route: wybiera, który nazwany klucz gałęzi w krawędzi słownika ({"revise": ..., "approved": ...}) zostanie uruchomiony jako następny.
  • output: przekazuje wpisaną wartość lub ciąg znaków w Pythonie wzdłuż krawędzi jako node_input do następnego węzła bez renderowania wiadomości na czacie.
  • message i state: message emituje tekst widoczny dla użytkownika w transkrypcji czatu, a state zatwierdza aktualizacje klucz-wartość (stateDelta) w stanie sesji.

Schemat architektury przedstawiający travel_workflow z itinerary_critic_agent przekazującym route_critique, który kieruje „revise” do itinerary_refiner_agent (pętla z powrotem do itinerary_critic_agent) lub „approved” do finalize_itinerary

Dołączanie specjalistów od krytyki i ulepszania

Sprawdź, czy usługa adk web jest zatrzymana (Ctrl + C), a następnie otwórz travel_planner/specialists.py:

cloudshell edit ~/adk-graph-agents/travel_planner/specialists.py

Dołącz itinerary_critic_agent i itinerary_refiner_agent na dole travel_planner/specialists.py (rafiner nie potrzebuje narzędzia google_search ani exit_loop, ponieważ zamienia elementy zebrane już w {final_itinerary} i działa tylko wtedy, gdy route_critique wybierze "revise"):

# --- Specialist 5: The Budget & Constraint Critic ---
itinerary_critic_agent = Agent(
    model=MODEL,
    name="itinerary_critic_agent",
    description="Audits the itinerary draft against budget and negative constraints.",
    instruction="""
        Audit the itinerary in the input against User Preferences ({user_preferences}).
        Verify that the USD total stays within the user's budget and no excluded venue types appear.
        If all checks pass, respond EXACTLY with the single word "APPROVED".
        Otherwise, output 1-2 concise revision instructions explaining what to swap or adjust.
    """,
)

# --- Specialist 6: The Itinerary Refiner ---
itinerary_refiner_agent = Agent(
    model=MODEL,
    name="itinerary_refiner_agent",
    description="Revises the itinerary draft using the critic's feedback.",
    instruction="""
        Revise Current Itinerary ({final_itinerary}) using the auditor critique in the input
        so all USD budget and exclusion rules in {user_preferences} pass.
        Output ONLY the updated Markdown itinerary.
    """,
    output_key="final_itinerary",
)

Dodaj route_critique i cykl

Otwórz travel_planner/workflows.py:

cloudshell edit ~/adk-graph-agents/travel_planner/workflows.py

Zastąp całą zawartość pliku travel_planner/workflows.py pełnym przepływem pracy grafu:

from google.adk import Event, Workflow
from google.adk.workflow import JoinNode
from .specialists import (
    activity_finder_agent, itinerary_critic_agent, itinerary_refiner_agent,
    itinerary_synthesizer_agent, restaurant_finder_agent, user_profiler_agent,
)

MAX_REVISIONS = 2
research_join = JoinNode(name="research_join")


def route_critique(node_input: str, revision_count: int = 0):
    """Routes to 'approved' when constraints pass, or 'revise' up to MAX_REVISIONS."""
    approved = node_input.strip().rstrip(".").upper() == "APPROVED"
    if approved or revision_count >= MAX_REVISIONS:
        return Event(route="approved", output=approved)
    return Event(
        route="revise", output=node_input, state={"revision_count": revision_count + 1}
    )


def finalize_itinerary(node_input: bool, final_itinerary: str, revision_count: int = 0):
    """Emits the verified itinerary and resets revision_count for the next turn."""
    status = (
        f"Itinerary approved after {revision_count} revision(s)."
        if node_input
        else f"Max revisions ({revision_count}) reached; returning best-effort draft."
    )
    return Event(
        message=f"{status}\n\n{final_itinerary}",
        output=final_itinerary,
        state={"revision_count": 0},
    )


travel_workflow = Workflow(
    name="travel_workflow",
    description="Orchestrates the end-to-end travel planning process as a graph workflow.",
    edges=[
        (
            "START",
            user_profiler_agent,
            (activity_finder_agent, restaurant_finder_agent),
            research_join,
            itinerary_synthesizer_agent,
            itinerary_critic_agent,
            route_critique,
        ),
        (route_critique, {"revise": itinerary_refiner_agent, "approved": finalize_itinerary}),
        (itinerary_refiner_agent, itinerary_critic_agent),
    ],
)

🔍 Sprawdź swoją pracę

Sprawdź, czy travel_planner/workflows.py kompiluje się bez błędów i wyświetla krawędzie grafu skierowanego:

cd ~/adk-graph-agents && source .venv/bin/activate
python3 -c "from travel_planner.agent import root_agent; print('\n'.join(f'{e.from_node.name} -> {e.to_node.name} (route={e.route})' for e in root_agent.graph.edges))"

Dane wyjściowe powinny być podobne do tych:

__START__ -> user_profiler_agent (route=None)
user_profiler_agent -> activity_finder_agent (route=None)
user_profiler_agent -> restaurant_finder_agent (route=None)
activity_finder_agent -> research_join (route=None)
restaurant_finder_agent -> research_join (route=None)
research_join -> itinerary_synthesizer_agent (route=None)
itinerary_synthesizer_agent -> itinerary_critic_agent (route=None)
itinerary_critic_agent -> route_critique (route=None)
route_critique -> itinerary_refiner_agent (route=revise)
route_critique -> finalize_itinerary (route=approved)
itinerary_refiner_agent -> itinerary_critic_agent (route=None)

Następnie uruchom adk web, aby przetestować zarówno natychmiastowe zatwierdzanie, jak i zmiany wywołane przez budżet:

adk web --allow_origins "*"

Otwórz podgląd w przeglądarce na porcie 8000, kliknij + Nowa sesja i wyślij prompta z wieloma ograniczeniami dotyczącego Kioto:

Design a full-day itinerary for Kyoto with a total budget of $100. My main interests are historical landmarks, temples, and authentic local cuisine. Please do not include any modern art museums or galleries.

Aby zobaczyć, jak działa pętla route="revise", wyślij kolejnego prompta z ograniczonym budżetem 15 USD:

Now design a full-day Kyoto itinerary with paid temple admissions, lunch, and dinner on a strict total budget of $15 USD.

W widoku Zdarzenia powinna być widoczna sekwencja wykonywania podobna do tej:

1. user_profiler_agent -> writes JSON constraints to state["user_preferences"]
2. (activity_finder_agent, restaurant_finder_agent) -> run google_search concurrently -> research_join
3. itinerary_synthesizer_agent -> writes initial draft to state["final_itinerary"]
4. itinerary_critic_agent -> outputs "APPROVED" (or revision instructions when over budget)
5. route_critique -> emits route="approved" (skipping refiner) or route="revise" -> itinerary_refiner_agent -> itinerary_critic_agent
6. finalize_itinerary -> emits "Itinerary approved after N revision(s)." (or max-revisions notice) and resets revision_count=0

W terminalu naciśnij Ctrl + C, aby zatrzymać adk web.

9. Rozwiązywanie problemów

Menu agenta jest puste w: adk web

  • Przyczyna: polecenie adk web zostało uruchomione w katalogu travel_planner/ zamiast w katalogu nadrzędnym ~/adk-graph-agents lub sesja Cloud Shell została uruchomiona ponownie.
  • Rozwiązanie: naciśnij Ctrl + C, wróć do katalogu nadrzędnego i ponownie uruchom polecenie cd ~/adk-graph-agents && source .venv/bin/activate && adk web --allow_origins "*".

404 NOT_FOUND błąd modelu

  • Przyczyna: podczas adk create naciśnięto Enter, aby zaakceptować domyślny region us-central1, zamiast wpisać global.
  • Rozwiązanie: otwórz ~/adk-graph-agents/travel_planner/.env i upewnij się, że ustawiono GOOGLE_CLOUD_LOCATION=global.

ERROR: [Errno 98] address already in use na porcie 8000

  • Przyczyna: poprzedni serwer adk web nadal działa w Cloud Shell.
  • Rozwiązanie: naciśnij Ctrl + C w aktywnym terminalu lub zatrzymaj istniejący proces za pomocą pkill -f "adk web", zanim ponownie uruchomisz adk web.

10. Czyszczenie danych

Usuń lokalny obszar roboczy i opcjonalnie wyłącz interfejs API lub usuń dedykowany projekt warsztatowy.

Usuwanie lokalnych plików projektu

Usuń katalog ~/adk-graph-agents i środowisko wirtualne:

cd ~
rm -rf ~/adk-graph-agents

Wyłączanie interfejsu Agent Platform API (opcjonalnie)

Wyłącz Agent Platform API, jeśli nie potrzebujesz już tego interfejsu w tym projekcie:

gcloud services disable aiplatform.googleapis.com --quiet

🔍 Sprawdź swoją pracę

Potwierdź, że usunięto ~/adk-graph-agents:

test ! -d ~/adk-graph-agents && echo "Workspace ~/adk-graph-agents removed"

Dane wyjściowe powinny być podobne do tych:

Workspace ~/adk-graph-agents removed

11. Gratulacje

Udało Ci się utworzyć wieloagentowy system planowania podróży za pomocą pakietu ADK 2 Workflow, łącząc sekwencyjne łańcuchy, równoległe rozgałęzianie google_search z JoinNode oraz warunkowe cykle weryfikacji w jednym deklaratywnym grafie krawędziowym.

osiągnięcia,

  • Zadeklarowano potok wieloagentowy za pomocą ADK 2 Workflow(edges=[...])
  • Przekazywanie danych bezpośrednio między węzłami funkcji Agent, JoinNode i Python za pomocą parametrów node_input i parametrów powiązanych ze stanem przy zachowaniu minimalnego stanu sesji.
  • Rozgałęziono równoległe gałęzie google_search i zsynchronizowano je z JoinNode
  • Zastąpiono LoopAgent i eskalację pętli opartą na narzędziach deterministycznym routerem Event(route=...), który pomija niepotrzebne wywołania LLM w zatwierdzonych wersjach roboczych.

Dalsze kroki