1. مقدمة
في هذا الدرس التطبيقي حول الترميز، ستُنشئ تطبيقًا يستخدم البحث بالاستناد إلى المتجهات لاقتراح أوضاع اليوغا.
من خلال ورشة رموز البرامج، ستطبّق نهجًا خطوة بخطوة على النحو التالي:
- استخدِم مجموعة بيانات حالية من Hugging Face تتضمّن أوضاع اليوغا (بتنسيق JSON).
- يمكنك تحسين مجموعة البيانات من خلال إضافة وصف حقل إضافي يستخدم Gemini لإنشاء أوصاف لكل وضع من الأوضاع.
- حمِّل بيانات وضعيات اليوغا كمجموعة من المستندات في مجموعة Firestore مع تضمينات تم إنشاؤها.
- أنشئ فهرسًا مركبًا في Firestore للسماح بميزة "البحث باستخدام المتجهات".
- استخدِم ميزة "البحث بالصور" في تطبيق Node.js يجمع كل العناصر كما هو موضّح أدناه:
المهام التي ستنفّذها
- تصميم وإنشاء ونشر تطبيق ويب يستخدم ميزة "البحث بالاستناد إلى المتجهات" لاقتراح أوضاع اليوغا
ما ستتعرّف عليه
- كيفية استخدام Gemini لإنشاء محتوى نصي، وضمن سياق هذا الدليل التعليمي حول رموز البرامج، إنشاء أوصاف لحركات اليوغا
- كيفية تحميل السجلّات من مجموعة بيانات محسّنة من Hugging Face إلى Firestore مع إدراجات المتجهات
- كيفية استخدام ميزة "البحث المتجه" في Firestore للبحث عن البيانات استنادًا إلى طلب بحث باللغة الطبيعية
- كيفية استخدام واجهة برمجة التطبيقات Google Cloud Text to Speech API لإنشاء محتوى صوتي
المتطلبات
- متصفّح الويب Chrome
- حساب Gmail
- مشروع على Cloud تم تفعيل الفوترة فيه
تم تصميم هذا الدليل التعليمي للرمز البرمجي للمطوّرين من جميع المستويات (بما في ذلك المبتدئين)، ويستخدم JavaScript وNode.js في نموذج تطبيقه. ومع ذلك، لا يُشترط معرفة JavaScript وNode.js لفهم المفاهيم المعروضة.
2. قبل البدء
إنشاء مشروع
- في Google Cloud Console، في صفحة أداة اختيار المشاريع، اختَر مشروعًا على Google Cloud أو أنشِئه.
- تأكَّد من تفعيل الفوترة لمشروعك على Cloud. تعرَّف على كيفية التحقّق مما إذا كانت الفوترة مفعَّلة في مشروع .
- ستستخدم Cloud Shell، وهي بيئة سطر أوامر تعمل في Google Cloud ومزوّدة مسبقًا بـ bq. انقر على "تفعيل Cloud Shell" في أعلى "وحدة تحكّم Google Cloud".
- بعد الاتصال بخدمة Cloud Shell، تأكَّد من أنّك سبق أن تم مصادقة حسابك وأنّه تم ضبط المشروع على معرّف مشروعك باستخدام الأمر التالي:
gcloud auth list
- شغِّل الأمر التالي في Cloud Shell للتأكّد من أنّ الأمر gcloud يعرف مشروعك.
gcloud config list project
- إذا لم يتم ضبط مشروعك، استخدِم الأمر التالي لضبطه:
gcloud config set project <YOUR_PROJECT_ID>
- فعِّل واجهات برمجة التطبيقات المطلوبة من خلال الأمر الموضَّح أدناه. قد تستغرق هذه العملية بضع دقائق، لذا يُرجى الانتظار.
gcloud services enable firestore.googleapis.com \
compute.googleapis.com \
cloudresourcemanager.googleapis.com \
servicenetworking.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com \
cloudfunctions.googleapis.com \
aiplatform.googleapis.com \
texttospeech.googleapis.com
عند تنفيذ الأمر بنجاح، من المفترض أن تظهر لك رسالة مشابهة للرسالة الموضّحة أدناه:
Operation "operations/..." finished successfully.
يمكنك استخدام وحدة التحكّم للبحث عن كل منتج أو استخدام هذا الرابط كبديل لأمر gcloud.
في حال عدم تفعيل أي واجهة برمجة تطبيقات، يمكنك تفعيلها في أي وقت أثناء عملية التنفيذ.
راجِع المستندات لمعرفة أوامر gcloud وكيفية استخدامها.
استنساخ المستودع وإعداد إعدادات البيئة
الخطوة التالية هي استنساخ نموذج المستودع الذي سنشير إليه في بقية ورشة رموز البرامج. لنفترض أنّك تستخدم Cloud Shell، أدخِل الأمر التالي من دليلك الرئيسي:
git clone https://github.com/rominirani/yoga-poses-recommender-nodejs
لتشغيل المحرِّر، انقر على "فتح المحرِّر" في شريط الأدوات في نافذة Cloud Shell. انقر على شريط القوائم في أعلى يمين الشاشة واختَر ملف (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 ومنطقة قاعدة بيانات 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، ثم انقر على Terminal → New Terminal
.
انتقِل إلى المجلد الجذر للمستودع الذي نسخته باستخدام الأمر التالي:
cd yoga-poses-recommender-nodejs
ثبِّت تبعيات Node.js باستخدام الأمر التالي:
npm install
رائع. أصبحنا الآن جاهزين للانتقال إلى مهمة إعداد قاعدة بيانات Firestore.
3- إعداد Firestore
Cloud Firestore هي قاعدة بيانات مستندات مُدارة بالكامل بدون خادم، وسنستخدمها كخدمة خلفية لبيانات تطبيقاتنا. يتم تنظيم البيانات في Cloud Firestore في مجموعات من المستندات.
تهيئة قاعدة بيانات Firestore
انتقِل إلى صفحة Firestore في Cloud Console.
إذا لم يسبق لك إعداد قاعدة بيانات Firestore في المشروع، أنشئ قاعدة بيانات default
بالنقر على Create Database
. أثناء إنشاء قاعدة البيانات، استخدِم القيم التالية:
- وضع Firestore:
Native.
- الموقع الجغرافي: يمكنك استخدام الإعدادات التلقائية للموقع الجغرافي.
- بالنسبة إلى قواعد الأمان، اختَر
Test rules
. - أنشئ قاعدة البيانات.
في القسم التالي، سنضع الأساس لإنشاء مجموعة باسم poses
في قاعدة بيانات Firestore التلقائية. ستحتوي هذه المجموعة على نماذج بيانات (مستندات) أو معلومات عن وضعيات اليوغا، وسنستخدمها بعد ذلك في تطبيقنا.
وبهذا نكون قد أكملنا قسم إعداد قاعدة بيانات Firestore.
4. إعداد مجموعة بيانات وضعيات اليوغا
مهمتنا الأولى هي إعداد مجموعة بيانات أوضاع اليوغا التي سنستخدمها في التطبيق. سنبدأ بمجموعة بيانات حالية من Hugging Face ثم نُحسِّنها بمعلومات إضافية.
اطّلِع على مجموعة بيانات "التعرّف على الوجه" لتحديد أوضاع اليوغا. تجدر الإشارة إلى أنّه على الرغم من أنّ هذا الدليل التعليمي للترميز يستخدم إحدى مجموعات البيانات، يمكنك في الواقع استخدام أي مجموعة بيانات أخرى واتّباع الأساليب نفسها الموضّحة لتحسين مجموعة البيانات.
إذا انتقلنا إلى قسم Files and versions
، يمكننا الحصول على ملف بيانات JSON لجميع الوضعيات.
لقد نزّلنا yoga_poses.json
وقدّمنا لك هذا الملف. اسم هذا الملف هو yoga_poses_alldata.json
، وهو متوفّر في المجلد /data
.
انتقِل إلى ملف data/yoga_poses.json
في محرِّر Cloud Shell واطّلِع على قائمة عناصر 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
له.
في محرِّر Cloud Shell، انتقِل إلى ملف 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
إذا طُلب منك تقديم أي تفويض، يُرجى تقديمه.
ستلاحظ أنّ التطبيق بدأ التنفيذ. لقد أضفنا تأخيرًا لمدة 30 ثانية بين السجلات لتجنُّب أي حصص لعدد عمليات النقل في الثانية قد تكون متوفّرة في حسابات Google Cloud الجديدة، لذا يُرجى الانتظار.
في ما يلي مثال على عملية تشغيل جارية:
بعد تحسين جميع السجلات الثلاثة من خلال مكالمة Gemini، سيتم إنشاء ملف data/yoga_poses_with_description.json
. يمكنك الاطّلاع على ذلك.
أصبحنا الآن جاهزين باستخدام ملف البيانات، والخطوة التالية هي فهم كيفية تعبئة قاعدة بيانات Firestore به، بالإضافة إلى إنشاء الحِزم.
5- استيراد البيانات إلى Firestore وإنشاء embeddings في النماذج المتجهّة
لدينا ملف data/yoga_poses_with_description.json
ونحتاج الآن إلى تعبئة قاعدة بيانات Firestore به، والأهم من ذلك، إنشاء "إدراج في مصفوفة" لكل سجل. ستكون ميزة "إدراج المتجهات" مفيدة لاحقًا عندما نحتاج إلى إجراء بحث تشابه عليها باستخدام طلب بحث المستخدم الذي تم تقديمه بلغة طبيعية.
في ما يلي الخطوات التي يجب اتّباعها لإجراء ذلك:
- سنحوّل قائمة كائنات JSON إلى قائمة بكائنات. سيتضمّن كل مستند سمتَين:
content
وmetadata
. سيحتوي عنصر البيانات الوصفية على عنصر JSON بالكامل الذي يحتوي على سمات مثلname
وdescription
وsanskrit_name
وما إلى ذلك. وسيكونcontent
نصًا سلسلة سيكون تسلسلًا لبعض الحقول. - بعد الحصول على قائمة بالمستندات، سنستخدم فئة "عمليات التضمين" في Vertex AI لإنشاء عملية التضمين لحقل المحتوى. ستتم إضافة هذا التضمين إلى كل سجلّ مستند، وبعد ذلك سنستخدم واجهة برمجة التطبيقات Firestore API لحفظ هذه القائمة بعناصر المستندات في المجموعة (نحن نستخدم المتغيّر
TEST_COLLECTION
الذي يشير إلىtest-poses
).
في ما يلي رمز import-data.js
(تم اقتطاع أجزاء من الرمز لتبسيطه):
import { Firestore,
FieldValue,
} from '@google-cloud/firestore';
import { VertexAIEmbeddings } from "@langchain/google-vertexai";
import * as dotenv from 'dotenv';
import fs from 'fs/promises';
// Load environment variables
dotenv.config();
// Configure logging
const logger = {
info: (message) => console.log(`INFO - ${new Date().toISOString()} - ${message}`),
error: (message) => console.error(`ERROR - ${new Date().toISOString()} - ${message}`),
};
async function loadYogaPosesDataFromLocalFile(filename) {
try {
const data = await fs.readFile(filename, 'utf-8');
const poses = JSON.parse(data);
logger.info(`Loaded ${poses.length} poses.`);
return poses;
} catch (error) {
logger.error(`Error loading dataset: ${error}`);
return null;
}
}
function createFirestoreDocuments(poses) {
const documents = [];
for (const pose of poses) {
// Convert the pose to a string representation for pageContent
const pageContent = `
name: ${pose.name || ''}
description: ${pose.description || ''}
sanskrit_name: ${pose.sanskrit_name || ''}
expertise_level: ${pose.expertise_level || 'N/A'}
pose_type: ${pose.pose_type || 'N/A'}
`.trim();
// The metadata will be the whole pose
const metadata = pose;
documents.push({ pageContent, metadata });
}
logger.info(`Created ${documents.length} Langchain documents.`);
return documents;
}
async function main() {
const allPoses = await loadYogaPosesDataFromLocalFile('./data/yoga_poses_with_descriptions.json');
const documents = createFirestoreDocuments(allPoses);
logger.info(`Successfully created Firestore documents. Total documents: ${documents.length}`);
const embeddings = new VertexAIEmbeddings({
model: process.env.EMBEDDING_MODEL_NAME,
});
// Initialize Firestore
const firestore = new Firestore({
projectId: process.env.PROJECT_ID,
databaseId: process.env.DATABASE,
});
const collectionName = process.env.TEST_COLLECTION;
for (const doc of documents) {
try {
// 1. Generate Embeddings
const singleVector = await embeddings.embedQuery(doc.pageContent);
// 2. Store in Firestore with Embeddings
const firestoreDoc = {
content: doc.pageContent,
metadata: doc.metadata, // Store the original data as metadata
embedding: FieldValue.vector(singleVector), // Add the embedding vector
};
const docRef = firestore.collection(collectionName).doc();
await docRef.set(firestoreDoc);
logger.info(`Document ${docRef.id} added to Firestore with embedding.`);
} catch (error) {
logger.error(`Error processing document: ${error}`);
}
}
logger.info('Finished adding documents to Firestore.');
}
main();
لننفِّذ هذا التطبيق. افتح نافذة محطة طرفية جديدة (Ctrl + Shift + C) وأدخِل الأمر التالي:
npm run import-data
إذا سارت الأمور على ما يرام، من المفترض أن تظهر لك رسالة مشابهة للرسالة أدناه:
INFO - 2025-01-28T07:01:14.463Z - Loaded 3 poses.
INFO - 2025-01-28T07:01:14.464Z - Created 3 Langchain documents.
INFO - 2025-01-28T07:01:14.464Z - Successfully created Firestore documents. Total documents: 3
INFO - 2025-01-28T07:01:17.623Z - Document P46d5F92z9FsIhVVYgkd added to Firestore with embedding.
INFO - 2025-01-28T07:01:18.265Z - Document bjXXISctkXl2ZRSjUYVR added to Firestore with embedding.
INFO - 2025-01-28T07:01:19.285Z - Document GwzZMZyPfTLtiX6qBFFz added to Firestore with embedding.
INFO - 2025-01-28T07:01:19.286Z - Finished adding documents to Firestore.
للتحقّق مما إذا تم إدراج السجلات بنجاح وإنشاء البيانات المضمّنة، انتقِل إلى صفحة Firestore في وحدة تحكّم Cloud.
انقر على قاعدة البيانات (التلقائية)، ومن المفترض أن تظهر مجموعة test-poses
ومستندات متعددة ضمن هذه المجموعة. كل مستند يعرض وضعية يوغا واحدة.
انقر على أيٍّ من المستندات للاطّلاع على الحقول. بالإضافة إلى الحقول التي استوردناها، ستجد أيضًا حقل embedding
، وهو حقل متّجه، وقد أنشأنا قيمته من خلال نموذج text-embedding-004
لدمج Vertex AI.
الآن بعد أن حمّلنا السجلات إلى قاعدة بيانات Firestore مع تضمين العناصر، يمكننا الانتقال إلى الخطوة التالية والاطّلاع على كيفية إجراء "بحث التشابه بين المتجهات" في 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
ستستغرق عملية الاستيراد بضع ثوانٍ، وبعد أن تصبح جاهزة، يمكنك التحقّق من صحة قاعدة بيانات Firestore والمجموعة من خلال الانتقال إلى https://console.cloud.google.com/firestore/databases واختيار قاعدة بيانات default
والمجموعة poses
كما هو موضّح أدناه:
يُكمِل هذا الإجراء عملية إنشاء مجموعة Firestore التي سنستخدمها في تطبيقنا.
7- إجراء بحث تشابه بين المتجهات في Firestore
لإجراء بحث التشابه بين المتجهات، سنتلقّى طلب البحث من المستخدم. يمكن أن يكون مثال على طلب البحث هذا "Suggest me some exercises to relieve back pain"
.
اطّلِع على ملف search-data.js
. الدالة الرئيسية التي يجب الاطّلاع عليها هي دالة search
الموضّحة أدناه. على مستوى عالٍ، يتم إنشاء فئة تضمين ستُستخدَم لإنشاء عملية التضمين لطلب بحث المستخدم. بعد ذلك، يتم إنشاء اتصال بقاعدة بيانات Firestore ومجموعة 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 Vector لتحميل السجلّات وإنشاء عمليات التضمين وإجراء بحث تشابه في النماذج. يمكننا الآن إنشاء تطبيق ويب يدمج البحث بالاستناد إلى المتجهات في واجهة مستخدم الويب.
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
بعد الانتهاء من عملية الإعداد والتشغيل، انتقِل إلى عنوان 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>>
نفِّذ الأمر أعلاه من المجلد الجذر للتطبيق. قد يُطلب منك أيضًا تفعيل Google Cloud APIs، وتقديم تأكيد على الأذونات المختلفة، يُرجى إجراء ذلك.
ستستغرق عملية النشر مدة تتراوح بين 5 و7 دقائق تقريبًا، لذا يُرجى الانتظار.
بعد نشر التطبيق بنجاح، سيقدّم ناتج النشر عنوان URL لخدمة Cloud Run. وسيكون بالتنسيق التالي:
Service URL: https://yogaposes-<UNIQUEID>.us-central1.run.app
يُرجى الانتقال إلى عنوان URL العام هذا، ومن المفترض أن يظهر لك تطبيق الويب نفسه مُنشئًا وجارٍ تشغيله بنجاح.
يمكنك أيضًا الانتقال إلى Cloud Run من وحدة تحكّم Google Cloud وستظهر لك قائمة الخدمات في Cloud Run. يجب أن تكون خدمة yogaposes
إحدى الخدمات (إن لم تكن الخدمة الوحيدة) المدرَجة هناك.
يمكنك الاطّلاع على تفاصيل الخدمة، مثل عنوان URL والإعدادات والسجلات وغير ذلك، من خلال النقر على اسم الخدمة المحدّد (yogaposes
في حالتنا).
يُكمِل ذلك عملية تطوير تطبيق الويب المُقترِح لحركات اليوغا ونشره على Cloud Run.
10. تهانينا
مبروك، لقد نجحت في إنشاء تطبيق يحمّل مجموعة بيانات إلى Firestore، وينشئ الحِزم ويُجري بحثًا عن التشابه بين المتجهات استنادًا إلى طلب بحث المستخدمين.