জেমা এবং জেমিনিকে নিয়ে GKE তে একটি হাইব্রিড এআই চ্যাট অ্যাপ তৈরি করুন

১. ভূমিকা

সংক্ষিপ্ত বিবরণ

এই ল্যাবে, আপনি গুগল কুবারনেটিস ইঞ্জিন (GKE)-এ একটি ফুল-স্ট্যাক এআই-চালিত চ্যাট অ্যাপ্লিকেশন তৈরি এবং ডেপ্লয় করবেন। এই "হাইব্রিড" অ্যাপ্লিকেশনটি একটি শক্তিশালী আর্কিটেকচারাল প্যাটার্ন প্রদর্শন করে: আপনার ক্লাস্টারে সরাসরি চলমান একটি সেলফ-হোস্টেড ওপেন মডেল (Gemma 3 12B) এবং একটি ম্যানেজড এআই সার্ভিসের (Vertex AI-এর মাধ্যমে Gemini 2.5 Flash) মধ্যে নির্বিঘ্নে সুইচ করার ক্ষমতা।

                                   +----------------------+
                                   |   User (Web Browser) |
                                   +-----------+----------+
                                               |
                                               v
+----------------------------------------------+---------------------------------------------+
| Google Cloud Platform                        |                                             |
|   |                                  +-------+-------+                                     |
|   |                                  | Load Balancer |                                     |
|   |                                  +-------+-------+                                     |
|   |                                          v                                             |
|   +------------------------------------------+-----------------------------------------+   |
|   |  Google Kubernetes Engine (GKE)          |                                         |   |
|   |                                          v                                         |   |
|   |                              +-----------+-----------+                             |   |
|   |                              |    Gradio Chat App    |                             |   |
|   |                              +--+-----------------+--+                             |   |
|   |                                 |                 |                                |   |
|   |                   (Self-hosted) |                 | (Managed via SDK)              |   |
|   |                                 v                 |                                |   |
|   |                  +--------------+---+             |                                |   |
|   |                  | Gemma 3 Model    |             |                                |   |
|   |                  | (GPU Node)       |             |                                |   |
|   |                  +------------------+             |                                |   |
|   +---------------------------------------------------|--------------------------------+   |
|                                                       |                                    |
|                                                       v                                    |
|                                            +----------+-----------+                        |
|                                            | Vertex AI (Gemini)   |                        |
|                                            +----------------------+                        |
|                                                       | (Save History)                     |
|                                                       v                                    |
|                                            +----------+-----------+                        |
|                                            | Firestore Database   |                        |
|                                            +----------------------+                        |
+--------------------------------------------------------------------------------------------+

আপনি Terraform ব্যবহার করে অবকাঠামো প্রস্তুত করবেন, যার মধ্যে একটি GKE Autopilot ক্লাস্টার এবং চ্যাট সেশনের ইতিহাস সংরক্ষণের জন্য একটি Firestore ডেটাবেস অন্তর্ভুক্ত থাকবে। এরপর আপনি একাধিক পালায় কথোপকথন পরিচালনা, উভয় AI মডেলের সাথে ইন্টারফেস করা এবং Cloud Build ও Skaffold ব্যবহার করে চূড়ান্ত অ্যাপ্লিকেশনটি স্থাপন করার জন্য Python অ্যাপ্লিকেশন কোডটি সম্পূর্ণ করবেন।

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

  • Terraform ব্যবহার করে GKE এবং Firestore পরিকাঠামো প্রস্তুত করুন।
  • Kubernetes manifest ব্যবহার করে GKE Autopilot-এ একটি বৃহৎ ল্যাঙ্গুয়েজ মডেল (Gemma) ডেপ্লয় করুন।
  • পাইথনে একটি গ্র্যাডিও চ্যাট ইন্টারফেস তৈরি করুন যা বিভিন্ন এআই ব্যাকএন্ডের মধ্যে পরিবর্তন করতে পারে।
  • চ্যাট সেশনের ইতিহাস সংরক্ষণ ও পুনরুদ্ধার করতে ফায়ারস্টোর ব্যবহার করুন।
  • আপনার GKE ওয়ার্কলোডগুলিকে Google Cloud পরিষেবাগুলিতে (Vertex AI, Firestore) নিরাপদে অ্যাক্সেস দেওয়ার জন্য ওয়ার্কলোড আইডেন্টিটি কনফিগার করুন।

পূর্বশর্ত

  • বিলিং সক্ষম একটি গুগল ক্লাউড প্রজেক্ট।
  • পাইথন, কুবারনেটিস এবং প্রচলিত কমান্ড-লাইন টুল সম্পর্কে প্রাথমিক ধারণা।
  • জেমা মডেলগুলিতে অ্যাক্সেস সহ একটি হাগিং ফেস টোকেন

২. প্রকল্প স্থাপন

  1. যদি আপনার আগে থেকে কোনো গুগল অ্যাকাউন্ট না থাকে, তাহলে আপনাকে অবশ্যই একটি গুগল অ্যাকাউন্ট তৈরি করতে হবে।
    • কর্মক্ষেত্র বা শিক্ষা প্রতিষ্ঠানের অ্যাকাউন্টের পরিবর্তে ব্যক্তিগত অ্যাকাউন্ট ব্যবহার করুন। কর্মক্ষেত্র এবং শিক্ষা প্রতিষ্ঠানে এমন কিছু সীমাবদ্ধতা থাকতে পারে, যার ফলে আপনি এই ল্যাবের জন্য প্রয়োজনীয় এপিআই (API) সক্রিয় করতে পারবেন না।
  2. গুগল ক্লাউড কনসোলে সাইন-ইন করুন।
  3. ক্লাউড কনসোলে বিলিং চালু করুন
    • এই ল্যাবটি সম্পন্ন করতে ক্লাউড রিসোর্সে ১ মার্কিন ডলারেরও কম খরচ হওয়া উচিত।
    • পরবর্তী চার্জ এড়াতে, এই ল্যাবের শেষে দেওয়া ধাপগুলো অনুসরণ করে আপনি রিসোর্সগুলো মুছে ফেলতে পারেন।
    • নতুন ব্যবহারকারীরা ৩০০ মার্কিন ডলারের ফ্রি ট্রায়ালের জন্য যোগ্য।
  4. একটি নতুন প্রজেক্ট তৈরি করুন অথবা বিদ্যমান কোনো প্রজেক্ট পুনরায় ব্যবহার করুন।

ওপেন ক্লাউড শেল এডিটর

  1. সরাসরি ক্লাউড শেল এডিটর- এ যেতে এই লিঙ্কে ক্লিক করুন।
  2. আজ যেকোনো সময়ে অনুমোদনের জন্য অনুরোধ করা হলে, চালিয়ে যাওয়ার জন্য 'অনুমোদন করুন' (Authorize) বোতামে ক্লিক করুন। ক্লাউড শেল অনুমোদন করতে ক্লিক করুন
  3. যদি স্ক্রিনের নিচে টার্মিনালটি দেখা না যায়, তাহলে এটি খুলুন:
    • ভিউ ক্লিক করুন
    • টার্মিনালে ক্লিক করুন ক্লাউড শেল এডিটরে নতুন টার্মিনাল খুলুন
  4. টার্মিনালে এই কমান্ডটি দিয়ে আপনার প্রজেক্ট সেট করুন:
    • বিন্যাস:
      gcloud config set project [PROJECT_ID]
      
    • উদাহরণ:
      gcloud config set project lab-project-id-example
      
    • যদি আপনি আপনার প্রজেক্ট আইডি মনে করতে না পারেন:
      • আপনি আপনার সমস্ত প্রজেক্ট আইডি তালিকাভুক্ত করতে পারেন:
        gcloud projects list | awk '/PROJECT_ID/{print $2}'
        
      ক্লাউড শেল এডিটর টার্মিনালে প্রজেক্ট আইডি সেট করুন
  5. আপনি এই বার্তাটি দেখতে পাবেন:
    Updated property [core/project].
    
    যদি আপনি একটি WARNING দেখতে পান এবং আপনাকে Do you want to continue (Y/n)? জিজ্ঞাসা করা হয়, তাহলে সম্ভবত আপনি প্রজেক্ট আইডি ভুলভাবে প্রবেশ করিয়েছেন। n চাপুন, Enter চাপুন এবং gcloud config set project কমান্ডটি আবার চালানোর চেষ্টা করুন।

রিপোজিটরি ক্লোন করুন

আপনার ক্লাউড শেল টার্মিনালে, প্রজেক্ট রিপোজিটরিটি ক্লোন করুন এবং প্রজেক্ট ডিরেক্টরিতে যান:

git clone https://github.com/GoogleCloudPlatform/devrel-demos.git
cd devrel-demos/containers/gradio-chat-gke

প্রকল্পের কাঠামোটি অন্বেষণ করতে একটু সময় নিন:

gradio-chat-gke/
├── app/
   ├── app.py                # Main application logic (you will edit this)
   ├── requirements.txt      # Python dependencies
   └── themes.py             # UI theming
├── deploy/
   ├── chat-deploy.yaml      # Kubernetes deployment for the chat app
   ├── Dockerfile            # Container definition for the chat app
   └── gemma3-12b-deploy.yaml# Kubernetes deployment for Gemma model
├── infra/
   └── main.tf               # Terraform infrastructure definition
└── skaffold.yaml             # Skaffold configuration for building/deploying

পরিবেশ ভেরিয়েবল সেট করুন

আপনার প্রজেক্ট আইডি এবং প্রজেক্ট নম্বরের জন্য এনভায়রনমেন্ট ভেরিয়েবল সেট করুন। এগুলো Terraform এবং পরবর্তী কমান্ডগুলোতে ব্যবহৃত হবে।

export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $GOOGLE_CLOUD_PROJECT --format="value(projectNumber)")
export REGION=us-central1

ক্লাউড রিসোর্স ম্যানেজার এপিআই সক্রিয় করুন

আপনার প্রোজেক্টের রিসোর্সগুলো পরিচালনা করার জন্য টেরাফর্মে ক্লাউড রিসোর্স ম্যানেজার এপিআই (Cloud Resource Manager API) সক্রিয় থাকা প্রয়োজন, তাই আমাদের প্রথমে সেটি সক্রিয় করতে হবে। পরবর্তীতে, আমরা স্ক্যাফোল্ড (Skaffold) ব্যবহার করে আমাদের চ্যাট অ্যাপ্লিকেশনটি ডেপ্লয় করব, যা আমাদের কন্টেইনার ইমেজ তৈরি করার জন্য ক্লাউড বিল্ড (Cloud Build) ব্যবহার করে। এখন আমরা স্টোরেজ এপিআই (storage api) সক্রিয় করব এবং ক্লাউড বিল্ডের জন্য প্রয়োজনীয় বাকেট (bucket) তৈরি করব। এই প্রোজেক্টের জন্য প্রয়োজনীয় বাকি এপিআইগুলো সক্রিয় করতে আমরা সরাসরি টেরাফর্মই ব্যবহার করব।

gcloud services enable cloudresourcemanager.googleapis.com storage-api.googleapis.com

ক্লাউড বিল্ড স্টেজিং বাকেট তৈরি করুন

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

এর অস্তিত্ব নিশ্চিত করতে এখনই এটি তৈরি করুন:

gcloud storage buckets create gs://${GOOGLE_CLOUD_PROJECT}_cloudbuild

(যদি বাকেটটি আগে থেকেই বিদ্যমান থাকার কোনো ত্রুটি বার্তা আসে, তবে আপনি নিশ্চিন্তে তা উপেক্ষা করতে পারেন)।

৩. টেরাফর্ম ব্যবহার করে ইনফ্রাস্ট্রাকচার প্রস্তুত করুন

আমরা প্রয়োজনীয় গুগল ক্লাউড রিসোর্সগুলো সেট আপ করার জন্য টেরাফর্ম ব্যবহার করব। এটি একটি পুনরুৎপাদনযোগ্য এবং সামঞ্জস্যপূর্ণ পরিবেশ নিশ্চিত করে।

  1. পরিকাঠামো ডিরেক্টরিতে যান:
    cd infra
    

এই ফাইলটিতে এই প্রোজেক্টের জন্য আমাদের প্রয়োজনীয় অতিরিক্ত API-গুলো সংজ্ঞায়িত করা হয়েছে: cloudbuild, artifactregistry, container (gke), firestore, এবং aiplatform (vertexai)। Terraform-এর মাধ্যমে কীভাবে API-গুলো সক্রিয় করা হয় তা দেখতে ফাইলটিতে বা নিচে দেখুন:

resource "google_project_service" "cloudbuild" {
  service            = "cloudbuild.googleapis.com"
  disable_on_destroy = false
  project            = var.project_id
}

resource "google_project_service" "artifactregistry" {
  service            = "artifactregistry.googleapis.com"
  disable_on_destroy = false
  project            = var.project_id
}

resource "google_project_service" "container" {
  service            = "container.googleapis.com"
  disable_on_destroy = false
  project            = var.project_id
}

resource "google_project_service" "firestore" {
  service            = "firestore.googleapis.com"
  disable_on_destroy = false
  project            = var.project_id
}

resource "google_project_service" "vertexai" {
  service            = "aiplatform.googleapis.com"
  disable_on_destroy = false
  project            = var.project_id
}

GKE ক্লাস্টার সংজ্ঞায়িত করুন

আপনার এডিটরে infra/main.tf খুলুন। আপনি কয়েকটি # TODO কমেন্ট দেখতে পাবেন। আপনি এটি ম্যানুয়ালি খুলতে পারেন অথবা এডিটরে ফাইলটি খোলার জন্য এই কমান্ডটি ব্যবহার করতে পারেন:

cloudshell edit main.tf

প্রথমে, আমাদের কুবারনেটিস ক্লাস্টারটি সংজ্ঞায়িত করতে হবে। আমরা জিকেই অটোপাইলট (GKE Autopilot) ব্যবহার করব, যা এআই (AI) ওয়ার্কলোডের জন্য আদর্শ, কারণ এটি স্বয়ংক্রিয়ভাবে নোড ব্যবস্থাপনা পরিচালনা করে।

# TODO: Create a GKE Autopilot Cluster খুঁজুন এবং এর নিচে নিম্নলিখিত ব্লকটি যোগ করুন:

# Create a GKE Autopilot Cluster
resource "google_container_cluster" "primary" {
  name     = var.cluster_name
  location = var.region
  project  = var.project_id

  # Enable Autopilot mode
  enable_autopilot = true

  deletion_protection = false

  # Networking
  network    = "default"
  subnetwork = "projects/${var.project_id}/regions/${var.region}/subnetworks/default"

  # Timeout for cluster creation
  timeouts {
    create = "30m"
    update = "30m"
  }

  depends_on = [google_project_service.container]
}

enable_autopilot = true লক্ষ্য করুন। এই একটি মাত্র লাইন আমাদের নোড পুল পরিচালনা, অটোস্কেলিং এবং জিপিইউ ওয়ার্কলোড বিন-প্যাক করার ঝামেলা থেকে বাঁচায়।

ফায়ারস্টোর ডেটাবেস সংজ্ঞায়িত করুন

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

# TODO: Create a Firestore Database এবং যোগ করুন:

resource "google_firestore_database" "database" {
  project     = var.project_id
  name        = "chat-app-db"
  location_id = "nam5"
  type        = "FIRESTORE_NATIVE"

  depends_on = [google_project_service.firestore]
}

ডাটাবেস রিসোর্স যোগ করার পর, # TODO: Create an initial Firestore Document খুঁজুন এবং নিম্নলিখিত ব্লকটি যোগ করুন। এই রিসোর্সটি আমাদের কালেকশনে একটি প্রাথমিক প্লেসহোল্ডার ডকুমেন্ট তৈরি করে, যা ডাটাবেসের কাঠামো শুরু করতে সহায়ক।

resource "google_firestore_document" "initial_document" {
  project     = var.project_id
  collection  = "chat_sessions"
  document_id = "initialize"
  fields = <<EOF
  EOF

  depends_on = [google_firestore_database.database]
}

ওয়ার্কলোড পরিচয় সংজ্ঞায়িত করুন

অবশেষে, আমাদের নিরাপত্তা কনফিগার করতে হবে। আমরা চাই আমাদের Kubernetes পডগুলো যেন কোনো সিক্রেট বা এপিআই কী পরিচালনা না করেই Vertex AI এবং Firestore অ্যাক্সেস করতে পারে। আমরা Workload Identity-এর মাধ্যমে এটি করে থাকি।

আমাদের অ্যাপটি যে কুবারনেটিস সার্ভিস অ্যাকাউন্ট (KSA) ব্যবহার করবে, সেটিকে আমরা প্রয়োজনীয় IAM রোলগুলো প্রদান করব।

দ্রষ্টব্য: এই বাইন্ডিংগুলিতে উল্লেখিত Kubernetes সার্ভিস অ্যাকাউন্ট ( gradio-chat-ksa ) এখনও বিদ্যমান নেই! আমরা যখন ক্লাস্টারে আমাদের অ্যাপ্লিকেশনটি ডেপ্লয় করব, তখন এটি তৈরি করা হবে। এই IAM বাইন্ডিংগুলি আগে থেকে প্রস্তুত করে রাখা সম্পূর্ণ ঠিক আছে (এবং এটি একটি প্রচলিত অভ্যাস)।

# TODO: Configure Workload Identity IAM bindings খুঁজুন এবং যোগ করুন:

locals {
  ksa_principal = "principal://iam.googleapis.com/projects/${var.project_number}/locations/global/workloadIdentityPools/${var.project_id}.svc.id.goog/subject/ns/default/sa/gradio-chat-ksa"
}

resource "google_project_iam_member" "ksa_token_creator" {
  project = var.project_id
  role    = "roles/iam.serviceAccountTokenCreator"
  member  = local.ksa_principal
}

resource "google_project_iam_member" "ksa_vertex_user" {
  project = var.project_id
  role    = "roles/aiplatform.user"
  member  = local.ksa_principal
}

resource "google_project_iam_member" "ksa_datastore_user" {
  project = var.project_id
  role    = "roles/datastore.user"
  member  = local.ksa_principal
}

কনফিগারেশন প্রয়োগ করুন

এখন যেহেতু আমাদের পরিকাঠামো সংজ্ঞায়িত করা হয়েছে, চলুন এটিকে প্রস্তুত করি।

  1. টেরাফর্ম ব্যবহারের জন্য আমাদের প্রথমে কয়েকটি ভেরিয়েবল সেট করতে হবে। আমরা এনভায়রনমেন্ট ভেরিয়েবল ব্যবহার করে এটি করব:
export TF_VAR_project_id=$(gcloud config get-value project)
export TF_VAR_project_number=$(gcloud projects describe $TF_VAR_project_id --format="value(projectNumber)")
export TF_VAR_region="us-central1"
  1. টেরাফর্ম শুরু করুন:
terraform init
  1. কী কী রিসোর্স তৈরি হবে তা প্রিভিউ করতে terraform plan ব্যবহার করুন।
terraform plan
  1. কনফিগারেশনটি প্রয়োগ করুন। নিশ্চিত করতে বলা হলে, yes টাইপ করুন।
terraform apply

দ্রষ্টব্য: একটি GKE ক্লাস্টার প্রস্তুত করতে ১০-১৫ মিনিট সময় লাগতে পারে। অপেক্ষা করার সময়, আপনি পরবর্তী বিভাগে অ্যাপ্লিকেশন কোড পর্যালোচনা করতে পারেন।

  1. একবার সম্পন্ন হলে, আপনার নতুন ক্লাস্টারের সাথে যোগাযোগের জন্য kubectl কনফিগার করুন:
gcloud container clusters get-credentials gradio-chat-cluster --region us-central1 --project $TF_VAR_project_id

৪. GKE-তে সেলফ-হোস্টেড জেমা স্থাপন করুন

এরপরে, আমরা Gemma 3 12B মডেলটি সরাসরি আপনার GKE ক্লাস্টারে ডেপ্লয় করব। এর ফলে কম ল্যাটেন্সিতে ইনফারেন্স করা যায় এবং মডেল এক্সিকিউশন এনভায়রনমেন্টের উপর সম্পূর্ণ নিয়ন্ত্রণ রাখা সম্ভব হয়।

হাগিং ফেস ক্রেডেনশিয়াল কনফিগার করুন

জেমা মডেলটি ডাউনলোড করার জন্য, আপনার ক্লাস্টারকে হাগিং ফেস (Hugging Face) দিয়ে প্রমাণীকরণ করতে হবে।

  1. আপনার কাছে একটি হাগিং ফেস টোকেন আছে কিনা তা নিশ্চিত করুন।
  2. আপনার টোকেন দিয়ে একটি Kubernetes Secret তৈরি করুন- [YOUR_HF_TOKEN]-এর জায়গায় আপনার আসল টোকেনটি বসান :
    kubectl create secret generic hf-secret --from-literal=hf_api_token=[YOUR_HF_TOKEN]
    

মডেলটি স্থাপন করুন

মডেলটি চালানোর জন্য আমরা একটি স্ট্যান্ডার্ড কুবারনেটিস ডিপ্লয়মেন্ট ব্যবহার করব। ম্যানিফেস্টটি deploy/gemma3-12b-deploy.yaml এ অবস্থিত। আপনি এটি ম্যানুয়ালি খুলতে পারেন অথবা এডিটরে ফাইলটি খোলার জন্য এই কমান্ডটি ব্যবহার করতে পারেন:

cd ../deploy
cloudshell edit gemma3-12b-deploy.yaml

এক মুহূর্ত সময় নিয়ে এই ফাইলটি পরীক্ষা করুন। resources সেকশনটি লক্ষ্য করুন:

        resources:
          requests:
            nvidia.com/gpu: 4
      nodeSelector:
        cloud.google.com/gke-accelerator: nvidia-l4

এটি একটি ডিক্লারেটিভ এআই ইনফ্রাস্ট্রাকচার। আমরা GKE অটোপাইলটকে বলছি যে এই নির্দিষ্ট পডটির জন্য ৪টি NVIDIA L4 GPU প্রয়োজন। অটোপাইলট এমন একটি নোড খুঁজে বের করবে বা প্রোভিশন করবে যা এই প্রয়োজনীয়তাগুলো হুবহু পূরণ করে। যদি প্রোভিশন করার জন্য কোনো নোড উপলব্ধ না থাকে, তবে এটি ততক্ষণ চেষ্টা করতে থাকবে যতক্ষণ না প্রয়োজনীয়তা পূরণকারী একটি নোড পাওয়া যায়।

  1. ডিপ্লয়মেন্ট ম্যানিফেস্ট প্রয়োগ করুন:
    cd ..
    kubectl apply -f deploy/gemma3-12b-deploy.yaml
    
    এটি মডেলের ওয়েট ডাউনলোড করার এবং ইনফারেন্স সার্ভার চালু করার প্রক্রিয়া শুরু করবে। সাধারণত, এতে কয়েক মিনিট সময় লাগতে পারে। এই জেমা ডেপ্লয়মেন্টটি জিপিইউ (GPU) ব্যবহার করে, যেগুলোর প্রাপ্যতায় ঘাটতি দেখা দিতে পারে। যদি জিপিইউ উপলব্ধ না থাকে, তাহলে সেগুলো উপলব্ধ না হওয়া পর্যন্ত জেমা পডটি "পেন্ডিং" অবস্থায় থাকবে এবং গুগল ক্লাউড কনসোলে "Cannot schedule pods: Preemption is not helpful for scheduling." এবং/অথবা "Cannot schedule pods: node(s) didn't match Pod's node affinity/selector."-এর মতো একটি এরর দেখাবে। এর মানে হলো, GKE এখনও আপনার জন্য কোনো জিপিইউ সংগ্রহ করতে পারেনি। জিপিইউ সংগ্রহ করতে সক্ষম না হওয়া পর্যন্ত এটি চেষ্টা চালিয়ে যাবে। জিপিইউ-এর প্রাপ্যতার উপর নির্ভর করে এতে কয়েক মিনিট বা কয়েক দিন সময় লাগতে পারে। আপনি এর স্ট্যাটাস চেক করতে পারেন:
    kubectl get pods
    
    gemma পডটি এখনও চালু না হলেও আপনি অ্যাপ্লিকেশনটি ডেপ্লয় করার জন্য এগিয়ে যেতে পারেন । চ্যাট অ্যাপটি gemma সার্ভিসটি উপলব্ধ হলেই তার সাথে সংযুক্ত হবে। মনে রাখবেন যে, gemma পডটির স্ট্যাটাস Running এবং 1/1 না দেখানো পর্যন্ত আপনি আপনার চ্যাট অ্যাপের মাধ্যমে Gemma-এর সাথে যোগাযোগ করতে পারবেন না । কিন্তু এই সময়ের মধ্যে আপনি এর পরিবর্তে Gemini-এর সাথে চ্যাট করতে পারেন!

৫. চ্যাট অ্যাপ্লিকেশনটি তৈরি করুন

এখন, চলুন পাইথন অ্যাপ্লিকেশনটি সম্পূর্ণ করি। ক্লাউড শেল এডিটরে app/app.py খুলুন। আপনি সেখানে বেশ কয়েকটি # TODO ব্লক দেখতে পাবেন, যেগুলো অ্যাপ্লিকেশনটিকে কার্যকর করার জন্য পূরণ করতে হবে।

cloudshell edit app/app.py

ধাপ ১: কথোপকথনের ইতিহাস প্রক্রিয়া করুন

এলএলএম-দের জন্য কথোপকথনের ইতিহাস একটি নির্দিষ্ট বিন্যাসে সাজানো প্রয়োজন, যাতে তারা বুঝতে পারেন কে কী বলেছেন।

"ইউনিভার্সাল ট্রান্সলেটর" প্যাটার্ন: লক্ষ্য করুন যে আমরা একই চ্যাট হিস্ট্রি প্রসেস করার জন্য দুটি ভিন্ন ফাংশন লিখতে যাচ্ছি। মাল্টি-মডেল অ্যাপ্লিকেশনগুলিতে এটি একটি গুরুত্বপূর্ণ প্যাটার্ন।

  • সত্যের উৎস (গ্র্যাডিও): আমাদের অ্যাপ ইতিহাসটি একটি সহজ, সাধারণ বিন্যাসে রাখে: [[user_msg1, bot_msg1], ...]
  • টার্গেট ১ (জেমা): এটিকে নির্দিষ্ট বিশেষ টোকেনসহ একটি একক র স্ট্রিং-এ রূপান্তর করতে হবে।
  • টার্গেট ২ (মিথুন): এটিকে এপিআই অবজেক্টের একটি কাঠামোগত তালিকায় রূপান্তর করা প্রয়োজন।

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

জেমার জন্য (স্ব-হোস্টেড)

চ্যাট টেমপ্লেট বোঝা: যখন আপনি আপনার নিজের ওপেন মডেল হোস্ট করেন, তখন সাধারণত আপনাকে প্রম্পটটিকে ম্যানুয়ালি একটি নির্দিষ্ট স্ট্রিং-এ ফরম্যাট করতে হয়, যেটিকে মডেলটি একটি কথোপকথন হিসেবে চিনতে প্রশিক্ষিত। এটি 'চ্যাট টেমপ্লেট' নামে পরিচিত।

app.py ফাইলে process_message_gemma ফাংশনটি খুঁজুন এবং এটিকে নিচের কোড দিয়ে প্রতিস্থাপন করুন:

# This function takes a user's message and the conversation history as input.
#   Its job is to format these elements into a single,
#   structured prompt that can be understood by the language model (LLM).
#   This structured format helps the LLM maintain context and generate more relevant responses.
def process_message_gemma(message, history):
    user_prompt_format = "User's Turn:\n>>> {prompt}\n"
    assistant_prompt_format = "Assistant's Turn:\n>>> {prompt}\n"

    history_message = ""
    for user_turn, assistant_turn in history:
        history_message += user_prompt_format.format(prompt=user_turn)
        history_message += assistant_prompt_format.format(prompt=assistant_turn)

    # Format the new user message
    new_user_message = user_prompt_format.format(prompt=message)
    # Create a new aggregated message to be used as a single flat string in a json object sent to the LLM
    aggregated_message = (
        history_message + new_user_message + assistant_prompt_format.format(prompt="")
    )
    return aggregated_message

মিথুন রাশির জন্য (পরিচালিত)

ম্যানেজড সার্ভিসগুলো প্রায়শই র স্ট্রিংয়ের চেয়ে স্ট্রাকচার্ড অবজেক্ট বেশি পছন্দ করে। জেমিনি এসডিকে-এর জন্য হিস্ট্রিকে types.Content অবজেক্টে ফরম্যাট করতে আমাদের একটি আলাদা ফাংশন প্রয়োজন।

process_message_gemini খুঁজে বের করুন এবং এটিকে নিম্নলিখিত দিয়ে প্রতিস্থাপন করুন:

def process_message_gemini(message, history):
    contents = []
    for user_turn, model_turn in history:
        contents.append(
            types.Content(role="user", parts=[types.Part.from_text(text=user_turn)])
        )
        contents.append(
            types.Content(role="model", parts=[types.Part.from_text(text=model_turn)])
        )

    contents.append(
        types.Content(role="user", parts=[types.Part.from_text(text=message)])
    )
    return contents

ধাপ ২: সেলফ-হোস্টেড জেমা মডেলে কল করুন

আমাদের ক্লাস্টারে চলমান জেমা (Gemma) সার্ভিসে আমাদের ফরম্যাট করা প্রম্পটটি পাঠাতে হবে। এর জন্য আমরা সার্ভিসটির অভ্যন্তরীণ ডিএনএস (DNS) নামে একটি সাধারণ HTTP POST অনুরোধ ব্যবহার করব।

call_gemma_model ফাংশনটি খুঁজুন এবং এটিকে নিম্নলিখিত দিয়ে প্রতিস্থাপন করুন:

# Construct the request, send it to Gemma, return the model's response
# aggregated_message = current user message + history
def call_gemma_model(aggregated_message, model_temperature, top_p, max_tokens):
    json_message = {
        "prompt": aggregated_message,
        "temperature": model_temperature,
        "top_p": top_p,
        "max_tokens": max_tokens,
        "stop": ["User's Turn:"],
    }

    # Log what will be sent to the LLM
    print("*** JSON request: " + str(json_message))

    # Send the constructed json with the user prompt to the model and put the model's response in the json_data variable
    json_data = post_request(json_message)

    # The response from the model is a list of predictions. We'll take the first result.
    raw_output = json_data["predictions"][0]

    # The vLLM server returns the full prompt in the response. We need to extract
    # just the newly generated text from the model.
    assistant_turn_marker = "Assistant's Turn:\n>>>"
    marker_pos = raw_output.rfind(assistant_turn_marker)

    if marker_pos != -1:
        output = raw_output[marker_pos + len(assistant_turn_marker) :]
    else:
        output = raw_output

    # Clean up potential over-generation
    stop_marker = "User's Turn:"
    stop_pos = output.lower().find(stop_marker.lower())
    if stop_pos != -1:
        output = output[:stop_pos]

    return output.strip()

ধাপ ৩: ভার্টেক্স এআই জেমিনি মডেলটিকে কল করুন

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

call_gemini_model ফাংশনটি খুঁজুন এবং এটিকে নিম্নলিখিত দিয়ে প্রতিস্থাপন করুন:

# Send a request to Gemini via the VertexAI API. Return the model's response
# contents = list of types.Content objects
def call_gemini_model(contents, model_temperature, top_p, max_tokens):
    gemini_model = "gemini-2.5-flash"

    response = client.models.generate_content(
        model=gemini_model,
        contents=contents,
        config={
            "temperature": model_temperature,
            "max_output_tokens": max_tokens,
            "top_p": top_p,
        },
    )
    return response.text

ধাপ ৪: প্রধান ইনফারেন্স ইন্টারফেসটি বাস্তবায়ন করুন।

অবশেষে, আমাদের মূল অর্কেস্ট্রেটর ফাংশনটি প্রয়োজন, যেটিকে Gradio কল করে। এই ফাংশনটির যা যা করা প্রয়োজন:

  1. হিস্ট্রি খালি থাকলে তা শুরু করুন।
  2. বার্তাটি প্রক্রিয়া করুন।
  3. অনুরোধটি নির্বাচিত মডেলের (জেমা বা জেমিনি) কাছে পাঠান।
  4. ইন্টারঅ্যাকশনটি ফায়ারস্টোরে সংরক্ষণ করুন।
  5. UI-তে প্রতিক্রিয়াটি ফেরত দিন।

গ্র্যাডিও এবং স্টেট ম্যানেজমেন্ট: গ্র্যাডিও-র ChatInterface স্বয়ংক্রিয়ভাবে সেশন-স্তরের স্টেট পরিচালনা করে (ব্রাউজারে বার্তা প্রদর্শন করে)। তবে, এতে বাহ্যিক ডেটাবেসের জন্য অন্তর্নির্মিত সমর্থন নেই।

দীর্ঘ সময়ের জন্য চ্যাট হিস্ট্রি সংরক্ষণ করতে, আমরা একটি স্ট্যান্ডার্ড প্যাটার্ন ব্যবহার করি: আমরা inference_interface ফাংশনটিতে হুক করি। আর্গুমেন্ট হিসেবে request: gr.Request গ্রহণ করার মাধ্যমে, Gradio স্বয়ংক্রিয়ভাবে আমাদের কাছে বর্তমান ব্যবহারকারীর সেশনের বিবরণ পাঠিয়ে দেয়। আমরা এটি ব্যবহার করে প্রতিটি ব্যবহারকারীর জন্য একটি স্বতন্ত্র Firestore ডকুমেন্ট তৈরি করি, যা নিশ্চিত করে যে একাধিক ব্যবহারকারীর পরিবেশে কথোপকথনগুলো যেন মিশে না যায়।

inference_interface ফাংশনটি খুঁজুন এবং এটিকে নিম্নলিখিত দিয়ে প্রতিস্থাপন করুন:

# This is the primary chat function. Every time a user sends a message, gradio calls this function,
# which sends the user's input to the appropriate AI (as indicated on the user interface), updates
# the chat history for future use during this session, and records the chat history in Firestore.
def inference_interface(
    message,
    history,
    model_name,
    model_temperature,
    top_p,
    max_tokens,
    request: gr.Request,
):

    # set history to empty array
    if history is None:
        history = []

    # Get or create session document
    session_hash = request.session_hash
    doc_id = f"session-{session_hash}"
    doc_ref = db.collection("chat_sessions").document(doc_id)

    # Create the session document if it doesn't exist
    if not doc_ref.get().exists:
        doc_ref.set({"Session start": datetime.datetime.now()})

    # Log info
    print("Model: " + model_name)
    print("LLM Engine: " + llm_engine)
    print("* History: " + str(history))

    # Pass the message and history to the appropriate model, as indicated by the user via the ui
    if model_name == "Gemma3 12b it":
        aggregated_message = process_message_gemma(message, history)
        output = call_gemma_model(
            aggregated_message, model_temperature, top_p, max_tokens
        )

    elif model_name == "Gemini":
        gemini_contents = process_message_gemini(message, history)
        output = call_gemini_model(
            gemini_contents, model_temperature, top_p, max_tokens
        )

    else:
        # Handle the case where no valid model is selected
        output = "Error: Invalid model selected."

    interaction = {"user": message, model_name: output}

    # Log the updated chat history
    print("* History: " + str(history) + " " + str(interaction))

    # Save the updated history to Firestore
    save_chat_history(interaction, doc_ref)

    return output

৬. আপনার app.py ফাইলটি যাচাই করুন।

এই পর্যায়ে, আপনার গ্রেডিও-ভিত্তিক চ্যাট অ্যাপ্লিকেশনটি ডেপ্লয় করার জন্য প্রস্তুত থাকা উচিত। নিশ্চিত করুন যে এটি নিম্নলিখিত সম্পূর্ণ ফাইলটির সাথে হুবহু মিলে যায়।

সমস্যা সমাধান: আপনি যদি আপনার অ্যাপ্লিকেশনটি ডেপ্লয় করার পর সংযোগ করার চেষ্টা করার সময় "সংযোগ করতে অস্বীকার করা হয়েছে" বা "এই সাইটে পৌঁছানো যাচ্ছে না" ত্রুটি পান, তাহলে এই সম্পূর্ণ ফাইলটি কপি করে আপনার app.py ফাইলে পেস্ট করার মাধ্যমে শুরু করে, এই পর্যায় থেকে ধাপগুলো আবার চেষ্টা করুন।

# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import datetime

import google.auth
import google.cloud
import gradio as gr
import requests
import themes
from google import genai

from google.cloud import firestore
from google.genai import types

## Do one-time initialization things

## grab the project id from google auth
_, project = google.auth.default()
print(f"Project: {project}")

# Set initial values for model
llm_engine = "vllm"
host = "http://gemma-service:8000"
context_path = "/generate"

# initialize vertex for interacting with Gemini
client = genai.Client(
    vertexai=True,
    project=project,
    location="global",
)

# Initialize Firestore client
db = firestore.Client(database="chat-app-db")


# This is the primary chat function. Every time a user sends a message, gradio calls this function,
# which sends the user's input to the appropriate AI (as indicated on the user interface), updates
# the chat history for future use during this session, and records the chat history in Firestore.
def inference_interface(
    message,
    history,
    model_name,
    model_temperature,
    top_p,
    max_tokens,
    request: gr.Request,
):

    # set history to empty array
    if history is None:
        history = []

    # Get or create session document
    session_hash = request.session_hash
    doc_id = f"session-{session_hash}"
    doc_ref = db.collection("chat_sessions").document(doc_id)

    # Create the session document if it doesn't exist
    if not doc_ref.get().exists:
        doc_ref.set({"Session start": datetime.datetime.now()})

    # Log info
    print("Model: " + model_name)
    print("LLM Engine: " + llm_engine)
    print("* History: " + str(history))

    # Pass the message and history to the appropriate model, as indicated by the user via the ui
    if model_name == "Gemma3 12b it":
        aggregated_message = process_message_gemma(message, history)
        output = call_gemma_model(
            aggregated_message, model_temperature, top_p, max_tokens
        )

    elif model_name == "Gemini":
        gemini_contents = process_message_gemini(message, history)
        output = call_gemini_model(
            gemini_contents, model_temperature, top_p, max_tokens
        )

    else:
        # Handle the case where no valid model is selected
        output = "Error: Invalid model selected."

    interaction = {"user": message, model_name: output}

    # Log the updated chat history
    print("* History: " + str(history) + " " + str(interaction))

    # Save the updated history to Firestore
    save_chat_history(interaction, doc_ref)

    return output


# Construct the request, send it to Gemma, return the model's response
# aggregated_message = current user message + history
def call_gemma_model(aggregated_message, model_temperature, top_p, max_tokens):
    json_message = {
        "prompt": aggregated_message,
        "temperature": model_temperature,
        "top_p": top_p,
        "max_tokens": max_tokens,
        "stop": ["User's Turn:"],
    }

    # Log what will be sent to the LLM
    print("*** JSON request: " + str(json_message))  # Log the JSON request

    # Send the constructed json with the user prompt to the model and put the model's response in the json_data variable
    json_data = post_request(json_message)

    # The response from the model is a list of predictions.
    # We'll take the first result.
    raw_output = json_data["predictions"][0]

    # The vLLM server returns the full prompt in the response. We need to extract
    # just the newly generated text from the model. The prompt ends with
    # "Assistant's Turn:\n>>>", so we find the last occurrence of that and
    # take everything after it.
    assistant_turn_marker = "Assistant's Turn:\n>>>"
    marker_pos = raw_output.rfind(assistant_turn_marker)

    if marker_pos != -1:
        # Get the text generated by the assistant
        output = raw_output[marker_pos + len(assistant_turn_marker) :]
    else:
        # Fallback in case the marker isn't found
        output = raw_output

    # The model sometimes continues the conversation and includes the next user's turn.
    # The 'stop' parameter is a good hint, but we parse the output as a safeguard.
    stop_marker = "User's Turn:"
    stop_pos = output.lower().find(stop_marker.lower())
    if stop_pos != -1:
        output = output[:stop_pos]

    # The model also sometimes prefixes its response with "Output:". We'll remove this.
    output = output.lstrip()
    prefix_marker = "Output:"
    if output.lower().startswith(prefix_marker.lower()):
        output = output[len(prefix_marker) :]

    return output.strip()


# Send a request to Gemini via the VertexAI API. Return the model's response
# contents = list of types.Content objects
def call_gemini_model(contents, model_temperature, top_p, max_tokens):
    gemini_model = "gemini-2.5-flash"

    response = client.models.generate_content(
        model=gemini_model,
        contents=contents,
        config={
            "temperature": model_temperature,
            "max_output_tokens": max_tokens,
            "top_p": top_p,
        },
    )
    output = response.text  # Extract the generated text
    # Consider handling additional response attributes (safety, usage, etc.)
    return output


def process_message_gemini(message, history):
    contents = []
    for user_turn, model_turn in history:
        contents.append(
            types.Content(role="user", parts=[types.Part.from_text(text=user_turn)])
        )
        contents.append(
            types.Content(role="model", parts=[types.Part.from_text(text=model_turn)])
        )

    contents.append(
        types.Content(role="user", parts=[types.Part.from_text(text=message)])
    )
    return contents


# This function takes a user's message and the conversation history as input.
#   Its job is to format these elements into a single,
#   structured prompt that can be understood by the language model (LLM).
#   This structured format helps the LLM maintain context and generate more relevant responses.
def process_message_gemma(message, history):
    user_prompt_format = "User's Turn:\n>>> {prompt}\n"
    assistant_prompt_format = "Assistant's Turn:\n>>> {prompt}\n"

    history_message = ""
    for user_turn, assistant_turn in history:
        history_message += user_prompt_format.format(prompt=user_turn)
        history_message += assistant_prompt_format.format(prompt=assistant_turn)

    # Format the new user message
    new_user_message = user_prompt_format.format(prompt=message)
    # Create a new aggregated message to be used as a single flat string in a json object sent to the LLM
    aggregated_message = (
        history_message + new_user_message + assistant_prompt_format.format(prompt="")
    )
    return aggregated_message


# Function to save chat history to Firestore
def save_chat_history(interaction, doc_ref):
    timestamp_str = str(datetime.datetime.now())

    # Save the chat history, merging with existing data
    doc_ref.update({timestamp_str: interaction})

    print("Chat history saved successfully!")  # Optional: Log success


# Send the json message to the model and return the model's response. This is used for Gemma but not Gemini. It could also be used for other models.
def post_request(json_message):
    print("*** Request" + str(json_message), flush=True)
    # Set a timeout and check for HTTP errors. This will raise an exception on a bad status code (4xx or 5xx).
    response = requests.post(host + context_path, json=json_message, timeout=60)
    response.raise_for_status()
    json_data = response.json()
    print("*** Output: " + str(json_data), flush=True)
    return json_data


# custom css to hide default footer
css = """
footer {display: none !important;} .gradio-container {min-height: 0px !important;}
"""

# Add a dropdown to select the model to chat with
model_dropdown = gr.Dropdown(
    ["Gemma3 12b it", "Gemini"],
    label="Model",
    info="Select the model you would like to chat with.",
    value="Gemma3 12b it",
)

# Make the model temperature, top_p, and max tokents modifiable via sliders in the GUI
model_temperature = gr.Slider(
    minimum=0.1, maximum=1.0, value=0.9, label="Temperature", render=False
)
top_p = gr.Slider(minimum=0.1, maximum=1.0, value=0.95, label="Top_p", render=False)
max_tokens = gr.Slider(
    minimum=1, maximum=4096, value=1024, label="Max Tokens", render=False
)

# Call gradio to create the chat interface
app = gr.ChatInterface(
    inference_interface,
    additional_inputs=[model_dropdown, model_temperature, top_p, max_tokens],
    theme=themes.google_theme(),
    css=css,
    title="Chat with AI",
)

app.launch(server_name="0.0.0.0", allowed_paths=["images"])

৭. চ্যাট অ্যাপ্লিকেশনটি স্থাপন করুন

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

দ্রষ্টব্য: এটি ওয়ার্কলোড আইডেন্টিটির জন্য আমাদের প্রয়োজনীয় কুবারনেটিস সার্ভিস অ্যাকাউন্টটিও ডেপ্লয় করবে। আপনি deploy/chat-deploy.yaml ফাইলে এর সংজ্ঞা দেখতে পারেন। রেফারেন্সের জন্য, এখানে এর সংজ্ঞাটি দেখে নিন:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: gradio-chat-ksa

বিল্ড এবং ডিপ্লয় করতে স্ক্যাফোল্ড চালান:

skaffold run --default-repo=us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/chat-app-repo

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

৮. অ্যাপ্লিকেশনটি পরীক্ষা করুন

  1. চ্যাট অ্যাপ্লিকেশন পরিষেবাটির একটি বাহ্যিক আইপি ঠিকানা পাওয়ার জন্য অপেক্ষা করুন:
    kubectl get svc gradio-chat-service --watch
    
    EXTERNAL-IP pending থেকে একটি প্রকৃত IP অ্যাড্রেসে পরিবর্তিত হয়ে গেলে, পর্যবেক্ষণ বন্ধ করতে Ctrl+C চাপুন।
  2. একটি ওয়েব ব্রাউজার খুলুন এবং http://[EXTERNAL-IP]:7860 -এ যান।
  3. মডেলটির সাথে ইন্টারঅ্যাক্ট করার চেষ্টা করুন! অ্যাপটি ডিফল্টরূপে এমনভাবে কনফিগার করা আছে যাতে আপনি আপনার লোকালি হোস্ট করা জেমা (Gemma) মডেলের সাথে চ্যাট করতে পারেন। আপনি যদি জেমিনি (Gemini)-র সাথে চ্যাট করতে চান, তাহলে 'অতিরিক্ত ইনপুট' (Additional Inputs) ড্রপডাউন থেকে মডেলটি পরিবর্তন করুন। উদাহরণস্বরূপ, এআই-কে (AI) জিজ্ঞাসা করে দেখুন: "আমাকে একটি কুবারনেটিস (Kubernetes) কৌতুক বলুন।"

সমস্যা সমাধান:

  1. যদি "এই সাইটে পৌঁছানো যাচ্ছে না" বা "[EXTERNAL-IP] সংযোগ করতে অস্বীকার করেছে"-এর মতো কোনো ত্রুটি পান, তাহলে আপনার app.py ফাইলে কোনো সমস্যা হয়ে থাকতে পারে। "আপনার app.py ফাইল পরীক্ষা করুন" শিরোনামের ধাপে ফিরে যান এবং সেখান থেকে ধাপগুলো আবার অনুসরণ করুন।
  2. UI ডিফল্টভাবে "Gemma3 12b it" মডেলে সেট করা থাকে। যদি সাথে সাথেই কোনো এরর আসে, তাহলে সম্ভবত এর কারণ হলো Gemma পডটি এখনো প্রস্তুত নয়। টিপস: Gemma ইনিশিয়ালাইজ হওয়ার জন্য অপেক্ষা করার সময় চ্যাট অ্যাপ্লিকেশনটি ব্যবহার করে দেখার জন্য আপনি ড্রপডাউনটি "Gemini"-তে পরিবর্তন করতে পারেন!

জেমা পরীক্ষা করুন: নিশ্চিত করুন যে ড্রপডাউনে 'Gemma3 12b it' নির্বাচিত আছে এবং একটি বার্তা পাঠান (যেমন, "আমাকে Kubernetes নিয়ে একটি কৌতুক বলুন")।

জেমিনি পরীক্ষা করুন: ড্রপডাউনটি 'জেমিনি'-তে পরিবর্তন করুন এবং আরেকটি প্রশ্ন জিজ্ঞাসা করুন (যেমন, "পড এবং নোডের মধ্যে পার্থক্য কী?")।

ইতিহাস যাচাই করুন: চ্যাট অ্যাপে কোনো মডেলের (জেমা বা জেমিনি) সাথে সফলভাবে চ্যাট করার পর, চ্যাট লগগুলো দেখতে ফায়ারস্টোরে আপনার "chat-app-db" ডেটাবেসটি দেখুন। আপনি যদি উভয় মডেলের সাথেই চ্যাট করতে সক্ষম হন, তবে লক্ষ্য করুন যে মডেল পরিবর্তন করলেও কথোপকথনের ইতিহাস সংরক্ষিত থাকে।

৯. আরও অগ্রসর হওয়া

এখন যেহেতু আপনার কাছে একটি কার্যকর হাইব্রিড চ্যাট অ্যাপ্লিকেশন রয়েছে, আপনার বোঝাপড়া আরও গভীর করার জন্য এই চ্যালেঞ্জগুলো বিবেচনা করুন:

  1. কাস্টম পার্সোনা: process_message_gemma এবং process_message_gemini ফাংশনগুলোর শুরুতে একটি 'সিস্টেম প্রম্পট' যোগ করে সেগুলোকে পরিবর্তন করার চেষ্টা করুন। উদাহরণস্বরূপ, মডেলগুলোকে বলুন "আপনি একজন সহায়ক জলদস্যু সহকারী।" এবং দেখুন এটি তাদের প্রতিক্রিয়া কীভাবে পরিবর্তন করে।
  2. স্থায়ী ব্যবহারকারী পরিচয়: বর্তমানে, অ্যাপ্লিকেশনটি প্রতিটি সেশনের জন্য একটি নতুন র‍্যান্ডম UUID তৈরি করে। আপনি কীভাবে একটি প্রকৃত প্রমাণীকরণ সিস্টেম (যেমন গুগল সাইন-ইন) একীভূত করবেন, যাতে একজন ব্যবহারকারী বিভিন্ন ডিভাইসে তার অতীতের কথোপকথনের ইতিহাস দেখতে পারেন?
  3. মডেল পরীক্ষণ: UI-তে থাকা temperature স্লাইডারটি পরিবর্তন করে দেখুন। কম তাপমাত্রার (প্রায় ০.১) তুলনায় উচ্চ তাপমাত্রা (প্রায় ১.০) প্রতিক্রিয়ার সৃজনশীলতা বনাম নির্ভুলতাকে কীভাবে প্রভাবিত করে?

১০. উপসংহার

অভিনন্দন! আপনি সফলভাবে একটি হাইব্রিড এআই অ্যাপ্লিকেশন তৈরি করেছেন। আপনি শিখেছেন কীভাবে:

  • গুগল ক্লাউডে ইনফ্রাস্ট্রাকচার-অ্যাজ-কোড এর জন্য টেরাফর্ম ব্যবহার করুন।
  • সম্পূর্ণ নিয়ন্ত্রণের জন্য GKE-তে আপনার নিজস্ব ওপেন-ওয়েট LLM আয়োজন করুন।
  • নমনীয়তার জন্য ভার্টেক্স এআই-এর মতো পরিচালিত এআই পরিষেবাগুলিকে একীভূত করুন।
  • ডেটা সংরক্ষণের জন্য ফায়ারস্টোর ব্যবহার করে একটি স্টেটফুল অ্যাপ্লিকেশন তৈরি করুন।
  • Workload Identity ব্যবহার করে আপনার ওয়ার্কলোড সুরক্ষিত করুন।

পরিষ্কার করা

চার্জ এড়ানোর জন্য, আপনার তৈরি করা রিসোর্সগুলো ধ্বংস করুন:

cd infra
terraform destroy -var="project_id=$GOOGLE_CLOUD_PROJECT" -var="project_number=$PROJECT_NUMBER" -var="region=$REGION"