1. Введение
На этом практическом занятии вы выйдете за рамки простых чат-ботов без сохранения состояния и создадите «умного консьержа кафе» — ИИ-агента на базе Gemini, который будет выступать в роли дружелюбного бариста. Он будет принимать заказы на кофе, отслеживаемые в состоянии сессии, запоминать долгосрочные диетические предпочтения в состоянии, заданном пользователем, и сохранять все данные в базе данных Cloud SQL PostgreSQL. В конце ваш агент запомнит, что у вас непереносимость лактозы, даже после перезапуска приложения и начала нового разговора.
Вот архитектура системы, которую мы будем строить.

Предварительные требования
- Учетная запись Google Cloud с пробной платной учетной записью.
- Базовые знания Python.
- Опыт работы с ADK, агентами искусственного интеллекта или Cloud SQL не требуется.
Что вы узнаете
- Создайте ИИ-агента, используя комплект разработки агентов Google (ADK) с пользовательскими инструментами.
- Определите инструменты, которые читают и записывают состояние сессии через
ToolContext - Различайте состояние, ограниченное сессией, и состояние, ограниченное пользователем (префикс
user::). - Создайте экземпляр Cloud SQL PostgreSQL и подключитесь к нему из Cloud Shell.
- Перейдите с локального хранилища (которое используется по умолчанию при применении
adk web) наDatabaseSessionServiceдля постоянного хранения данных с выделенной базой данных. - Убедитесь, что память агента сохраняется после перезапуска приложения и в разных сеансах общения.
Что вам понадобится
- Рабочий компьютер и надежное интернет-соединение.
- Для доступа к консоли Google Cloud потребуется браузер, например Chrome .
- Любознательный ум и стремление к знаниям.
2. Настройте свою среду.
Этот шаг подготавливает среду Cloud Shell и настраивает ваш проект Google Cloud.
Открытая облачная оболочка
Откройте Cloud Shell в браузере. Cloud Shell предоставляет предварительно настроенную среду со всеми необходимыми инструментами для выполнения этого практического задания. При появлении запроса нажмите «Авторизовать».
Ваш интерфейс должен выглядеть примерно так.

Это будет наш основной интерфейс: IDE сверху, терминал снизу.
Настройте рабочую директорию.
Создайте свою рабочую директорию. Весь код, написанный вами в этом практическом занятии, будет храниться здесь — отдельно от эталонного репозитория:
# Create your working directory
mkdir -p ~/build-agent-adk-cloudsql
# Change cloudshell workspace and working directory into previously created dir
cloudshell workspace ~/build-agent-adk-cloudsql && cd ~/build-agent-adk-cloudsql
Чтобы открыть терминал, перейдите в меню «Вид» -> «Терминал».

Настройте свой проект Google Cloud и начальные переменные среды.
Загрузите скрипт установки проекта в свою рабочую директорию:
curl -sL https://raw.githubusercontent.com/alphinside/cloud-trial-project-setup/main/setup_verify_trial_project.sh -o setup_verify_trial_project.sh
Запустите скрипт. Он проверит вашу учетную запись для пробной версии, создаст новый проект (или проверит существующий), сохранит идентификатор вашего проекта в файл .env в текущем каталоге и установит активный проект в терминале.
bash setup_verify_trial_project.sh && source .env
При запуске вам будет предложено ввести имя проекта (ID), для продолжения нажмите Enter

Если после некоторого ожидания вы увидите следующий вывод в консоли, значит, вы готовы перейти к следующему шагу. 
Выполняемый скрипт совершает следующие действия:
- Убедитесь, что у вас есть активный пробный аккаунт для оплаты.
- Проверьте наличие существующего проекта в
.env(если таковой имеется). - Создайте новый проект или используйте существующий.
- Свяжите пробный аккаунт с вашим проектом.
- Сохраните идентификатор проекта в файл .env.
- Установите этот проект в качестве активного проекта gcloud.
Убедитесь, что проект настроен правильно, проверив желтый текст рядом с вашим рабочим каталогом в командной строке терминала Cloud Shell. Там должен отображаться идентификатор вашего проекта.

Включите необходимые API.
Включите API Google Cloud, необходимые для выполнения этого практического задания:
gcloud services enable \
aiplatform.googleapis.com \
sqladmin.googleapis.com \
compute.googleapis.com
- API Vertex AI (
aiplatform.googleapis.com) — ваш агент использует модели Gemini через Vertex AI. - Cloud SQL Admin API (
sqladmin.googleapis.com) — позволяет создавать и управлять экземпляром PostgreSQL для постоянного хранения данных. - API Compute Engine (
compute.googleapis.com) — необходим для создания экземпляров Cloud SQL.
Настройка региона продуктов Gemini и Cloud
Прежде чем продолжить, давайте также настроим необходимую конфигурацию местоположения/региона для продукта, с которым мы взаимодействуем. Добавьте следующую конфигурацию в наш файл .env
# This is for our Gemini endpoint
echo "GOOGLE_CLOUD_LOCATION=global" >> .env
# This is for our other Cloud products
echo "REGION=us-central1" >> .env
source .env
Перейдём к следующему шагу.
3. Настройка Cloud SQL
На этом этапе создается экземпляр Cloud SQL PostgreSQL, и ваш агент переключается с хранения данных в оперативной памяти на хранение данных в базе данных. Создание экземпляра занимает несколько минут, поэтому сначала запустите его, а мы сможем продолжить обсуждение следующей темы, пока он не завершится.
Начать создание экземпляра
Добавьте пароль к базе данных в файл .env и перезагрузите его; в качестве пароля мы будем использовать cafe-agent-pwd-2025 .
echo "DB_PASSWORD=cafe-agent-pwd-2025" >> .env
source .env
Выполните эту команду для создания экземпляра Cloud SQL PostgreSQL. Процесс займет несколько минут — оставьте его работать и перейдите к следующему разделу .
gcloud sql instances create cafe-concierge-db \
--database-version=POSTGRES_17 \
--edition=ENTERPRISE \
--region=${REGION} \
--availability-type=ZONAL \
--project=${GOOGLE_CLOUD_PROJECT} \
--tier=db-f1-micro \
--root-password=${DB_PASSWORD} \
--quiet &
Несколько замечаний по поводу приведенной выше команды:
-
db-f1-micro— это самый маленький (и самый дешевый) уровень Cloud SQL, которого достаточно для выполнения этого практического задания. -
--root-passwordустанавливает пароль для пользователя postgres по умолчанию. - Добавление символа
&в команду запускает её в фоновом режиме, позволяя вам продолжать работу.
Процесс будет выполняться в фоновом режиме, однако вывод консоли будет периодически отображаться в текущем терминале. Давайте откроем новую вкладку терминала в Cloud Shell (нажмите значок «+»), чтобы лучше сосредоточиться.

Снова перейдите в свою рабочую директорию и активируйте проект, используя предыдущий скрипт настройки.
cd ~/build-agent-adk-cloudsql
bash setup_verify_trial_project.sh && source .env
Итак, перейдём к следующему разделу.
4. Создайте агента-консьержа для кафе.
На этом шаге создается структура проекта для вашего агента ADK и определяется базовый интерфейс Cafe Concierge с инструментом меню.
Инициализируйте проект Python.
В этом практическом занятии используется uv — быстрый менеджер пакетов Python, который управляет виртуальными средами и зависимостями в одном инструменте. Он предустановлен в Cloud Shell.
Инициализируйте проект Python и добавьте ADK в качестве зависимости:
uv init
uv add google-adk==1.25.0 asyncpg
uv init создает файл ` pyproject.toml и виртуальное окружение. Команда uv add` устанавливает зависимость и записывает ее в pyproject.toml .
Инициализация структуры проекта агента.
ADK ожидает определенную структуру папок: каталог, названный в честь вашего агента и содержащий файлы __init__.py , agent.py , а также .env внутри каталога агента.
В ADK есть встроенная команда, которая поможет вам быстро это сделать; выполните следующую команду.
uv run adk create cafe_concierge \
--model gemini-2.5-flash \
--project ${GOOGLE_CLOUD_PROJECT} \
--region ${GOOGLE_CLOUD_LOCATION}
Эта команда создаст структуру агента, в которой gemini-2.5-flash будет выступать в качестве «мозга». Теперь ваша директория должна выглядеть примерно так:
build-agent-adk-cloudsql/ ├── cafe_concierge/ │ ├── __init__.py │ ├── agent.py │ └── .env ├── pyproject.toml ├── .env ├── .venv/ └── ...
Напишите агенту
Откройте cafe_concierge/agent.py в редакторе Cloud Shell.
cloudshell edit cafe_concierge/agent.py
и перезапишите файл следующим кодом.
# cafe_concierge/agent.py
from google.adk.agents import LlmAgent
from google.adk.tools import ToolContext
CAFE_MENU = {
"espresso": {
"price": 3.50,
"description": "Rich and bold single shot",
"tags": ["vegan", "dairy-free", "gluten-free"],
},
"latte": {
"price": 5.00,
"description": "Espresso with steamed milk",
"tags": ["gluten-free"],
},
"oat milk latte": {
"price": 5.50,
"description": "Espresso with steamed oat milk",
"tags": ["vegan", "dairy-free", "gluten-free"],
},
"cappuccino": {
"price": 4.50,
"description": "Espresso with equal parts steamed milk and foam",
"tags": ["gluten-free"],
},
"cold brew": {
"price": 4.00,
"description": "Slow-steeped for 12 hours, served over ice",
"tags": ["vegan", "dairy-free", "gluten-free"],
},
"matcha latte": {
"price": 5.50,
"description": "Ceremonial grade matcha with steamed milk",
"tags": ["gluten-free"],
},
"croissant": {
"price": 3.00,
"description": "Buttery, flaky French pastry",
"tags": [],
},
"banana bread": {
"price": 3.50,
"description": "Homemade with walnuts",
"tags": ["vegan"],
},
}
def get_menu() -> dict:
"""Returns the full cafe menu with prices, descriptions, and dietary tags.
Use this tool when the customer asks what's available, wants to see
the menu, or asks about specific items.
"""
return CAFE_MENU
root_agent = LlmAgent(
name="cafe_concierge",
model="gemini-2.5-flash",
instruction="""You are a friendly and knowledgeable barista at "The Cloud Cafe".
Your job:
- Help customers browse the menu and answer questions about items.
- Take coffee and food orders.
- Remember and respect dietary preferences.
Be conversational, warm, and concise. If a customer mentions a dietary
restriction, acknowledge it and suggest suitable options from the menu.
""",
tools=[get_menu],
)
Это описание базового агента с одним инструментом: get_menu() . Агент может отвечать на вопросы о меню, но пока не может отслеживать заказы или запоминать предпочтения.
Убедитесь, что агент запущен.
Запустите пользовательский интерфейс разработчика ADK из вашей рабочей директории:
cd ~/build-agent-adk-cloudsql
uv run adk web
Откройте URL-адрес, отображаемый в терминале (обычно http://localhost:8000 ), используя функцию веб-предварительного просмотра Cloud Shell. Выберите cafe_concierge из выпадающего списка агентов в верхнем левом углу.
Введите следующий текст в строку чата и убедитесь, что оператор ответит, указав пункты меню и цены.
What's on the menu?

Перед продолжением остановите работу пользовательского интерфейса разработчика, нажав Ctrl+C.
5. Добавить управление заказами с сохранением состояния.
Агент может отображать меню, но не может принимать заказы или запоминать предпочтения. На этом этапе добавляются четыре инструмента, которые используют систему состояний ADK для отслеживания заказов в рамках диалога и сохранения диетических предпочтений между диалогами.
Поймите ход событий на сессии и состояние дел.
Каждый диалог в ADK хранится внутри объекта Session . Сессия отслеживает две разные вещи: события и состояние . Понимание этой разницы является ключом к созданию агентов, которые запоминают нужную информацию правильным образом.
События — это хронологический журнал всего, что происходит в разговоре. Каждое сообщение пользователя, каждый ответ агента, каждый вызов инструмента и его возвращаемое значение — всё это записывается как Event и добавляется в список событий сессии. События неизменяемы: однажды записанные, они никогда не меняются. Представьте себе события как полную стенограмму разговора.
Состояние — это своего рода блокнот с ключами и значениями, который агент читает и записывает во время разговора. В отличие от событий, состояние изменчиво — значения меняются по мере развития разговора. В состоянии агент хранит структурированные данные, необходимые ему для дальнейших действий: текущий заказ, предпочтения клиента, итоговая сумма. Представьте состояние как стикеры, которые агент хранит рядом с расшифровкой разговора.
Вот как они связаны:

Инструменты считывают и записывают состояние через ToolContext — объект, который ADK автоматически внедряет в любую функцию инструмента, объявляющую его в качестве параметра. Вы не создаёте его самостоятельно. Через tool_context.state инструмент может считывать и записывать временный файл состояния сессии. ADK проверяет сигнатуру функции: параметры типа ToolContext внедряются, все остальные параметры заполняются LLM на основе диалога.
Когда инструмент записывает данные в tool_context.state , ADK регистрирует это изменение как state_delta внутри события. Затем SessionService применяет эту дельту к текущему состоянию сессии. Это означает, что изменения состояния всегда можно отследить до события, которое их вызвало. Это также справедливо для других форм контекста, таких как callback_context
Разберитесь в префиксах штатов.
Ключи состояния используют префиксы для управления своей областью действия:
Префикс | Объем | Переживает ли перезапуск? (с базой данных) |
(никто) | Только текущая сессия | Да |
| Все сессии для этого пользователя | Да |
| Все сессии, все пользователи | Да |
| Только текущий вызов | Нет |
В этом практическом задании вы используете два из этих префиксов: ключи без префикса для данных, ограниченных сессией (текущий порядок — актуален только для этого разговора), и ключи user: для данных, ограниченных пользователем (пищевые предпочтения — актуальны для всех разговоров этого пользователя).
Добавьте инструменты для работы с состоянием.
Откройте cafe_concierge/agent.py в редакторе Cloud Shell.
cloudshell edit cafe_concierge/agent.py
Затем добавьте следующие четыре функции выше определения root_agent:
# cafe_concierge/agent.py (add below get_menu, above root_agent)
def place_order(tool_context: ToolContext, items: list[str]) -> dict:
"""Places an order for the specified menu items.
Use this tool when the customer confirms they want to order something.
Args:
tool_context: Provided automatically by ADK.
items: A list of menu item names the customer wants to order.
"""
valid_items = []
invalid_items = []
total = 0.0
for item in items:
item_lower = item.lower()
if item_lower in CAFE_MENU:
valid_items.append(item_lower)
total += CAFE_MENU[item_lower]["price"]
else:
invalid_items.append(item)
if not valid_items:
return {"error": f"None of these items are on our menu: {invalid_items}"}
order = {"items": valid_items, "total": round(total, 2)}
tool_context.state["current_order"] = order
result = {"order": order}
if invalid_items:
result["warning"] = f"These items are not on our menu: {invalid_items}"
return result
def get_order_summary(tool_context: ToolContext) -> dict:
"""Returns the current order summary for this session.
Use this tool when the customer asks about their current order,
wants to review what they ordered, or asks for the total.
Args:
tool_context: Provided automatically by ADK.
"""
order = tool_context.state.get("current_order")
if order:
return {"order": order}
return {"message": "No order has been placed yet in this session."}
def set_dietary_preference(tool_context: ToolContext, preference: str) -> dict:
"""Saves a dietary preference that persists across all conversations.
Use this tool when the customer mentions a dietary restriction or
preference (e.g., "I'm vegan", "I'm lactose intolerant",
"I have a nut allergy").
Args:
tool_context: Provided automatically by ADK.
preference: The dietary preference to save (e.g., "vegan",
"lactose intolerant", "nut allergy").
"""
existing = tool_context.state.get("user:dietary_preferences", [])
if not isinstance(existing, list):
existing = []
preference_lower = preference.lower().strip()
if preference_lower not in existing:
existing.append(preference_lower)
tool_context.state["user:dietary_preferences"] = existing
return {
"saved": preference_lower,
"all_preferences": existing,
}
def get_dietary_preferences(tool_context: ToolContext) -> dict:
"""Retrieves the customer's saved dietary preferences.
Use this tool when you need to check the customer's dietary
restrictions before making recommendations.
Args:
tool_context: Provided automatically by ADK.
"""
preferences = tool_context.state.get("user:dietary_preferences", [])
if preferences:
return {"preferences": preferences}
return {"message": "No dietary preferences saved yet."}
Следует обратить внимание на две вещи:
-
place_orderиget_order_summaryиспользуют ключи без префикса (current_order). Это состояние привязано к текущей сессии — новый диалог начинается с пустого заказа. -
set_dietary_preferenceиget_dietary_preferencesиспользуют префиксuser:(user:dietary_preferences). Это состояние используется во всех сессиях для одного и того же пользователя.
Обновите информацию об агенте, предоставив ему новые инструменты и инструкции.
Замените существующее определение root_agent в конце файла следующим:
# cafe_concierge/agent.py (replace the existing root_agent)
root_agent = LlmAgent(
name="cafe_concierge",
model="gemini-2.5-flash",
instruction="""You are a friendly and knowledgeable barista at "The Cloud Cafe".
Your job:
- Help customers browse the menu and answer questions about items.
- Take coffee and food orders.
- Remember and respect dietary preferences.
The customer's saved dietary preferences are: {user:dietary_preferences?}
IMPORTANT RULES:
- When a customer mentions a dietary restriction, ALWAYS save it using the
set_dietary_preference tool before doing anything else.
- Before recommending items, check the customer's dietary preferences. If they
have preferences saved, only recommend items compatible with those
restrictions. Check the menu item tags to determine compatibility.
- When placing an order, confirm the items and total with the customer.
Be conversational, warm, and concise.
""",
tools=[
get_menu,
place_order,
get_order_summary,
set_dietary_preference,
get_dietary_preferences,
],
)
Данная инструкция использует шаблон внедрения состояния {user:dietary_preferences?} для непосредственного внедрения сохраненных предпочтений этого клиента в командную строку.
Проверьте весь файл.
Теперь ваш cafe_concierge/agent.py должен содержать:
- Словарь
CAFE_MENU - Пять функциональных инструментов:
get_menu,place_order,get_order_summary,set_dietary_preference,get_dietary_preferences - Определение
root_agentсо всеми пятью инструментами.
6. Протестируйте агент с помощью пользовательского интерфейса разработчика ADK.
На этом этапе запускается агент и проверяются все функции, связанные с сохранением состояния: упорядочивание, отслеживание предпочтений и межсессионная память (в рамках одного процесса). Вы также изучите панели «События» и «Состояние», чтобы увидеть, как ADK отслеживает разговор внутри системы.
Запустите пользовательский интерфейс разработчика.
cd ~/build-agent-adk-cloudsql
uv run adk web
Откройте веб-версию на порту 8000 и выберите cafe_concierge из выпадающего списка.
Диалог 1: Оформите заказ и укажите предпочтения.
Попробуйте выполнить следующие действия в указанной последовательности:
What's on the menu?
I'm lactose intolerant
What would you recommend?
I'll have an oat milk latte and a banana bread
What's my order?
Проверить события сессии
Все события будут зафиксированы и отображены в веб-интерфейсе. В чате вы увидите не только ваши запросы и ответы, но и tool_call и tool_response

Вы должны увидеть список событий в хронологическом порядке. Каждое событие имеет автора (того, кто его создал) и тип (какое взаимодействие оно представляет):
Автор | Тип | Что это представляет собой |
| | Сообщение, которое вы набрали в чате. |
| | Текстовый ответ агента |
| | Агент решил вызвать инструмент (отображается название функции + аргументы). |
| | Возвращаемое значение при вызове инструмента |
Щёлкните по одному из событий tool_call — например, по вызову set_dietary_preference . Вы должны увидеть:
- Название функции :
set_dietary_preference - Аргументы :
{"preference": "lactose intolerant"}
Теперь щелкните по соответствующему событию tool_response расположенному непосредственно под ним. Вы должны увидеть возвращаемое значение:
- Ответ :
{"saved": "lactose intolerant", "all_preferences": ["lactose intolerant"]}

Найдите поле state_delta в событии tool_response. Оно точно показывает, какое состояние изменилось в результате вызова этого инструмента:
state_delta: {"user:dietary_preferences": ["lactose intolerant"]}
Каждое изменение состояния отслеживается по конкретному событию. Именно так ADK обеспечивает синхронизацию временной памяти с историей переписки.
Проверить состояние сессии
Перейдите на вкладку «Состояние» . В отличие от журнала событий (который отображает полную историю), вкладка «Состояние» показывает моментальный снимок того, что известно агенту в данный момент — текущее значение каждого ключа состояния.

Вы должны увидеть две записи:
-
current_order—{"items": ["oat milk latte", "banana bread"], "total": 9.0} -
user:dietary_preferences—["lactose intolerant"]
Обратите внимание на разницу в названиях ключей:
-
current_orderне имеет префикса — он ограничен областью действия сессии. Он существует только в рамках этого диалога и исчезает после завершения сессии. - У
user:dietary_preferencesесть префиксuser:— это свойство, доступное только этому пользователю. Оно используется во всех сессиях для данного пользователя.
Это различие незаметно в коде (в обоих случаях используется tool_context.state ), но оно определяет, насколько далеко распространяются данные. Вы увидите это в следующем тесте.
Диалог 2: Проверка состояния пользователя в разных сессиях
Нажмите кнопку «Новая сессия» в пользовательском интерфейсе разработчика, чтобы начать новый разговор. Это создаст новую сессию для того же пользователя.

Попробуйте эту подсказку:
What do you recommend for me?
Проверьте вкладку «Состояние» в новой сессии. Ключ user:dietary_preferences сохраняется, но current_order исчезает — это состояние было привязано к предыдущей сессии.

7. Соблюдайте ограничения по объему локального хранилища.
Агент запоминает предпочтения между сессиями — но только пока существует локальное хранилище. Этот шаг демонстрирует фундаментальное ограничение локального хранилища.
Запустите агент снова.
В конце предыдущего шага вы остановили пользовательский интерфейс разработчика. Теперь давайте удалим локальное хранилище и запустим его снова, чтобы имитировать бессерверную среду, которая не сохраняет состояние:
cd ~/build-agent-adk-cloudsql
rm -f cafe_concierge/.adk/session.db
uv run adk web
Теперь откройте веб-просмотр на порту 8000 и выберите cafe_concierge .
Тест на запоминание предпочтений
Тип:
Do you remember my dietary preferences?
Агент ничего не помнит. Предпочтения в еде, история заказов — всё стёрлось.

При удалении локального хранилища все данные были полностью стёрты, что обычно происходит при использовании бессерверной среды. Файл session.db хранит всё состояние в памяти процесса. Его удаление приводит к полному удалению всех данных.
Решение: укажите DatabaseSessionService , который в этом руководстве будет хранить все данные сессий в базе данных PostgreSQL в Cloud SQL. Код агента и инструменты остаются точно такими же — меняется только бэкэнд хранилища.
Перед продолжением остановите работу пользовательского интерфейса разработчика, нажав Ctrl+C .
8. Вернитесь к настройке базы данных.
На этом этапе создание экземпляра базы данных должно быть уже завершено. Давайте проверим это, выполнив следующую команду.
gcloud sql instances describe cafe-concierge-db --format="value(state)"
Вы должны увидеть следующий результат, отметьте его как завершенный.
RUNNABLE
Создайте базу данных
Создайте отдельную базу данных для данных сеансов агента:
gcloud sql databases create agent_db --instance=cafe-concierge-db
Запустите прокси-сервер аутентификации Cloud SQL.
Cloud SQL Auth Proxy обеспечивает безопасное, аутентифицированное соединение между Cloud Shell и вашим экземпляром Cloud SQL без необходимости добавления IP-адресов в белый список. Он уже предустановлен в Cloud Shell.
cloud-sql-proxy ${GOOGLE_CLOUD_PROJECT}:${REGION}:cafe-concierge-db --port 5432 &
Добавление символа & в команду заставляет прокси-сервер работать в фоновом режиме. Вы должны увидеть сообщение, подтверждающее готовность прокси-сервера, как показано ниже.
[your-project-id:your-region:cafe-concierge-db] Listening on 127.0.0.1:5432 The proxy has started successfully and is ready for new connections!
Проверьте соединение.
Проверьте, можете ли вы подключиться к базе данных через прокси-сервер:
psql "host=127.0.0.1 port=5432 dbname=agent_db user=postgres password=$DB_PASSWORD" -c "SELECT 'Connection ok' AS status;"
Вам следует увидеть:
status --------------------- Connection ok (1 row)
9. Проверка стойкой памяти в разных сессиях.
Этот шаг доказывает, что память вашего агента сохраняется после сброса, если мы убедимся, что локальная база данных cafe_concierge/.adk/session_db удалена и распространяется на все сеансы общения.
Запустите агента
Убедитесь, что Cloud SQL Auth Proxy по-прежнему запущен (проверьте с помощью заданий). Если он не запущен, перезапустите его:
if ss -tlnp | grep -q ':5432 '; then
echo "Cloud SQL Auth Proxy is already running."
else
cloud-sql-proxy ${GOOGLE_CLOUD_PROJECT}:${REGION}:cafe-concierge-db --port 5432 &
fi
А теперь давайте запустим пользовательский интерфейс разработчика ADK, указав базу данных в качестве службы сессий.
uv run adk web --session_service_uri postgresql+asyncpg://postgres:${DB_PASSWORD}@127.0.0.1:5432/agent_db
Откройте веб-просмотр на порту 8000 и выберите cafe_concierge.
Тест 1: Оформите заказ и установите предпочтения.
На первом занятии выполните следующие действия:
Show me the menu
I'm vegan
What can I eat?
I'll have a cold brew and banana bread
Тест 2: Пережить перезагрузку
Остановите пользовательский интерфейс разработчика, нажав Ctrl+C , и убедитесь, что локальный session.db удален.
rm -f cafe_concierge/.adk/session.db
Затем перезапустите сервер пользовательского интерфейса для разработчиков.
uv run adk web --session_service_uri postgresql+asyncpg://postgres:${DB_PASSWORD}@127.0.0.1:5432/agent_db
Откройте веб-просмотр на порту 8000, выберите cafe_concierge и начните новую сессию. Затем задайте вопрос.
What are my dietary preferences?
Агент отвечает вашими сохраненными настройками — vegan . Данные сохранились после перезапуска, поскольку теперь они хранятся в PostgreSQL, а не в локальном хранилище. То же самое произойдет, если мы создадим новую сессию от имени user: состояние будет сохраняться в каждой новой сессии для этого пользователя.

Проверьте базу данных напрямую.
Откройте новую вкладку терминала в Cloud Shell и выполните запрос к базе данных, чтобы просмотреть сохраненные данные:
psql "host=127.0.0.1 port=5432 dbname=agent_db user=postgres password=$DB_PASSWORD" -c "\dt"
Вы должны увидеть таблицы, созданные ADK автоматически для хранения сессий, событий и состояния, как в этом примере.
List of relations Schema | Name | Type | Owner --------+-----------------------+-------+---------- public | adk_internal_metadata | table | postgres public | app_states | table | postgres public | events | table | postgres public | sessions | table | postgres public | user_states | table | postgres (5 rows)
Краткий обзор поведения государства
Ключ состояния | Префикс | Объем | Используется на разных сессиях? |
| (никто) | Сессия | Нет |
| | Пользователь | Да |
10. Поздравления / Уборка
Поздравляем! Вы успешно создали постоянно работающего, сохраняющего состояние ИИ-агента с использованием ADK и Cloud SQL.
Что вы узнали
- Как создать агент ADK с пользовательскими инструментами для чтения и записи состояния сессии.
- Разница между состоянием, ограниченным сессией (без префикса), и состоянием, ограниченным пользователем (префикс
user::). - Почему локальная
session.dbadk по умолчанию подходит только для разработки — все данные теряются при удалении (и их легко удалить, резервное копирование не требуется), она не подходит для бессерверного развертывания, которое не сохраняет состояние. - Как создать экземпляр Cloud SQL PostgreSQL и подключиться к нему с помощью Cloud SQL Auth Proxy
- Как подключиться к DatabaseSessionService с PostgreSQL в CloudSQL с минимальными изменениями в коде — те же инструменты, тот же агент, другой бэкенд.
- Как состояние, заданное пользователем, сохраняется между отдельными сеансами общения
Уборка
Чтобы избежать списания средств с вашего аккаунта Google Cloud, очистите ресурсы, созданные в ходе этого практического занятия.
Вариант 1: Удалить проект (рекомендуется)
Самый простой способ навести порядок — удалить проект. Это удалит все ресурсы, связанные с проектом.
gcloud projects delete ${GOOGLE_CLOUD_PROJECT}
Вариант 2: Удаление отдельных ресурсов
Если вы хотите сохранить проект, но удалить только ресурсы, созданные в этом практическом задании:
gcloud sql instances delete cafe-concierge-db --quiet