1. مقدمه
در این نرم افزار کد، اپلیکیشنی خواهید ساخت که از جستجوی برداری برای توصیه ژست های یوگا استفاده می کند.
از طریق کد لبه، شما یک رویکرد گام به گام را به شرح زیر به کار خواهید گرفت:
- از مجموعه دادههای موجود در آغوش گرفتن صورت یوگا (فرمت JSON) استفاده کنید.
- مجموعه داده را با یک توضیح فیلد اضافی که از Gemini برای تولید توضیحات برای هر یک از حالت ها استفاده می کند، تقویت کنید.
- بارگذاری داده های یوگا به عنوان مجموعه ای از اسناد در مجموعه Firestore با جاسازی های ایجاد شده.
- یک نمایه ترکیبی در Firestore ایجاد کنید تا امکان جستجوی برداری فراهم شود.
- از جستجوی برداری در یک برنامه Node.js استفاده کنید که همه چیز را مطابق شکل زیر گرد هم می آورد:
کاری که خواهی کرد
- طراحی، ساخت و استقرار یک برنامه وب که از جستجوی برداری برای توصیه ژست های یوگا استفاده می کند.
چیزی که یاد خواهید گرفت
- نحوه استفاده از Gemini برای تولید محتوای متنی و در چارچوب این کد لبه، توضیحاتی برای ژستهای یوگا ایجاد کنید
- نحوه بارگیری رکوردها از مجموعه داده های پیشرفته از Hugging Face در Firestore همراه با جاسازی های برداری
- نحوه استفاده از Firestore Vector Search برای جستجوی داده ها بر اساس یک جستار زبان طبیعی
- نحوه استفاده از Google Cloud Text to Speech API برای تولید محتوای صوتی
آنچه شما نیاز دارید
- مرورگر وب کروم
- یک اکانت جیمیل
- یک پروژه Cloud با فعال کردن صورتحساب
این Codelab که برای توسعه دهندگان در تمام سطوح (از جمله مبتدیان) طراحی شده است، از JavaScript و Node.js در برنامه نمونه خود استفاده می کند. با این حال، دانش JavaScript و Node.js برای درک مفاهیم ارائه شده مورد نیاز نیست.
2. قبل از شروع
یک پروژه ایجاد کنید
- در Google Cloud Console ، در صفحه انتخاب پروژه، یک پروژه Google Cloud را انتخاب یا ایجاد کنید.
- مطمئن شوید که صورتحساب برای پروژه Cloud شما فعال است. با نحوه بررسی فعال بودن صورتحساب در پروژه آشنا شوید.
- شما از Cloud Shell استفاده خواهید کرد، یک محیط خط فرمان در حال اجرا در Google Cloud که با bq از قبل بارگذاری شده است. روی Activate Cloud Shell در بالای کنسول Google Cloud کلیک کنید.
- پس از اتصال به Cloud Shell، با استفاده از دستور زیر بررسی میکنید که قبلاً احراز هویت شدهاید و پروژه به ID پروژه شما تنظیم شده است:
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 به اسناد مراجعه کنید.
کلون کردن مخزن و تنظیمات محیط راه اندازی
گام بعدی کلون کردن مخزن نمونه است که در بقیه قسمت های Codelab به آن ارجاع خواهیم داد. با فرض اینکه در 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 انتخاب کردهاید، بهروزرسانی کنید. در حالت ایدهآل، ما میخواهیم مقادیر 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 برویم.
3. Firestore را راه اندازی کنید
Cloud Firestore یک پایگاه داده اسناد بدون سرور کاملاً مدیریت شده است که ما از آن به عنوان پشتیبان برای داده های برنامه خود استفاده خواهیم کرد. داده ها در Cloud Firestore در مجموعه ای از اسناد ساختار یافته اند.
راه اندازی پایگاه داده Firestore
از صفحه Firestore در کنسول Cloud دیدن کنید.
اگر قبلاً در پروژه پایگاه داده Firestore را مقداردهی اولیه نکرده اید، با کلیک بر روی Create Database
پایگاه داده default
را ایجاد کنید. در هنگام ایجاد پایگاه داده، مقادیر زیر را دنبال کنید:
- حالت Firestore:
Native.
- مکان: با تنظیمات موقعیت مکانی پیش فرض بروید.
- برای قوانین امنیتی، با
Test rules
بروید. - پایگاه داده را ایجاد کنید.
در بخش بعدی، زمینه ایجاد مجموعه ای به نام poses
در پایگاه داده Firestore پیش فرض خود ایجاد می کنیم. این مجموعه داده های نمونه (اسناد) یا اطلاعات پوزهای یوگا را در خود نگه می دارد که سپس در برنامه خود از آنها استفاده خواهیم کرد.
این بخش راه اندازی پایگاه داده Firestore را تکمیل می کند.
4. مجموعه داده پوزهای یوگا را آماده کنید
اولین وظیفه ما این است که مجموعه داده Yoga Poses را آماده کنیم که از آن برای برنامه استفاده خواهیم کرد. ما با مجموعه داده Hagging Face موجود شروع می کنیم و سپس آن را با اطلاعات اضافی تقویت می کنیم.
مجموعه داده های صورت در آغوش گرفته برای ژست های یوگا را بررسی کنید. توجه داشته باشید که در حالی که این نرمافزار از یکی از مجموعههای داده استفاده میکند، در واقع میتوانید از هر مجموعه داده دیگری استفاده کنید و از همان تکنیکهای نشاندادهشده برای بهبود مجموعه دادهها پیروی کنید.
اگر به قسمت Files and versions
برویم، می توانیم فایل داده JSON را برای همه پوزها دریافت کنیم.
ما yoga_poses.json
را دانلود کرده ایم و آن فایل را در اختیار شما قرار داده ایم. نام این فایل yoga_poses_alldata.json
است و در پوشه /data
وجود دارد.
به فایل data/yoga_poses.json
در ویرایشگر پوسته ابری بروید و نگاهی به لیست اشیاء JSON بیندازید، جایی که هر شی JSON نشان دهنده یک حالت یوگا است. ما در مجموع 3 رکورد داریم و یک نمونه رکورد در زیر نشان داده شده است:
{
"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 وجود داشته باشد، 30 ثانیه تاخیر بین رکوردها اضافه کردهایم، بنابراین لطفاً صبور باشید.
یک نمونه اجرا در حال انجام در زیر نشان داده شده است:
هنگامی که هر 3 رکورد با تماس جمینی بهبود یافتند، یک فایل data/yoga_poses_with_description.json
ایجاد خواهد شد. می توانید نگاهی به آن بیندازید.
ما اکنون با فایل داده خود آماده هستیم و گام بعدی این است که بدانیم چگونه یک پایگاه داده Firestore را با آن پر کنیم، همراه با تولید embeddings.
5. وارد کردن داده ها به Firestore و تولید Vector Embeddings
ما فایل data/yoga_poses_with_description.json
را داریم و اکنون باید پایگاه داده Firestore را با آن پر کنیم و مهمتر از همه، جاسازی های برداری را برای هر یک از رکوردها ایجاد کنیم. جاسازیهای برداری بعداً زمانی مفید خواهند بود که باید جستجوی مشابهی را روی آنها با درخواست کاربر که به زبان طبیعی ارائه شده است انجام دهیم.
مراحل انجام آن به شرح زیر خواهد بود:
- ما لیست اشیاء 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 است که مقدار آن را از طریق مدل text-embedding-004
Vertex AI Embedding ایجاد کردیم.
اکنون که رکوردهای آپلود شده در پایگاه داده Firestore را با جاسازیهای موجود در اختیار داریم، میتوانیم به مرحله بعدی برویم و ببینیم که چگونه جستجوی Vector Similarity را در Firestore انجام دهیم.
6. ژست های کامل یوگا را به مجموعه پایگاه داده Firestore وارد کنید
ما اکنون مجموعه poses
را ایجاد می کنیم که یک لیست کامل از 160 حالت یوگا است که برای آن یک فایل واردات پایگاه داده ایجاد کرده ایم که می توانید مستقیماً آن را وارد کنید. این کار برای صرفه جویی در زمان در آزمایشگاه انجام می شود. فرآیند تولید پایگاه داده که حاوی توضیحات و جاسازی است، همان است که در بخش قبل دیدیم.
با دنبال کردن مراحل زیر، پایگاه داده را وارد کنید:
- با دستور
gsutil
که در زیر داده شده است یک سطل در پروژه خود ایجاد کنید. متغیر<PROJECT_ID>
در دستور زیر را با شناسه پروژه Google Cloud خود جایگزین کنید.
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
وارد کردن چند ثانیه طول میکشد و پس از آماده شدن، میتوانید با مراجعه به https://console.cloud.google.com/firestore/databases پایگاه داده Firestore و مجموعه را تأیید کنید، پایگاه داده default
و مجموعه poses
را مطابق شکل زیر انتخاب کنید:
این کار ایجاد مجموعه Firestore را که در برنامه خود استفاده خواهیم کرد، تکمیل می کند.
7. جستجوی شباهت برداری را در Firestore انجام دهید
برای انجام جستجوی Vector Similarity ، پرس و جو را از کاربر دریافت می کنیم. نمونه ای از این پرس و جو می تواند این باشد که "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
و فایل HTML جلویی در views/index.html.
توصیه می شود هر دو فایل را مشاهده کنید. ابتدا با فایل app.js
شروع کنید که حاوی /search
handler است، که دستوری را میگیرد که از فایل HTML index.html
جلویی ارسال شده است. سپس متد جستجو را فراخوانی می کند، که جستجوی Vector Similarity را که در بخش قبل به آن نگاه کردیم، انجام می دهد.
سپس پاسخ با لیستی از توصیه ها به index.html
ارسال می شود. سپس index.html
توصیه ها را به صورت کارت های مختلف نمایش می دهد.
برنامه را به صورت محلی اجرا کنید
یک پنجره ترمینال جدید (Ctrl+Shift+C) یا هر پنجره ترمینال موجود را اجرا کنید و دستور زیر را بدهید:
npm run start
یک نمونه اجرای در زیر نشان داده شده است:
...
Server listening on port 8080
پس از راهاندازی و اجرا، با کلیک بر روی دکمه پیشنمایش وب که در زیر نشان داده شده است، از URL اصلی برنامه دیدن کنید:
باید فایل index.html
ارائه شده را مطابق شکل زیر به شما نشان دهد:
یک نمونه پرس و جو ارائه کنید (مثال: Provide me some exercises for back pain relief
) و روی دکمه Search
کلیک کنید. این باید برخی از توصیه ها را از پایگاه داده بازیابی کند. همچنین دکمه Play Audio
را مشاهده خواهید کرد که بر اساس توضیحات یک جریان صوتی ایجاد می کند که می توانید مستقیماً آن را بشنوید.
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>>
دستور بالا را از پوشه ریشه برنامه اجرا کنید. همچنین ممکن است از شما خواسته شود APIهای Google Cloud را فعال کنید، مجوزهای مختلف را تأیید کنید، لطفاً این کار را انجام دهید.
مراحل استقرار حدود 5 تا 7 دقیقه طول می کشد، بنابراین لطفا صبور باشید.
پس از استقرار موفقیت آمیز، خروجی استقرار URL سرویس Cloud Run را ارائه می دهد. به این شکل خواهد بود:
Service URL: https://yogaposes-<UNIQUEID>.us-central1.run.app
از آن URL عمومی بازدید کنید و خواهید دید که همان برنامه وب مستقر شده و با موفقیت اجرا می شود.
همچنین می توانید از طریق کنسول Google Cloud به Cloud Run مراجعه کنید و لیست خدمات را در Cloud Run مشاهده خواهید کرد. سرویس yogaposes
باید یکی از خدمات (اگر نه تنها) باشد که در آنجا ذکر شده است.
با کلیک کردن روی نام سرویس خاص (در مورد ما yogaposes
) میتوانید جزئیات سرویس مانند URL، پیکربندیها، گزارشها و موارد دیگر را مشاهده کنید.
این توسعه و استقرار برنامه وب توصیهکننده پوزهای یوگا ما را در Cloud Run کامل میکند.
10. تبریک می گویم
تبریک میگوییم، شما با موفقیت برنامهای ساختهاید که مجموعه دادهای را در Firestore آپلود میکند، جاسازیها را ایجاد میکند و یک جستجوی شباهت برداری بر اساس درخواست کاربران انجام میدهد.
اسناد مرجع
1. مقدمه
در این نرم افزار کد، اپلیکیشنی خواهید ساخت که از جستجوی برداری برای توصیه ژست های یوگا استفاده می کند.
از طریق کد لبه، شما یک رویکرد گام به گام را به شرح زیر به کار خواهید گرفت:
- از مجموعه دادههای موجود در آغوش گرفتن صورت یوگا (فرمت JSON) استفاده کنید.
- مجموعه داده را با یک توضیح فیلد اضافی که از Gemini برای تولید توضیحات برای هر یک از حالت ها استفاده می کند، تقویت کنید.
- بارگذاری داده های یوگا به عنوان مجموعه ای از اسناد در مجموعه Firestore با جاسازی های ایجاد شده.
- یک نمایه ترکیبی در Firestore ایجاد کنید تا امکان جستجوی برداری فراهم شود.
- از جستجوی برداری در یک برنامه Node.js استفاده کنید که همه چیز را مطابق شکل زیر گرد هم می آورد:
کاری که خواهی کرد
- طراحی، ساخت و استقرار یک برنامه وب که از جستجوی برداری برای توصیه ژست های یوگا استفاده می کند.
چیزی که یاد خواهید گرفت
- نحوه استفاده از Gemini برای تولید محتوای متنی و در چارچوب این کد لبه، توضیحاتی برای ژستهای یوگا ایجاد کنید
- نحوه بارگیری رکوردها از مجموعه داده های پیشرفته از Hugging Face در Firestore همراه با جاسازی های برداری
- نحوه استفاده از Firestore Vector Search برای جستجوی داده ها بر اساس یک جستار زبان طبیعی
- نحوه استفاده از Google Cloud Text to Speech API برای تولید محتوای صوتی
آنچه شما نیاز دارید
- مرورگر وب کروم
- یک اکانت جیمیل
- یک پروژه Cloud با فعال کردن صورتحساب
این Codelab که برای توسعه دهندگان در تمام سطوح (از جمله مبتدیان) طراحی شده است، از JavaScript و Node.js در برنامه نمونه خود استفاده می کند. با این حال، دانش JavaScript و Node.js برای درک مفاهیم ارائه شده مورد نیاز نیست.
2. قبل از شروع
یک پروژه ایجاد کنید
- در Google Cloud Console ، در صفحه انتخاب پروژه، یک پروژه Google Cloud را انتخاب یا ایجاد کنید.
- مطمئن شوید که صورتحساب برای پروژه Cloud شما فعال است. با نحوه بررسی فعال بودن صورتحساب در پروژه آشنا شوید.
- شما از Cloud Shell استفاده خواهید کرد، یک محیط خط فرمان در حال اجرا در Google Cloud که با bq از قبل بارگذاری شده است. روی Activate Cloud Shell در بالای کنسول Google Cloud کلیک کنید.
- پس از اتصال به Cloud Shell، با استفاده از دستور زیر بررسی میکنید که قبلاً احراز هویت شدهاید و پروژه به ID پروژه شما تنظیم شده است:
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 به اسناد مراجعه کنید.
کلون کردن مخزن و تنظیمات محیط راه اندازی
گام بعدی کلون کردن مخزن نمونه است که در بقیه قسمت های Codelab به آن ارجاع خواهیم داد. با فرض اینکه در 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 انتخاب کردهاید، بهروزرسانی کنید. در حالت ایدهآل، ما میخواهیم مقادیر 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 برویم.
3. Firestore را راه اندازی کنید
Cloud Firestore یک پایگاه داده اسناد بدون سرور کاملاً مدیریت شده است که ما از آن به عنوان پشتیبان برای داده های برنامه خود استفاده خواهیم کرد. داده ها در Cloud Firestore در مجموعه ای از اسناد ساختار یافته اند.
راه اندازی پایگاه داده Firestore
از صفحه Firestore در کنسول Cloud دیدن کنید.
اگر قبلاً در پروژه پایگاه داده Firestore را مقداردهی اولیه نکرده اید، با کلیک بر روی Create Database
پایگاه داده default
را ایجاد کنید. در هنگام ایجاد پایگاه داده، مقادیر زیر را دنبال کنید:
- حالت Firestore:
Native.
- مکان: با تنظیمات موقعیت مکانی پیش فرض بروید.
- برای قوانین امنیتی، با
Test rules
بروید. - پایگاه داده را ایجاد کنید.
در بخش بعدی، زمینه ایجاد مجموعه ای به نام poses
در پایگاه داده Firestore پیش فرض خود ایجاد می کنیم. این مجموعه داده های نمونه (اسناد) یا اطلاعات پوزهای یوگا را در خود نگه می دارد که سپس در برنامه خود از آنها استفاده خواهیم کرد.
این بخش راه اندازی پایگاه داده Firestore را تکمیل می کند.
4. مجموعه داده پوزهای یوگا را آماده کنید
اولین وظیفه ما این است که مجموعه داده Yoga Poses را آماده کنیم که از آن برای برنامه استفاده خواهیم کرد. ما با مجموعه داده Hagging Face موجود شروع می کنیم و سپس آن را با اطلاعات اضافی تقویت می کنیم.
مجموعه داده های صورت در آغوش گرفته برای ژست های یوگا را بررسی کنید. توجه داشته باشید که در حالی که این نرمافزار از یکی از مجموعههای داده استفاده میکند، در واقع میتوانید از هر مجموعه داده دیگری استفاده کنید و از همان تکنیکهای نشاندادهشده برای بهبود مجموعه دادهها پیروی کنید.
اگر به قسمت Files and versions
برویم، می توانیم فایل داده JSON را برای همه پوزها دریافت کنیم.
ما yoga_poses.json
را دانلود کرده ایم و آن فایل را در اختیار شما قرار داده ایم. نام این فایل yoga_poses_alldata.json
است و در پوشه /data
وجود دارد.
به فایل data/yoga_poses.json
در ویرایشگر پوسته ابری بروید و نگاهی به لیست اشیاء JSON بیندازید، جایی که هر شی JSON نشان دهنده یک حالت یوگا است. ما در مجموع 3 رکورد داریم و یک نمونه رکورد در زیر نشان داده شده است:
{
"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 وجود داشته باشد، 30 ثانیه تاخیر بین رکوردها اضافه کردهایم، بنابراین لطفاً صبور باشید.
یک نمونه اجرا در حال انجام در زیر نشان داده شده است:
هنگامی که هر 3 رکورد با تماس جمینی بهبود یافتند، یک فایل data/yoga_poses_with_description.json
ایجاد خواهد شد. می توانید نگاهی به آن بیندازید.
ما اکنون با فایل داده خود آماده هستیم و گام بعدی این است که بدانیم چگونه یک پایگاه داده Firestore را با آن پر کنیم، همراه با تولید embeddings.
5. وارد کردن داده ها به Firestore و تولید Vector Embeddings
ما فایل data/yoga_poses_with_description.json
را داریم و اکنون باید پایگاه داده Firestore را با آن پر کنیم و مهمتر از همه، جاسازی های برداری را برای هر یک از رکوردها ایجاد کنیم. جاسازیهای برداری بعداً زمانی مفید خواهند بود که باید جستجوی مشابهی را روی آنها با درخواست کاربر که به زبان طبیعی ارائه شده است انجام دهیم.
مراحل انجام آن به شرح زیر خواهد بود:
- ما لیست اشیاء 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 است که مقدار آن را از طریق مدل text-embedding-004
Vertex AI Embedding ایجاد کردیم.
اکنون که رکوردهای آپلود شده در پایگاه داده Firestore را با جاسازیهای موجود در اختیار داریم، میتوانیم به مرحله بعدی برویم و ببینیم که چگونه جستجوی Vector Similarity را در Firestore انجام دهیم.
6. ژست های کامل یوگا را به مجموعه پایگاه داده Firestore وارد کنید
ما اکنون مجموعه poses
را ایجاد می کنیم که یک لیست کامل از 160 حالت یوگا است که برای آن یک فایل واردات پایگاه داده ایجاد کرده ایم که می توانید مستقیماً آن را وارد کنید. این کار برای صرفه جویی در زمان در آزمایشگاه انجام می شود. فرآیند تولید پایگاه داده که حاوی توضیحات و جاسازی است، همان است که در بخش قبل دیدیم.
با دنبال کردن مراحل زیر، پایگاه داده را وارد کنید:
- با دستور
gsutil
که در زیر داده شده است یک سطل در پروژه خود ایجاد کنید. متغیر<PROJECT_ID>
در دستور زیر را با شناسه پروژه Google Cloud خود جایگزین کنید.
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
وارد کردن چند ثانیه طول میکشد و پس از آماده شدن، میتوانید با مراجعه به https://console.cloud.google.com/firestore/databases پایگاه داده Firestore و مجموعه را تأیید کنید، پایگاه داده default
و مجموعه poses
را مطابق شکل زیر انتخاب کنید:
این کار ایجاد مجموعه Firestore را که در برنامه خود استفاده خواهیم کرد، تکمیل می کند.
7. جستجوی شباهت برداری را در Firestore انجام دهید
برای انجام جستجوی Vector Similarity ، پرس و جو را از کاربر دریافت می کنیم. نمونه ای از این پرس و جو می تواند این باشد که "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
موجود است و پرونده HTML جلوی آن در views/index.html.
توصیه می شود که به هر دو پرونده نگاهی بیندازید. ابتدا با فایل app.js
که شامل کنترل کننده /search
است ، شروع کنید ، که سریعاً از پرونده HTML index.html
منتقل شده است. این سپس روش جستجو را فراخوانی می کند ، که جستجوی شباهت بردار را که در بخش قبلی به آن نگاه کردیم ، انجام می دهد.
پاسخ سپس با لیست توصیه ها به 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
را مشاهده خواهید کرد که بر اساس توضیحات ، یک جریان صوتی ایجاد می کند ، که می توانید مستقیماً آن را بشنوید.
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>>
دستور فوق را از پوشه ریشه برنامه اجرا کنید. همچنین ممکن است از شما خواسته شود که API های Google Cloud را فعال کنید ، تأیید خود را برای مجوزهای مختلف ارائه دهید ، لطفاً این کار را انجام دهید.
روند استقرار حدود 5-7 دقیقه طول خواهد کشید ، بنابراین لطفاً صبور باشید.
پس از استقرار موفقیت آمیز ، خروجی استقرار URL سرویس Cloud Run را ارائه می دهد. این شکل خواهد بود:
Service URL: https://yogaposes-<UNIQUEID>.us-central1.run.app
از آن URL عمومی بازدید کنید و باید همان برنامه وب را با موفقیت مستقر و اجرا کنید.
همچنین می توانید از Cloud Run از کنسول Google Cloud بازدید کنید و لیست خدمات را در Cloud Run مشاهده خواهید کرد. سرویس yogaposes
باید یکی از خدمات (اگر نه تنها) ذکر شده در آنجا باشد.
با کلیک بر روی نام سرویس خاص ( yogaposes
در مورد ما) می توانید جزئیات سرویس مانند URL ، تنظیمات ، سیاههها و موارد دیگر را مشاهده کنید.
این کار توسعه و استقرار یوگا ما برنامه وب توصیه کننده را در Cloud Run ارائه می دهد.
10. تبریک می گویم
تبریک می گویم ، شما با موفقیت برنامه ای را ایجاد کرده اید که مجموعه ای از مجموعه داده ها را برای Firestore بارگذاری می کند ، تعبیه ها را تولید می کند و یک جستجوی شباهت بردار را بر اساس پرس و جو کاربران انجام می دهد.