1. Giới thiệu
Trong lớp học lập trình này, bạn sẽ xây dựng một ứng dụng sử dụng tính năng tìm kiếm vectơ để đề xuất các tư thế Yoga.
Trong lớp học lập trình này, bạn sẽ áp dụng phương pháp từng bước như sau:
- Sử dụng Tập dữ liệu Hugging Face hiện có về các tư thế Yoga (định dạng JSON).
- Cải thiện tập dữ liệu bằng một nội dung mô tả trường bổ sung sử dụng Gemini để tạo nội dung mô tả cho từng tư thế.
- Tải dữ liệu về tư thế Yoga dưới dạng một tập hợp Tài liệu trong tập hợp Firestore với các phần nhúng được tạo.
- Tạo một chỉ mục tổng hợp trong Firestore để cho phép tìm kiếm Vectơ.
- Sử dụng tính năng Tìm kiếm vectơ trong Ứng dụng Node.js để kết hợp mọi thứ như minh hoạ bên dưới:
Bạn sẽ thực hiện
- Thiết kế, xây dựng và triển khai một ứng dụng web sử dụng tính năng Tìm kiếm vectơ để đề xuất các tư thế Yoga.
Kiến thức bạn sẽ học được
- Cách sử dụng Gemini để tạo nội dung văn bản và trong bối cảnh của lớp học lập trình này, hãy tạo nội dung mô tả cho các tư thế yoga
- Cách tải bản ghi từ một tập dữ liệu nâng cao của Hugging Face vào Firestore cùng với Vector Embeddings
- Cách sử dụng tính năng Tìm kiếm vectơ Firestore để tìm dữ liệu dựa trên truy vấn bằng ngôn ngữ tự nhiên
- Cách sử dụng API Chuyển văn bản sang lời nói của Google Cloud để tạo nội dung Âm thanh
Bạn cần có
- Trình duyệt web Chrome
- Tài khoản Gmail
- Một dự án trên Google Cloud đã bật tính năng thanh toán
Lớp học lập trình này được thiết kế cho các nhà phát triển ở mọi cấp độ (kể cả người mới bắt đầu), sử dụng JavaScript và Node.js trong ứng dụng mẫu. Tuy nhiên, bạn không cần có kiến thức về JavaScript và Node.js để hiểu các khái niệm được trình bày.
2. Trước khi bắt đầu
Tạo một dự án
- Trong Google Cloud Console, trên trang bộ chọn dự án, hãy chọn hoặc tạo một dự án trên Google Cloud.
- Đảm bảo bạn đã bật tính năng thanh toán cho dự án trên Cloud. Tìm hiểu cách kiểm tra xem tính năng thanh toán có được bật trên dự án hay không .
- Bạn sẽ sử dụng Cloud Shell, một môi trường dòng lệnh chạy trong Google Cloud và được tải sẵn bq. Nhấp vào Kích hoạt Cloud Shell ở đầu bảng điều khiển Google Cloud.
- Sau khi kết nối với Cloud Shell, hãy kiểm tra để đảm bảo bạn đã được xác thực và dự án được đặt thành mã dự án của bạn bằng lệnh sau:
gcloud auth list
- Chạy lệnh sau trong Cloud Shell để xác nhận rằng lệnh gcloud biết về dự án của bạn.
gcloud config list project
- Nếu dự án của bạn chưa được thiết lập, hãy sử dụng lệnh sau để thiết lập:
gcloud config set project <YOUR_PROJECT_ID>
- Bật các API bắt buộc thông qua lệnh hiển thị bên dưới. Quá trình này có thể mất vài phút. Vui lòng kiên nhẫn chờ đợi.
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
Khi thực thi thành công lệnh này, bạn sẽ thấy một thông báo tương tự như thông báo dưới đây:
Operation "operations/..." finished successfully.
Bạn có thể sử dụng bảng điều khiển để tìm kiếm từng sản phẩm hoặc sử dụng đường liên kết này thay cho lệnh gcloud.
Nếu thiếu bất kỳ API nào, bạn luôn có thể bật API đó trong quá trình triển khai.
Tham khảo tài liệu để biết các lệnh và cách sử dụng gcloud.
Sao chép kho lưu trữ và thiết lập chế độ cài đặt môi trường
Bước tiếp theo là sao chép kho lưu trữ mẫu mà chúng ta sẽ tham chiếu trong phần còn lại của lớp học lập trình. Giả sử bạn đang ở Cloud Shell, hãy nhập lệnh sau từ thư mục gốc:
git clone https://github.com/rominirani/yoga-poses-recommender-nodejs
Để khởi chạy trình chỉnh sửa, hãy nhấp vào Open Editor (Mở trình chỉnh sửa) trên thanh công cụ của cửa sổ Cloud Shell. Nhấp vào thanh trình đơn ở góc trên cùng bên trái rồi chọn File (Tệp) → Open Folder (Mở thư mục) như minh hoạ bên dưới:
Chọn thư mục yoga-poses-recommender-nodejs
và bạn sẽ thấy thư mục mở ra với các tệp sau như minh hoạ bên dưới:
Bây giờ, chúng ta cần thiết lập các biến môi trường mà chúng ta sẽ sử dụng. Nhấp vào tệp env-template
và bạn sẽ thấy nội dung như sau:
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
Vui lòng cập nhật các giá trị cho PROJECT_ID
và LOCATION
theo những gì bạn đã chọn trong khi tạo Dự án Google Cloud và khu vực Cơ sở dữ liệu Firestore. Lý tưởng nhất là các giá trị của LOCATION
phải giống nhau cho Dự án Google Cloud và Cơ sở dữ liệu Firestore, ví dụ: us-central1
.
Để phục vụ mục đích của lớp học lập trình này, chúng ta sẽ sử dụng các giá trị sau (tất nhiên, ngoại trừ PROJECT_ID
và LOCATION
mà bạn cần đặt theo cấu hình của mình).
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
Vui lòng lưu tệp này dưới dạng .env
trong cùng thư mục với tệp env-template
.
Chuyển đến trình đơn chính ở trên cùng bên trái trong Cloud Shell IDE, rồi chuyển đến Terminal → New Terminal
.
Chuyển đến thư mục gốc của kho lưu trữ mà bạn đã sao chép thông qua lệnh sau:
cd yoga-poses-recommender-nodejs
Cài đặt các phần phụ thuộc Node.js thông qua lệnh:
npm install
Tuyệt vời! Bây giờ, chúng ta đã sẵn sàng chuyển sang nhiệm vụ thiết lập cơ sở dữ liệu Firestore.
3. Thiết lập Firestore
Cloud Firestore là một cơ sở dữ liệu tài liệu không máy chủ được quản lý toàn diện mà chúng ta sẽ sử dụng làm phần phụ trợ cho dữ liệu ứng dụng. Dữ liệu trong Cloud Firestore được sắp xếp theo bộ sưu tập gồm các tài liệu.
Khởi chạy Cơ sở dữ liệu Firestore
Truy cập vào trang Firestore trong Cloud Console.
Nếu bạn chưa khởi chạy cơ sở dữ liệu Firestore trong dự án, hãy tạo cơ sở dữ liệu default
bằng cách nhấp vào Create Database
. Trong quá trình tạo cơ sở dữ liệu, hãy sử dụng các giá trị sau:
- Chế độ Firestore:
Native.
- Vị trí: Chọn chế độ cài đặt vị trí mặc định.
- Đối với Quy tắc bảo mật, hãy chọn
Test rules
. - Tạo cơ sở dữ liệu.
Trong phần tiếp theo, chúng ta sẽ đặt nền tảng để tạo một tập hợp có tên poses
trong cơ sở dữ liệu Firestore mặc định. Tập hợp này sẽ chứa dữ liệu mẫu (tài liệu) hoặc thông tin về tư thế Yoga mà chúng ta sẽ sử dụng trong ứng dụng.
Như vậy là bạn đã hoàn tất phần thiết lập cơ sở dữ liệu Firestore.
4. Chuẩn bị tập dữ liệu về tư thế Yoga
Nhiệm vụ đầu tiên của chúng ta là chuẩn bị tập dữ liệu Tư thế yoga mà chúng ta sẽ sử dụng cho ứng dụng. Chúng ta sẽ bắt đầu với một tập dữ liệu Hugging Face hiện có, sau đó cải thiện tập dữ liệu đó bằng thông tin bổ sung.
Hãy xem Dữ liệu tập hợp khuôn mặt ôm cho các tư thế yoga. Xin lưu ý rằng mặc dù lớp học lập trình này sử dụng một trong các tập dữ liệu, nhưng trên thực tế, bạn có thể sử dụng bất kỳ tập dữ liệu nào khác và làm theo các kỹ thuật tương tự được minh hoạ để cải thiện tập dữ liệu đó.
Nếu chuyển đến phần Files and versions
, chúng ta có thể lấy tệp dữ liệu JSON cho tất cả các tư thế.
Chúng tôi đã tải yoga_poses.json
xuống và cung cấp tệp đó cho bạn. Tệp này có tên là yoga_poses_alldata.json
và nằm trong thư mục /data
.
Chuyển đến tệp data/yoga_poses.json
trong Trình chỉnh sửa Cloud Shell và xem danh sách các đối tượng JSON, trong đó mỗi đối tượng JSON đại diện cho một tư thế Yoga. Chúng ta có tổng cộng 3 bản ghi và một bản ghi mẫu như sau:
{
"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"]
}
Đây là cơ hội tuyệt vời để chúng ta giới thiệu Gemini và cách sử dụng chính mô hình mặc định để tạo trường description
cho mô hình đó.
Trong Trình chỉnh sửa Cloud Shell, hãy chuyển đến tệp generate-descriptions.js
. Nội dung của tệp này được trình bày bên dưới:
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();
Ứng dụng này sẽ thêm một trường description
mới vào mỗi bản ghi JSON về tư thế Yoga. Phương thức này sẽ lấy nội dung mô tả thông qua lệnh gọi đến mô hình Gemini, nơi chúng ta sẽ cung cấp câu lệnh cần thiết. Trường này được thêm vào tệp JSON và tệp mới được ghi vào tệp data/yoga_poses_with_descriptions.json
.
Hãy cùng xem các bước chính:
- Trong hàm
main()
, bạn sẽ thấy hàm này gọi hàmadd_descriptions_to_json
và cung cấp tệp đầu vào và tệp đầu ra dự kiến. - Hàm
add_descriptions_to_json
thực hiện những việc sau cho mỗi bản ghi JSON, tức là thông tin về bài đăng về Yoga: - Hàm này trích xuất
pose_name
,sanskrit_name
,expertise_level
vàpose_types
. - Hàm này gọi hàm
callGemini
để tạo lời nhắc, sau đó gọi lớp mô hình LangchainVertexAI để lấy văn bản phản hồi. - Sau đó, văn bản phản hồi này được thêm vào đối tượng JSON.
- Sau đó, danh sách đối tượng JSON đã cập nhật sẽ được ghi vào tệp đích.
Hãy chạy ứng dụng này. Chạy một cửa sổ dòng lệnh mới (Ctrl+Shift+C) và nhập lệnh sau:
npm run generate-descriptions
Nếu bạn được yêu cầu cấp quyền, vui lòng cung cấp quyền đó.
Bạn sẽ thấy ứng dụng bắt đầu thực thi. Chúng tôi đã thêm độ trễ 30 giây giữa các bản ghi để tránh mọi hạn mức tốc độ có thể có trên các tài khoản Google Cloud mới. Vì vậy, vui lòng kiên nhẫn chờ đợi.
Dưới đây là một ví dụ về quá trình chạy mẫu:
Sau khi tất cả 3 bản ghi được nâng cao bằng lệnh gọi Gemini, một tệp data/yoga_poses_with_description.json
sẽ được tạo. Bạn có thể xem xét vấn đề đó.
Chúng ta đã có tệp dữ liệu và bước tiếp theo là tìm hiểu cách điền tệp dữ liệu đó vào Cơ sở dữ liệu Firestore, cùng với việc tạo nội dung nhúng.
5. Nhập dữ liệu vào Firestore và tạo vectơ nhúng
Chúng ta có tệp data/yoga_poses_with_description.json
và hiện cần điền tệp đó vào Cơ sở dữ liệu Firestore, quan trọng là tạo Vecteur Embeddings cho từng bản ghi. Các vectơ nhúng sẽ hữu ích sau này khi chúng ta phải tìm kiếm nội dung tương đồng trên các vectơ đó bằng cụm từ tìm kiếm của người dùng được cung cấp bằng ngôn ngữ tự nhiên.
Các bước thực hiện như sau:
- Chúng ta sẽ chuyển đổi danh sách đối tượng JSON thành danh sách đối tượng. Mỗi tài liệu sẽ có hai thuộc tính:
content
vàmetadata
. Đối tượng siêu dữ liệu sẽ chứa toàn bộ đối tượng JSON có các thuộc tính nhưname
,description
,sanskrit_name
, v.v.content
sẽ là một văn bản chuỗi kết hợp của một vài trường. - Sau khi có danh sách tài liệu, chúng ta sẽ sử dụng lớp Nhúng AI của Vertex để tạo nội dung nhúng cho trường nội dung. Nội dung nhúng này sẽ được thêm vào từng bản ghi tài liệu, sau đó chúng ta sẽ sử dụng API Firestore để lưu danh sách đối tượng tài liệu này trong bộ sưu tập (chúng ta đang sử dụng biến
TEST_COLLECTION
trỏ đếntest-poses
).
Mã cho import-data.js
được đưa ra bên dưới (các phần của mã đã bị cắt bớt để ngắn gọn):
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();
Hãy chạy ứng dụng này. Chạy một cửa sổ dòng lệnh mới (Ctrl+Shift+C) và nhập lệnh sau:
npm run import-data
Nếu mọi thứ diễn ra suôn sẻ, bạn sẽ thấy một thông báo tương tự như thông báo bên dưới:
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.
Để kiểm tra xem các bản ghi đã được chèn thành công và các mục nhúng đã được tạo hay chưa, hãy truy cập vào trang Firestore trong Cloud Console.
Nhấp vào cơ sở dữ liệu (mặc định), thao tác này sẽ hiển thị tập hợp test-poses
và nhiều tài liệu trong tập hợp đó. Mỗi tài liệu là một tư thế Yoga.
Nhấp vào một trong các tài liệu để kiểm tra các trường. Ngoài các trường mà chúng ta đã nhập, bạn cũng sẽ thấy trường embedding
. Đây là trường Vectơ, có giá trị được tạo thông qua mô hình Nhúng Vertex AI text-embedding-004
.
Giờ đây, chúng ta đã tải các bản ghi lên Cơ sở dữ liệu Firestore cùng với các mục nhúng, chúng ta có thể chuyển sang bước tiếp theo và xem cách thực hiện Tìm kiếm theo độ tương đồng vectơ trong Firestore.
6. Nhập toàn bộ tư thế Yoga vào bộ sưu tập Cơ sở dữ liệu Firestore
Bây giờ, chúng ta sẽ tạo tập hợp poses
, là danh sách đầy đủ gồm 160 tư thế Yoga. Chúng tôi đã tạo một tệp nhập cơ sở dữ liệu mà bạn có thể nhập trực tiếp. Việc này được thực hiện để tiết kiệm thời gian trong phòng thí nghiệm. Quy trình tạo cơ sở dữ liệu chứa nội dung mô tả và nội dung nhúng giống như quy trình chúng ta đã thấy trong phần trước.
Nhập cơ sở dữ liệu bằng cách làm theo các bước dưới đây:
- Tạo một bộ chứa trong dự án bằng lệnh
gsutil
dưới đây. Thay thế biến<PROJECT_ID>
trong lệnh bên dưới bằng Mã dự án Google Cloud của bạn.
gsutil mb -l us-central1 gs://<PROJECT_ID>-my-bucket
- Giờ đây, khi đã tạo bộ chứa, chúng ta cần sao chép tệp xuất cơ sở dữ liệu mà chúng ta đã chuẩn bị vào bộ chứa này trước khi có thể nhập tệp đó vào cơ sở dữ liệu Firebase. Sử dụng lệnh dưới đây:
gsutil cp -r gs://yoga-database-firestore-export-bucket/2025-01-27T05:11:02_62615 gs://<PROJECT_ID>-my-bucket
Bây giờ, chúng ta đã có dữ liệu để nhập, nên có thể chuyển sang bước cuối cùng là nhập dữ liệu vào cơ sở dữ liệu Firebase (default
) mà chúng ta đã tạo.
- Sử dụng lệnh gcloud như bên dưới:
gcloud firestore import gs://<PROJECT_ID>-my-bucket/2025-01-27T05:11:02_62615
Quá trình nhập sẽ mất vài giây. Sau khi quá trình này hoàn tất, bạn có thể xác thực cơ sở dữ liệu Firestore và tập hợp bằng cách truy cập vào https://console.cloud.google.com/firestore/databases, chọn cơ sở dữ liệu default
và tập hợp poses
như minh hoạ bên dưới:
Thao tác này sẽ hoàn tất việc tạo tập hợp Firestore mà chúng ta sẽ sử dụng trong ứng dụng.
7. Tìm kiếm vectơ tương tự trong Firestore
Để thực hiện tìm kiếm Tương đồng vectơ, chúng ta sẽ lấy truy vấn của người dùng. Ví dụ về truy vấn này có thể là "Suggest me some exercises to relieve back pain"
.
Hãy xem tệp search-data.js
. Hàm chính cần xem xét là hàm search
, như minh hoạ dưới đây. Nhìn chung, lớp này sẽ tạo một lớp nhúng được dùng để tạo nội dung nhúng cho truy vấn của người dùng. Sau đó, lớp này sẽ thiết lập kết nối với cơ sở dữ liệu và bộ sưu tập Firestore. Sau đó, trên tập hợp này, phương thức này sẽ gọi phương thức findNearest để thực hiện Tìm kiếm theo độ tương đồng vectơ.
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}`);
}
}
Trước khi chạy ứng dụng này với một vài ví dụ về truy vấn, trước tiên, bạn phải tạo một chỉ mục tổng hợp Firestore. Đây là điều kiện cần để các cụm từ tìm kiếm của bạn thành công. Nếu bạn chạy ứng dụng mà không tạo chỉ mục, một lỗi sẽ xuất hiện cho biết bạn cần tạo chỉ mục trước kèm theo lệnh để tạo chỉ mục.
Lệnh gcloud
để tạo chỉ mục tổng hợp được hiển thị bên dưới:
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
Quá trình tạo chỉ mục sẽ mất vài phút vì có hơn 150 bản ghi trong cơ sở dữ liệu. Sau khi hoàn tất, bạn có thể xem chỉ mục thông qua lệnh dưới đây:
gcloud firestore indexes composite list
Bạn sẽ thấy chỉ mục mà bạn vừa tạo trong danh sách.
Hãy thử lệnh sau:
node search-data.js --prompt "Recommend me some exercises for back pain relief"
Bạn sẽ thấy một số đề xuất. Dưới đây là một lần chạy mẫu:
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']
Khi bạn đã thực hiện xong việc này, chúng ta đã hiểu cách hoạt động của Cơ sở dữ liệu vectơ Firestore để tải bản ghi lên, tạo nội dung nhúng và thực hiện Tìm kiếm vectơ theo mức độ tương đồng. Bây giờ, chúng ta có thể tạo một ứng dụng web tích hợp tính năng tìm kiếm vectơ vào giao diện người dùng web.
8. Ứng dụng web
Ứng dụng web Python Flask có trong tệp app.js
và tệp HTML giao diện người dùng có trong views/index.html.
Bạn nên xem cả hai tệp. Trước tiên, hãy bắt đầu với tệp app.js
chứa trình xử lý /search
. Trình xử lý này sẽ lấy lời nhắc đã được truyền từ tệp HTML index.html
ở giao diện người dùng. Thao tác này sẽ gọi phương thức tìm kiếm, thực hiện tìm kiếm Tương đồng vectơ mà chúng ta đã xem xét trong phần trước.
Sau đó, hệ thống sẽ gửi phản hồi về index.html
cùng danh sách đề xuất. Sau đó, index.html
sẽ hiển thị các đề xuất dưới dạng các thẻ khác nhau.
Chạy ứng dụng trên máy
Chạy một cửa sổ dòng lệnh mới (Ctrl+Shift+C) hoặc bất kỳ cửa sổ dòng lệnh nào hiện có và đưa ra lệnh sau:
npm run start
Dưới đây là một ví dụ về quá trình thực thi mẫu:
...
Server listening on port 8080
Sau khi khởi động và chạy, hãy truy cập vào URL trang chủ của ứng dụng bằng cách nhấp vào nút Xem trước trên web như dưới đây:
Bạn sẽ thấy tệp index.html
được phân phát như sau:
Cung cấp một truy vấn mẫu (Ví dụ : Provide me some exercises for back pain relief
) rồi nhấp vào nút Search
. Thao tác này sẽ truy xuất một số đề xuất từ cơ sở dữ liệu. Bạn cũng sẽ thấy nút Play Audio
. Nút này sẽ tạo một luồng âm thanh dựa trên nội dung mô tả mà bạn có thể nghe trực tiếp.
9. (Không bắt buộc) Triển khai lên Google Cloud Run
Bước cuối cùng là triển khai ứng dụng này lên Google Cloud Run. Lệnh triển khai được hiển thị bên dưới. Hãy đảm bảo rằng trước khi triển khai, bạn thay thế các giá trị hiển thị dưới dạng in đậm bên dưới. Đây là những giá trị mà bạn có thể truy xuất từ tệp .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>>
Thực thi lệnh trên từ thư mục gốc của ứng dụng. Bạn cũng có thể được yêu cầu bật API Google Cloud, xác nhận các quyền khác nhau. Vui lòng làm như vậy.
Quá trình triển khai sẽ mất khoảng 5 đến 7 phút để hoàn tất, vì vậy, vui lòng kiên nhẫn chờ đợi.
Sau khi triển khai thành công, kết quả triển khai sẽ cung cấp URL dịch vụ Cloud Run. URL này sẽ có dạng:
Service URL: https://yogaposes-<UNIQUEID>.us-central1.run.app
Truy cập vào URL công khai đó và bạn sẽ thấy cùng một ứng dụng web được triển khai và chạy thành công.
Bạn cũng có thể truy cập vào Cloud Run từ Google Cloud Console và sẽ thấy danh sách các dịch vụ trong Cloud Run. Dịch vụ yogaposes
phải là một trong các dịch vụ (nếu không phải là dịch vụ duy nhất) được liệt kê ở đó.
Bạn có thể xem thông tin chi tiết về dịch vụ như URL, cấu hình, nhật ký và nhiều thông tin khác bằng cách nhấp vào tên dịch vụ cụ thể (yogaposes
trong trường hợp này).
Như vậy là bạn đã hoàn tất việc phát triển và triển khai ứng dụng web đề xuất tư thế Yoga trên Cloud Run.
10. Xin chúc mừng
Xin chúc mừng! Bạn đã tạo thành công một ứng dụng tải tập dữ liệu lên Firestore, tạo các mục nhúng và thực hiện Tìm kiếm theo độ tương đồng vectơ dựa trên truy vấn của người dùng.