कोडलैब - Firestore, Vector Search, Langchain, और Gemini (Node.js वर्शन) की मदद से, संदर्भ के हिसाब से योग आसन सुझाने वाला ऐप्लिकेशन बनाएं

1. परिचय

इस कोडलैब में, आपको एक ऐसा ऐप्लिकेशन बनाना होगा जो योग के आसनों के सुझाव देने के लिए, वेक्टर सर्च का इस्तेमाल करता है.

कोडलैब में, आपको सिलसिलेवार तरीके से यह तरीका अपनाना होगा:

  1. योग आसनों (JSON फ़ॉर्मैट) के लिए, Hugging Face के मौजूदा डेटासेट का इस्तेमाल करें.
  2. डेटासेट को बेहतर बनाने के लिए, एक और फ़ील्ड का ब्यौरा जोड़ें. यह ब्यौरा, हर पोज़ के लिए जानकारी जनरेट करने के लिए Gemini का इस्तेमाल करता है.
  3. जनरेट किए गए एम्बेड के साथ, Firestore कलेक्शन में दस्तावेज़ों के कलेक्शन के तौर पर योग आसनों का डेटा लोड करें.
  4. वेक्टर खोज की सुविधा इस्तेमाल करने के लिए, Firestore में कंपोजिट इंडेक्स बनाएं.
  5. Node.js ऐप्लिकेशन में वेक्टर सर्च का इस्तेमाल करें, जो नीचे दिखाए गए तरीके से सभी चीज़ों को एक साथ लाता है:

84e1cbf29cbaeedc.png

आपको क्या करना होगा

  • वेब ऐप्लिकेशन को डिज़ाइन, बनाएं, और डिप्लॉय करें. यह ऐप्लिकेशन, योग के आसनों के सुझाव देने के लिए वेक्टर सर्च का इस्तेमाल करता है.

आपको क्या सीखने को मिलेगा

  • टेक्स्ट कॉन्टेंट जनरेट करने के लिए, Gemini का इस्तेमाल करने का तरीका. साथ ही, इस कोडलैब के कॉन्टेक्स्ट में, योग के आसनों के लिए ब्यौरे जनरेट करना
  • Hugging Face के बेहतर डेटासेट से, वेक्टर एम्बेडिंग के साथ-साथ Firestore में रिकॉर्ड लोड करने का तरीका
  • सामान्य भाषा की क्वेरी के आधार पर डेटा खोजने के लिए, Firestore वेक्टर सर्च का इस्तेमाल करने का तरीका
  • ऑडियो कॉन्टेंट जनरेट करने के लिए, Google Cloud के Text to Speech API का इस्तेमाल करने का तरीका

आपको इन चीज़ों की ज़रूरत होगी

  • Chrome वेब ब्राउज़र
  • Gmail खाता
  • बिलिंग की सुविधा वाला Cloud प्रोजेक्ट

इस कोडलैब को सभी लेवल के डेवलपर (शुरुआती डेवलपर भी) के लिए डिज़ाइन किया गया है. इसमें सैंपल ऐप्लिकेशन के लिए, JavaScript और Node.js का इस्तेमाल किया गया है. हालांकि, यहां बताए गए कॉन्सेप्ट को समझने के लिए, JavaScript और Node.js के बारे में जानकारी होना ज़रूरी नहीं है.

2. शुरू करने से पहले

प्रोजेक्ट बनाना

  1. Google Cloud Console में, प्रोजेक्ट चुनने वाले पेज पर, Google Cloud प्रोजेक्ट चुनें या बनाएं.
  2. पक्का करें कि आपके Cloud प्रोजेक्ट के लिए बिलिंग की सुविधा चालू हो. किसी प्रोजेक्ट के लिए बिलिंग की सुविधा चालू है या नहीं, यह देखने का तरीका जानें .
  3. इसके लिए, आपको Cloud Shell का इस्तेमाल करना होगा. यह Google Cloud में चलने वाला कमांड-लाइन एनवायरमेंट है, जिसमें bq पहले से लोड होता है. Google Cloud कंसोल में सबसे ऊपर, 'Cloud Shell चालू करें' पर क्लिक करें.

Cloud Shell चालू करने के लिए बटन की इमेज

  1. Cloud Shell से कनेक्ट होने के बाद, यह जांच करें कि आपकी पुष्टि पहले ही हो चुकी है या नहीं. साथ ही, यह भी देखें कि प्रोजेक्ट आपके प्रोजेक्ट आईडी पर सेट है या नहीं. इसके लिए, यह कमांड इस्तेमाल करें:
gcloud auth list
  1. Cloud Shell में यह कमांड चलाकर पुष्टि करें कि gcloud कमांड को आपके प्रोजेक्ट के बारे में पता है.
gcloud config list project
  1. अगर आपका प्रोजेक्ट सेट नहीं है, तो इसे सेट करने के लिए इस निर्देश का इस्तेमाल करें:
gcloud config set project <YOUR_PROJECT_ID>
  1. नीचे दिए गए निर्देश का इस्तेमाल करके, ज़रूरी एपीआई चालू करें. इसमें कुछ मिनट लग सकते हैं. इसलिए, कृपया इंतज़ार करें.
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 कमांड के बजाय, कंसोल में जाकर हर प्रॉडक्ट को खोजें या इस लिंक का इस्तेमाल करें.

अगर कोई एपीआई छूट जाता है, तो उसे लागू करने के दौरान कभी भी चालू किया जा सकता है.

gcloud के निर्देशों और इस्तेमाल के बारे में जानने के लिए, दस्तावेज़ देखें.

रिपॉज़िटरी को क्लोन करना और एनवायरमेंट सेटिंग सेट अप करना

अगला चरण, सैंपल रिपॉज़िटरी को क्लोन करना है. इसका इस्तेमाल, हम कोडलैब के बाकी हिस्से में करेंगे. मान लें कि आप Cloud Shell में हैं, तो अपनी होम डायरेक्ट्री से यह कमांड दें:

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

एडिटर लॉन्च करने के लिए, Cloud Shell विंडो के टूलबार में मौजूद, 'एडिटर खोलें' पर क्लिक करें. सबसे ऊपर बाएं कोने में मौजूद मेन्यू बार पर क्लिक करें. इसके बाद, फ़ाइल → फ़ोल्डर खोलें को चुनें, जैसा कि यहां दिखाया गया है:

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

Google Cloud प्रोजेक्ट और Firestore डेटाबेस की जगह बनाते समय, आपने जो वैल्यू चुनी थीं उसके हिसाब से, कृपया PROJECT_ID और LOCATION की वैल्यू अपडेट करें. आम तौर पर, हम चाहते हैं कि Google Cloud प्रोजेक्ट और Firestore डेटाबेस के लिए, 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

बढ़िया ! अब हम Firestore डेटाबेस सेट अप करने के लिए पूरी तरह तैयार हैं.

3. Firestore सेटअप करना

Cloud Firestore, पूरी तरह से मैनेज किया जाने वाला सर्वरलेस दस्तावेज़ डेटाबेस है. इसका इस्तेमाल, हम अपने ऐप्लिकेशन डेटा के लिए बैकएंड के तौर पर करेंगे. Cloud Firestore में डेटा, दस्तावेज़ों के संग्रह में स्ट्रक्चर्ड होता है.

Firestore डेटाबेस को शुरू करना

Cloud Console में Firestore पेज पर जाएं.

अगर आपने प्रोजेक्ट में पहले कभी Firestore डेटाबेस को शुरू नहीं किया है, तो Create Database पर क्लिक करके default डेटाबेस बनाएं. डेटाबेस बनाते समय, इन वैल्यू का इस्तेमाल करें:

  • Firestore मोड: Native.
  • जगह की जानकारी: जगह की जानकारी की डिफ़ॉल्ट सेटिंग का इस्तेमाल करें.
  • सुरक्षा नियमों के लिए, Test rules पर जाएं.
  • डेटाबेस बनाएं.

504cabdb99a222a5.png

अगले सेक्शन में, हम अपने डिफ़ॉल्ट Firestore डेटाबेस में poses नाम का कलेक्शन बनाने के लिए बुनियादी तैयारी करेंगे. इस कलेक्शन में, योग के आसनों की जानकारी या सैंपल डेटा (दस्तावेज़) सेव किए जाएंगे. इसका इस्तेमाल, हम अपने ऐप्लिकेशन में करेंगे.

इससे, Firestore डेटाबेस सेट अप करने का सेक्शन पूरा हो जाता है.

4. योग की मुद्राओं का डेटासेट तैयार करना

हमारा पहला काम, योग के आसनों का डेटासेट तैयार करना है. इसका इस्तेमाल हम ऐप्लिकेशन के लिए करेंगे. हम Hugging Face के मौजूदा डेटासेट से शुरुआत करेंगे. इसके बाद, ज़्यादा जानकारी के साथ इसे बेहतर बनाएंगे.

योग मुद्राओं के लिए Hugging Face का डेटासेट देखें. ध्यान दें कि इस कोडलैब में किसी एक डेटासेट का इस्तेमाल किया गया है. हालांकि, किसी भी दूसरे डेटासेट का इस्तेमाल किया जा सकता है. साथ ही, डेटासेट को बेहतर बनाने के लिए, यहां बताई गई तकनीकों का इस्तेमाल किया जा सकता है.

298cfae7f23e4bef.png

Files and versions सेक्शन में जाकर, सभी पोज़ की JSON डेटा फ़ाइल देखी जा सकती है.

3fe6e55abdc032ec.png

हमने yoga_poses.json डाउनलोड कर लिया है और आपको वह फ़ाइल उपलब्ध करा दी है. इस फ़ाइल का नाम yoga_poses_alldata.json है और यह /data फ़ोल्डर में मौजूद है.

Cloud Shell एडिटर में 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"]
 }

अब हमारे पास Gemini को पेश करने का एक शानदार मौका है. साथ ही, हम यह भी बता सकते हैं कि इसके लिए डिफ़ॉल्ट मॉडल का इस्तेमाल करके, description फ़ील्ड कैसे जनरेट किया जा सकता है.

Cloud Shell Editor में, 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 फ़ील्ड जोड़ देगा. यह Gemini मॉडल को कॉल करके जानकारी हासिल करेगा. इसके लिए, हम उसे ज़रूरी प्रॉम्प्ट देंगे. फ़ील्ड को 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

अगर आपसे कोई अनुमति मांगी जाती है, तो कृपया उसे दें.

आपको पता चलेगा कि ऐप्लिकेशन चलने लगा है. हमने रिकॉर्ड के बीच 30 सेकंड की देरी जोड़ी है, ताकि नए Google Cloud खातों पर दर की सीमा से जुड़े कोटे से बचा जा सके. इसलिए, कृपया थोड़ा इंतज़ार करें.

यहां चल रहे सैंपल का उदाहरण दिया गया है:

469ede91ba007c1f.png

Gemini कॉल की मदद से तीनों रिकॉर्ड बेहतर होने के बाद, एक फ़ाइल data/yoga_poses_with_description.json जनरेट होगी. इस पर एक नज़र डालें.

अब हम अपनी डेटा फ़ाइल के साथ तैयार हैं. अगले चरण में, हमें एम्बेड जनरेट करने के साथ-साथ, Firestore डेटाबेस को डेटा से भरने का तरीका समझना होगा.

5. Firestore में डेटा इंपोर्ट करना और वेक्टर एम्बेड जनरेट करना

हमारे पास data/yoga_poses_with_description.json फ़ाइल है और अब हमें उससे Firestore डेटाबेस को पॉप्युलेट करना है. साथ ही, हर रिकॉर्ड के लिए वेक्टर एम्बेडिंग जनरेट करना है. वेक्टर एम्बेडिंग तब काम आएंगे, जब हमें उपयोगकर्ता की सामान्य भाषा में दी गई क्वेरी के साथ, उन पर मिलती-जुलती खोज करनी होगी.

ऐसा करने के लिए, यह तरीका अपनाएं:

  1. हम JSON ऑब्जेक्ट की सूची को ऑब्जेक्ट की सूची में बदल देंगे. हर दस्तावेज़ में दो एट्रिब्यूट होंगे: content और metadata. मेटाडेटा ऑब्जेक्ट में पूरा JSON ऑब्जेक्ट होगा, जिसमें name, description, sanskrit_name वगैरह जैसे एट्रिब्यूट होंगे. content एक स्ट्रिंग टेक्स्ट होगा, जो कुछ फ़ील्ड को जोड़कर बनाया जाएगा.
  2. दस्तावेज़ों की सूची मिलने के बाद, हम कॉन्टेंट फ़ील्ड के लिए एम्बेडिंग जनरेट करने के लिए, Vertex AI एम्बेडिंग क्लास का इस्तेमाल करेंगे. यह एम्बेडिंग, हर दस्तावेज़ रिकॉर्ड में जोड़ दी जाएगी. इसके बाद, हम 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.

यह देखने के लिए कि रिकॉर्ड सही से डाले गए हैं या नहीं और एम्बेड जनरेट हुए हैं या नहीं, Cloud Console में Firestore पेज पर जाएं.

504cabdb99a222a5.png

(डिफ़ॉल्ट) डेटाबेस पर क्लिक करें. इससे आपको test-poses कलेक्शन और उस कलेक्शन में मौजूद कई दस्तावेज़ दिखेंगे. हर दस्तावेज़ में योग का एक आसन है.

9f37aa199c4b547a.png

फ़ील्ड की जांच करने के लिए, किसी भी दस्तावेज़ पर क्लिक करें. इंपोर्ट किए गए फ़ील्ड के अलावा, आपको embedding फ़ील्ड भी दिखेगा. यह एक वेक्टर फ़ील्ड है, जिसकी वैल्यू हमने text-embedding-004 Vertex AI एम्बेडिंग मॉडल की मदद से जनरेट की है.

f0ed92124519beaf.png

अब हमारे पास Firestore डेटाबेस में, एम्बेड किए गए रिकॉर्ड अपलोड हैं. इसलिए, हम अगले चरण पर जा सकते हैं और Firestore में वेक्टर मिलती-जुलती खोज करने का तरीका देख सकते हैं.

6. Firestore डेटाबेस कलेक्शन में योग के अलग-अलग आसन इंपोर्ट करना

अब हम poses कलेक्शन बनाएंगे. इसमें योग के 160 आसनों की पूरी सूची होगी. इसके लिए, हमने एक डेटाबेस इंपोर्ट फ़ाइल जनरेट की है, जिसे सीधे इंपोर्ट किया जा सकता है. ऐसा लैब में समय बचाने के लिए किया जाता है. ब्यौरा और एम्बेड करने की सुविधा वाले डेटाबेस को जनरेट करने की प्रोसेस, पिछले सेक्शन में बताई गई प्रोसेस जैसी ही है.

डेटाबेस इंपोर्ट करने के लिए, यह तरीका अपनाएं:

  1. नीचे दिए गए gsutil कमांड का इस्तेमाल करके, अपने प्रोजेक्ट में एक बकेट बनाएं. नीचे दिए गए निर्देश में, <PROJECT_ID> वैरिएबल की जगह अपना Google Cloud प्रोजेक्ट आईडी डालें.
gsutil mb -l us-central1 gs://<PROJECT_ID>-my-bucket
  1. बकेट बन जाने के बाद, हमें Firebase डेटाबेस में इंपोर्ट करने से पहले, तैयार किए गए डेटाबेस एक्सपोर्ट को इस बकेट में कॉपी करना होगा. नीचे दिए गए निर्देश का इस्तेमाल करें:
gsutil cp -r gs://yoga-database-firestore-export-bucket/2025-01-27T05:11:02_62615  gs://<PROJECT_ID>-my-bucket

अब हमारे पास इंपोर्ट करने के लिए डेटा है. इसलिए, हम अपने बनाए गए Firebase डेटाबेस (default) में डेटा इंपोर्ट करने के आखिरी चरण पर जा सकते हैं.

  1. नीचे दिए गए gcloud कमांड का इस्तेमाल करें:
gcloud firestore import gs://<PROJECT_ID>-my-bucket/2025-01-27T05:11:02_62615

इंपोर्ट होने में कुछ सेकंड लगेंगे. इसके बाद, https://console.cloud.google.com/firestore/databases पर जाकर, अपने Firestore डेटाबेस और कलेक्शन की पुष्टि की जा सकती है. इसके लिए, यहां दिखाए गए तरीके से default डेटाबेस और poses कलेक्शन चुनें:

561f3cb840de23d8.png

इससे, Firestore कलेक्शन बनाने की प्रोसेस पूरी हो जाती है. इसका इस्तेमाल, हम अपने ऐप्लिकेशन में करेंगे.

7. Firestore में वेक्टर मिलती-जुलती खोज करना

वेक्टर मिलती-जुलती खोज करने के लिए, हम उपयोगकर्ता की क्वेरी का इस्तेमाल करेंगे. इस क्वेरी का उदाहरण "Suggest me some exercises to relieve back pain" हो सकता है.

search-data.js फ़ाइल देखें. यहां search फ़ंक्शन को देखें. यह एक एम्बेडिंग क्लास बनाता है, जिसका इस्तेमाल उपयोगकर्ता की क्वेरी के लिए एम्बेडिंग जनरेट करने के लिए किया जाएगा. इसके बाद, यह Firestore डेटाबेस और कलेक्शन से कनेक्ट हो जाता है. इसके बाद, यह कलेक्शन पर findNearest मेथड को लागू करता है, जो वेक्टर मिलती-जुलती खोज करता है.

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}`);
 }
}

कुछ क्वेरी के उदाहरणों के साथ इसे चलाने से पहले, आपको पहले Firestore कंपोजिट इंडेक्स जनरेट करना होगा. यह आपकी खोज क्वेरी के काम करने के लिए ज़रूरी है. इंडेक्स बनाए बिना ऐप्लिकेशन को चलाने पर, आपको एक गड़बड़ी का मैसेज दिखेगा. इसमें बताया जाएगा कि आपको इंडेक्स बनाना होगा. इसके बाद, इंडेक्स बनाने का कमांड दिखेगा.

कॉम्पोनेंट इंडेक्स बनाने के लिए 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

डेटाबेस में 150 से ज़्यादा रिकॉर्ड मौजूद हैं. इसलिए, इंडेक्स होने में कुछ मिनट लगेंगे. यह प्रोसेस पूरी होने के बाद, इंडेक्स को देखने के लिए यहां दिए गए निर्देश का इस्तेमाल करें:

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']

इस प्रोसेस के काम करने के बाद, हमें यह समझ आ गया है कि रिकॉर्ड अपलोड करने, एम्बेड जनरेट करने, और वेक्टर मिलते-जुलते खोजने के लिए, Firestore वेक्टर डेटाबेस को कैसे काम करना चाहिए. अब हम एक वेब ऐप्लिकेशन बना सकते हैं, जो वेक्टर सर्च को वेब फ़्रंट-एंड में इंटिग्रेट करेगा.

8. वेब ऐप्लिकेशन

Python Flask वेब ऐप्लिकेशन, app.js फ़ाइल में उपलब्ध है. साथ ही, फ़्रंट-एंड एचटीएमएल फ़ाइल, views/index.html. में मौजूद है

हमारा सुझाव है कि आप दोनों फ़ाइलों को देखें. सबसे पहले, app.js फ़ाइल से शुरू करें, जिसमें /search हैंडलर होता है. यह फ़ाइल, फ़्रंट-एंड एचटीएमएल index.html फ़ाइल से पास किए गए प्रॉम्प्ट को लेता है. इसके बाद, खोज का तरीका लागू होता है. यह वेक्टर मिलती-जुलती खोज करता है, जिसे हमने पिछले सेक्शन में देखा था.

इसके बाद, सुझावों की सूची के साथ रिस्पॉन्स को 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

9. (ज़रूरी नहीं) Google Cloud Run में डिप्लॉय करना

आखिरी चरण में, इस ऐप्लिकेशन को Google Cloud Run पर डिप्लॉय करना होगा. डिप्लॉयमेंट कमांड नीचे दिया गया है. इसे डिप्लॉय करने से पहले, पक्का करें कि आपने नीचे बोल्ड में दिखाई गई वैल्यू बदल दी हों. ये वैल्यू, .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 API चालू करने के लिए भी कहा जा सकता है. साथ ही, आपको अलग-अलग अनुमतियों के लिए सहमति भी देनी होगी. कृपया ऐसा करें.

डिप्लॉयमेंट की प्रोसेस पूरी होने में करीब पांच से सात मिनट लगेंगे. इसलिए, कृपया इंतज़ार करें.

3a6d86fd32e4a5e.png

डिप्लॉय हो जाने के बाद, डिप्लॉयमेंट आउटपुट में Cloud Run सेवा का यूआरएल दिखेगा. यह इस फ़ॉर्मैट में होगा:

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

उस सार्वजनिक यूआरएल पर जाएं. आपको वही वेब ऐप्लिकेशन दिखेगा जो डिप्लॉय किया गया है और सही तरीके से काम कर रहा है.

84e1cbf29cbaeedc.png

Google Cloud Console से Cloud Run पर भी जाया जा सकता है. यहां आपको Cloud Run में मौजूद सेवाओं की सूची दिखेगी. yogaposes सेवा, सूची में दी गई सेवाओं में से एक होनी चाहिए.

f2b34a8c9011be4c.png

सेवा के नाम (हमारे मामले में yogaposes) पर क्लिक करके, सेवा की जानकारी देखी जा सकती है. जैसे, यूआरएल, कॉन्फ़िगरेशन, लॉग वगैरह.

faaa5e0c02fe0423.png

इससे, Cloud Run पर योग आसनों के सुझाव देने वाले वेब ऐप्लिकेशन को डेवलप और डिप्लॉय करने की प्रोसेस पूरी हो जाती है.

10. बधाई हो

बधाई हो, आपने एक ऐसा ऐप्लिकेशन बना लिया है जो Firestore में डेटासेट अपलोड करता है, एम्बेड जनरेट करता है, और उपयोगकर्ता की क्वेरी के आधार पर वेक्टर मिलती-जुलती खोज करता है.

रेफ़रंस दस्तावेज़