Создайте приложение Agentic AI, с которым ваши клиенты смогут взаимодействовать через Telegram.

1. Введение

Создавайте бесшовные интерактивные интерфейсы Agentic AI, с которыми ваши клиенты смогут взаимодействовать непосредственно из уже используемого ими приложения для обмена сообщениями. Узнайте, как разрабатывать и развертывать интеллектуальные приложения, которые бесперебойно работают в веб-интерфейсах и современных каналах обмена сообщениями.

Что вы построите

Интеграция полноценного приложения «Ресторанный консьерж» на базе ADK и Gemini, помогающего посетителям просматривать меню ресторана и бронировать столики, с чатом Telegram. Вы можете взаимодействовать с ботом Telegram и запрашивать описания на естественном языке, например: «Я хочу что-нибудь острое и вегетарианское». Затем бот подключается к агенту ADK, который считывает и записывает данные в базу данных Cloud SQL PostgreSQL полностью через MCP Toolbox for Databases, который обрабатывает весь доступ к базе данных, включая автоматическую генерацию векторных представлений для векторного поиска. При этом пользователь сможет видеть, что бот подтверждает сообщение и набирает ... typing ожидая ответа от агента ADK.

c1d28343ed68358a.png

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

  • Разверните работающее приложение «Ресторанный консьерж» на основе ADK, работающее на платформе Gemini.
  • Настройка чат-бота в Telegram с помощью BotFather
  • Напишите приложение на Python для прослушивания веб-хуков бота.
  • Отправьте действие в чате ... typing в Telegram при получении сообщения от пользователя, и периодически отправляйте уведомление о ... typing ожидая реального ответа.
  • Облачная платформа Call Restaurant Concierge обрабатывает запросы пользователей.
  • Обработать возврат от агента ADK, отправить сообщение в Telegram и закрыть буфер.
  • Разверните приложение Python в облачной среде.
  • Взаимодействуйте со своим Telegram-ботом.

Предварительные требования

2. Настройка среды — продолжение предыдущего практического занятия.

Представленные в этом практическом занятии материалы являются продолжением предыдущего практического занятия: «Agentic RAG с ADK, MCP Toolbox и Cloud SQL» или «Agents at Scale: Multi-Agent Architecture with A2A Protocol on Agent Runtime and ADK Integration» . Вы можете продолжить работу, начатую в предыдущем практическом занятии.

Мы можем начать сборку в предыдущей рабочей директории Codelab (рабочая директория должна называться build-agent-adk-toolbox-cloudsql или adk-a2a-agent-runtime-starter ). Чтобы избежать путаницы, переименуем директорию, присвоив ей то же имя, которое мы используем при начале работы с нуля.

Если вы продолжаете работу с лабораторной программы Agentic RAG с использованием ADK, MCP Toolbox и Cloud SQL :

mv ~/build-agent-adk-toolbox-cloudsql ~/build-agent-adk-telegram

В противном случае, если вы продолжаете изучение темы «Масштабируемые агенты: многоагентная архитектура с протоколом A2A в среде выполнения агентов и интеграция с ADK» из лабораторной работы,

mv ~/adk-a2a-agent-runtime-starter ~/build-agent-adk-telegram

Затем смените на неё наш рабочий каталог.

cloudshell workspace ~/build-agent-adk-telegram && cd ~/build-agent-adk-telegram
source .env

После этого убедитесь, что ваш restaurant-agent уже развернут и имеет общедоступный URL-адрес для доступа.

AGENT_URL=$(gcloud run services describe restaurant-agent \
    --region="$REGION" \
    --format='value(status.url)')

echo "      ✓ Agent service deployed"
echo "      Agent URL: $AGENT_URL"
echo ""

Если вы можете получить доступ к URL-адресу, то можете переходить к следующему разделу: Create Telegram Bot

3. Настройка среды — Новый старт с использованием стартового репозитория.

На этом этапе подготавливается среда Cloud Shell, настраивается проект Google Cloud и клонируется стартовый репозиторий.

Открытая облачная оболочка

Откройте Cloud Shell в браузере. Cloud Shell предоставляет предварительно настроенную среду со всеми необходимыми инструментами для выполнения этого практического задания. При появлении запроса нажмите «Авторизовать».

Затем нажмите « Вид » -> « Терминал », чтобы открыть терминал. Ваш интерфейс должен выглядеть примерно так.

86307fac5da2f077.png

Это будет наш основной интерфейс: IDE сверху, терминал снизу.

Настройте рабочую директорию.

Клонируйте стартовый репозиторий, весь код, который вы напишете в этом практическом занятии, будет находиться здесь:

rm -rf ~/build-agent-adk-telegram
git clone https://github.com/alphinside/adk-a2a-agent-runtime-starter.git build-agent-adk-telegram
cloudshell workspace ~/build-agent-adk-telegram && cd ~/build-agent-adk-telegram

Создайте файл .env на основе предоставленного шаблона:

cp .env.example .env

Чтобы упростить настройку проекта в терминале, загрузите этот скрипт настройки проекта в свою рабочую директорию:

curl -sL https://raw.githubusercontent.com/alphinside/cloud-trial-project-setup/main/setup_verify_trial_project.sh -o setup_verify_trial_project.sh

Запустите скрипт. Он проверит вашу учетную запись для оплаты пробной версии, создаст новый проект (или проверит существующий), сохранит идентификатор вашего проекта в файл .env в текущем каталоге и установит активный проект в gcloud .

bash setup_verify_trial_project.sh && source .env

Сценарий будет:

  1. Убедитесь, что у вас есть активный пробный аккаунт для оплаты.
  2. Проверьте наличие существующего проекта в .env (если таковой имеется).
  3. Создайте новый проект или используйте существующий.
  4. Свяжите пробный аккаунт с вашим проектом.
  5. Сохраните идентификатор проекта в файл .env
  6. Установите этот проект в качестве активного проекта gcloud

Убедитесь, что проект настроен правильно, проверив желтый текст рядом с вашим рабочим каталогом в командной строке терминала Cloud Shell. Там должен отображаться идентификатор вашего проекта.

5c515e235ee1179f.png

Начальная настройка инфраструктуры

Сначала нам нужно установить зависимости Python с помощью uv — это быстрый менеджер пакетов и проектов Python, написанный на Rust (документация uv). В этом практическом задании он используется для повышения скорости и упрощения поддержки проекта Python.

uv sync

Затем запустите полный скрипт настройки, который создаст экземпляр Cloud SQL, заполнит данные и развернет службу Toolbox, которая будет выступать в качестве начального состояния нашего агента для ресторанов.

bash scripts/full_setup.sh > logs/full_setup.log 2>&1 &

Это позволит:

  • Создайте экземпляр Cloud SQL и заполните базу данных данными (этап 1).
  • Сгенерируйте конфигурацию среды агента и запустите локальную службу Toolbox (этап 2).
  • Развертывание сервисов Toolbox и Agent в Cloud Run (этап 3)

После завершения развертывания вы сможете получить доступ к пользовательскому интерфейсу разработчика ADK по URL-адресу Cloud Run.

source .env
AGENT_URL=$(gcloud run services describe restaurant-agent \
    --region="$REGION" \
    --format='value(status.url)')

echo "      ✓ Agent service deployed"
echo "      Agent URL: $AGENT_URL"
echo ""

Откройте пользовательский интерфейс разработчика ADK, выберите restaurant_agent и протестируйте с помощью запросов, подобных приведенному ниже примеру:

What Italian dishes do you have?

Или,

I want something spicy and creamy

Теперь следующий шаг — как нам перейти от использования только веб-интерфейса для разработки к использованию мессенджера Telegram.

4. Создайте Telegram-бота.

Telegram — это известная бесплатная платформа для обмена сообщениями, широко используемая для взаимодействия в сообществах. Одна из причин — это множество способов интеграции, благодаря которым люди могут легко создавать собственных ботов с разнообразными функциями.

В нашем случае мы впервые будем использовать BotFather для создания собственного бота. Имейте в виду, что хотя мы используем Telegram для этого занятия, тот же метод можно использовать для WhatsApp или других мессенджеров по вашему выбору.

Используйте BotFather для создания собственного бота.

Откройте веб-браузер и перейдите по ссылке https://telegram.me/BotFather , чтобы начать создавать своего собственного Telegram-бота.

1b817e758c699a79.png

Начните взаимодействовать с The BotFather

ad3daa08e73502db.png

Отправьте команду /start

Чтобы начать работу с BotFather и создать своего первого бота, вам нужно отправить BotFather сообщение /start , после чего он начнет передавать вам все команды для дальнейшего взаимодействия с ним.

/start

Инициируйте создание бота с помощью команды /newbot

Давайте создадим нашего нового бота, отправив команду /newbot в BotFather. Он попросит вас назвать бота, а затем указать username бота, которое всегда должно заканчиваться на bot . Например, TetrisBot или tetris_bot . Это имя должно быть уникальным.

1f6a74f494d48986.png

После успешного создания бота вы получите следующее сообщение от BotFather.

Done! Congratulations on your new bot. You will find it at t.me/AdkTelegramTest_bot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands. By the way, when you've finished creating your cool bot, ping our Bot Support if you want a better username for it. Just make sure the bot is fully operational before you do this.

Use this token to access the HTTP API:
<YOUR_TELEGRAM_API_KEY>
Keep your token secure and store it safely, it can be used by anyone to control your bot.

For a description of the Bot API, see this page: https://core.telegram.org/bots/api

Обратите внимание на значение YOUR_TELEGRAM_API_KEY мы будем использовать его в следующем разделе.

5. Разработайте приложение для веб-перехватчиков Telegram.

Давайте подготовим рабочую директорию для начала разработки вашего приложения Telegram WebHook.

mkdir ~/build-agent-adk-telegram/telegram-integration
cd ~/build-agent-adk-telegram

Добавьте необходимые зависимости.

Создайте скрипт requirements.txt со следующим содержимым, чтобы указать необходимые зависимости для скрипта прослушивателя веб-перехватчика Telegram.

cloudshell edit ./telegram-integration/requirements.txt

Затем добавьте следующие зависимости.

python-telegram-bot[webhooks]
httpx

Создайте скрипт для обработчика веб-хуков Telegram.

После установки необходимых зависимостей мы можем создать скрипт main.py на Python для интеграционного приложения.

cloudshell edit ~/build-agent-adk-telegram/telegram-integration/main.py

Затем скопируйте в него следующий код.

# ./telegram-integration/main.py

import asyncio
import os
import sys
from telegram import Update
from telegram.ext import Application, CommandHandler, MessageHandler, filters, CallbackContext
from telegram.constants import ChatAction
import httpx

# Read token from environment variable
TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN")
ADK_SERVER_URL = os.environ.get("ADK_SERVER_URL", "http://localhost:8000")
ADK_APP_NAME = os.environ.get("ADK_APP_NAME", "restaurant_agent")

# Parse base URL out of ADK_SERVER_URL
BASE_URL = ADK_SERVER_URL.rstrip('/')
if BASE_URL.endswith('/run'):
    BASE_URL = BASE_URL[:-4]
elif BASE_URL.endswith('/query'):
    BASE_URL = BASE_URL[:-6]

if not TOKEN:
    print("Error: TELEGRAM_BOT_TOKEN environment variable not set.")
    print("Please set it before running the application.")
    sys.exit(1)

async def start(update: Update, context: CallbackContext) -> None:
    """Send a message when the command /start is issued."""
    await update.message.reply_text('Hi! I am your ADK Integration Bot. Send me a message and I will forward it to the ADK server.')

async def send_typing_loop(chat_id: int, bot, stop_event: asyncio.Event):
    """Send typing action periodically until the stop event is set."""
    while not stop_event.is_set():
        try:
            await bot.send_chat_action(chat_id=chat_id, action=ChatAction.TYPING)
            # The research suggested repeating every 4 seconds
            await asyncio.sleep(4)
        except Exception as e:
            print(f"Error sending chat action: {e}")
            await asyncio.sleep(1) # Wait a bit before retrying if error

async def handle_message(update: Update, context: CallbackContext) -> None:
    """Handle incoming user messages."""
    user_message = update.message.text
    chat_id = update.message.chat_id
    raw_user_id = str(update.message.from_user.id)
    
    # Derive unique user_id and session_id for this user
    user_id = f"tg_{raw_user_id}"
    session_id = f"tg_sess_{raw_user_id}"

    print(f"Received message from {user_id}: {user_message}")

    # Create a stop event for the typing loop
    stop_event = asyncio.Event()
    
    # Start the typing loop as a background task
    typing_task = asyncio.create_task(send_typing_loop(chat_id, context.bot, stop_event))

    try:
        async with httpx.AsyncClient() as client:
            # 1. Check if the session exists
            session_url = f"{BASE_URL}/apps/{ADK_APP_NAME}/users/{user_id}/sessions/{session_id}"
            session_check = await client.get(session_url, timeout=10.0)
            
            if session_check.status_code == 404:
                # 2. If session doesn't exist, create it
                print(f"Session {session_id} not found. Creating session...")
                session_create = await client.post(session_url, json={}, timeout=10.0)
                if session_create.status_code != 200:
                    raise Exception(f"Failed to create session: {session_create.status_code} {session_create.text}")
            elif session_check.status_code != 200:
                raise Exception(f"Error checking session: {session_check.status_code} {session_check.text}")
            
            # 3. Run the ADK agent
            run_url = f"{BASE_URL}/run"
            payload = {
                "appName": ADK_APP_NAME,
                "userId": user_id,
                "sessionId": session_id,
                "newMessage": {
                    "role": "user",
                    "parts": [{"text": user_message}]
                }
            }
            response = await client.post(run_url, json=payload, timeout=60.0)
            
        if response.status_code == 200:
            events = response.json()
            if isinstance(events, list) and len(events) > 0:
                # The last event contains the final text response
                last_event = events[-1]
                content = last_event.get("content", {})
                parts = content.get("parts", [])
                if parts and "text" in parts[0]:
                    reply_text = parts[0]["text"]
                else:
                    reply_text = "ADK agent returned an empty or non-text response."
            else:
                reply_text = "No events returned from ADK agent."
        else:
            reply_text = f"Error communicating with ADK server (Status: {response.status_code})."
            
    except Exception as e:
        reply_text = f"Failed to connect to ADK server: {e}"
    finally:
        # Stop the typing loop
        stop_event.set()
        await typing_task

    # Send the final response back to the user
    await update.message.reply_text(reply_text)

def main() -> None:
    """Start the bot."""
    # Create the Application and pass it your bot's token.
    application = Application.builder().token(TOKEN).build()

    # on different commands - answer in Telegram
    application.add_handler(CommandHandler("start", start))

    # on non command i.e message - echo the message on Telegram
    application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))

    # Check if running in webhook mode (e.g., on Cloud Run)
    port = os.environ.get("PORT")
    service_url = os.environ.get("SERVICE_URL")

    if port and service_url:
        if not service_url.startswith("http"):
            service_url = f"https://{service_url}"
        
        print(f"Starting bot in WEBHOOK mode on port {port} with url {service_url}")
        
        application.run_webhook(
            listen="0.0.0.0",
            port=int(port),
            url_path=TOKEN,
            webhook_url=f"{service_url}/{TOKEN}",
            allowed_updates=Update.ALL_TYPES
        )
    else:
        print("Starting bot in POLLING mode")
        # Run the bot until the user presses Ctrl-C
        application.run_polling(allowed_updates=Update.ALL_TYPES)


if __name__ == "__main__":
    main()

Разбираемся в коде интеграции Telegram-бота.

23b346f5ceb4712a.png

Когда пользователь отправляет сообщение, в функции handle_message() выполняется следующий конвейер обработки.

Шаг 1: Идентификация и определение сессии

Бот сопоставляет идентификатор пользователя Telegram с уникальными идентификаторами ADK, чтобы различать пользовательские сессии:

user_id = f"tg_{raw_user_id}"
session_id = f"tg_sess_{raw_user_id}"

Шаг 2: Асинхронный статус "набора текста" (строки 53–58)

Для обеспечения высокой скорости отклика пользователя во время обработки запроса агентом ADK (что может занять несколько секунд), бот запускает асинхронный фоновый цикл:

  • Объект asyncio.Event создается как stop_event .
  • asyncio.create_task запускает send_typing_loop(...) в фоновом режиме.
  • Цикл отправляет действие ChatAction.TYPING в Telegram каждые 4 секунды, пока не будет установлено значение stop_event .

Шаг 3: Проверка и создание сессии ADK (строки 61–72)

Перед запуском агента бот проверяет, существует ли уже сессия:

  1. Отправляет GET запрос по адресу /apps/{appName}/users/{userId}/sessions/{sessionId} .
  2. Если в ответ приходит ошибка 404 Not Found , сессия создается с помощью POST запроса к тому же URL-адресу с пустым JSON-телом.
  3. Если возвращается статус, отличный от 200 или 404 , генерируется исключение.

Шаг 4: Отправка запроса агенту (строки 74–85)

Содержимое сообщения перенаправляется на конечную точку ADK /run :

  • Конечная точка : POST /run
  • Время ожидания запроса установлено на 60.0 секунд, чтобы учесть сложные логические рассуждения или задержку со стороны вышестоящего сервера.
  • Структура полезной нагрузки :
{
  "appName": "restaurant_agent",
  "userId": "tg_<user_id>",
  "sessionId": "tg_sess_<user_id>",
  "newMessage": {
    "role": "user",
    "parts": [{"text": "<user_message>"}]
  }
}

Шаг 5: Анализ ответа (строки 87–101)

Сервер ADK возвращает список событий сообщений. Бот анализирует возвращенный массив:

  • Он извлекает последнее событие из списка ( events[-1] ).
  • Переход к текстовому содержимому осуществляется через event["content"]["parts"][0]["text"] .
  • Если события не возвращаются или отсутствует структура текста, устанавливается описательный текст-заполнитель.

Шаг 6: Демонтаж и отправка ответного сигнала (строки 103–111)

  • В блоке finally устанавливается значение stop_event , что останавливает цикл ввода текста.
  • Бот ожидает завершения задачи typing_task , чтобы обеспечить чистоту ресурсов.
  • В заключение бот отправляет в чат Telegram разобранный текст ответа.

6. Разверните приложение Telegram Webhook в Cloud Run.

Далее мы развернем Telegram Webhook Listener в Cloud Run, чтобы наш бот мог с ним взаимодействовать.

Создайте Dockerfile.

Сначала нам нужно создать Dockerfile.

cloudshell edit ~/build-agent-adk-telegram/telegram-integration/Dockerfile

Затем скопируйте в него следующий код.

# Use an official Python runtime as a parent image
FROM python:3.11-slim

# Prevent Python from writing pyc files to disc and buffering stdout/stderr
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

# Set the working directory in the container
WORKDIR /app

# Install system dependencies if needed
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

# Copy the dependencies file to the working directory
COPY requirements.txt .

# Install any needed packages specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

# Copy the rest of the application code
COPY main.py .

# Expose the port that Cloud Run will provide via environment variable
EXPOSE 8080

# Run main.py when the container launches
CMD ["python", "main.py"]

Сервис контейнеризирован с использованием python:3.11-slim , чтобы уменьшить размер образа:

  • Устанавливает зависимости из requirements.txt ( python-telegram-bot[webhooks] и httpx ).
  • Предоставляет доступ к стандартному порту 8080 .
  • Запускает python main.py

Подготовьте переменные среды.

После этого давайте ещё раз проверим, успешно ли развёрнулся наш агент.

AGENT_URL=$(gcloud run services describe restaurant-agent \
    --region="$REGION" \
    --format='value(status.url)')

echo "      ✓ Agent service deployed"
echo "      Agent URL: $AGENT_URL"
echo ""

Далее, давайте добавим полученный ранее TELEGRAM_BOT_TOKEN в файл .env

echo "TELEGRAM_BOT_TOKEN=YOUR_TELEGRAM_API_KEY" >> .env

Затем давайте заполним данные в файле .env другими необходимыми нам значениями.

echo "ADK_SERVER_URL=$AGENT_URL" >> .env
echo "ADK_APP_NAME=restaurant_agent" >> .env
echo "SERVICE_NAME=telegram-integration" >> .env
source .env

Создание скрипта развертывания

Давайте создадим скрипт развертывания, который обеспечит полную проверку и развернет приложение в Cloud Run.

cloudshell edit ~/build-agent-adk-telegram/telegram-integration/deploy.sh

И скопируйте следующий код в файл.

#!/usr/bin/env bash
# ./telegram-integration/deploy.sh

# Exit immediately if a command exits with a non-zero status
set -euo pipefail

# Color codes for neat terminal output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0;37m' # No Color
# Load environment variables from .env if it exists
if [ -f .env ]; then
    echo -e "${GREEN}✔ Loading environment variables from .env...${NC}"
    export $(grep -v '^#' .env | xargs)
fi

echo -e "${BLUE}====================================================${NC}"
echo -e "${BLUE}   Google Cloud Run Deployment: Telegram Bot        ${NC}"
echo -e "${BLUE}====================================================${NC}"

# 1. Check for gcloud CLI
if ! command -v gcloud &> /dev/null; then
    echo -e "${RED}Error: 'gcloud' CLI is not installed.${NC}"
    echo "Please install the Google Cloud SDK and try again."
    echo "See: https://cloud.google.com/sdk/docs/install"
    exit 1
fi

# 2. Check active gcloud account/auth
ACTIVE_ACCOUNT=$(gcloud auth list --filter=status:ACTIVE --format="value(account)" 2>/dev/null || true)
if [ -z "$ACTIVE_ACCOUNT" ]; then
    echo -e "${RED}Error: No active Google Cloud account found.${NC}"
    echo "Please run: gcloud auth login"
    exit 1
fi

# 3. Detect / Prompt for GCP Project
DEFAULT_PROJECT=${GCP_PROJECT_ID:-$(gcloud config get-value project 2>/dev/null || true)}
if [ -n "${DEFAULT_PROJECT}" ]; then
    echo -e "${GREEN}✔ Using GCP Project: $DEFAULT_PROJECT${NC}"
    GCP_PROJECT="$DEFAULT_PROJECT"
else
    echo -n "Enter GCP Project ID: "
    read -r GCP_PROJECT
fi

if [ -z "$GCP_PROJECT" ]; then
    echo -e "${RED}Error: GCP Project ID is required.${NC}"
    exit 1
fi

# Set active project
gcloud config set project "$GCP_PROJECT" &> /dev/null

# 4. Configure Service Parameters
DEFAULT_SERVICE=${SERVICE_NAME:-"telegram-integration"}
if [ -n "${SERVICE_NAME:-}" ]; then
    echo -e "${GREEN}✔ Using Cloud Run Service Name: $SERVICE_NAME${NC}"
else
    echo -n "Enter Cloud Run Service Name [Default: $DEFAULT_SERVICE]: "
    read -r SERVICE_NAME
    SERVICE_NAME=${SERVICE_NAME:-$DEFAULT_SERVICE}
fi

DEFAULT_REGION=${REGION:-"us-central1"}
if [ -n "${REGION:-}" ]; then
    echo -e "${GREEN}✔ Using Cloud Run Region: $REGION${NC}"
else
    echo -n "Enter Cloud Run Region [Default: $DEFAULT_REGION]: "
    read -r REGION
    REGION=${REGION:-$DEFAULT_REGION}
fi

DEFAULT_ADK_APP=${ADK_APP_NAME:-"restaurant_agent"}
if [ -n "${ADK_APP_NAME:-}" ]; then
    echo -e "${GREEN}✔ Using ADK App Name: $ADK_APP_NAME${NC}"
    ADK_APP="$ADK_APP_NAME"
else
    echo -n "Enter ADK App Name [Default: $DEFAULT_ADK_APP]: "
    read -r ADK_APP
    ADK_APP=${ADK_APP:-$DEFAULT_ADK_APP}
fi

# 5. Retrieve/Prompt for Telegram Bot Token
if [ -n "${TELEGRAM_BOT_TOKEN:-}" ]; then
    echo -e "${GREEN}✔ Found TELEGRAM_BOT_TOKEN in environment.${NC}"
    BOT_TOKEN="$TELEGRAM_BOT_TOKEN"
else
    echo -e "${YELLOW}TELEGRAM_BOT_TOKEN is not set in your environment.${NC}"
    echo -n "Enter your Telegram Bot Token (input will be hidden): "
    read -s -r BOT_TOKEN
    echo ""
fi

if [ -z "$BOT_TOKEN" ]; then
    echo -e "${RED}Error: Telegram Bot Token is required.${NC}"
    exit 1
fi

# 6. Retrieve/Prompt for ADK Server URL
DEFAULT_ADK_URL="http://localhost:8000"
if [ -n "${ADK_SERVER_URL:-}" ]; then
    echo -e "${GREEN}✔ Found ADK_SERVER_URL in environment: $ADK_SERVER_URL${NC}"
    ADK_URL="$ADK_SERVER_URL"
else
    echo -n "Enter your ADK Server URL [Default: $DEFAULT_ADK_URL]: "
    read -r ADK_URL
    ADK_URL=${ADK_URL:-$DEFAULT_ADK_URL}
fi

# Enable required GCP services
echo -e "\n${YELLOW}Checking and enabling required GCP services...${NC}"
gcloud services enable run.googleapis.com cloudbuild.googleapis.com artifactregistry.googleapis.com --project "$GCP_PROJECT"

# Determine source directory dynamically
SOURCE_DIR="."
if [ -d "telegram-integration" ]; then
    SOURCE_DIR="telegram-integration"
    echo -e "${GREEN}✔ Found source directory: telegram-integration${NC}"
elif [ -f "Dockerfile" ]; then
    SOURCE_DIR="."
    echo -e "${GREEN}✔ Dockerfile found in current directory. Using current directory as source.${NC}"
else
    echo -e "${RED}Error: Could not find source directory 'telegram-integration' or Dockerfile in current directory.${NC}"
    exit 1
fi

# 7. First-pass Deployment with placeholder SERVICE_URL
# This boots the container in Webhook mode (so health check binds to port)
# but uses a high-reliability placeholder URL (google.com) to pass DNS verification checks.
echo -e "\n${YELLOW}Deploying to Cloud Run (Step 1/2: Initial Deploy)...${NC}"
gcloud run deploy "$SERVICE_NAME" \
  --source "$SOURCE_DIR" \
  --region "$REGION" \
  --allow-unauthenticated \
  --set-env-vars "TELEGRAM_BOT_TOKEN=$BOT_TOKEN,ADK_SERVER_URL=$ADK_URL,ADK_APP_NAME=$ADK_APP,SERVICE_URL=https://google.com" \
  --project "$GCP_PROJECT"

# 8. Retrieve the actual service URL
echo -e "\n${YELLOW}Retrieving service URL...${NC}"
SERVICE_URL=$(gcloud run services describe "$SERVICE_NAME" --region "$REGION" --project "$GCP_PROJECT" --format 'value(status.url)')
echo -e "${GREEN}✔ Service URL is: $SERVICE_URL${NC}"

# 9. Update service environment variables with the real SERVICE_URL
# This triggers a rolling update and registers the correct webhook with Telegram automatically!
echo -e "\n${YELLOW}Updating configuration with final Webhook URL (Step 2/2)...${NC}"
gcloud run services update "$SERVICE_NAME" \
  --region "$REGION" \
  --set-env-vars "TELEGRAM_BOT_TOKEN=$BOT_TOKEN,ADK_SERVER_URL=$ADK_URL,ADK_APP_NAME=$ADK_APP,SERVICE_URL=$SERVICE_URL" \
  --project "$GCP_PROJECT"

echo -e "\n${GREEN}====================================================${NC}"
echo -e "${GREEN}   Deployment Completed Successfully! 🎉            ${NC}"
echo -e "${GREEN}====================================================${NC}"
echo -e "Service Name:   ${BLUE}$SERVICE_NAME${NC}"
echo -e "Region:         ${BLUE}$REGION${NC}"
echo -e "Active URL:     ${BLUE}$SERVICE_URL${NC}"
echo -e "Webhook Path:   ${BLUE}$SERVICE_URL/<bot-token>${NC}"
echo -e "ADK Backend:    ${BLUE}$ADK_URL${NC}"
echo -e "ADK App Name:   ${BLUE}$ADK_APP${NC}"
echo -e "${GREEN}====================================================${NC}"
echo "Your Telegram Bot has been configured to use webhooks."
echo "Any message sent to your bot will now trigger this Cloud Run instance."

Скрипт двойного развертывания (deploy.sh)

При развертывании в Google Cloud Run боту необходимо указать свой собственный URL (SERVICE_URL) в переменных окружения, чтобы зарегистрировать его в качестве целевого объекта веб-перехватчика в Telegram. Для разрешения этой циклической зависимости (URL неизвестен до развертывания, но сервису требуется этот URL для запуска без сбоев проверки работоспособности) deploy.sh выполняет двухэтапное развертывание:

  1. Шаг 1: Первоначальное развертывание : Запускает контейнер с временным DNS-сервером ( https://google.com ), чтобы служба успешно запустилась, привязалась к локальному порту и прошла первоначальные проверки работоспособности Cloud Run.
  2. Шаг 2: Получение URL-адреса : Программно извлекает вновь созданную конечную точку Cloud Run с помощью gcloud run services describe .
  3. Шаг 3: Обновление конфигурации : Обновляет переменные среды, указывая фактический URL-адрес работающего сервиса. Это запускает чистое поэтапное обновление в Cloud Run и безопасно регистрирует правильный целевой веб-перехватчик в API Telegram.

Развертывание в облаке. Запуск.

Скрипт развертывания выводит URL-адрес агента. Откройте его в браузере, чтобы получить доступ к тому же пользовательскому интерфейсу разработчика ADK, работающему в Cloud Run.

cd ~/build-agent-adk-telegram
bash ./telegram-integration/deploy.sh

Если все пройдет хорошо, то сейчас самое время начать общаться со своим ботом прямо из приложения Telegram, найти только что созданного бота и начать с ним взаимодействовать:

What Italian dishes do you have?

Или,

I want something spicy and creamy

Следите за тем, как бот отправляет статус "...печатает", и вскоре он вернет сообщение из созданного вами ранее ADK!

c62fd4016ddd3c9b.png

7. Поздравляем!

Вы разработали, развернули и полностью интегрировали наш интеллектуальный помощник по меню ресторана на основе ADK с ИИ-агентом в Telegram, используя HTTP-соединение клиент-сервер, и позволяете людям запрашивать меню и бронировать столик в ресторане.

Что вы узнали

  • Развертывание и настройка Restaurant Concierge, агента на основе ADK и MCP Toolbox для работы в облаке.
  • Как настроить Telegram-бота с помощью BotFather
  • Как написать скрипты на Python для прослушивания веб-хуков Telegram и взаимодействия с агентом ADK для передачи запросов пользователей и получения соответствующих ответов?
  • Как реализовать функцию "... typing" в Telegram для оповещения пользователей о том, что сообщения обрабатываются в режиме реального времени, пока ожидается ответ от агента ADK.
  • Как развернуть скрипт Python в облаке и взаимодействовать с ним.

Уборка

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

gcloud projects delete $GOOGLE_CLOUD_PROJECT

Вариант 2: Удаление отдельных ресурсов

# If you follow from previous A2A Agent Runtime codelab
# Delete the Agent Runtime deployment (skip if not found)
uv run python -c "
import vertexai
from google.genai import types
vertexai.init(project='$GOOGLE_CLOUD_PROJECT', location='$REGION')
client = vertexai.Client(
    project='$GOOGLE_CLOUD_PROJECT', location='$REGION',
    http_options=types.HttpOptions(api_version='v1beta1'),
)
try:
    agent = client.agent_engines.get(name='$RESERVATION_AGENT_RESOURCE_NAME')
    agent.delete(force=True)
    print('Agent Runtime deployment deleted.')
except Exception as e:
    print(f'No agent deployment found or already deleted, skipping. ({e})')
"

# Delete GCS staging bucket (skip if STAGING_BUCKET is not set)
if [ -n "$STAGING_BUCKET" ]; then
  gsutil rm -r gs://$STAGING_BUCKET
else
  echo "STAGING_BUCKET not set, skipping bucket deletion."
fi

# Delete Cloud Run services
gcloud run services delete restaurant-agent --region=$REGION --quiet
gcloud run services delete toolbox-service --region=$REGION --quiet
gcloud run services delete telegram-integration --region=$REGION --quiet

# Delete Cloud SQL instance
gcloud sql instances delete $DB_INSTANCE --quiet