Codelab - Firestore, Vector Search, Langchain এবং Gemini (Node.js সংস্করণ) সহ একটি প্রাসঙ্গিক যোগ পোজ সুপারিশকারী অ্যাপ তৈরি করুন

১. ভূমিকা

এই কোডল্যাবে, আপনি এমন একটি অ্যাপ্লিকেশন তৈরি করবেন যা ভেক্টর সার্চ ব্যবহার করে যোগাসন সুপারিশ করে।

কোডল্যাবের মাধ্যমে, আপনি নিম্নলিখিত ধাপে ধাপে পদ্ধতিটি অনুসরণ করবেন:

  1. যোগাসনের একটি বিদ্যমান আলিঙ্গনরত মুখের ডেটাসেট (JSON ফরম্যাট) ব্যবহার করুন।
  2. একটি অতিরিক্ত ফিল্ড বিবরণ যোগ করে ডেটাসেটটিকে আরও উন্নত করুন, যা জেমিনি ব্যবহার করে প্রতিটি পোজের জন্য বিবরণ তৈরি করবে।
  3. জেনারেট করা এমবেডিং সহ যোগাসনের ডেটা ফায়ারস্টোর কালেকশনে ডকুমেন্টের একটি সংগ্রহ হিসাবে লোড করুন।
  4. ভেক্টর সার্চ সক্ষম করার জন্য ফায়ারস্টোরে একটি কম্পোজিট ইনডেক্স তৈরি করুন।
  5. একটি Node.js অ্যাপ্লিকেশনে ভেক্টর সার্চ ব্যবহার করুন যা নিচে দেখানো অনুযায়ী সবকিছুকে একত্রিত করে:

84e1cbf29cbaeedc.png

আপনি যা করবেন

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

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

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

আপনার যা যা লাগবে

  • ক্রোম ওয়েব ব্রাউজার
  • একটি জিমেইল অ্যাকাউন্ট
  • বিলিং সক্ষম একটি ক্লাউড প্রজেক্ট

সকল স্তরের (শিক্ষানবিশ সহ) ডেভেলপারদের জন্য ডিজাইন করা এই কোডল্যাবটির নমুনা অ্যাপ্লিকেশনে জাভাস্ক্রিপ্ট এবং নোড.জেএস ব্যবহার করা হয়েছে। তবে, এখানে উপস্থাপিত ধারণাগুলো বোঝার জন্য জাভাস্ক্রিপ্ট এবং নোড.জেএস সম্পর্কে পূর্বজ্ঞান থাকা আবশ্যক নয়।

২. শুরু করার আগে

একটি প্রকল্প তৈরি করুন

  1. গুগল ক্লাউড কনসোলের প্রজেক্ট সিলেক্টর পেজে, একটি গুগল ক্লাউড প্রজেক্ট নির্বাচন করুন বা তৈরি করুন।
  2. আপনার ক্লাউড প্রোজেক্টের জন্য বিলিং চালু আছে কিনা তা নিশ্চিত করুন। কোনো প্রোজেক্টে বিলিং চালু আছে কিনা তা কীভাবে পরীক্ষা করবেন, তা জেনে নিন।
  3. আপনি ক্লাউড শেল ব্যবহার করবেন, যা গুগল ক্লাউডে চলমান একটি কমান্ড-লাইন পরিবেশ এবং এটি bq-এর সাথে আগে থেকেই লোড করা থাকে। গুগল ক্লাউড কনসোলের শীর্ষে থাকা ‘Activate Cloud Shell’-এ ক্লিক করুন।

ক্লাউড শেল সক্রিয় করুন বোতামের ছবি

  1. ক্লাউড শেলে সংযুক্ত হওয়ার পর, আপনি নিম্নলিখিত কমান্ডটি ব্যবহার করে যাচাই করে নিন যে আপনি ইতিমধ্যেই প্রমাণীকৃত এবং প্রজেক্টটি আপনার প্রজেক্ট আইডিতে সেট করা আছে:
gcloud auth list
  1. gcloud কমান্ডটি আপনার প্রজেক্ট সম্পর্কে অবগত আছে কিনা, তা নিশ্চিত করতে ক্লাউড শেলে নিম্নলিখিত কমান্ডটি চালান।
gcloud config list project
  1. আপনার প্রজেক্টটি সেট করা না থাকলে, এটি সেট করতে নিম্নলিখিত কমান্ডটি ব্যবহার করুন:
gcloud config set project <YOUR_PROJECT_ID>
  1. নিচে দেখানো কমান্ডের মাধ্যমে প্রয়োজনীয় API-গুলো সক্রিয় করুন। এতে কয়েক মিনিট সময় লাগতে পারে, তাই অনুগ্রহ করে ধৈর্য ধরুন।
gcloud services enable firestore.googleapis.com \
                       compute.googleapis.com \
                       cloudresourcemanager.googleapis.com \
                       servicenetworking.googleapis.com \
                       run.googleapis.com \
                       cloudbuild.googleapis.com \
                       cloudfunctions.googleapis.com \
                       aiplatform.googleapis.com \
                       texttospeech.googleapis.com

কমান্ডটি সফলভাবে কার্যকর হলে, আপনি নিচে দেখানো বার্তার মতো একটি বার্তা দেখতে পাবেন:

Operation "operations/..." finished successfully.

gcloud কমান্ডের বিকল্প হলো কনসোলের মাধ্যমে প্রতিটি পণ্য অনুসন্ধান করা অথবা এই লিঙ্কটি ব্যবহার করা।

যদি কোনো API বাদ পড়ে যায়, তাহলে আপনি বাস্তবায়ন চলাকালীন সময়েই তা সক্রিয় করে নিতে পারেন।

gcloud কমান্ড এবং এর ব্যবহার সম্পর্কে জানতে ডকুমেন্টেশন দেখুন।

রিপোজিটরি ক্লোন করুন এবং পরিবেশ সেটিংস সেটআপ করুন

পরবর্তী ধাপ হলো স্যাম্পল রিপোজিটরিটি ক্লোন করা, যা আমরা কোডল্যাবের বাকি অংশে রেফারেন্স হিসেবে ব্যবহার করব। ধরে নিন আপনি ক্লাউড শেলে আছেন, আপনার হোম ডিরেক্টরি থেকে নিম্নলিখিত কমান্ডটি দিন:

git clone https://github.com/rominirani/yoga-poses-recommender-nodejs

এডিটর চালু করতে, ক্লাউড শেল উইন্ডোর টুলবারে থাকা ‘ওপেন এডিটর’-এ ক্লিক করুন। উপরের বাম কোণার মেনু বারে ক্লিক করুন এবং নিচে দেখানো অনুযায়ী ‘ফাইল → ওপেন ফোল্ডার’ নির্বাচন করুন:

66221fd0d0e5202f.png

yoga-poses-recommender-nodejs ফোল্ডারটি নির্বাচন করুন এবং আপনি দেখবেন যে নিচে দেখানো ফাইলগুলি সহ ফোল্ডারটি খুলে গেছে:

7dbe126ee112266d.png

এখন আমাদের ব্যবহারযোগ্য এনভায়রনমেন্ট ভেরিয়েবলগুলো সেট আপ করতে হবে। env-template ফাইলটিতে ক্লিক করুন এবং আপনি নীচে দেখানো ছবির মতো বিষয়বস্তু দেখতে পাবেন:

PROJECT_ID=<YOUR_GOOGLE_CLOUD_PROJECT_ID>
LOCATION=us-<GOOGLE_CLOUD_REGION_NAME>
GEMINI_MODEL_NAME=<GEMINI_MODEL_NAME>
EMBEDDING_MODEL_NAME=<GEMINI_EMBEDDING_MODEL_NAME>
IMAGE_GENERATION_MODEL_NAME=<IMAGEN_MODEL_NAME>
DATABASE=<FIRESTORE_DATABASE_NAME>
COLLECTION=<FIRESTORE_COLLECTION_NAME>
TEST_COLLECTION=test-poses
TOP_K=3

গুগল ক্লাউড প্রজেক্ট এবং ফায়ারস্টোর ডেটাবেস রিজিয়ন তৈরি করার সময় আপনি যা নির্বাচন করেছেন, সেই অনুযায়ী PROJECT_ID এবং LOCATION এর মান আপডেট করুন। আদর্শগতভাবে, আমরা চাই গুগল ক্লাউড প্রজেক্ট এবং ফায়ারস্টোর ডেটাবেস উভয়ের জন্যই LOCATION এর মান একই হোক, যেমন us-central1

এই কোডল্যাবের জন্য, আমরা নিম্নলিখিত মানগুলি ব্যবহার করব (অবশ্যই PROJECT_ID এবং LOCATION বাদে, যা আপনাকে আপনার কনফিগারেশন অনুযায়ী সেট করতে হবে)।

PROJECT_ID=<YOUR_GOOGLE_CLOUD_PROJECT_ID>
LOCATION=us-<GOOGLE_CLOUD_REGION_NAME>
GEMINI_MODEL_NAME=gemini-1.5-flash-002
EMBEDDING_MODEL_NAME=text-embedding-004
IMAGE_GENERATION_MODEL_NAME=imagen-3.0-fast-generate-001
DATABASE=(default)
COLLECTION=poses
TEST_COLLECTION=test-poses
TOP_K=3

অনুগ্রহ করে এই ফাইলটি env-template ফাইলের সাথে একই ফোল্ডারে .env নামে সংরক্ষণ করুন।

Cloud Shell IDE-এর উপরের বাম দিকের প্রধান মেনুতে যান এবং তারপর Terminal → New Terminal

নিম্নলিখিত কমান্ড ব্যবহার করে আপনার ক্লোন করা রিপোজিটরির রুট ফোল্ডারে যান:

cd yoga-poses-recommender-nodejs

এই কমান্ডের মাধ্যমে Node.js নির্ভরতাগুলি ইনস্টল করুন:

npm install

চমৎকার! আমরা এখন ফায়ারস্টোর ডাটাবেস সেটআপ করার কাজে এগিয়ে যাওয়ার জন্য পুরোপুরি প্রস্তুত।

৩. ফায়ারস্টোর সেটআপ করুন

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

ফায়ারস্টোর ডেটাবেস প্রারম্ভিককরণ

ক্লাউড কনসোলে ফায়ারস্টোর পৃষ্ঠাটি পরিদর্শন করুন।

যদি আপনি প্রজেক্টে আগে কোনো ফায়ারস্টোর ডাটাবেস তৈরি না করে থাকেন, তাহলে Create Database এ ক্লিক করে default ডাটাবেসটি তৈরি করুন। ডাটাবেস তৈরির সময়, নিম্নলিখিত মানগুলি ব্যবহার করুন:

  • ফায়ারস্টোর মোড: Native.
  • অবস্থানের ধরণ হিসেবে Region নির্বাচন করুন এবং অঞ্চলের জন্য us-central1 অবস্থানটি নির্বাচন করুন।
  • নিরাপত্তা বিধির জন্য Test rules অনুসরণ করুন।
  • ডাটাবেসটি তৈরি করুন।

61d0277510803c8d.png

পরবর্তী অংশে, আমরা আমাদের ডিফল্ট ফায়ারস্টোর ডেটাবেসে ' poses নামের একটি কালেকশন তৈরির ভিত্তি স্থাপন করব। এই কালেকশনটিতে নমুনা ডেটা (ডকুমেন্ট) বা যোগাসনের তথ্য থাকবে, যা আমরা পরবর্তীতে আমাদের অ্যাপ্লিকেশনে ব্যবহার করব।

এর মাধ্যমে ফায়ারস্টোর ডাটাবেস সেটআপ করার পর্বটি সম্পন্ন হলো।

৪. যোগাসনের ডেটাসেট প্রস্তুত করুন।

আমাদের প্রথম কাজ হলো অ্যাপ্লিকেশনটির জন্য ব্যবহৃত যোগাসনের ডেটাসেটটি প্রস্তুত করা। আমরা একটি বিদ্যমান হাগিং ফেস ডেটাসেট দিয়ে শুরু করব এবং তারপর অতিরিক্ত তথ্য দিয়ে এটিকে সমৃদ্ধ করব।

যোগাসনের জন্য হাগিং ফেস ডেটাসেটটি দেখুন। উল্লেখ্য যে, যদিও এই কোডল্যাবটি ডেটাসেটগুলোর মধ্যে একটি ব্যবহার করে, আপনি চাইলে অন্য যেকোনো ডেটাসেটও ব্যবহার করতে পারেন এবং ডেটাসেটটিকে উন্নত করার জন্য প্রদর্শিত একই কৌশলগুলো অনুসরণ করতে পারেন।

298cfae7f23e4bef.png

আমরা যদি Files and versions বিভাগে যাই, তাহলে সমস্ত পোজের জন্য JSON ডেটা ফাইলটি পেতে পারি।

3fe6e55abdc032ec.png

আমরা yoga_poses.json ফাইলটি ডাউনলোড করে আপনাকে দিয়েছি। এই ফাইলটির নাম yoga_poses_alldata.json এবং এটি /data ফোল্ডারে রয়েছে।

ক্লাউড শেল এডিটরে থাকা data/yoga_poses.json ফাইলটিতে যান এবং JSON অবজেক্টগুলোর তালিকাটি দেখুন, যেখানে প্রতিটি JSON অবজেক্ট একটি যোগাসনকে নির্দেশ করে। এখানে মোট ৩টি রেকর্ড রয়েছে এবং নিচে একটি নমুনা রেকর্ড দেখানো হলো:

{
   "name": "Big Toe Pose",
   "sanskrit_name": "Padangusthasana",
   "photo_url": "https://pocketyoga.com/assets/images/full/ForwardBendBigToe.png",
   "expertise_level": "Beginner",
   "pose_type": ["Standing", "Forward Bend"]
 }

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

ক্লাউড শেল এডিটরে, generate-descriptions.js ফাইলটিতে যান। এই ফাইলটির বিষয়বস্তু নিচে দেখানো হলো:

import { VertexAI } from "@langchain/google-vertexai";
import fs from 'fs/promises'; // Use fs/promises for async file operations
import dotenv from 'dotenv';
import pRetry from 'p-retry';
import { promisify } from 'util';

const sleep = promisify(setTimeout);

// Load environment variables
dotenv.config();

async function callGemini(poseName, sanskritName, expertiseLevel, poseTypes) {

   const prompt = `
   Generate a concise description (max 50 words) for the yoga pose: ${poseName}
   Also known as: ${sanskritName}
   Expertise Level: ${expertiseLevel}
   Pose Type: ${poseTypes.join(', ')}

   Include key benefits and any important alignment cues.
   `;

   try {
     // Initialize Vertex AI Gemini model
     const model = new VertexAI({
       model: process.env.GEMINI_MODEL_NAME,
       location: process.env.LOCATION,
       project: process.env.PROJECT_ID,
     });
      // Invoke the model
     const response = await model.invoke(prompt);
      // Return the response
     return response;
   } catch (error) {
     console.error("Error calling Gemini:", error);
     throw error; // Re-throw the error for handling in the calling function
   }
 }

// Configure logging (you can use a library like 'winston' for more advanced logging)
const logger = {
 info: (message) => console.log(`INFO - ${new Date().toISOString()} - ${message}`),
 error: (message) => console.error(`ERROR - ${new Date().toISOString()} - ${message}`),
};

async function generateDescription(poseName, sanskritName, expertiseLevel, poseTypes) {
 const prompt = `
   Generate a concise description (max 50 words) for the yoga pose: ${poseName}
   Also known as: ${sanskritName}
   Expertise Level: ${expertiseLevel}
   Pose Type: ${poseTypes.join(', ')}

   Include key benefits and any important alignment cues.
   `;

 const req = {
   contents: [{ role: 'user', parts: [{ text: prompt }] }],
 };

 const runWithRetry = async () => {
   const resp = await generativeModel.generateContent(req);
   const response = await resp.response;
   const text = response.candidates[0].content.parts[0].text;
   return text;
 };

 try {
   const text = await pRetry(runWithRetry, {
     retries: 5,
     onFailedAttempt: (error) => {
       logger.info(
         `Attempt ${error.attemptNumber} failed. There are ${error.retriesLeft} retries left. Waiting ${error.retryDelay}ms...`
       );
     },
     minTimeout: 4000, // 4 seconds (exponential backoff will adjust this)
     factor: 2, // Exponential factor
   });
   return text;
 } catch (error) {
   logger.error(`Error generating description for ${poseName}: ${error}`);
   return '';
 }
}

async function addDescriptionsToJSON(inputFile, outputFile) {
 try {
   const data = await fs.readFile(inputFile, 'utf-8');
   const yogaPoses = JSON.parse(data);

   const totalPoses = yogaPoses.length;
   let processedCount = 0;

   for (const pose of yogaPoses) {
     if (pose.name !== ' Pose') {
       const startTime = Date.now();
       pose.description = await callGemini(
         pose.name,
         pose.sanskrit_name,
         pose.expertise_level,
         pose.pose_type
       );

       const endTime = Date.now();
       const timeTaken = (endTime - startTime) / 1000;
       processedCount++;
       logger.info(`Processed: ${processedCount}/${totalPoses} - ${pose.name} (${timeTaken.toFixed(2)} seconds)`);
     } else {
       pose.description = '';
       processedCount++;
       logger.info(`Processed: ${processedCount}/${totalPoses} - ${pose.name} (${timeTaken.toFixed(2)} seconds)`);
     }

     // Add a delay to avoid rate limit
     await sleep(30000); // 30 seconds
   }

   await fs.writeFile(outputFile, JSON.stringify(yogaPoses, null, 2));
   logger.info(`Descriptions added and saved to ${outputFile}`);
 } catch (error) {
   logger.error(`Error processing JSON file: ${error}`);
 }
}

async function main() {
 const inputFile = './data/yoga_poses.json';
 const outputFile = './data/yoga_poses_with_descriptions.json';

 await addDescriptionsToJSON(inputFile, outputFile);
}

main();

এই অ্যাপ্লিকেশনটি প্রতিটি যোগাসনের JSON রেকর্ডে একটি নতুন description ক্ষেত্র যোগ করবে। এটি জেমিনি মডেলে একটি কলের মাধ্যমে বিবরণটি সংগ্রহ করবে, যেখানে আমরা এটিকে প্রয়োজনীয় প্রম্পট সরবরাহ করব। ক্ষেত্রটি JSON ফাইলে যোগ করা হয় এবং নতুন ফাইলটি data/yoga_poses_with_descriptions.json ফাইলে লেখা হয়।

চলুন প্রধান ধাপগুলো দেখে নেওয়া যাক:

  1. main() ফাংশনের ভেতরে আপনি দেখতে পাবেন যে, এটি add_descriptions_to_json ফাংশনটিকে কল করে এবং প্রত্যাশিত ইনপুট ও আউটপুট ফাইল সরবরাহ করে।
  2. add_descriptions_to_json ফাংশনটি প্রতিটি JSON রেকর্ডের জন্য, অর্থাৎ যোগা পোস্টের তথ্যের জন্য, নিম্নলিখিত কাজগুলো করে থাকে:
  3. এটি pose_name , sanskrit_name , expertise_level এবং pose_types বের করে আনে।
  4. এটি callGemini ফাংশনটিকে কল করে যা একটি প্রম্পট তৈরি করে এবং তারপরে প্রতিক্রিয়া টেক্সটটি পাওয়ার জন্য `LangchainVertexAI` মডেল ক্লাসটিকে কল করে।
  5. এরপর এই প্রতিক্রিয়া টেক্সটটি JSON অবজেক্টে যোগ করা হয়।
  6. এরপর অবজেক্টগুলোর হালনাগাদকৃত JSON তালিকাটি গন্তব্য ফাইলে লেখা হয়।

চলুন এই অ্যাপ্লিকেশনটি চালানো যাক। একটি নতুন টার্মিনাল উইন্ডো খুলুন (Ctrl+Shift+C) এবং নিম্নলিখিত কমান্ডটি দিন:

npm run generate-descriptions

যদি আপনার কাছে কোনো অনুমতি চাওয়া হয়, তবে অনুগ্রহ করে তা প্রদান করুন।

আপনি দেখবেন যে অ্যাপ্লিকেশনটি চালু হতে শুরু করেছে। নতুন গুগল ক্লাউড অ্যাকাউন্টগুলিতে থাকতে পারে এমন কোনো রেট লিমিট কোটা এড়ানোর জন্য আমরা রেকর্ডগুলির মধ্যে ৩০ সেকেন্ডের একটি বিলম্ব যোগ করেছি, তাই অনুগ্রহ করে ধৈর্য ধরুন।

চলমান একটি নমুনা রান নিচে দেখানো হলো:

469ede91ba007c1f.png

একবার জেমিনি কলের মাধ্যমে তিনটি রেকর্ডই উন্নত করা হয়ে গেলে, data/yoga_poses_with_description.json নামের একটি ফাইল তৈরি হবে। আপনি সেটি দেখে নিতে পারেন।

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

৫. ফায়ারস্টোরে ডেটা ইম্পোর্ট করুন এবং ভেক্টর এমবেডিং তৈরি করুন

আমাদের কাছে data/yoga_poses_with_description.json ফাইলটি আছে এবং এখন আমাদের এটি দিয়ে ফায়ারস্টোর ডেটাবেসটি পূরণ করতে হবে এবং সবচেয়ে গুরুত্বপূর্ণভাবে, প্রতিটি রেকর্ডের জন্য ভেক্টর এমবেডিং তৈরি করতে হবে। এই ভেক্টর এমবেডিংগুলো পরবর্তীতে কাজে আসবে, যখন আমাদের ব্যবহারকারীর স্বাভাবিক ভাষায় দেওয়া কোয়েরির সাথে এগুলোর সাদৃশ্য অনুসন্ধান করতে হবে।

তা করার ধাপগুলো নিম্নরূপ হবে:

  1. আমরা JSON অবজেক্টের তালিকাটিকে অবজেক্টের একটি তালিকায় রূপান্তর করব। প্রতিটি ডকুমেন্টের দুটি অ্যাট্রিবিউট থাকবে: content এবং metadata । metadata অবজেক্টটিতে সম্পূর্ণ JSON অবজেক্টটি থাকবে, যেটিতে name , description , sanskrit_name ইত্যাদির মতো অ্যাট্রিবিউটগুলো রয়েছে। content হবে একটি স্ট্রিং টেক্সট, যা কয়েকটি ফিল্ডের সংযোগ বা কনক্যাটেনেশন হবে।
  2. একবার আমাদের কাছে ডকুমেন্টের একটি তালিকা চলে এলে, আমরা কন্টেন্ট ফিল্ডের জন্য এমবেডিং তৈরি করতে Vertex AI Embeddings ক্লাসটি ব্যবহার করব। এই এমবেডিংটি প্রতিটি ডকুমেন্ট রেকর্ডে যুক্ত করা হবে এবং তারপর আমরা Firestore API ব্যবহার করে ডকুমেন্ট অবজেক্টের এই তালিকাটি কালেকশনে সংরক্ষণ করব (আমরা TEST_COLLECTION ভেরিয়েবলটি ব্যবহার করছি যা test-poses কে নির্দেশ করে)।

import-data.js এর কোড নিচে দেওয়া হলো (সংক্ষিপ্ততার জন্য কোডের কিছু অংশ ছেঁটে ফেলা হয়েছে):

import { Firestore,
        FieldValue,
} from '@google-cloud/firestore';
import { VertexAIEmbeddings } from "@langchain/google-vertexai";
import * as dotenv from 'dotenv';
import fs from 'fs/promises';

// Load environment variables
dotenv.config();

// Configure logging
const logger = {
 info: (message) => console.log(`INFO - ${new Date().toISOString()} - ${message}`),
 error: (message) => console.error(`ERROR - ${new Date().toISOString()} - ${message}`),
};

async function loadYogaPosesDataFromLocalFile(filename) {
 try {
   const data = await fs.readFile(filename, 'utf-8');
   const poses = JSON.parse(data);
   logger.info(`Loaded ${poses.length} poses.`);
   return poses;
 } catch (error) {
   logger.error(`Error loading dataset: ${error}`);
   return null;
 }
}

function createFirestoreDocuments(poses) {
 const documents = [];
 for (const pose of poses) {
   // Convert the pose to a string representation for pageContent
   const pageContent = `
name: ${pose.name || ''}
description: ${pose.description || ''}
sanskrit_name: ${pose.sanskrit_name || ''}
expertise_level: ${pose.expertise_level || 'N/A'}
pose_type: ${pose.pose_type || 'N/A'}
   `.trim();

   // The metadata will be the whole pose
   const metadata = pose;
   documents.push({ pageContent, metadata });
 }
 logger.info(`Created ${documents.length} Langchain documents.`);
 return documents;
}

async function main() {
 const allPoses = await loadYogaPosesDataFromLocalFile('./data/yoga_poses_with_descriptions.json');
 const documents = createFirestoreDocuments(allPoses);
 logger.info(`Successfully created Firestore documents. Total documents: ${documents.length}`);

 const embeddings = new VertexAIEmbeddings({
   model: process.env.EMBEDDING_MODEL_NAME,
 });
  // Initialize Firestore
 const firestore = new Firestore({
   projectId: process.env.PROJECT_ID,
   databaseId: process.env.DATABASE,
 });

 const collectionName = process.env.TEST_COLLECTION;

 for (const doc of documents) {
   try {
     // 1. Generate Embeddings
     const singleVector = await embeddings.embedQuery(doc.pageContent);

     // 2. Store in Firestore with Embeddings
     const firestoreDoc = {
       content: doc.pageContent,
       metadata: doc.metadata, // Store the original data as metadata
       embedding: FieldValue.vector(singleVector), // Add the embedding vector
     };

     const docRef = firestore.collection(collectionName).doc();
     await docRef.set(firestoreDoc);
     logger.info(`Document ${docRef.id} added to Firestore with embedding.`);
   } catch (error) {
     logger.error(`Error processing document: ${error}`);
   }
 }

 logger.info('Finished adding documents to Firestore.');
}

main();

চলুন এই অ্যাপ্লিকেশনটি চালানো যাক। একটি নতুন টার্মিনাল উইন্ডো খুলুন (Ctrl+Shift+C) এবং নিম্নলিখিত কমান্ডটি দিন:

npm run import-data

সবকিছু ঠিকঠাক থাকলে, আপনি নিচেরটির মতো একটি বার্তা দেখতে পাবেন:

INFO - 2025-01-28T07:01:14.463Z - Loaded 3 poses.
INFO - 2025-01-28T07:01:14.464Z - Created 3 Langchain documents.
INFO - 2025-01-28T07:01:14.464Z - Successfully created Firestore documents. Total documents: 3
INFO - 2025-01-28T07:01:17.623Z - Document P46d5F92z9FsIhVVYgkd added to Firestore with embedding.
INFO - 2025-01-28T07:01:18.265Z - Document bjXXISctkXl2ZRSjUYVR added to Firestore with embedding.
INFO - 2025-01-28T07:01:19.285Z - Document GwzZMZyPfTLtiX6qBFFz added to Firestore with embedding.
INFO - 2025-01-28T07:01:19.286Z - Finished adding documents to Firestore.

রেকর্ডগুলি সফলভাবে সন্নিবেশিত হয়েছে কিনা এবং এমবেডিংগুলি তৈরি হয়েছে কিনা তা পরীক্ষা করতে, ক্লাউড কনসোলে ফায়ারস্টোর পৃষ্ঠাটি দেখুন।

504cabdb99a222a5.png

(ডিফল্ট) ডেটাবেসে ক্লিক করুন, এতে ' test-poses কালেকশন এবং তার অধীনে একাধিক ডকুমেন্ট দেখা যাবে। প্রতিটি ডকুমেন্ট হলো একটি যোগাসন।

9f37aa199c4b547a.png

ফিল্ডগুলো খতিয়ে দেখতে যেকোনো একটি ডকুমেন্টে ক্লিক করুন। আমাদের ইম্পোর্ট করা ফিল্ডগুলো ছাড়াও আপনি embedding ফিল্ডটিও পাবেন, যেটি একটি ভেক্টর ফিল্ড এবং যার মান আমরা text-embedding-004 Vertex AI Embedding মডেলের মাধ্যমে তৈরি করেছি।

f0ed92124519beaf.png

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

৬. ফায়ারস্টোর ডেটাবেস সংগ্রহে সম্পূর্ণ যোগাসনগুলো ইম্পোর্ট করুন।

আমরা এখন poses কালেকশনটি তৈরি করব, যা হলো ১৬০টি যোগাসনের একটি সম্পূর্ণ তালিকা। এর জন্য আমরা একটি ডাটাবেস ইম্পোর্ট ফাইল তৈরি করেছি যা আপনি সরাসরি ইম্পোর্ট করতে পারবেন। ল্যাবে সময় বাঁচানোর জন্যই এটি করা হয়। যে ডাটাবেসে বর্ণনা এবং এমবেডিংগুলো থাকে, তা তৈরি করার প্রক্রিয়াটি আগের অংশে দেখা পদ্ধতির মতোই।

নিচে দেওয়া ধাপগুলো অনুসরণ করে ডাটাবেস ইম্পোর্ট করুন:

  1. নিচে দেওয়া gsutil কমান্ডটি ব্যবহার করে আপনার প্রজেক্টে একটি বাকেট তৈরি করুন। নিচের কমান্ডে থাকা <PROJECT_ID> ভেরিয়েবলটির জায়গায় আপনার গুগল ক্লাউড প্রজেক্ট আইডি বসান।
gsutil mb -l us-central1 gs://<PROJECT_ID>-my-bucket
  1. বাকেটটি তৈরি হয়ে গেলে, ফায়ারবেস ডেটাবেসে ইম্পোর্ট করার আগে, আমাদের প্রস্তুত করা ডেটাবেস এক্সপোর্টটি এই বাকেটে কপি করতে হবে। নিচে দেওয়া কমান্ডটি ব্যবহার করুন:
gsutil cp -r gs://yoga-database-firestore-export-bucket/2025-01-27T05:11:02_62615  gs://<PROJECT_ID>-my-bucket

এখন যেহেতু আমাদের কাছে ইম্পোর্ট করার জন্য ডেটা আছে, আমরা আমাদের তৈরি করা ফায়ারবেস ডেটাবেসে ( default ) ডেটা ইম্পোর্ট করার চূড়ান্ত ধাপে যেতে পারি।

  1. নিচে দেওয়া gcloud কমান্ডটি ব্যবহার করুন:
gcloud firestore import gs://<PROJECT_ID>-my-bucket/2025-01-27T05:11:02_62615

ইম্পোর্ট হতে কয়েক সেকেন্ড সময় লাগবে এবং এটি প্রস্তুত হয়ে গেলে, আপনি https://console.cloud.google.com/firestore/databases- এ গিয়ে আপনার ফায়ারস্টোর ডেটাবেস এবং কালেকশনটি যাচাই করতে পারবেন, এবং নিচে দেখানো অনুযায়ী default ডেটাবেস ও poses কালেকশনটি নির্বাচন করুন:

561f3cb840de23d8.png

এর মাধ্যমে আমাদের অ্যাপ্লিকেশনে ব্যবহৃত ফায়ারস্টোর কালেকশনটি তৈরি সম্পন্ন হলো।

৭. ফায়ারস্টোরে ভেক্টর সাদৃশ্য অনুসন্ধান সম্পাদন করুন

ভেক্টর সিমিলারিটি সার্চ করার জন্য, আমরা ব্যবহারকারীর কাছ থেকে কোয়েরিটি নেব। এই কোয়েরির একটি উদাহরণ হতে পারে "Suggest me some exercises to relieve back pain"

search-data.js ফাইলটি দেখুন। এখানে দেখার মতো মূল ফাংশনটি হলো search ফাংশন, যা নিচে দেখানো হলো। সহজ ভাষায় বলতে গেলে, এটি একটি এমবেডিং ক্লাস তৈরি করে যা ব্যবহারকারীর কোয়েরির জন্য এমবেডিং তৈরি করতে ব্যবহৃত হবে। এরপর এটি ফায়ারস্টোর ডাটাবেস এবং কালেকশনের সাথে একটি সংযোগ স্থাপন করে। তারপর কালেকশনটির উপর এটি findNearest মেথডটি কল করে, যা একটি ভেক্টর সিমিলারিটি সার্চ (Vector Similarity Search) করে।

async function search(query) {
 try {

   const embeddings = new VertexAIEmbeddings({
       model: process.env.EMBEDDING_MODEL_NAME,
     });
  
   // Initialize Firestore
   const firestore = new Firestore({
       projectId: process.env.PROJECT_ID,
       databaseId: process.env.DATABASE,
   });

   log.info(`Now executing query: ${query}`);
   const singleVector = await embeddings.embedQuery(query);

   const collectionRef = firestore.collection(process.env.COLLECTION);
   let vectorQuery = collectionRef.findNearest(
   "embedding",
   FieldValue.vector(singleVector), // a vector with 768 dimensions
   {
       limit: process.env.TOP_K,
       distanceMeasure: "COSINE",
   }
   );
   const vectorQuerySnapshot = await vectorQuery.get();

   for (const result of vectorQuerySnapshot.docs) {
     console.log(result.data().content);
   }
 } catch (error) {
   log.error(`Error during search: ${error.message}`);
 }
}

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

কম্পোজিট ইনডেক্স তৈরি করার জন্য gcloud কমান্ডটি নিচে দেখানো হলো:

gcloud firestore indexes composite create --project=<YOUR_PROJECT_ID> --collection-group=poses --query-scope=COLLECTION --field-config=vector-config='{"dimension":"768","flat": "{}"}',field-path=embedding

ডাটাবেসে ১৫০টিরও বেশি রেকর্ড থাকায় ইনডেক্সটি সম্পূর্ণ হতে কয়েক মিনিট সময় লাগবে। এটি সম্পূর্ণ হয়ে গেলে, আপনি নিচে দেখানো কমান্ডটির মাধ্যমে ইনডেক্সটি দেখতে পারবেন:

gcloud firestore indexes composite list

আপনি এইমাত্র তৈরি করা ইনডেক্সটি তালিকায় দেখতে পাবেন।

এখন নিম্নলিখিত কমান্ডটি ব্যবহার করে দেখুন:

node search-data.js --prompt "Recommend me some exercises for back pain relief"

আপনাকে কয়েকটি সুপারিশ প্রদান করা হবে। নিচে একটি নমুনা রান দেখানো হলো:

2025-01-28T07:09:05.250Z - INFO - Now executing query: Recommend me some exercises for back pain relief
name: Sphinx Pose
description: A gentle backbend, Sphinx Pose (Salamba Bhujangasana) strengthens the spine and opens the chest.  Keep shoulders relaxed, lengthen the tailbone, and engage the core for optimal alignment. Beginner-friendly.

sanskrit_name: Salamba Bhujangasana
expertise_level: Beginner
pose_type: ['Prone']
name: Supine Spinal Twist Pose
description: A gentle supine twist (Supta Matsyendrasana), great for beginners.  Releases spinal tension, improves digestion, and calms the nervous system.  Keep shoulders flat on the floor and lengthen your spine throughout the twist.

sanskrit_name: Supta Matsyendrasana
expertise_level: Beginner
pose_type: ['Supine', 'Twist']
name: Reverse Corpse Pose
description: Reverse Corpse Pose (Advasana) is a beginner prone pose.  Lie on your belly, arms at your sides, relaxing completely.  Benefits include stress release and spinal decompression. Ensure your forehead rests comfortably on the mat.

sanskrit_name: Advasana
expertise_level: Beginner
pose_type: ['Prone']

একবার এটি চালু হয়ে গেলে, আমরা এখন বুঝে গেছি কীভাবে ফায়ারস্টোর ভেক্টর ডেটাবেস ব্যবহার করে রেকর্ড আপলোড করতে, এমবেডিং তৈরি করতে এবং ভেক্টর সিমিলারিটি সার্চ করতে হয়। আমরা এখন একটি ওয়েব অ্যাপ্লিকেশন তৈরি করতে পারি যা এই ভেক্টর সার্চটিকে একটি ওয়েব ফ্রন্ট-এন্ডের সাথে একীভূত করবে।

৮. ওয়েব অ্যাপ্লিকেশন

পাইথন ফ্লাস্ক ওয়েব অ্যাপ্লিকেশনটি app.js ফাইলে এবং ফ্রন্ট-এন্ড HTML ফাইলটি views/index.html.

উভয় ফাইলই দেখে নেওয়ার পরামর্শ দেওয়া হচ্ছে। প্রথমে app.js ফাইলটি দিয়ে শুরু করুন, যেটিতে /search হ্যান্ডলারটি রয়েছে। এই হ্যান্ডলারটি ফ্রন্ট-এন্ড HTML index.html ফাইল থেকে পাঠানো প্রম্পটটি গ্রহণ করে। এরপর এটি search মেথডটিকে কল করে, যা ভেক্টর সিমিলারিটি সার্চটি সম্পন্ন করে, যা আমরা পূর্ববর্তী অংশে আলোচনা করেছি।

এরপর সুপারিশগুলোর তালিকা সহ প্রতিক্রিয়াটি index.html এ ফেরত পাঠানো হয়। তারপর index.html সুপারিশগুলোকে আলাদা আলাদা কার্ড হিসেবে প্রদর্শন করে।

অ্যাপ্লিকেশনটি স্থানীয়ভাবে চালান

একটি নতুন টার্মিনাল উইন্ডো (Ctrl+Shift+C) অথবা যেকোনো বিদ্যমান টার্মিনাল উইন্ডো চালু করুন এবং নিম্নলিখিত কমান্ডটি দিন:

npm run start

নিচে একটি নমুনা কার্যসম্পাদন দেখানো হলো:

...
Server listening on port 8080

একবার চালু হয়ে গেলে, নিচে দেখানো ওয়েব প্রিভিউ বোতামটিতে ক্লিক করে অ্যাপ্লিকেশনটির হোম ইউআরএল-এ যান:

de297d4cee10e0bf.png

এটি আপনাকে index.html ফাইলটি নিচে দেখানো অনুযায়ী পরিবেশন করবে:

20240a0e885ac17b.png

একটি নমুনা কোয়েরি দিন (উদাহরণ: Provide me some exercises for back pain relief ) এবং Search বাটনে ক্লিক করুন। এটি ডাটাবেস থেকে কিছু সুপারিশ নিয়ে আসবে। আপনি একটি Play Audio বাটনও দেখতে পাবেন, যা বর্ণনার উপর ভিত্তি করে একটি অডিও স্ট্রিম তৈরি করবে, যা আপনি সরাসরি শুনতে পারবেন।

789b4277dc40e2be.png

৯. (ঐচ্ছিক) গুগল ক্লাউড রান-এ ডেপ্লয় করা

আমাদের চূড়ান্ত ধাপ হবে এই অ্যাপ্লিকেশনটি গুগল ক্লাউড রান-এ ডেপ্লয় করা। ডেপ্লয়মেন্ট কমান্ডটি নিচে দেখানো হলো, ডেপ্লয় করার আগে নিশ্চিত করুন যে আপনি নিচে বন্ধনীতে (<<>>) দেখানো বিভিন্ন মানগুলো প্রতিস্থাপন করেছেন। এই মানগুলো আপনি .env ফাইল থেকে সংগ্রহ করতে পারবেন।

gcloud run deploy yogaposes --source . \
  --port=8080 \
  --allow-unauthenticated \
  --region=<<YOUR_LOCATION>> \
  --platform=managed  \
  --project=<<YOUR_PROJECT_ID>> \
--set-env-vars=PROJECT_ID="<<YOUR_PROJECT_ID>>",LOCATION="<<YOUR_LOCATION>>",EMBEDDING_MODEL_NAME="<<EMBEDDING_MODEL_NAME>>",DATABASE="<<FIRESTORE_DATABASE_NAME>>",COLLECTION="<<FIRESTORE_COLLECTION_NAME>>",TOP_K=<<YOUR_TOP_K_VALUE>>

অ্যাপ্লিকেশনটির রুট ফোল্ডার থেকে উপরের কমান্ডটি চালান। আপনাকে গুগল ক্লাউড এপিআই (Google Cloud APIs) সক্রিয় করতেও বলা হতে পারে এবং বিভিন্ন অনুমতির জন্য আপনার সম্মতি জানাতে বলা হলে, অনুগ্রহ করে তা করুন।

স্থাপন প্রক্রিয়াটি সম্পন্ন হতে প্রায় ৫-৭ মিনিট সময় লাগবে, তাই অনুগ্রহ করে ধৈর্য ধরুন।

3a6d86fd32e4a5e.png

সফলভাবে ডেপ্লয়মেন্ট সম্পন্ন হলে, ডেপ্লয়মেন্ট আউটপুটে ক্লাউড রান সার্ভিস ইউআরএলটি দেওয়া হবে। এটি দেখতে এইরকম হবে:

Service URL: https://yogaposes-<UNIQUEID>.us-central1.run.app

ওই পাবলিক ইউআরএল-টি ভিজিট করলে আপনি দেখবেন যে একই ওয়েব অ্যাপ্লিকেশনটি সফলভাবে ডেপ্লয় হয়ে চলছে।

84e1cbf29cbaeedc.png

আপনি গুগল ক্লাউড কনসোল থেকেও ক্লাউড রান-এ যেতে পারেন এবং সেখানে সার্ভিসগুলোর তালিকা দেখতে পাবেন। সেখানে তালিকাভুক্ত সার্ভিসগুলোর মধ্যে yogaposes সার্ভিসটি একটি হওয়া উচিত (যদি একমাত্রটি না-ও হয়)।

f2b34a8c9011be4c.png

নির্দিষ্ট সার্ভিসের নামে (আমাদের ক্ষেত্রে yogaposes ) ক্লিক করে আপনি সার্ভিসটির ইউআরএল, কনফিগারেশন, লগ এবং আরও অনেক কিছুর বিস্তারিত তথ্য দেখতে পারেন।

faaa5e0c02fe0423.png

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

১০. অভিনন্দন

অভিনন্দন, আপনি সফলভাবে এমন একটি অ্যাপ্লিকেশন তৈরি করেছেন যা ফায়ারস্টোরে একটি ডেটাসেট আপলোড করে, এমবেডিং তৈরি করে এবং ব্যবহারকারীর কোয়েরির উপর ভিত্তি করে ভেক্টর সিমিলারিটি সার্চ করে।

রেফারেন্স নথি