CodeLab - ساخت یوگا متنی برنامه پیشنهادی را با Firestore ، Search Vector ، Langchain و Gemini (نسخه Node.js) ایجاد می کند

۱. مقدمه

در این آزمایشگاه کد، شما یک برنامه کاربردی خواهید ساخت که از جستجوی برداری برای پیشنهاد حرکات یوگا استفاده می‌کند.

از طریق codelab، شما یک رویکرد گام به گام به شرح زیر را به کار خواهید گرفت:

  1. از یک مجموعه داده موجود از حالت‌های یوگا با موضوع چهره در حال بغل کردن (با فرمت JSON) استفاده کنید.
  2. مجموعه داده‌ها را با یک توصیف فیلد اضافی که از Gemini برای تولید توصیفات برای هر یک از حالت‌ها استفاده می‌کند، بهبود بخشید.
  3. داده‌های یوگا پوزیشن‌ها را به عنوان مجموعه‌ای از اسناد در مجموعه فایراستور با جاسازی‌های تولید شده بارگذاری کنید.
  4. یک فهرست ترکیبی در Firestore ایجاد کنید تا امکان جستجوی برداری فراهم شود.
  5. از جستجوی برداری در یک برنامه Node.js استفاده کنید که همه چیز را مطابق شکل زیر گرد هم می‌آورد:

84e1cbf29cbaeedc.png

کاری که انجام خواهید داد

  • طراحی، ساخت و استقرار یک برنامه وب که از جستجوی برداری برای پیشنهاد حرکات یوگا استفاده می‌کند.

آنچه یاد خواهید گرفت

  • نحوه استفاده از Gemini برای تولید محتوای متنی و در چارچوب این codelab، تولید توضیحات برای حرکات یوگا
  • نحوه بارگذاری رکوردها از یک مجموعه داده پیشرفته از Hugging Face به Firestore به همراه Vector Embeddings
  • نحوه استفاده از Firestore Vector Search برای جستجوی داده‌ها بر اساس یک پرس‌وجوی زبان طبیعی
  • نحوه استفاده از API تبدیل متن به گفتار گوگل کلود برای تولید محتوای صوتی

آنچه نیاز دارید

  • مرورگر وب کروم
  • یک حساب جیمیل
  • یک پروژه ابری با قابلیت پرداخت صورتحساب

این آزمایشگاه کد که برای توسعه‌دهندگان در تمام سطوح (از جمله مبتدیان) طراحی شده است، در برنامه نمونه خود از جاوا اسکریپت و Node.js استفاده می‌کند. با این حال، برای درک مفاهیم ارائه شده، دانش جاوا اسکریپت و Node.js لازم نیست.

۲. قبل از شروع

ایجاد یک پروژه

  1. در کنسول گوگل کلود ، در صفحه انتخاب پروژه، یک پروژه گوگل کلود را انتخاب یا ایجاد کنید.
  2. مطمئن شوید که صورتحساب برای پروژه ابری شما فعال است. یاد بگیرید که چگونه بررسی کنید که آیا صورتحساب در یک پروژه فعال است یا خیر .
  3. شما از Cloud Shell ، یک محیط خط فرمان که در Google Cloud اجرا می‌شود و bq از قبل روی آن بارگذاری شده است، استفاده خواهید کرد. روی Activate Cloud Shell در بالای کنسول Google Cloud کلیک کنید.

تصویر دکمه فعال کردن 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. 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 به مستندات مراجعه کنید.

مخزن کلون و تنظیمات محیط راه‌اندازی

مرحله بعدی، کپی کردن مخزن نمونه‌ای است که در ادامه‌ی کد به آن ارجاع خواهیم داد. با فرض اینکه در Cloud Shell هستید، دستور زیر را از دایرکتوری خانگی خود اجرا کنید:

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

برای اجرای ویرایشگر، روی گزینه Open Editor در نوار ابزار پنجره Cloud Shell کلیک کنید. روی نوار منو در گوشه بالا سمت چپ کلیک کنید و مطابق شکل زیر، File → Open Folder را انتخاب کنید:

۶۶۲۲۱fd0d0e5202f.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 را مطابق با آنچه هنگام ایجاد منطقه Google Cloud Project و Firestore Database انتخاب کرده‌اید، به‌روزرسانی کنید. در حالت ایده‌آل، ما می‌خواهیم مقادیر LOCATION برای پروژه Google Cloud و پایگاه داده Firestore یکسان باشد، مثلاً 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 و در همان پوشه‌ای که فایل env-template قرار دارد، ذخیره کنید.

به منوی اصلی در سمت چپ بالای Cloud Shell IDE و سپس Terminal → New Terminal بروید.

با استفاده از دستور زیر به پوشه ریشه مخزنی که کلون کرده‌اید بروید:

cd yoga-poses-recommender-nodejs

وابستگی‌های Node.js را از طریق دستور زیر نصب کنید:

npm install

عالی! حالا همه چیز برای شروع راه‌اندازی پایگاه داده Firestore آماده است.

۳. نصب فایراستور

کلود فایراستور یک پایگاه داده سند بدون سرور و کاملاً مدیریت‌شده است که ما به عنوان بک‌اند برای داده‌های برنامه خود از آن استفاده خواهیم کرد. داده‌ها در کلود فایراستور در مجموعه‌ای از اسناد ساختار یافته‌اند.

مقداردهی اولیه پایگاه داده Firestore

از صفحه Firestore در کنسول Cloud دیدن کنید.

اگر قبلاً در پروژه، پایگاه داده Firestore را مقداردهی اولیه نکرده‌اید، با کلیک روی Create Database ، پایگاه داده default ایجاد کنید. در حین ایجاد پایگاه داده، مقادیر زیر را وارد کنید:

  • حالت فایراستور: Native.
  • نوع مکان را روی Region انتخاب کنید و مکان us-central1 را برای منطقه انتخاب کنید.
  • برای قوانین امنیتی، از Test rules استفاده کنید.
  • پایگاه داده را ایجاد کنید.

61d0277510803c8d.png

در بخش بعدی، مقدمات ایجاد مجموعه‌ای به نام poses در پایگاه داده پیش‌فرض Firestore خود فراهم خواهیم کرد. این مجموعه داده‌های نمونه (اسناد) یا اطلاعات پوزهای Yoga را در خود نگه می‌دارد که سپس در برنامه خود از آنها استفاده خواهیم کرد.

این بخش مربوط به راه‌اندازی پایگاه داده Firestore را تکمیل می‌کند.

۴. مجموعه داده‌های حرکات یوگا را آماده کنید

اولین وظیفه ما آماده‌سازی مجموعه داده‌های حالت‌های یوگا است که برای برنامه از آنها استفاده خواهیم کرد. ما با یک مجموعه داده موجود از چهره‌های در حال آغوش گرفتن شروع می‌کنیم و سپس آن را با اطلاعات اضافی بهبود می‌دهیم.

مجموعه داده‌های چهره در آغوش گرفته برای حالت‌های یوگا را بررسی کنید. توجه داشته باشید که اگرچه این آزمایشگاه کد از یکی از این مجموعه داده‌ها استفاده می‌کند، در واقع می‌توانید از هر مجموعه داده دیگری استفاده کنید و همان تکنیک‌های نشان داده شده را برای بهبود مجموعه داده دنبال کنید.

298cfae7f23e4bef.png

اگر به بخش Files and versions برویم، می‌توانیم فایل داده JSON را برای همه پوزها دریافت کنیم.

3fe6e55abdc032ec.png

ما فایل yoga_poses.json را دانلود کرده و آن را در اختیار شما قرار داده‌ایم. نام این فایل yoga_poses_alldata.json است و در پوشه /data قرار دارد.

به فایل data/yoga_poses.json در ویرایشگر Cloud Shell بروید و نگاهی به لیست اشیاء 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 برای آن است.

در ویرایشگر پوسته ابری، به فایل 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();

این برنامه یک فیلد description جدید به هر رکورد JSON مربوط به حرکات یوگا اضافه می‌کند. این توضیحات را از طریق فراخوانی مدل 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

اگر از شما هرگونه مجوزی خواسته شد، لطفاً آن را ارائه دهید.

متوجه خواهید شد که برنامه شروع به اجرا می‌کند. ما یک تأخیر ۳۰ ثانیه‌ای بین رکوردها اضافه کرده‌ایم تا از هرگونه سهمیه محدودیت نرخ که ممکن است در حساب‌های جدید Google Cloud وجود داشته باشد، جلوگیری شود، بنابراین لطفاً صبور باشید.

نمونه‌ای از اجرای در حال انجام در زیر نشان داده شده است:

469ede91ba007c1f.png

زمانی که هر سه رکورد با فراخوانی Gemini بهبود داده شدند، فایلی data/yoga_poses_with_description.json ایجاد خواهد شد. می‌توانید نگاهی به آن بیندازید.

اکنون فایل داده ما آماده است و گام بعدی این است که نحوه پر کردن یک پایگاه داده Firestore با آن، همراه با تولید جاسازی‌ها را درک کنیم.

۵. داده‌ها را به Firestore وارد کنید و Vector Embeddings ایجاد کنید

ما فایل data/yoga_poses_with_description.json را داریم و اکنون باید پایگاه داده Firestore را با آن پر کنیم و مهم‌تر از آن، Vector Embeddings را برای هر یک از رکوردها تولید کنیم. Vector Embeddings بعداً زمانی که باید جستجوی مشابهی روی آنها با پرس‌وجوی کاربر که به زبان طبیعی ارائه شده است، انجام دهیم، مفید خواهد بود.

مراحل انجام آن به شرح زیر خواهد بود:

  1. ما لیست اشیاء JSON را به لیستی از اشیاء تبدیل خواهیم کرد. هر سند دارای دو ویژگی خواهد بود: content و 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.

برای بررسی اینکه آیا رکوردها با موفقیت درج شده‌اند و جاسازی‌ها ایجاد شده‌اند، به صفحه Firestore در کنسول Cloud مراجعه کنید.

504cabdb99a222a5.png

روی پایگاه داده (پیش‌فرض) کلیک کنید، این باید مجموعه test-poses و چندین سند زیر آن مجموعه را نشان دهد. هر سند مربوط به یک حالت یوگا است.

9f37aa199c4b547a.png

برای بررسی فیلدها، روی هر یک از اسناد کلیک کنید. علاوه بر فیلدهایی که وارد کرده‌ایم، فیلد embedding را نیز خواهید یافت که یک فیلد برداری (Vector) است و مقدار آن را از طریق مدل جاسازی هوش مصنوعی Vertex text-embedding-004 تولید کرده‌ایم.

f0ed92124519beaf.png

اکنون که رکوردها را به همراه جاسازی‌ها در پایگاه داده Firestore بارگذاری کرده‌ایم، می‌توانیم به مرحله بعدی برویم و نحوه انجام جستجوی تشابه برداری در Firestore را بررسی کنیم.

۶. وارد کردن تمام حرکات یوگا به مجموعه پایگاه داده Firestore

اکنون مجموعه poses را ایجاد خواهیم کرد که فهرستی کامل از ۱۶۰ حرکت یوگا است و برای آن یک فایل ورودی پایگاه داده ایجاد کرده‌ایم که می‌توانید مستقیماً آن را وارد کنید. این کار برای صرفه‌جویی در زمان در آزمایشگاه انجام می‌شود. فرآیند تولید پایگاه داده‌ای که شامل توضیحات و جاسازی‌ها است، همان فرآیندی است که در بخش قبلی دیدیم.

با دنبال کردن مراحل زیر، پایگاه داده را وارد کنید:

  1. با استفاده از دستور gsutil که در زیر آمده است، یک باکت در پروژه خود ایجاد کنید. متغیر <PROJECT_ID> را در دستور زیر با شناسه پروژه گوگل کلود خود جایگزین کنید.
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

وارد کردن چند ثانیه طول می‌کشد و پس از آماده شدن، می‌توانید پایگاه داده Firestore و مجموعه آن را با مراجعه به https://console.cloud.google.com/firestore/databases اعتبارسنجی کنید، پایگاه داده default و مجموعه poses را مطابق شکل زیر انتخاب کنید:

561f3cb840de23d8.png

این کار ایجاد مجموعه Firestore را که در برنامه خود استفاده خواهیم کرد، تکمیل می‌کند.

۷. جستجوی تشابه برداری را در 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

تکمیل ایندکس چند دقیقه طول می‌کشد زیرا بیش از ۱۵۰ رکورد در پایگاه داده وجود دارد. پس از تکمیل، می‌توانید ایندکس را از طریق دستور زیر مشاهده کنید:

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 کار کنیم تا رکوردها را آپلود کنیم، جاسازی‌ها را ایجاد کنیم و جستجوی شباهت برداری را انجام دهیم. اکنون می‌توانیم یک برنامه وب ایجاد کنیم که جستجوی برداری را در یک رابط کاربری وب ادغام کند.

۸. برنامه وب

برنامه وب پایتون فلسک در فایل app.js و فایل HTML سمت کاربر در views/index.html.

توصیه می‌شود که هر دو فایل را بررسی کنید. ابتدا با فایل app.js که شامل هندلر /search است شروع کنید، که اعلانی را که از فایل 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

۹. (اختیاری) استقرار در 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>>

دستور بالا را از پوشه ریشه برنامه اجرا کنید. همچنین ممکن است از شما خواسته شود که API های Google Cloud را فعال کنید، برای مجوزهای مختلف تأیید خود را ارائه دهید، لطفاً این کار را انجام دهید.

مراحل نصب حدود ۵ تا ۷ دقیقه طول می‌کشد، پس صبور باشید.

3a6d86fd32e4a5e.png

پس از استقرار موفقیت‌آمیز، خروجی استقرار، URL سرویس Cloud Run را ارائه می‌دهد که به شکل زیر خواهد بود:

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

به آن URL عمومی مراجعه کنید و باید همان برنامه وب را که با موفقیت مستقر و اجرا شده است، ببینید.

84e1cbf29cbaeedc.png

همچنین می‌توانید از کنسول گوگل کلود به Cloud Run مراجعه کنید و لیست سرویس‌ها را در Cloud Run مشاهده خواهید کرد. سرویس yogaposes باید یکی از سرویس‌هایی باشد که در آنجا فهرست شده است (اگر نگوییم تنها سرویس).

f2b34a8c9011be4c.png

شما می‌توانید جزئیات سرویس مانند URL، پیکربندی‌ها، گزارش‌ها و موارد دیگر را با کلیک روی نام سرویس خاص (در مورد ما yogaposes ) مشاهده کنید.

faaa5e0c02fe0423.png

بدین ترتیب، توسعه و استقرار برنامه وب توصیه‌گر یوگا پوزها در Cloud Run تکمیل می‌شود.

۱۰. تبریک

تبریک می‌گویم، شما با موفقیت برنامه‌ای ساختید که یک مجموعه داده را در Firestore آپلود می‌کند، جاسازی‌ها را تولید می‌کند و بر اساس جستجوی کاربران، جستجوی تشابه برداری را انجام می‌دهد.

اسناد مرجع