Agentische KI-App erstellen, mit der Ihr Kunde über Telegram interagieren kann

1. Einführung

Sie können nahtlose, interaktive agentische KI-Erlebnisse schaffen, mit denen Ihre Kunden direkt über die Messaging-App interagieren können, die sie bereits verwendet haben. Hier erfahren Sie, wie Sie intelligente Anwendungen entwickeln und bereitstellen, die reibungslos über Webschnittstellen und moderne Messaging-Kanäle hinweg funktionieren.

Umfang

Integration zwischen einem vollwertigen „Restaurant Concierge“, einer ADK-basierten Anwendung, die auf Gemini basiert und Gästen hilft, das Menü eines Restaurants zu durchsuchen und Reservierungen vorzunehmen, und der Telegram-Chat-App. Sie können mit dem Telegram-Bot interagieren und nach Beschreibungen in natürlicher Sprache fragen, z. B. „Ich möchte etwas Scharfes und Vegetarisches.“ Der Bot stellt dann eine Verbindung zum ADK-Agenten her, der Daten aus einer Cloud SQL for PostgreSQL-Datenbank liest und in diese schreibt. Dies erfolgt ausschließlich über die MCP Toolbox for Databases, die den gesamten Datenbankzugriff übernimmt, einschließlich der automatischen Generierung von Einbettungen für die Vektorsuche. Währenddessen sieht der Nutzer, dass der Bot die Nachricht empfangen hat und ... typing für die Antwort eingibt, während er auf die Rückgabe des ADK-Agenten wartet.

c1d28343ed68358a.png

Lerninhalte

  • Eine funktionierende „Restaurant Concierge“-Anwendung bereitstellen, die auf dem ADK basiert und von Gemini unterstützt wird
  • Telegram-Chatbot mit BotFather einrichten
  • Python-Anwendung schreiben, um den Bot-Webhook zu überwachen
  • Sende die Chat-Aktion, um die Benachrichtigung ... typing in Telegram für die Nutzernachricht bereitzustellen, und führe regelmäßig Abfragen durch, um ... typing zu senden, während auf die tatsächliche Antwort gewartet wird.
  • Restaurant Concierge-Cloud Run-Endpunkt aufrufen, um die Anfrage des Nutzers zu verarbeiten
  • Rückgabe vom ADK-Agenten verarbeiten, Nachricht an Telegram senden und den Puffer schließen
  • Python-Anwendung in Cloud Run bereitstellen
  • Mit Ihrem Telegram-Bot interagieren

Voraussetzungen

2. Umgebung einrichten – Fortsetzung des vorherigen Codelabs

Die in diesem Codelab beschriebenen Szenarien sind die Fortsetzung der folgenden Codelabs mit Voraussetzungen: Agentic RAG with ADK, MCP Toolbox, and Cloud SQL oder Agents at Scale: Multi-Agent Architecture with A2A Protocol on Agent Runtime and ADK Integration. Sie können mit dem vorherigen Codelab fortfahren.

Wir können mit dem Erstellen im Arbeitsverzeichnis des vorherigen Codelabs beginnen ( das Arbeitsverzeichnis sollte build-agent-adk-toolbox-cloudsql oder adk-a2a-agent-runtime-starter sein). Um Verwirrung zu vermeiden, benennen wir das Verzeichnis mit demselben Verzeichnisnamen um, den wir verwenden, wenn wir von vorn beginnen.

Wenn Sie das Lab Agentische RAG mit ADK, MCP Toolbox und Cloud SQL abgeschlossen haben, gehen Sie so vor :

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

Andernfalls fahren Sie mit dem Lab Agents at Scale: Multi-Agent Architecture with A2A Protocol on Agent Runtime and ADK Integration fort.

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

Wechseln Sie dann in das Arbeitsverzeichnis.

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

Prüfen Sie dann, ob Ihre restaurant-agent bereits bereitgestellt wurde und eine öffentliche URL für den Zugriff vorhanden ist.

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

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

Wenn Sie auf die URL zugreifen können, fahren Sie mit dem nächsten Abschnitt fort: Create Telegram Bot

3. Umgebung einrichten – Mit dem Starter-Repository neu beginnen

In diesem Schritt wird Ihre Cloud Shell-Umgebung vorbereitet, Ihr Google Cloud-Projekt konfiguriert und das Starter-Repository geklont.

Cloud Shell öffnen

Öffnen Sie Cloud Shell in Ihrem Browser. Cloud Shell bietet eine vorkonfigurierte Umgebung mit allen Tools, die Sie für dieses Codelab benötigen. Klicken Sie auf Autorisieren, wenn Sie dazu aufgefordert werden.

Klicken Sie dann auf Ansicht -> Terminal, um das Terminal zu öffnen.Die Benutzeroberfläche sollte so aussehen:

86307fac5da2f077.png

Das ist unsere Hauptoberfläche: oben die IDE, unten das Terminal.

Arbeitsverzeichnis einrichten

Klonen Sie das Starter-Repository. Der gesamte Code, den Sie in diesem Codelab schreiben, wird hier gespeichert:

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

Erstellen Sie die Datei .env aus der bereitgestellten Vorlage:

cp .env.example .env

Um die Einrichtung des Projekts in Ihrem Terminal zu vereinfachen, laden Sie dieses Setupscript in Ihr Arbeitsverzeichnis herunter:

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

Führen Sie das Skript aus. Dabei wird Ihr Probeabo-Rechnungskonto überprüft, ein neues Projekt erstellt (oder ein vorhandenes Projekt validiert), Ihre Projekt-ID in einer .env-Datei im aktuellen Verzeichnis gespeichert und das aktive Projekt in gcloud festgelegt.

bash setup_verify_trial_project.sh && source .env

Das Skript führt Folgendes aus:

  1. Prüfen, ob Sie ein aktives Testabrechnungskonto haben
  2. Prüfen Sie, ob in .env ein Projekt vorhanden ist.
  3. Neues Projekt erstellen oder vorhandenes Projekt wiederverwenden
  4. Test-Rechnungskonto mit Ihrem Projekt verknüpfen
  5. Speichern Sie die Projekt-ID in .env.
  6. Projekt als aktives gcloud-Projekt festlegen

Prüfen Sie, ob das Projekt richtig eingestellt ist. Sehen Sie dazu im Cloud Shell-Terminal-Prompt nach dem gelben Text neben Ihrem Arbeitsverzeichnis. Dort sollte Ihre Projekt-ID angezeigt werden.

5c515e235ee1179f.png

Starter Infrastructure Setup

Zuerst müssen wir Python-Abhängigkeiten mit uv installieren. uv ist ein schneller Python-Paket- und Projektmanager, der in Rust geschrieben wurde ( uv-Dokumentation ). In diesem Codelab wird er verwendet, um das Python-Projekt schnell und einfach zu verwalten.

uv sync

Führen Sie dann das vollständige Setupscript aus, mit dem die Cloud SQL-Instanz erstellt, Daten eingefügt und der Toolbox-Dienst bereitgestellt wird, der als Ausgangszustand unseres Restaurant-Agents dient.

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

Dadurch geschieht Folgendes:

  • Cloud SQL-Instanz erstellen und Datenbank mit Daten füllen (Phase 1)
  • Agent-Umgebungskonfiguration generieren und lokalen Toolbox-Dienst starten (Phase 2)
  • Toolbox- und Agent-Dienste in Cloud Run bereitstellen (Phase 3)

Nach Abschluss der Bereitstellung können Sie über die Cloud Run-URL auf die ADK-Entwicklungsoberfläche zugreifen.

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

Öffnen Sie die ADK-Entwicklungsoberfläche, wählen Sie restaurant_agent aus und testen Sie mit Abfragen wie im folgenden Beispiel:

What Italian dishes do you have?

Oder:

I want something spicy and creamy

Wie können wir von der reinen Webentwicklungs-Schnittstelle zum Telegram-Messaging-Kanal wechseln?

4. Telegram-Bot erstellen

Telegram ist eine bekannte kostenlose Messaging-Plattform, die häufig für die Community-basierte Interaktion verwendet wird. Das liegt unter anderem daran, dass sie viele Möglichkeiten zur einfachen Integration bietet. So können Nutzer ganz einfach ihren eigenen Bot mit einer Vielzahl von Funktionen erstellen.

In unserem Fall verwenden wir BotFather, um unseren eigenen Bot zum ersten Mal zu erstellen. Wir verwenden Telegram für diese Sitzung, aber dieselbe Methode kann auch für WhatsApp oder andere Messaging-Plattformen verwendet werden.

BotFather zum Erstellen eines eigenen Bots verwenden

Rufen Sie in Ihrem Webbrowser https://telegram.me/BotFather auf, um mit der Erstellung Ihres eigenen Telegram-Bots zu beginnen.

1b817e758c699a79.png

Mit The BotFather interagieren

ad3daa08e73502db.png

/start-Befehl senden

Um mit dem BotFather zu beginnen und Ihren ersten Bot zu erstellen, müssen Sie den Befehl /start an den BotFather senden. Er teilt Ihnen dann alle Befehle mit, mit denen Sie mit ihm interagieren können.

/start

Bot-Erstellung mit dem Befehl „/newbot“ starten

Wir erstellen unseren neuen Bot, indem wir den Befehl /newbot an BotFather senden. Sie werden aufgefordert, Ihrem Bot einen Namen zu geben und dann username für den Bot anzugeben. Diese muss immer mit einem bot enden. Beispiel: TetrisBot oder tetris_bot. Dieser Wert muss eindeutig sein.

1f6a74f494d48986.png

Nachdem Sie den Bot erfolgreich erstellt haben, erhalten Sie die folgende Nachricht vom 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

Notieren Sie sich YOUR_TELEGRAM_API_KEY, da wir es im nächsten Abschnitt verwenden.

5. Telegram-Webhook-Anwendung entwickeln

Vorbereiten des Arbeitsverzeichnisses für die Entwicklung Ihrer Telegram-Webhook-Anwendung

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

Erforderliche Abhängigkeiten hinzufügen

Erstellen Sie das Script „requirements.txt“ mit folgendem Inhalt, um die erforderlichen Abhängigkeiten für das Telegram-Webhook-Listener-Script bereitzustellen.

cloudshell edit ./telegram-integration/requirements.txt

Fügen Sie dann die folgenden Abhängigkeiten hinzu:

python-telegram-bot[webhooks]
httpx

Skript für Telegram-Webhook-Listener erstellen

Sobald die Abhängigkeit installiert ist. Jetzt können wir ein Python-Skript main.py script für die Integrationsanwendung erstellen.

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

Kopieren Sie dann den folgenden Code in die Datei.

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

Integrationscode für Telegram-Bots

23b346f5ceb4712a.png

Wenn ein Nutzer eine Nachricht sendet, wird die folgende Pipeline unter handle_message() ausgeführt.

Schritt 1: Identitäts- und Sitzungsableitung

Der Bot ordnet die Telegram-Nutzer-ID eindeutigen ADK-Kennungen zu, um Nutzersitzungen zu unterscheiden:

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

Schritt 2: Asynchroner „Schreib“-Status (Zeilen 53–58)

Damit die Nutzerfreundlichkeit hoch bleibt, während der ADK-Agent den Prompt verarbeitet (was mehrere Sekunden dauern kann), startet der Bot eine asynchrone Hintergrundschleife:

  • asyncio.Event wird als stop_event instanziiert.
  • asyncio.create_task wird im Hintergrund gestartet.send_typing_loop(...)
  • Die Schleife sendet alle 4 Sekunden eine ChatAction.TYPING-Aktion an Telegram, bis stop_event festgelegt ist.

Schritt 3: ADK-Sitzungsüberprüfung und ‑erstellung (Zeilen 61–72)

Bevor der Agent ausgeführt wird, prüft der Bot, ob bereits eine Sitzung vorhanden ist:

  1. Sendet eine GET-Anfrage an /apps/{appName}/users/{userId}/sessions/{sessionId}.
  2. Wenn die Antwort 404 Not Found ist, wird die Sitzung über eine POST-Anfrage an dieselbe URL mit einem leeren JSON-Text erstellt.
  3. Wenn ein anderer Status als 200 oder 404 zurückgegeben wird, wird eine Ausnahme ausgelöst.

Schritt 4: Anfrage an den Agent senden (Zeilen 74–85)

Die Nachrichtennutzlast wird an den ADK-Endpunkt /run weitergeleitet:

  • Endpunkt: POST /run
  • Das Zeitlimit für Anfragen ist auf 60.0 Sekunden festgelegt, um komplexes Reasoning oder Upstream-Latenz zu berücksichtigen.
  • Nutzlaststruktur:
{
  "appName": "restaurant_agent",
  "userId": "tg_<user_id>",
  "sessionId": "tg_sess_<user_id>",
  "newMessage": {
    "role": "user",
    "parts": [{"text": "<user_message>"}]
  }
}

Schritt 5: Antwort parsen (Zeilen 87–101)

Der ADK-Server gibt eine Liste von Nachrichtenevents zurück. Der Bot untersucht das zurückgegebene Array:

  • Ruft das letzte Ereignis in der Liste ab (events[-1]).
  • Über event["content"]["parts"][0]["text"] wird zum Textinhalt navigiert.
  • Wenn keine Ereignisse zurückgegeben werden oder die Textstruktur fehlt, wird ein beschreibender Platzhaltertext festgelegt.

Schritt 6: Teardown und Versand der Antwort (Zeilen 103–111)

  • Im finally-Block wird stop_event festgelegt, wodurch die Schleife der Eingabeaktion beendet wird.
  • Der Bot wartet auf den Abschluss von typing_task, um saubere Ressourcen zu gewährleisten.
  • Schließlich antwortet der Bot im Telegram-Chat mit dem geparsten Antworttext.

6. Telegram-Webhook-Anwendung in Cloud Run bereitstellen

Als Nächstes stellen wir den Telegram-Webhook-Listener in Cloud Run bereit, damit unser Bot mit ihm kommunizieren kann.

Dockerfile erstellen

Zuerst müssen wir das Dockerfile erstellen.

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

Kopieren Sie dann den folgenden Code in die Datei.

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

Der Dienst wird mit python:3.11-slim in einem Container ausgeführt, um die Größe des Images gering zu halten:

  • Installiert Abhängigkeiten aus requirements.txt (python-telegram-bot[webhooks] und httpx).
  • Macht den Standardport 8080 verfügbar.
  • Startet python main.py.

Umgebungsvariablen vorbereiten

Prüfen wir danach noch einmal, ob unser Agent erfolgreich bereitgestellt wurde.

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

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

Als Nächstes fügen wir das zuvor abgerufene TELEGRAM_BOT_TOKEN in .env ein.

echo "TELEGRAM_BOT_TOKEN=YOUR_TELEGRAM_API_KEY" >> .env

Als Nächstes füllen wir die .env-Daten mit anderen benötigten Werten.

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

Deployment-Script erstellen

Erstellen wir ein Bereitstellungsscript, das vollständige Prüfungen durchführt und die App in Cloud Run bereitstellt.

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

Kopieren Sie den folgenden Code in die Datei.

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

Double-Deploy-Skript (deploy.sh)

Bei der Bereitstellung in Google Cloud Run muss der Bot seine eigene URL (SERVICE_URL) in seiner Umgebung angeben, damit er sie als Webhook-Ziel bei Telegram registrieren kann. Um diese zirkuläre Abhängigkeit zu beheben (die URL ist erst nach der Bereitstellung bekannt, der Dienst benötigt die URL jedoch, um ohne Fehler bei der Systemdiagnose zu starten), führt deploy.sh eine zweistufige Bereitstellung durch:

  1. Schritt 1: Erste Bereitstellung: Der Container wird mit einem Platzhalter-DNS (https://google.com) gestartet, damit der Dienst erfolgreich gestartet wird, an den lokalen Port gebunden wird und die ersten Cloud Run-Systemdiagnosen besteht.
  2. Schritt 2: URL abrufen: Der neu erstellte Cloud Run-Endpunkt wird programmatisch mit gcloud run services describe extrahiert.
  3. Schritt 3: Konfiguration aktualisieren: Aktualisiert die Umgebungsvariablen mit der tatsächlichen Live-Dienst-URL. Dadurch wird ein sauberes Rolling Update in Cloud Run ausgelöst und das richtige Webhook-Ziel wird sicher bei der Telegram API registriert.

In Cloud Run bereitstellen

Das Bereitstellungsskript gibt die Agent-URL aus. Öffnen Sie sie in Ihrem Browser, um auf dieselbe ADK-Entwickler-UI zuzugreifen, die in Cloud Run ausgeführt wird.

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

Wenn alles gut geht, können Sie jetzt direkt über die Telegram-Chatanwendung mit Ihrem Bot chatten. Suchen Sie den Bot, den Sie gerade erstellt haben, und beginnen Sie mit der Interaktion:

What Italian dishes do you have?

Oder:

I want something spicy and creamy

Der Bot zeigt den Status „…schreibt“ an und gibt dann bald die Nachricht aus dem ADK zurück, das Sie zuvor erstellt haben.

c62fd4016ddd3c9b.png

7. Glückwunsch!

Sie haben unseren auf dem ADK basierenden KI-Agenten für intelligente Restaurantmenüs entwickelt, bereitgestellt und vollständig in Telegram integriert. Die Kommunikation erfolgt über HTTP-Client-Server. Nutzer können so ihr Lieblingsmenü abfragen und das Restaurant reservieren.

Lerninhalte

  • Restaurant Concierge, ADK-basierte KI-Agenten und MCP Toolbox in Cloud Run bereitstellen und konfigurieren
  • Telegram-Bot mit BotFather einrichten
  • Python-Skripts schreiben, um Telegram-Webhooks zu überwachen und mit ADK-Agenten zu interagieren, um die Nutzeranfrage und die Antwort entsprechend weiterzugeben
  • So implementieren Sie "... typing" in Telegram, um Nutzern in Echtzeit zu signalisieren, dass Nachrichten verarbeitet werden, während sie auf die Antwort des ADK-Agents warten.
  • Python-Skript in Cloud Run bereitstellen und damit interagieren

Aufräumen

Löschen Sie die in diesem Codelab erstellten Ressourcen, um zu vermeiden, dass Ihrem Google Cloud-Konto Gebühren in Rechnung gestellt werden.

gcloud projects delete $GOOGLE_CLOUD_PROJECT

Option 2: Einzelne Ressourcen löschen

# 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