এমন একটি এজেন্টিক এআই অ্যাপ তৈরি করুন যার সাথে আপনার গ্রাহক টেলিগ্রামের মাধ্যমে যোগাযোগ করতে পারবেন।

১. ভূমিকা

নির্বিঘ্ন ও ইন্টারেক্টিভ এজেন্টিক এআই অভিজ্ঞতা তৈরি করুন, যার সাথে আপনার গ্রাহকরা তাদের ব্যবহৃত মেসেজিং অ্যাপ্লিকেশন থেকেই সরাসরি যুক্ত হতে পারবেন। জানুন কীভাবে এমন ইন্টেলিজেন্ট অ্যাপ্লিকেশন তৈরি ও স্থাপন করতে হয় যা ওয়েব ইন্টারফেস এবং আধুনিক মেসেজিং চ্যানেল জুড়ে সাবলীলভাবে চলে।

আপনি যা তৈরি করবেন

জেমিনি দ্বারা চালিত একটি ADK-ভিত্তিক অ্যাপ্লিকেশন "রেস্টুরেন্ট কনসিয়ার্জ" (যা গ্রাহকদের রেস্তোরাঁর মেনু দেখতে ও রিজার্ভেশন বুক করতে সাহায্য করে) এবং টেলিগ্রাম চ্যাট অ্যাপের মধ্যে ইন্টিগ্রেশন। আপনি টেলিগ্রাম বটের সাথে যোগাযোগ করতে পারেন এবং স্বাভাবিক ভাষায় বর্ণনা চাইতে পারেন, যেমন "আমি ঝাল এবং নিরামিষ কিছু চাই।" এরপর বটটি ADK এজেন্টের সাথে সংযুক্ত হবে, যা MCP টুলবক্স ফর ডেটাবেসেস-এর মাধ্যমে একটি ক্লাউড SQL PostgreSQL ডেটাবেস থেকে ডেটা পড়ে এবং লেখে। এই টুলটি ভেক্টর সার্চের জন্য স্বয়ংক্রিয় এমবেডিং তৈরি সহ সমস্ত ডেটাবেস অ্যাক্সেস পরিচালনা করে। এদিকে, ব্যবহারকারী দেখতে পাবেন যে বটটি বার্তাটি স্বীকার করছে এবং ADK এজেন্টের উত্তরের জন্য অপেক্ষা করার সময় ... typing করছে।

c1d28343ed68358a.png

আপনি যা শিখবেন

  • জেমিনি দ্বারা চালিত একটি ADK ভিত্তিক অ্যাপ্লিকেশন "রেস্টুরেন্ট কনসিয়ার্জ" কার্যকর করে স্থাপন করুন।
  • বটফাদার ব্যবহার করে টেলিগ্রাম চ্যাট বট সেটআপ করুন
  • বট ওয়েব হুক শোনার জন্য একটি পাইথন অ্যাপ্লিকেশন লিখুন।
  • ব্যবহারকারীর মেসেজে টেলিগ্রামে ... typing নোটিফিকেশন দেওয়ার জন্য চ্যাট অ্যাকশন পাঠান, এবং আসল উত্তরের জন্য অপেক্ষা করার সময় পর্যায়ক্রমে ... typing পাঠানোর জন্য পোলিং করুন।
  • ব্যবহারকারীর অনুসন্ধান প্রক্রিয়া করতে Restaurant Concierge ক্লাউড রান এন্ডপয়েন্টে কল করুন।
  • ADK এজেন্ট থেকে প্রাপ্ত ফলাফল গ্রহণ করুন, টেলিগ্রামে বার্তা পাঠান এবং বাফারটি বন্ধ করুন।
  • পাইথন অ্যাপ্লিকেশনটি ক্লাউড রানে ডেপ্লয় করুন।
  • আপনার টেলিগ্রাম বটের সাথে যোগাযোগ করুন

পূর্বশর্ত

২. পরিবেশ সেটআপ - পূর্ববর্তী কোডল্যাবের ধারাবাহিকতায়

এই কোডল্যাবে আমরা যে বিবরণগুলো প্রদান করি, সেগুলো আসলে এই পূর্বশর্ত কোডল্যাবটিরই ধারাবাহিকতা: Agentic RAG with ADK, MCP Toolbox, and Cloud SQL অথবা Agents at Scale: Multi-Agent Architecture with A2A Protocol on Agent Runtime and ADK Integration । আপনি পূর্ববর্তী কোডল্যাব থেকে আপনার কাজ চালিয়ে যেতে পারেন।

আমরা আগের কোডল্যাব ওয়ার্কিং ডিরেক্টরিতে বিল্ড করা শুরু করতে পারি (ওয়ার্কিং ডিরেক্টরিটি build-agent-adk-toolbox-cloudsql অথবা adk-a2a-agent-runtime-starter হওয়া উচিত)। বিভ্রান্তি এড়াতে, নতুন করে শুরু করার সময় আমরা যে ডিরেক্টরি নামটি ব্যবহার করি, সেই একই নামে ডিরেক্টরিটির নাম পরিবর্তন করে নিই।

আপনি যদি ADK, MCP টুলবক্স এবং ক্লাউড SQL সহ Agentic RAG ল্যাব থেকে চালিয়ে যান:

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 ইতিমধ্যেই ডেপ্লয় করা হয়েছে এবং অ্যাক্সেস করার জন্য একটি পাবলিক ইউআরএল রয়েছে।

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

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

আপনি যদি ইউআরএল-টি অ্যাক্সেস করতে পারেন, তাহলে আপনি পরবর্তী বিভাগে যেতে প্রস্তুত: Create Telegram Bot

৩. পরিবেশ সেটআপ - স্টার্টার রিপো দিয়ে নতুন করে শুরু করুন

এই ধাপে আপনার ক্লাউড শেল এনভায়রনমেন্ট প্রস্তুত করা হয়, আপনার গুগল ক্লাউড প্রজেক্ট কনফিগার করা হয় এবং স্টার্টার রিপোজিটরি ক্লোন করা হয়।

ওপেন ক্লাউড শেল

আপনার ব্রাউজারে ক্লাউড শেল খুলুন। ক্লাউড শেল এই কোডল্যাবের জন্য আপনার প্রয়োজনীয় সমস্ত সরঞ্জাম সহ একটি পূর্ব-কনফিগার করা পরিবেশ প্রদান করে। অনুরোধ করা হলে অনুমোদন করুন (Authorize) বোতামে ক্লিক করুন।

এরপর টার্মিনাল খোলার জন্য " ভিউ " -> " টার্মিনাল "-এ ক্লিক করুন। আপনার ইন্টারফেসটি দেখতে এইরকম হবে।

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 প্রজেক্ট হিসেবে সেট করুন।

ক্লাউড শেল টার্মিনাল প্রম্পটে আপনার ওয়ার্কিং ডিরেক্টরির পাশে থাকা হলুদ লেখাটি দেখে প্রজেক্টটি সঠিকভাবে সেট করা হয়েছে কিনা তা যাচাই করুন। সেখানে আপনার প্রজেক্ট আইডি প্রদর্শিত হওয়া উচিত।

5c515e235ee1179f.png

স্টার্টার অবকাঠামো সেটআপ

প্রথমে, আমাদের uv ব্যবহার করে পাইথন ডিপেন্ডেন্সিগুলো ইনস্টল করতে হবে। এটি রাস্ট (Rust) ভাষায় লেখা একটি দ্রুতগতির পাইথন প্যাকেজ ও প্রজেক্ট ম্যানেজার (uv ডকুমেন্টেশন দেখুন)। এই কোডল্যাবটি পাইথন প্রজেক্ট রক্ষণাবেক্ষণে গতি ও সরলতার জন্য এটি ব্যবহার করে।

uv sync

এরপর, সম্পূর্ণ সেটআপ স্ক্রিপ্টটি চালান, যা ক্লাউড এসকিউএল ইনস্ট্যান্স তৈরি করে, ডেটা সিড করে এবং টুলবক্স সার্ভিসটি ডেপ্লয় করে, যা আমাদের রেস্তোরাঁ এজেন্টের প্রাথমিক অবস্থা হিসেবে কাজ করবে।

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

এর ফলে:

  • একটি ক্লাউড এসকিউএল ইনস্ট্যান্স তৈরি করুন এবং ডাটাবেসে ডেটা যুক্ত করুন (পর্যায় ১)
  • এজেন্ট এনভায়রনমেন্ট কনফিগারেশন তৈরি করুন এবং লোকাল টুলবক্স সার্ভিস চালু করুন (পর্যায় ২)
  • ক্লাউড রান-এ টুলবক্স এবং এজেন্ট পরিষেবাগুলি স্থাপন করুন (পর্যায় ৩)

এই ডেপ্লয়মেন্টটি সম্পন্ন হওয়ার পর, আপনি ক্লাউড রান ইউআরএল-এ ADK Dev UI অ্যাক্সেস করতে পারবেন।

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 ডেভ UI খুলুন, restaurant_agent নির্বাচন করুন, এবং নিচের উদাহরণের মতো কোয়েরি দিয়ে পরীক্ষা করুন:

What Italian dishes do you have?

অথবা,

I want something spicy and creamy

এখন, পরবর্তী পদক্ষেপ হলো, আমরা কীভাবে শুধুমাত্র ওয়েব ডেভেলপমেন্ট ইন্টারফেস থেকে টেলিগ্রাম মেসেজিং চ্যানেলে যেতে পারি।

৪. টেলিগ্রাম বট তৈরি করুন

টেলিগ্রাম একটি সুপরিচিত বিনামূল্যের মেসেজিং প্ল্যাটফর্ম যা কমিউনিটি-ভিত্তিক যোগাযোগের জন্য ব্যাপকভাবে ব্যবহৃত হচ্ছে। এর অন্যতম কারণ হলো, এটি সহজে ইন্টিগ্রেট করার অনেক উপায় প্রদান করে, যার ফলে ব্যবহারকারীরা বিভিন্ন ধরনের ফাংশনসহ সহজেই নিজেদের বট তৈরি করতে পারেন।

এক্ষেত্রে আমরা প্রথমবারের মতো নিজেদের বট তৈরি করার জন্য বটফাদার (BotFather) ব্যবহার করব। মনে রাখবেন যে, যদিও আমরা এই সেশনের জন্য টেলিগ্রাম ব্যবহার করছি, একই পদ্ধতি হোয়াটসঅ্যাপ বা আপনার পছন্দের অন্য যেকোনো মেসেজিং প্ল্যাটফর্মের জন্যও ব্যবহার করা যেতে পারে।

আপনার নিজের বট তৈরি করতে বটফাদার ব্যবহার করুন

আপনার নিজের টেলিগ্রাম বট তৈরি করা শুরু করতে আপনার ওয়েব ব্রাউজারে https://telegram.me/BotFather এই লিঙ্কে যান।

1b817e758c699a79.png

বটফাদারের সাথে আলাপচারিতা শুরু করুন

ad3daa08e73502db.png

/start কমান্ড পাঠান

বটফাদার ব্যবহার শুরু করতে এবং আপনার প্রথম বট তৈরি করতে, আপনাকে বটফাদার-এ /start মেসেজটি পাঠাতে হবে। এরপর এটি আপনাকে এর সমস্ত কমান্ড জানিয়ে দেবে, যা ব্যবহার করে আপনি এটিকে আরও পরিচালনা করতে পারবেন।

/start

/newbot কমান্ড ব্যবহার করে বট তৈরি শুরু করুন।

BotFather-এ /newbot কমান্ড পাঠিয়ে আমাদের নতুন বটটি তৈরি করা যাক। এটি আপনাকে আপনার বটের একটি নাম দিতে বলবে এবং তারপর বটের username দিতে বলবে, যার শেষে অবশ্যই ' bot থাকতে হবে। উদাহরণস্বরূপ, TetrisBot বা tetris_bot । এটি অবশ্যই অনন্য হতে হবে।

1f6a74f494d48986.png

সফলভাবে বট তৈরি হলে, আপনি বটফাদারের কাছ থেকে নিম্নলিখিত বার্তাটি পাবেন।

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 টি লিখে রাখুন, আমরা এটি পরবর্তী অংশে ব্যবহার করব।

৫. টেলিগ্রাম ওয়েবহুক অ্যাপ্লিকেশনটি তৈরি করুন।

আপনার টেলিগ্রাম ওয়েবহুক অ্যাপ্লিকেশন তৈরি শুরু করার জন্য চলুন ওয়ার্কিং ডিরেক্টরি প্রস্তুত করি।

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

প্রয়োজনীয় নির্ভরতা যোগ করুন

টেলিগ্রাম ওয়েবহুক লিসেনার স্ক্রিপ্টের জন্য পর্যাপ্ত ডিপেন্ডেন্সি সরবরাহ করতে নিম্নলিখিত বিষয়বস্তু সহ requirements.txt স্ক্রিপ্টটি তৈরি করুন।

cloudshell edit ./telegram-integration/requirements.txt

তারপর নিম্নলিখিত নির্ভরতাগুলি যোগ করুন।

python-telegram-bot[webhooks]
httpx

টেলিগ্রাম ওয়েবহুক লিসেনারের জন্য স্ক্রিপ্ট তৈরি করুন

একবার ডিপেন্ডেন্সি ইনস্টল হয়ে গেলে, এখন আমরা ইন্টিগ্রেশন অ্যাপ্লিকেশনের জন্য একটি পাইথন স্ক্রিপ্ট main.py ) তৈরি করতে পারি।

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

টেলিগ্রাম বট ইন্টিগ্রেশন কোড বোঝা

23b346f5ceb4712a.png

যখন কোনো ব্যবহারকারী বার্তা পাঠান, তখন handle_message() এর অধীনে নিম্নলিখিত পাইপলাইনটি চলে।

ধাপ ১: পরিচয় ও সেশন নির্ধারণ

ব্যবহারকারীর সেশনগুলোকে স্বতন্ত্র রাখতে বটটি টেলিগ্রাম ইউজার আইডিকে অনন্য ADK আইডেন্টিফায়ারের সাথে সংযুক্ত করে:

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

ধাপ ২: অ্যাসিঙ্ক্রোনাস "টাইপিং" স্ট্যাটাস (লাইন ৫৩–৫৮)

ADK এজেন্ট যখন প্রম্পটটি প্রসেস করে (যেটিতে কয়েক সেকেন্ড সময় লাগতে পারে), সেই সময়ে ব্যবহারকারীর অভিজ্ঞতা যেন অত্যন্ত দ্রুত সাড়া দেয় তা নিশ্চিত করতে বটটি একটি অ্যাসিঙ্ক্রোনাস ব্যাকগ্রাউন্ড লুপ চালু করে:

  • asyncio.Event stop_event হিসেবে ইনস্ট্যানশিয়েট করা হয়।
  • asyncio.create_task ব্যাকগ্রাউন্ডে send_typing_loop(...) চালু করে।
  • stop_event সেট না করা পর্যন্ত লুপটি প্রতি ৪ সেকেন্ড পর পর টেলিগ্রামে একটি ChatAction.TYPING অ্যাকশন পাঠায়।

ধাপ ৩: ADK সেশন যাচাইকরণ ও তৈরি (লাইন ৬১–৭২)

এজেন্টটি চালু করার আগে, বটটি যাচাই করে দেখে যে কোনো সেশন আগে থেকেই বিদ্যমান আছে কিনা:

  1. /apps/{appName}/users/{userId}/sessions/{sessionId} -এ একটি GET অনুরোধ পাঠানো হয়।
  2. যদি প্রতিক্রিয়াটি 404 Not Found হয়, তাহলে এটি একটি খালি JSON বডি সহ একই URL-এ একটি POST অনুরোধের মাধ্যমে সেশনটি তৈরি করে।
  3. যদি 200 বা 404 ব্যতীত অন্য কোনো স্ট্যাটাস ফেরত আসে, তাহলে একটি ব্যতিক্রম ঘটে।

ধাপ ৪: এজেন্টের কাছে অনুরোধ পাঠানো (লাইন ৭৪–৮৫)

মেসেজ পেলোডটি 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>"}]
  }
}

ধাপ ৫: প্রতিক্রিয়া বিশ্লেষণ (লাইন ৮৭–১০১)

ADK সার্ভার মেসেজ ইভেন্টের একটি তালিকা ফেরত দেয়। বটটি ফেরত আসা অ্যারেটি পরীক্ষা করে:

  • এটি তালিকার শেষ ইভেন্টটি ( events[-1] ) পুনরুদ্ধার করে।
  • এটি event["content"]["parts"][0]["text"] এর মাধ্যমে টেক্সট কন্টেন্টে নেভিগেট করে।
  • যদি কোনো ইভেন্ট ফেরত না আসে বা টেক্সট কাঠামো অনুপস্থিত থাকে, তাহলে একটি বর্ণনামূলক প্লেসহোল্ডার টেক্সট সেট করা হয়।

ধাপ ৬: সিস্টেম ভেঙে ফেলা এবং প্রতিক্রিয়া প্রেরণ (লাইন ১০৩–১১১)

  • finally ব্লকে stop_event সেট করা হয়, যা টাইপিং অ্যাকশন লুপটিকে থামিয়ে দেয়।
  • রিসোর্স পরিষ্কার আছে কিনা তা নিশ্চিত করার জন্য বটটি typing_task সম্পন্ন হওয়ার অপেক্ষা করে।
  • অবশেষে, বটটি পার্স করা প্রতিক্রিয়া টেক্সটটি দিয়ে টেলিগ্রাম চ্যাটে উত্তর দেয়।

৬. টেলিগ্রাম ওয়েবহুক অ্যাপ্লিকেশনটি ক্লাউড রান-এ স্থাপন করুন।

এরপরে, আমরা ক্লাউড রান-এ টেলিগ্রাম ওয়েবহুক লিসেনারটি স্থাপন করব, যাতে আমাদের বট এটির সাথে যোগাযোগ করতে পারে।

ডকারফাইল তৈরি করুন

প্রথমে, আমাদের ডকারফাইলটি তৈরি করতে হবে।

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

ডিপ্লয়মেন্ট স্ক্রিপ্ট তৈরি করুন

চলুন একটি ডেপ্লয়মেন্ট স্ক্রিপ্ট তৈরি করি যা সম্পূর্ণ চেক করবে এবং অ্যাপটিকে ক্লাউড রান-এ ডেপ্লয় করবে।

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) উল্লেখ করতে হয়, যাতে এটি টেলিগ্রামের সাথে ওয়েবহুক টার্গেট হিসেবে রেজিস্টার করতে পারে। এই চক্রাকার নির্ভরতা (ডেপ্লয়মেন্টের আগে URL-টি অজানা থাকে, কিন্তু হেলথ চেক ব্যর্থতা ছাড়াই সার্ভিসটি চালু হওয়ার জন্য URL-টির প্রয়োজন হয়) সমাধান করতে, deploy.sh একটি দুই-ধাপের ডেপ্লয়মেন্ট সম্পন্ন করে:

  1. ধাপ ১: প্রাথমিক ডেপ্লয়মেন্ট : একটি প্লেসহোল্ডার ডিএনএস ( https://google.com ) দিয়ে কন্টেইনারটি বুট করা হয়, যাতে সার্ভিসটি সফলভাবে চালু হয়, লোকাল পোর্টের সাথে বাইন্ড হয় এবং ক্লাউড রানের প্রাথমিক হেলথ চেকগুলো পাস করে।
  2. ধাপ ২: ইউআরএল সংগ্রহ করা : gcloud run services describe ব্যবহার করে প্রোগ্রাম্যাটিকভাবে নতুন তৈরি করা ক্লাউড রান এন্ডপয়েন্টটি বের করে আনা হয়।
  3. ধাপ ৩: কনফিগারেশন আপডেট করুন : এটি প্রকৃত লাইভ সার্ভিস URL দিয়ে এনভায়রনমেন্ট ভেরিয়েবলগুলো আপডেট করে। এর ফলে ক্লাউড রান-এ একটি ক্লিন রোলিং আপডেট চালু হয় এবং টেলিগ্রাম API-এর সাথে সঠিক ওয়েবহুক টার্গেটটি নিরাপদে রেজিস্টার হয়ে যায়।

ক্লাউড রানে স্থাপন করুন

ডিপ্লয়মেন্ট স্ক্রিপ্টটি এজেন্ট ইউআরএল প্রিন্ট করে। ক্লাউড রানে চলমান একই ADK ডেভ UI অ্যাক্সেস করতে এটি আপনার ব্রাউজারে খুলুন।

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

সবকিছু ঠিকঠাক থাকলে, এটাই সেই সময় যখন আপনি সরাসরি টেলিগ্রাম চ্যাট অ্যাপ্লিকেশন থেকে আপনার বটের সাথে চ্যাট করা শুরু করতে পারেন, আপনার এইমাত্র তৈরি করা বটটি খুঁজে বের করুন এবং এর সাথে কথোপকথন শুরু করুন:

What Italian dishes do you have?

অথবা,

I want something spicy and creamy

দেখুন বটটি "...টাইপ করছে" স্ট্যাটাস পাঠাচ্ছে এবং তারপরেই এটি আপনার আগে তৈরি করা ADK থেকে মেসেজটি ফেরত পাঠাবে!

c62fd4016ddd3c9b.png

৭. অভিনন্দন!

আপনি আমাদের স্মার্ট রেস্তোরাঁ মেনু অ্যাসিস্ট্যান্ট ADK ভিত্তিক এআই এজেন্টটি তৈরি, স্থাপন এবং টেলিগ্রামের সাথে HTTP ক্লায়েন্ট সার্ভার যোগাযোগের মাধ্যমে সম্পূর্ণরূপে সমন্বিত করেছেন, যা মানুষকে তাদের পছন্দের মেনু সম্পর্কে জানতে এবং রেস্তোরাঁ বুক করতে সক্ষম করে।

আপনি যা শিখেছেন

  • ক্লাউড রানে রেস্টুরেন্ট কনসিয়ার্জ, ADK ভিত্তিক এজেন্ট এবং MCP টুলবক্স স্থাপন ও কনফিগার করুন।
  • বটফাদার ব্যবহার করে কীভাবে টেলিগ্রাম বট সেটআপ করবেন
  • টেলিগ্রাম ওয়েবহুক শোনার জন্য এবং ব্যবহারকারীর জিজ্ঞাসা ও প্রতিক্রিয়া যথাযথভাবে প্রেরণ করতে ADK এজেন্টের সাথে যোগাযোগ করার জন্য কীভাবে পাইথন স্ক্রিপ্ট লিখতে হয়।
  • ADK এজেন্টের প্রতিক্রিয়ার জন্য অপেক্ষা করার সময়, মেসেজ প্রসেস হচ্ছে—এই সংকেতটি ব্যবহারকারীকে রিয়েল-টাইম ফিডব্যাক হিসেবে বোঝানোর জন্য টেলিগ্রামে কীভাবে "... typing" প্রয়োগ করা যায়।
  • পাইথন স্ক্রিপ্টটি ক্লাউড রানে কীভাবে ডেপ্লয় করবেন এবং এর সাথে কীভাবে ইন্টারঅ্যাক্ট করতে পারবেন

পরিষ্কার করা

আপনার গুগল ক্লাউড অ্যাকাউন্টে চার্জ হওয়া এড়াতে, এই কোডল্যাবে তৈরি করা রিসোর্সগুলো মুছে ফেলুন।

gcloud projects delete $GOOGLE_CLOUD_PROJECT

বিকল্প ২: স্বতন্ত্র রিসোর্সগুলো মুছে ফেলুন

# 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