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

کاری که انجام خواهید داد
- طراحی، ساخت و استقرار یک برنامه وب که از جستجوی برداری برای پیشنهاد حرکات یوگا استفاده میکند.
آنچه یاد خواهید گرفت
- نحوه استفاده از Gemini برای تولید محتوای متنی و در چارچوب این codelab، تولید توضیحات برای حرکات یوگا
- نحوه بارگذاری رکوردها از یک مجموعه داده پیشرفته از Hugging Face به Firestore به همراه Vector Embeddings
- نحوه استفاده از Firestore Vector Search برای جستجوی دادهها بر اساس یک پرسوجوی زبان طبیعی
- نحوه استفاده از API تبدیل متن به گفتار گوگل کلود برای تولید محتوای صوتی
آنچه نیاز دارید
- مرورگر وب کروم
- یک حساب جیمیل
- یک پروژه ابری با قابلیت پرداخت صورتحساب
این آزمایشگاه کد که برای توسعهدهندگان در تمام سطوح (از جمله مبتدیان) طراحی شده است، در برنامه نمونه خود از جاوا اسکریپت و Node.js استفاده میکند. با این حال، برای درک مفاهیم ارائه شده، دانش جاوا اسکریپت و Node.js لازم نیست.
۲. قبل از شروع
ایجاد یک پروژه
- در کنسول گوگل کلود ، در صفحه انتخاب پروژه، یک پروژه گوگل کلود را انتخاب یا ایجاد کنید.
- مطمئن شوید که صورتحساب برای پروژه ابری شما فعال است. یاد بگیرید که چگونه بررسی کنید که آیا صورتحساب در یک پروژه فعال است یا خیر .
- شما از Cloud Shell ، یک محیط خط فرمان که در Google Cloud اجرا میشود و bq از قبل روی آن بارگذاری شده است، استفاده خواهید کرد. روی Activate Cloud Shell در بالای کنسول Google Cloud کلیک کنید.

- پس از اتصال به Cloud Shell، با استفاده از دستور زیر بررسی میکنید که آیا از قبل احراز هویت شدهاید و پروژه روی شناسه پروژه شما تنظیم شده است یا خیر:
gcloud auth list
- دستور زیر را در Cloud Shell اجرا کنید تا تأیید شود که دستور gcloud از پروژه شما اطلاع دارد.
gcloud config list project
- اگر پروژه شما تنظیم نشده است، از دستور زیر برای تنظیم آن استفاده کنید:
gcloud config set project <YOUR_PROJECT_ID>
- 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 را انتخاب کنید:

پوشه yoga-poses-recommender-nodejs را انتخاب کنید. باید پوشهای را مشاهده کنید که حاوی فایلهای زیر است، همانطور که در زیر نشان داده شده است:

اکنون باید متغیرهای محیطی مورد استفاده خود را تنظیم کنیم. روی فایل 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استفاده کنید. - پایگاه داده را ایجاد کنید.

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

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

ما فایل 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 نوشته میشود.
بیایید مراحل اصلی را طی کنیم:
- در تابع
main()، متوجه خواهید شد که تابعadd_descriptions_to_jsonرا فراخوانی میکند و فایل ورودی و فایل خروجی مورد انتظار را ارائه میدهد. - تابع
add_descriptions_to_jsonبرای هر رکورد JSON، یعنی اطلاعات پست یوگا، کارهای زیر را انجام میدهد: - این تابع،
pose_name،sanskrit_name،expertise_levelوpose_typesرا استخراج میکند. - این تابع
callGeminiرا فراخوانی میکند که یک اعلان ایجاد میکند و سپس کلاس مدل LangchainVertexAI را برای دریافت متن پاسخ فراخوانی میکند. - این متن پاسخ سپس به شیء JSON اضافه میشود.
- سپس لیست بهروز شدهی JSON از اشیاء در فایل مقصد نوشته میشود.
بیایید این برنامه را اجرا کنیم. یک پنجره ترمینال جدید (Ctrl+Shift+C) باز کنید و دستور زیر را وارد کنید:
npm run generate-descriptions
اگر از شما هرگونه مجوزی خواسته شد، لطفاً آن را ارائه دهید.
متوجه خواهید شد که برنامه شروع به اجرا میکند. ما یک تأخیر ۳۰ ثانیهای بین رکوردها اضافه کردهایم تا از هرگونه سهمیه محدودیت نرخ که ممکن است در حسابهای جدید Google Cloud وجود داشته باشد، جلوگیری شود، بنابراین لطفاً صبور باشید.
نمونهای از اجرای در حال انجام در زیر نشان داده شده است:

زمانی که هر سه رکورد با فراخوانی Gemini بهبود داده شدند، فایلی data/yoga_poses_with_description.json ایجاد خواهد شد. میتوانید نگاهی به آن بیندازید.
اکنون فایل داده ما آماده است و گام بعدی این است که نحوه پر کردن یک پایگاه داده Firestore با آن، همراه با تولید جاسازیها را درک کنیم.
۵. دادهها را به Firestore وارد کنید و Vector Embeddings ایجاد کنید
ما فایل data/yoga_poses_with_description.json را داریم و اکنون باید پایگاه داده Firestore را با آن پر کنیم و مهمتر از آن، Vector Embeddings را برای هر یک از رکوردها تولید کنیم. Vector Embeddings بعداً زمانی که باید جستجوی مشابهی روی آنها با پرسوجوی کاربر که به زبان طبیعی ارائه شده است، انجام دهیم، مفید خواهد بود.
مراحل انجام آن به شرح زیر خواهد بود:
- ما لیست اشیاء JSON را به لیستی از اشیاء تبدیل خواهیم کرد. هر سند دارای دو ویژگی خواهد بود:
contentوmetadata. شیء فراداده شامل کل شیء JSON خواهد بود که دارای ویژگیهایی مانندname،description،sanskrit_nameو غیره است.contentیک متن رشتهای خواهد بود که از الحاق چند فیلد تشکیل شده است. - زمانی که فهرستی از اسناد را داشته باشیم، از کلاس 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 مراجعه کنید.

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

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

اکنون که رکوردها را به همراه جاسازیها در پایگاه داده Firestore بارگذاری کردهایم، میتوانیم به مرحله بعدی برویم و نحوه انجام جستجوی تشابه برداری در Firestore را بررسی کنیم.
۶. وارد کردن تمام حرکات یوگا به مجموعه پایگاه داده Firestore
اکنون مجموعه poses را ایجاد خواهیم کرد که فهرستی کامل از ۱۶۰ حرکت یوگا است و برای آن یک فایل ورودی پایگاه داده ایجاد کردهایم که میتوانید مستقیماً آن را وارد کنید. این کار برای صرفهجویی در زمان در آزمایشگاه انجام میشود. فرآیند تولید پایگاه دادهای که شامل توضیحات و جاسازیها است، همان فرآیندی است که در بخش قبلی دیدیم.
با دنبال کردن مراحل زیر، پایگاه داده را وارد کنید:
- با استفاده از دستور
gsutilکه در زیر آمده است، یک باکت در پروژه خود ایجاد کنید. متغیر<PROJECT_ID>را در دستور زیر با شناسه پروژه گوگل کلود خود جایگزین کنید.
gsutil mb -l us-central1 gs://<PROJECT_ID>-my-bucket
- حالا که باکت ایجاد شده است، قبل از اینکه بتوانیم خروجی پایگاه داده را به پایگاه داده Firebase وارد کنیم، باید آن را در این باکت کپی کنیم. از دستور زیر استفاده کنید:
gsutil cp -r gs://yoga-database-firestore-export-bucket/2025-01-27T05:11:02_62615 gs://<PROJECT_ID>-my-bucket
حالا که دادهها را برای وارد کردن داریم، میتوانیم به مرحله نهایی وارد کردن دادهها به پایگاه داده Firebase ( default ) که ایجاد کردهایم، برویم.
- از دستور 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 را مطابق شکل زیر انتخاب کنید:

این کار ایجاد مجموعه 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
پس از راهاندازی و اجرا، با کلیک بر روی دکمه پیشنمایش وب که در زیر نشان داده شده است، به آدرس اینترنتی اصلی برنامه مراجعه کنید:

باید فایل index.html را به صورت زیر نمایش دهد:

یک نمونه سوال (مثال: Provide me some exercises for back pain relief ) ارائه دهید و روی دکمه Search کلیک کنید. این باید برخی از توصیهها را از پایگاه داده بازیابی کند. همچنین دکمه Play Audio را مشاهده خواهید کرد که بر اساس توضیحات، یک جریان صوتی ایجاد میکند که میتوانید مستقیماً آن را بشنوید.

۹. (اختیاری) استقرار در 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 را فعال کنید، برای مجوزهای مختلف تأیید خود را ارائه دهید، لطفاً این کار را انجام دهید.
مراحل نصب حدود ۵ تا ۷ دقیقه طول میکشد، پس صبور باشید.

پس از استقرار موفقیتآمیز، خروجی استقرار، URL سرویس Cloud Run را ارائه میدهد که به شکل زیر خواهد بود:
Service URL: https://yogaposes-<UNIQUEID>.us-central1.run.app
به آن URL عمومی مراجعه کنید و باید همان برنامه وب را که با موفقیت مستقر و اجرا شده است، ببینید.

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

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

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