Создание ИИ-агентов с помощью ADK: управление рабочими процессами графа

1. Обзор

Чтобы выполнить запрос на поездку с несколькими ограничениями (например, спланировать однодневную поездку в Киото за 100 долларов США с посещением исторических храмов, местной раменной и без музеев современного искусства), необходимо извлечь структурированные предпочтения, одновременно изучить места, составить подробный черновик и проверить общую стоимость, прежде чем отвечать. В ADK 1.x для управления этими этапами требовалось вкладывать оболочки SequentialAgent, ParallelAgent и LoopAgent, сохранять все промежуточные выходные данные в состоянии сеанса и вызывать дополнительный инструмент LLM только для выхода из цикла уточнения.

В этой практической работе вы создадите многоагентный планировщик путешествий на платформе агентов, используя рабочие процессы графов (Workflow) Agent Development Kit (ADK) 2. На трех последовательных этапах вы подключите специализированные узлы Agent (на базе gemini-3.8-flash) в последовательную цепочку ребер (node_input), развернете параллельные поисковые агенты google_search, синхронизированные с JoinNode, и примените правила бюджета и исключения с помощью детерминированного маршрутизатора Python (Event(route=...)), который пропускает уточнение, когда первоначальный черновик проходит проверку.

Требования

Что вы узнаете

  • Как объявить конвейер с несколькими агентами в ADK 2, используя кортежи ребер Workflow
  • Как ADK 2 передает выходные данные напрямую по ребрам в виде node_input и связывает ключи состояния сеанса в качестве параметров функции.
  • Как запустить параллельные ветви исследования и объединить их результаты с помощью JoinNode
  • Как детерминированно маршрутизировать ограниченные циклы "создатель-проверяющий" на Python с помощью Event(route=...)
  • Как проверять скомпилированные графы рабочих процессов, временные шкалы параллельного выполнения и дельты состояний в веб-интерфейсе ADK

Что вам потребуется

  • Аккаунт Google (если вы хотите получить бонусы за участие в семинаре, используйте личный аккаунт @gmail.com).
  • ссылка на бонус от преподавателя (и камера телефона, чтобы отсканировать QR-код) или активный платежный аккаунт Cloud.
  • Google Chrome (рекомендуется использовать окно инкогнито, чтобы не путать личный и рабочий аккаунты).

2. Как получить бонусы Google Cloud (необязательно)

Если вы посещаете семинар, на котором предоставляется ссылка на бонус, воспользуйтесь бонусом Google Developer Program, чтобы создать платежный аккаунт без кредитной карты.

Получите бонус на рекламу

  1. Откройте ссылку на бонусы в окне инкогнито и войдите в аккаунт @gmail.com.
  2. На странице Claim Your Benefit (Получить бонус) нажмите Claim Credit (Получить бонус).

Карточка "Получите бонус" в Google Builders Hub с кнопкой "Получить бонус"

  1. Если вам будет предложено создать профиль Google Developer Program, заполните форму.
  2. Когда появится сообщение Отсканируйте, чтобы подтвердить, что вы человек, отсканируйте QR-код с помощью телефона и пройдите проверку. Если QR-код не появился, обновите страницу и снова нажмите Получить бонус.

Диалоговое окно с QR-кодом для подтверждения того, что вы человек, в Google Developer Program

  1. Дождитесь сообщения Бонус за участие в мероприятии успешно активирован и нажмите Открыть платежный аккаунт.

Google Builders Hub с сообщением об успешном получении бонуса и кнопкой "Открыть платежный аккаунт"

🔍 Проверьте свою работу

Настройка платежного аккаунта занимает до 3 минут.

  1. Откройте Google Cloud Billing (или скопируйте URL в окно инкогнито) и выберите Your billing accounts (Ваши платежные аккаунты).
https://console.cloud.google.com/billing
  1. Обновляйте страницу, пока у аккаунта GDP Credit не появится статус Активно.

Страница управления платежным аккаунтом Google Cloud, на которой показан активный платежный аккаунт GDP Credit

3. Создайте проект Google Cloud (необязательно)

Создайте отдельный проект Google Cloud, чтобы все ресурсы из этой практической работы были изолированы и их можно было легко удалить.

  1. Откройте страницу Создание проекта или скопируйте URL в окно инкогнито.
https://console.cloud.google.com/projectcreate
  1. Укажите сведения о проекте:
    • Название проекта, например google-cloud-workshop.
    • Платежный аккаунт (если показан): выберите GDP Credit (или ваш активный платежный аккаунт).
    • Parent resource (Родительский ресурс): No organization (Без организации) (или выберите свою организацию, если требуется).
  2. Запишите сгенерированный идентификатор проекта, который указан под названием проекта, и нажмите Создать.

Экран создания проекта Google Cloud с полями "Название проекта", "Идентификатор проекта", "Родительский ресурс" и кнопкой "Создать"

🔍 Проверьте свою работу

Нажмите Уведомления на панели инструментов в правом верхнем углу, а затем – Выбрать проект.

Меню уведомлений в консоли Google Cloud, в котором показаны ссылки "Создать проект" и "Выбрать проект"

4. Как настроить среду

Настройте Google Cloud Shell, включите Agent Platform API и создайте изолированную виртуальную среду Python 3.12 с помощью uv.

Как настроить Cloud Shell

  1. Нажмите Activate Cloud Shell (Активировать Cloud Shell) (>_) на панели инструментов консоли Google Cloud или откройте Google Cloud Shell напрямую (или скопируйте URL в окно инкогнито):
https://shell.cloud.google.com/?show=ide,terminal

Кнопка "Активировать Cloud Shell" на панели инструментов консоли Google Cloud

  1. При необходимости нажмите Авторизовать.

Диалоговое окно авторизации Cloud Shell

  1. Выберите активный идентификатор проекта Google Cloud в нумерованном меню терминала.
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

Вы должны увидеть примерно такой результат (для проектов без тегов также выводится информационная строка [environment: untagged]):

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

Убедитесь, что выбранный идентификатор проекта выделен желтым цветом в командной строке Cloud Shell:

Терминал Cloud Shell с выделенным желтым цветом идентификатором проекта

Включите необходимые API

Включите Agent Platform API, чтобы агенты ADK могли вызывать модели Gemini.

gcloud services enable aiplatform.googleapis.com

Вы должны увидеть примерно следующее:

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

Как создать виртуальную среду Python и установить ADK

Создайте рабочую область ~/adk-graph-agents и активируйте виртуальную среду Python 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

Убедитесь, что в начале командной строки терминала появился префикс (.venv):

Терминал Cloud Shell с префиксом .venv в активной виртуальной среде Python

Установите ADK 2 в активной виртуальной среде:

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

🔍 Проверьте свою работу

Проверьте установленную версию CLI adk:

adk --version

Вы должны увидеть примерно следующее:

adk, version 2.14.0

5. Создайте каркас планировщика путешествий

Сгенерируйте стартовый пакет travel_planner с помощью adk create и протестируйте базовый вариант с одним агентом по умолчанию в adk web.

Создайте стартовый пакет

Запустить скрипт "adk create" из навыка "~/adk-graph-agents":

cd ~/adk-graph-agents
adk create travel_planner

При появлении запроса выберите следующие параметры (если при первом запуске появится запрос Enable telemetry? [Y/n]:, нажмите Ввод или введите n и нажмите Ввод):

  1. Выберите модель для корневого агента. Введите 2, чтобы выбрать gemini-3.8-flash (вариант 1 выбирает gemini-3.5-flash), и нажмите Ввод.
  2. Выберите серверную часть. Введите 2, чтобы выбрать серверную часть Google Cloud Agent Platform (вариант 2 в меню CLI ниже), и нажмите Ввод.
  3. Введите идентификатор облачного проекта Google Cloud: Нажмите Ввод, чтобы принять обнаруженный идентификатор проекта в скобках.
  4. Enter Google Cloud region (Укажите регион Google Cloud). Введите global (чтобы использовать глобальную конечную точку Gemini) и нажмите Ввод.

adk create записывает GOOGLE_GENAI_USE_ENTERPRISE=1, идентификатор проекта и GOOGLE_CLOUD_LOCATION=global в travel_planner/.env, поэтому ваши учетные данные остаются неизменными, когда вы позже заменяете agent.py.

Вы должны увидеть примерно следующее:

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/)

Откройте ~/adk-graph-agents в редакторе кода Cloud Shell:

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

Повторно активируйте виртуальную среду в перезагруженном терминале:

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

Как протестировать начального агента

Запустите веб-интерфейс ADK, чтобы посмотреть, как один некоординируемый агент-инициатор отвечает на запрос о путешествии:

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

Нажмите на значок Web Preview (Предварительный просмотр) на панели инструментов Cloud Shell в правом верхнем углу, выберите Change Port (Изменить порт), введите 8000 и нажмите Change and Preview (Изменить и посмотреть).

Диалоговое окно настройки порта для предварительного просмотра в Cloud Shell. Выбран порт 8000.

🔍 Проверьте свою работу

В раскрывающемся списке в левом верхнем углу adk web выберите travel_planner и отправьте "Help me plan a trip to Japan":

Веб-интерфейс ADK Development с простым чатом с одним агентом

Вы должны увидеть в чате ответ, сгенерированный за один проход (с одним вызовом root_agent в Событиях):

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

Нажмите Ctrl + C в терминале Cloud Shell, чтобы остановить adk web, прежде чем редактировать файлы на следующем шаге.

6. Создание рабочего процесса с линейным графиком

Создайте свой первый ADK 2 Workflow: последовательную цепочку, соединяющую зарезервированную точку входа "START" с user_profiler_agent и itinerary_synthesizer_agent (любой узел без исходящих ребер действует как конечный узел). Каждое ребро (a, b) автоматически запускает узлы Agent (псевдоним для LlmAgent) в изолированном режиме single_turn и передает выходные данные узла a как node_input (входящее сообщение пользователя) узлу b, а {user_preferences} в строке инструкции интерполирует соответствующий ключ состояния сеанса, сохраненный через output_key.

Схема архитектуры, на которой показано, как travel_workflow (Workflow) соединяет START с user_profiler_agent и itinerary_synthesizer_agent

Создайте первых специалистов

Убедитесь, что adk web остановлен (Ctrl + C), а затем откройте новый файл с названием travel_planner/specialists.py:

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

Вставьте следующий код в travel_planner/specialists.py, чтобы определить user_profiler_agent и itinerary_synthesizer_agent (предварительно импортировав google_search для следующего шага):

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",
)

Как задать последовательную цепочку ребер

Откройте новый файл с названием travel_planner/workflows.py:

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

Вставьте следующее определение Workflow (где кортеж из трех или более узлов компилируется в последовательную цепочку из "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),
    ],
)

Предоставьте доступ к рабочему процессу как root_agent

Откройте travel_planner/agent.py:

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

Замените все содержимое файла travel_planner/agent.py следующими двумя строками:

from .workflows import travel_workflow

root_agent = travel_workflow

🔍 Проверьте свою работу

Запустить adk web из ~/adk-graph-agents:

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

Откройте веб-просмотр на порте 8000, нажмите + New Session (+ Новый сеанс) и отправьте:

Help me plan a trip to Kyoto

В представлении События вы увидите последовательность событий, похожую на следующую:

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

Веб-интерфейс ADK 2.14.0, в котором показано выполнение графа от START до user_profiler_agent и itinerary_synthesizer_agent, а также сгенерированный маршрут по Киото

Нажмите на любое событие в разделе Events (События), а затем – на значок Graph (График) (account_tree на панели инструментов сведений о событии), чтобы проверить скомпилированную топологию START -> user_profiler_agent -> itinerary_synthesizer_agent. Затем откройте вкладку State (Состояние) и убедитесь, что поля user_preferences и final_itinerary заполнены.

Прежде чем продолжить, нажмите Ctrl + C в терминале, чтобы остановить adk web.

7. Разветвление и объединение с помощью JoinNode

Обоснуйте маршрут с помощью актуальных веб-данных, добавив двух параллельных специалистов google_search (activity_finder_agent и restaurant_finder_agent) и синхронизировав их с JoinNode.

  • Кортеж разветвления. Если поместить (activity_finder_agent, restaurant_finder_agent) в кортеж ребра, выходные данные user_profiler_agent будут переданы в виде node_input обоим исследователям одновременно.
  • Барьер объединения (JoinNode). Если разместить JoinNode(name="research_join") сразу после кортежа, то система будет ждать, пока каждая входящая ветвь не завершится и не передаст один объединенный dict ({"activity_finder_agent": ..., "restaurant_finder_agent": ...}) как node_input в itinerary_synthesizer_agent, вместо того чтобы запускать синтез дважды.

Схема архитектуры, на которой показано, как travel_workflow разветвляется от user_profiler_agent через node_input к activity_finder_agent и restaurant_finder_agent параллельно, объединяется в research_join (JoinNode) и передает node_input (dict) в itinerary_synthesizer_agent.

Добавить специалистов по исследованиям

Убедитесь, что adk web остановлен (Ctrl + C), а затем откройте travel_planner/specialists.py:

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

Добавьте activity_finder_agent и restaurant_finder_agent в нижнюю часть travel_planner/specialists.py (ни для одного из них не требуется output_key, поскольку research_join пересылает оба выходных значения напрямую через 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],
)

Добавьте fan-out и JoinNode в workflows.py

Откройте travel_planner/workflows.py:

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

Замените все содержимое файла travel_planner/workflows.py на граф с разветвлением и 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,
        ),
    ],
)

🔍 Проверьте свою работу

Запустите adk web, чтобы протестировать одновременный поиск и синхронизацию JoinNode:

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

Откройте веб-просмотр на порте 8000, нажмите + New Session (+ Новый сеанс) и отправьте:

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

В представлении События вы должны увидеть последовательность событий, похожую на следующую:

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}

Прежде чем продолжить, нажмите Ctrl + C в терминале, чтобы остановить adk web.

8. Циклы пересмотра маршрутов

Примените бюджетные и отрицательные ограничения, добавив itinerary_critic_agent, itinerary_refiner_agent и два детерминированных узла функции Python (route_critique и finalize_itinerary), которые возвращают объекты ADK Event:

  • route выбирает, какой именованный ключ ветви в ребре dict ({"revise": ..., "approved": ...}) будет запущен следующим.
  • output. Передает типизированное значение Python или строку по ребру в качестве node_input следующему узлу без отображения сообщения в чате.
  • message и state. message отправляет видимый пользователю текст в расшифровку чата, а state фиксирует обновления пар "ключ-значение" (stateDelta) в состоянии сеанса.

Схема архитектуры, показывающая travel_workflow с itinerary_critic_agent, который передает route_critique, который направляет revise в itinerary_refiner_agent (возвращаясь к itinerary_critic_agent) или approved в finalize_itinerary.

Добавьте специалистов по критике и доработке

Убедитесь, что adk web остановлен (Ctrl + C), а затем откройте travel_planner/specialists.py:

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

Добавьте itinerary_critic_agent и itinerary_refiner_agent в нижнюю часть travel_planner/specialists.py (для уточнения не требуется google_search или exit_loop, поскольку оно меняет местами элементы, уже собранные в {final_itinerary}, и запускается только тогда, когда route_critique выбирает "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",
)

Добавьте route_critique и цикл

Откройте travel_planner/workflows.py:

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

Замените все содержимое файла travel_planner/workflows.py на полный рабочий процесс графа:

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),
    ],
)

🔍 Проверьте свою работу

Убедитесь, что travel_planner/workflows.py компилируется без ошибок и выводит ребра ориентированного графа:

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))"

Вы должны увидеть примерно следующее:

__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)

Затем запустите adk web, чтобы протестировать немедленное одобрение и изменения, вызванные бюджетом:

adk web --allow_origins "*"

Откройте предварительный просмотр веб-страницы на порте 8000, нажмите + New Session (+ Новый сеанс) и отправьте запрос с несколькими ограничениями:

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.

Чтобы увидеть, как работает цикл route="revise", отправьте следующий запрос с ограниченным бюджетом в 15 долларов США:

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

В представлении События вы увидите последовательность выполнения, похожую на следующую:

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

Чтобы остановить adk web, нажмите Ctrl + C в терминале.

9. Устранение неполадок

Раскрывающийся список агентов пуст в adk web

  • Причина. Команда adk web была запущена в каталоге travel_planner/, а не в родительском каталоге ~/adk-graph-agents, или сеанс Cloud Shell был перезапущен.
  • Решение. Нажмите Ctrl + C, вернитесь в родительский каталог и перезапустите команду с помощью cd ~/adk-graph-agents && source .venv/bin/activate && adk web --allow_origins "*".

Ошибка модели 404 NOT_FOUND

  • Причина. Вы нажали клавишу Ввод, чтобы принять регион us-central1 по умолчанию во время adk create, вместо того чтобы ввести global.
  • Решение. Откройте ~/adk-graph-agents/travel_planner/.env и убедитесь, что задано значение GOOGLE_CLOUD_LOCATION=global.

ERROR: [Errno 98] address already in use на порту 8000

  • Причина. Предыдущий сервер adk web все ещё работает в Cloud Shell.
  • Решение. Нажмите Ctrl + C в активном терминале или остановите существующий процесс с помощью pkill -f "adk web", прежде чем снова запускать adk web.

10. Организовать

Удалите локальную рабочую область и при необходимости отключите API или удалите специальный проект.

Удалите файлы проекта в локальном хранилище браузера

Удалите каталог ~/adk-graph-agents и виртуальную среду:

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

Как отключить Agent Platform API (необязательно)

Отключите Agent Platform API, если он больше не нужен в этом проекте:

gcloud services disable aiplatform.googleapis.com --quiet

🔍 Проверьте свою работу

Подтвердите, что ~/adk-graph-agents удален:

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

Вы должны увидеть примерно следующее:

Workspace ~/adk-graph-agents removed

11. Поздравляю!

Вы создали мультиагентный планировщик путешествий с помощью ADK 2 Workflow, объединив последовательные цепочки, параллельное разветвление google_search с помощью JoinNode и условные циклы проверки в одном декларативном графе.

Чего вы достигли

  • Объявили мультиагентный конвейер с помощью ADK 2 Workflow(edges=[...])
  • Передавайте данные напрямую между узлами функций Agent, JoinNode и Python с помощью node_input и параметров, связанных с состоянием, сохраняя при этом минимальное состояние сеанса.
  • Создано google_search параллельных ветвей, которые синхронизированы с помощью JoinNode.
  • Заменили LoopAgent и эскалацию цикла на основе инструментов детерминированным маршрутизатором Event(route=...), который пропускает ненужные вызовы LLM для одобренных черновиков.

Дальнейшие действия