Tworzenie aplikacji agentowej AI, z którą klienci mogą wchodzić w interakcje za pomocą Telegrama

1. Wprowadzenie

Twórz płynne, interaktywne doświadczenia oparte na AI agentowej, z których klienci mogą korzystać bezpośrednio w używanej przez siebie aplikacji do przesyłania wiadomości. Dowiedz się, jak tworzyć i wdrażać inteligentne aplikacje, które działają płynnie w interfejsach internetowych i nowoczesnych kanałach komunikacji.

Co utworzysz

Integracja pełnoprawnego „konsjerża restauracji”, czyli aplikacji opartej na ADK z Gemini, która pomaga gościom przeglądać menu restauracji i rezerwować stoliki, z aplikacją do czatowania Telegram. Możesz wchodzić w interakcję z botem Telegrama i prosić o opisy w języku naturalnym, np. „Chcę coś pikantnego i wegetariańskiego”. Bot połączy się następnie z agentem pakietu ADK, który odczytuje dane z bazy danych Cloud SQL PostgreSQL i zapisuje w niej dane w całości za pomocą MCP Toolbox for Databases, który obsługuje cały dostęp do bazy danych, w tym automatyczne generowanie wektorów dystrybucyjnych na potrzeby wyszukiwania wektorowego. W tym czasie użytkownik będzie widzieć, że bot potwierdza otrzymanie wiadomości i pisze ... typing w odpowiedzi, czekając na odpowiedź od agenta pakietu ADK.

c1d28343ed68358a.png

Czego się nauczysz

  • Wdrażanie działającej aplikacji „Restaurant Concierge” opartej na ADK i Gemini
  • Konfigurowanie bota na czacie Telegrama za pomocą BotFather
  • Napisz aplikację w języku Python, która będzie nasłuchiwać webhooka bota.
  • Wysyłanie działania czatu w celu wyświetlania powiadomienia ... typing w Telegramie w odpowiedzi na wiadomość użytkownika i wykonywanie sondowania w celu okresowego wysyłania ... typing podczas oczekiwania na rzeczywistą odpowiedź.
  • Wywołaj Restaurant Concierge punkt końcowy Cloud Run, aby przetworzyć zapytanie użytkownika.
  • Obsługa powrotu z agenta ADK, wysyłanie wiadomości do Telegrama i zamykanie bufora
  • Wdrażanie aplikacji Python w Cloud Run
  • Interakcja z botem Telegrama

Wymagania wstępne

2. Konfiguracja środowiska – kontynuacja poprzednich ćwiczeń z programowania

Opisy, które podajemy w tym ćwiczeniu, są kontynuacją tego ćwiczenia wstępnego: Agentowy RAG z ADK, MCP Toolbox i Cloud SQL lub Agenty na dużą skalę: architektura wieloagentowa z protokołem A2A w środowisku wykonawczym agenta i integracja ADK. Możesz kontynuować pracę z poprzednich warsztatów

Możemy zacząć tworzyć w poprzednim katalogu roboczym z ćwiczeń ( katalog roboczy powinien mieć nazwę build-agent-adk-toolbox-cloudsql lub adk-a2a-agent-runtime-starter). Aby uniknąć nieporozumień, zmieńmy nazwę katalogu na taką samą, jakiej używamy, gdy zaczynamy od nowa.

Jeśli kontynuujesz pracę z laboratorium Agentowy RAG z ADK, MCP Toolbox i Cloud SQL :

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

Jeśli kontynuujesz moduł Agenci na dużą skalę: architektura wieloagentowa z protokołem A2A w środowisku wykonawczym agenta i integracja pakietu ADK,

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

Następnie zmień katalog roboczy na ten katalog.

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

Następnie sprawdź, czy Twój restaurant-agent jest już wdrożony i ma publiczny adres URL, do którego można uzyskać dostęp.

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

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

Jeśli możesz uzyskać dostęp do adresu URL, przejdź do następnej sekcji: Create Telegram Bot

3. Konfiguracja środowiska – świeży start z repozytorium początkowym

Ten krok przygotowuje środowisko Cloud Shell, konfiguruje projekt Google Cloud i klonuje repozytorium początkowe.

Otwieranie Cloud Shell

Otwórz Cloud Shell w przeglądarce. Cloud Shell zapewnia wstępnie skonfigurowane środowisko ze wszystkimi narzędziami potrzebnymi do tego ćwiczenia. Gdy pojawi się prośba o autoryzację, kliknij Autoryzuj.

Następnie kliknij „Widok” –> „Terminal”, aby otworzyć terminal.Interfejs powinien wyglądać podobnie do tego:

86307fac5da2f077.png

Będzie to nasz główny interfejs: IDE u góry, terminal u dołu.

Konfigurowanie katalogu roboczego

Sklonuj repozytorium początkowe. Będzie ono zawierać cały kod, który napiszesz w tym ćwiczeniu:

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

Utwórz plik .env na podstawie podanego szablonu:

cp .env.example .env

Aby uprościć konfigurację projektu w terminalu, pobierz ten skrypt konfiguracji projektu do katalogu roboczego:

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

Uruchom skrypt. Weryfikuje ono Twoje próbne konto rozliczeniowe, tworzy nowy projekt (lub weryfikuje istniejący), zapisuje identyfikator projektu w pliku .env w bieżącym katalogu i ustawia aktywny projekt w gcloud.

bash setup_verify_trial_project.sh && source .env

Skrypt:

  1. Sprawdź, czy masz aktywne konto rozliczeniowe w wersji próbnej
  2. Sprawdź, czy w .env istnieje projekt (jeśli tak)
  3. Utwórz nowy projekt lub użyj istniejącego.
  4. Połącz próbne konto rozliczeniowe z projektem
  5. Zapisz identyfikator projektu w .env.
  6. Ustaw projekt jako aktywny projekt gcloud.

Sprawdź, czy projekt jest prawidłowo ustawiony, sprawdzając żółty tekst obok katalogu roboczego w wierszu poleceń terminala Cloud Shell. Powinien wyświetlać identyfikator projektu.

5c515e235ee1179f.png

Konfiguracja infrastruktury dla początkujących

Najpierw musimy zainstalować zależności Pythona za pomocą uv, czyli szybkiego menedżera pakietów i projektów Pythona napisanego w języku Rust ( dokumentacja uv). W tym ćwiczeniu używamy go ze względu na szybkość i prostotę utrzymywania projektu w Pythonie.

uv sync

Następnie uruchom pełny skrypt konfiguracji, który utworzy instancję Cloud SQL, wypełni ją danymi i wdroży usługę Toolbox, która będzie działać jako stan początkowy agenta restauracji.

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

Efekt:

  • Tworzenie instancji Cloud SQL i wypełnianie bazy danych (etap 1)
  • Generowanie konfiguracji środowiska agenta i uruchamianie lokalnej usługi Toolbox (etap 2)
  • Wdrażanie usług Toolbox i Agent w Cloud Run (etap 3)

Po zakończeniu wdrażania możesz uzyskać dostęp do interfejsu ADK Dev UI pod adresem 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 ""

Otwórz interfejs programisty ADK, kliknij restaurant_agent i przeprowadź testy za pomocą zapytań podobnych do tego przykładu:

What Italian dishes do you have?

lub

I want something spicy and creamy

Teraz musimy się zastanowić, jak przejść od interfejsu programowania stron internetowych do kanału wiadomości Telegram.

4. Tworzenie bota Telegrama

Telegram to znana bezpłatna platforma do przesyłania wiadomości, która jest powszechnie używana do angażowania społeczności. Jednym z powodów jest to, że oferuje wiele sposobów na łatwą integrację, dzięki czemu użytkownicy mogą łatwo tworzyć własne boty z wieloma różnymi funkcjami.

W naszym przypadku użyjemy BotFathera, aby po raz pierwszy utworzyć własnego bota. Pamiętaj, że w tej sesji używamy Telegrama, ale tej samej metody możesz użyć w przypadku WhatsAppa lub innych platform do przesyłania wiadomości.

Tworzenie własnego bota za pomocą BotFather

Otwórz przeglądarkę i przejdź na stronę https://telegram.me/BotFather, aby rozpocząć tworzenie własnego bota Telegrama.

1b817e758c699a79.png

Rozpoczęcie interakcji z BotFatherem

ad3daa08e73502db.png

Wysyłanie polecenia /start

Aby zacząć korzystać z BotFathera i utworzyć pierwszego bota, musisz wysłać do niego wiadomość /start. BotFather udostępni Ci wtedy wszystkie polecenia, za pomocą których możesz z nim wchodzić w interakcje.

/start

Inicjowanie tworzenia bota za pomocą polecenia /newbot

Utwórzmy nowego bota, wysyłając polecenie /newbot do BotFather. Poprosi Cię o nazwanie bota, a potem o podanie username, które zawsze musi kończyć się bot . Na przykład TetrisBot lub tetris_bot. Musi być unikalny.

1f6a74f494d48986.png

Po utworzeniu bota otrzymasz od BotFathera ten komunikat:

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

Zanotuj YOUR_TELEGRAM_API_KEY, ponieważ użyjemy go w następnej sekcji.

5. Tworzenie aplikacji webhooka Telegrama

Przygotujmy katalog roboczy, aby rozpocząć tworzenie aplikacji webhook Telegrama

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

Dodawanie wymaganych zależności

Utwórz skrypt requirements.txt z tą zawartością, aby zapewnić odpowiednie zależności dla skryptu odbiornika webhooka Telegrama.

cloudshell edit ./telegram-integration/requirements.txt

Następnie dodaj te zależności:

python-telegram-bot[webhooks]
httpx

Tworzenie skryptu dla odbiornika webhooka Telegrama

Po zainstalowaniu zależności. Teraz możemy utworzyć skrypt w Pythonie main.py skrypt dla aplikacji do integracji

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

Następnie skopiuj do niego ten kod:

# ./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()

Omówienie kodu integracji bota Telegrama

23b346f5ceb4712a.png

Gdy użytkownik wyśle wiadomość, w handle_message() zostanie uruchomiony ten potok:

Krok 1. Określanie tożsamości i sesji

Bot mapuje identyfikator użytkownika Telegrama na unikalne identyfikatory ADK, aby zachować odrębność sesji użytkowników:

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

Krok 2. Asynchroniczny stan „pisania” (wiersze 53–58)

Aby zapewnić użytkownikowi szybką reakcję, gdy agent ADK przetwarza prompt (co może potrwać kilka sekund), bot uruchamia asynchroniczną pętlę w tle:

  • asyncio.Event jest tworzony jako stop_event.
  • asyncio.create_task tworzy send_typing_loop(...) w tle.
  • Pętla wysyła działanie ChatAction.TYPING do Telegrama co 4 sekundy, dopóki nie zostanie ustawiona wartość stop_event.

Krok 3. Weryfikacja i tworzenie sesji ADK (wiersze 61–72)

Przed uruchomieniem agenta bot sprawdza, czy sesja już istnieje:

  1. Wysyła żądanie GET do /apps/{appName}/users/{userId}/sessions/{sessionId}.
  2. Jeśli odpowiedź to 404 Not Found, sesja jest tworzona za pomocą żądania POST wysłanego na ten sam adres URL z pustą treścią JSON.
  3. Jeśli zostanie zwrócony stan inny niż 200 lub 404, zostanie zgłoszony wyjątek.

Krok 4. Wysyłanie prośby do agenta (wiersze 74–85)

Ładunek wiadomości jest przekazywany do punktu końcowego ADK /run:

  • Punkt końcowy: POST /run
  • Limit czasu żądania wynosi 60.0 sekund, aby uwzględnić złożone rozumowanie lub opóźnienia w systemie nadrzędnym.
  • Struktura ładunku:
{
  "appName": "restaurant_agent",
  "userId": "tg_<user_id>",
  "sessionId": "tg_sess_<user_id>",
  "newMessage": {
    "role": "user",
    "parts": [{"text": "<user_message>"}]
  }
}

Krok 5. Parsowanie odpowiedzi (wiersze 87–101)

Serwer ADK zwraca listę zdarzeń związanych z wiadomościami. Bot sprawdza zwróconą tablicę:

  • Pobiera ostatnie zdarzenie na liście (events[-1]).
  • Przechodzi do treści tekstowej za pomocą elementu event["content"]["parts"][0]["text"].
  • Jeśli nie zostaną zwrócone żadne wydarzenia lub będzie brakować struktury tekstu, zostanie ustawiony opisowy tekst zastępczy.

Krok 6. Analiza i wysyłanie odpowiedzi (wiersze 103–111)

  • W bloku finally ustawiono stop_event, co zatrzymuje pętlę działania pisania.
  • Bot czeka na zakończenie działania funkcji typing_task, aby zapewnić czystość zasobów.
  • Na koniec bot odpowiada na czacie w Telegramie przetworzonym tekstem odpowiedzi.

6. Wdrażanie aplikacji webhooka Telegrama w Cloud Run

Następnie wdrożymy odbiornik webhooka Telegrama w Cloud Run, aby nasz bot mógł się z nim komunikować.

Utwórz plik Dockerfile

Najpierw musimy utworzyć plik Dockerfile.

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

Następnie skopiuj do niego ten kod:

# 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"]

Usługa jest konteneryzowana za pomocą python:3.11-slim, aby zmniejszyć rozmiar obrazu:

  • Instaluje zależności z requirements.txt (python-telegram-bot[webhooks]httpx).
  • Udostępnia standardowy port 8080.
  • Uruchomienie python main.py.

Przygotuj zmienne środowiskowe

Następnie sprawdźmy, czy agent został wdrożony.

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

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

Następnie wstawmy uzyskany wcześniej token TELEGRAM_BOT_TOKEN do .env.

echo "TELEGRAM_BOT_TOKEN=YOUR_TELEGRAM_API_KEY" >> .env

Następnie wypełnijmy dane .env innymi potrzebnymi wartościami.

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

Tworzenie skryptu wdrożenia

Utwórzmy skrypt wdrażania, który przeprowadzi pełne sprawdzenie i wdroży aplikację w Cloud Run.

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

i skopiuj do pliku ten kod:

#!/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."

Skrypt podwójnego wdrażania (deploy.sh)

Podczas wdrażania w Google Cloud Run bot musi określić własny adres URL (SERVICE_URL) w swoim środowisku, aby zarejestrować go jako miejsce docelowe webhooka w Telegramie. Aby rozwiązać ten problem z zależnością cykliczną (adres URL jest nieznany do momentu wdrożenia, ale usługa wymaga adresu URL do uruchomienia bez błędów sprawdzania stanu), deploy.sh przeprowadza wdrożenie dwuetapowe:

  1. Krok 1. Wstępne wdrożenie: uruchamia kontener z zastępczym adresem DNS (https://google.com), aby usługa uruchomiła się prawidłowo, powiązała się z lokalnym portem i przeszła wstępne kontrole stanu Cloud Run.
  2. Krok 2. Pobierz adres URL: programowo wyodrębnij nowo utworzony punkt końcowy Cloud Run za pomocą gcloud run services describe.
  3. Krok 3. Aktualizacja konfiguracji: aktualizuje zmienne środowiskowe o rzeczywisty adres URL usługi na żywo. Spowoduje to czystą aktualizację kroczącą w Cloud Run i bezpieczne zarejestrowanie prawidłowego miejsca docelowego webhooka w interfejsie Telegram API.

Wdróż w Cloud Run

Skrypt wdrażania wyświetla adres URL agenta. Otwórz go w przeglądarce, aby uzyskać dostęp do tego samego interfejsu programisty ADK działającego w Cloud Run.

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

Jeśli wszystko pójdzie dobrze, możesz zacząć czatować z botem bezpośrednio w aplikacji Telegram. Wyszukaj utworzonego właśnie bota i zacznij z nim rozmawiać:

What Italian dishes do you have?

lub

I want something spicy and creamy

Obserwuj stan wysyłania bota „...pisze”, a wkrótce otrzymasz wiadomość z utworzonego wcześniej ADK.

c62fd4016ddd3c9b.png

7. Gratulacje!

Zbudowano, wdrożono i w pełni zintegrowano naszego inteligentnego asystenta menu restauracji ADK opartego na agent AI z Telegramem za pomocą komunikacji między klientem HTTP a serwerem. Umożliwia on użytkownikom wysyłanie zapytań o ulubione pozycje w menu i rezerwowanie stolików w restauracji.

Czego się nauczysz

  • Wdrażanie i konfigurowanie usługi Restaurant Concierge, agenta opartego na ADK i MCP Toolbox w Cloud Run
  • Jak skonfigurować bota Telegrama za pomocą BotFather
  • Jak pisać skrypty w Pythonie, które nasłuchują webhooka Telegrama i wchodzą w interakcję z agentem ADK, aby przekazywać zapytania i odpowiedzi użytkowników.
  • Jak wdrożyć "... typing" w Telegramie, aby sygnalizować użytkownikom, że wiadomości są przetwarzane w czasie rzeczywistym, gdy czekają na odpowiedź agenta ADK.
  • Jak wdrożyć skrypt w Pythonie w Cloud Run i móc z nim wchodzić w interakcje

Czyszczenie danych

Aby uniknąć obciążenia konta Google Cloud opłatami, usuń zasoby utworzone w tym laboratorium.

gcloud projects delete $GOOGLE_CLOUD_PROJECT

Opcja 2. Usuwanie poszczególnych zasobów

# 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