1. Введение
В этом практическом занятии вы разработаете приложение, использующее векторный поиск для рекомендации поз йоги.
В ходе выполнения практического задания вы будете использовать следующий пошаговый подход:
- Используйте существующий набор данных «Обнимающие лица» с позами йоги (в формате JSON).
- Дополните набор данных описанием поля, используя Gemini для генерации описаний каждой из поз.
- Загрузите данные о позах йоги в виде коллекции документов в Firestore с помощью сгенерированных эмбеддингов.
- Создайте составной индекс в Firestore для обеспечения возможности векторного поиска.
- Используйте векторный поиск в приложении Node.js, который объединяет все необходимые инструменты, как показано ниже:

Что вы будете делать
- Разработать, создать и развернуть веб-приложение, использующее векторный поиск для рекомендации поз йоги.
Что вы узнаете
- Как использовать Gemini для генерации текстового контента и, в контексте этого практического занятия, для создания описаний поз йоги.
- Как загрузить записи из расширенного набора данных Hugging Face в Firestore вместе с векторными представлениями.
- Как использовать векторный поиск Firestore для поиска данных на основе запроса на естественном языке.
- Как использовать API Google Cloud Text to Speech для генерации аудиоконтента
Что вам понадобится
- Веб-браузер Chrome
- Аккаунт Gmail
- Облачный проект с включенной функцией выставления счетов.
Этот практический урок, разработанный для разработчиков всех уровней (включая начинающих), использует JavaScript и Node.js в своем примере приложения. Однако знание JavaScript и Node.js не требуется для понимания представленных концепций.
2. Прежде чем начать
Создать проект
- В консоли Google Cloud на странице выбора проекта выберите или создайте проект Google 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>
- Включите необходимые API с помощью команды, указанной ниже. Это может занять несколько минут, поэтому, пожалуйста, наберитесь терпения.
gcloud services enable firestore.googleapis.com \
compute.googleapis.com \
cloudresourcemanager.googleapis.com \
servicenetworking.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com \
cloudfunctions.googleapis.com \
aiplatform.googleapis.com \
texttospeech.googleapis.com
После успешного выполнения команды вы должны увидеть сообщение, похожее на показанное ниже:
Operation "operations/..." finished successfully.
Альтернативой команде gcloud является поиск каждого продукта в консоли или использование этой ссылки .
Если какой-либо API отсутствует, вы всегда можете включить его в процессе реализации.
Для получения информации о командах gcloud и их использовании обратитесь к документации .
Клонируйте репозиторий и настройте параметры среды.
Следующий шаг — клонирование репозитория с примерами кода, на который мы будем ссылаться в остальной части практического занятия. Предполагая, что вы находитесь в Cloud Shell, выполните следующую команду из своей домашней директории:
git clone https://github.com/rominirani/yoga-poses-recommender-nodejs
Чтобы запустить редактор, нажмите «Открыть редактор» на панели инструментов окна Cloud Shell. Щелкните строку меню в верхнем левом углу и выберите «Файл» → «Открыть папку», как показано ниже:

Выберите папку 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 IDE перейдите в главное меню в левом верхнем углу, затем Terminal → New Terminal .
Перейдите в корневую папку клонированного репозитория с помощью следующей команды:
cd yoga-poses-recommender-nodejs
Установите зависимости Node.js с помощью команды:
npm install
Отлично! Теперь мы готовы приступить к настройке базы данных Firestore.
3. Настройка Firestore
Cloud Firestore — это полностью управляемая бессерверная база данных документов, которую мы будем использовать в качестве бэкэнда для данных нашего приложения. Данные в Cloud Firestore структурированы в виде коллекций документов .
Инициализация базы данных Firestore
Перейдите на страницу Firestore в консоли Cloud.
Если вы ранее не инициализировали базу данных Firestore в проекте, создайте базу данных default , нажав кнопку Create Database . При создании базы данных используйте следующие значения:
- Режим Firestore:
Native. - Выберите тип местоположения
Regionи укажите местоположениеus-central1в качестве региона. - Для правил безопасности используйте
Test rules. - Создайте базу данных.

В следующем разделе мы заложим основу для создания коллекции с именем poses в нашей базе данных Firestore по умолчанию. Эта коллекция будет содержать примеры данных (документы) или информацию о позах йоги, которые мы затем будем использовать в нашем приложении.
На этом завершается раздел по настройке базы данных Firestore.
4. Подготовьте набор данных по позам йоги.
Наша первая задача — подготовить набор данных «Позы йоги», который мы будем использовать в приложении. Мы начнем с существующего набора данных «Обнимающее лицо», а затем дополним его дополнительной информацией.
Ознакомьтесь с набором данных «Обнимающее лицо» для поз йоги . Обратите внимание, что хотя в этом практическом занятии используется один из наборов данных, вы можете использовать любой другой набор данных и следовать тем же методам, которые были продемонстрированы для улучшения этого набора данных.

Если мы перейдем в раздел Files and versions , мы сможем получить JSON-файл с данными для всех поз.

Мы скачали файл yoga_poses.json и предоставили его вам. Этот файл называется yoga_poses_alldata.json и находится в папке /data .
В редакторе Cloud Shell перейдите к файлу 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 .
В редакторе 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, поэтому, пожалуйста, наберитесь терпения.
Ниже представлен пример запуска программы в процессе выполнения:

После того, как все 3 записи будут дополнены с помощью вызова Gemini, будет сгенерирован файл data/yoga_poses_with_description.json . Вы можете ознакомиться с ним.
Теперь у нас есть готовый файл с данными, и следующий шаг — понять, как заполнить им базу данных Firestore, а также как сгенерировать эмбеддинги.
5. Импортируйте данные в Firestore и сгенерируйте векторные представления.
У нас есть файл data/yoga_poses_with_description.json , и теперь нам нужно заполнить им базу данных Firestore, а главное — сгенерировать векторные представления для каждой записи. Векторные представления пригодятся позже, когда нам потребуется выполнить поиск сходства между ними и пользовательским запросом, предоставленным на естественном языке.
Для этого потребуется выполнить следующие действия:
- Мы преобразуем список JSON-объектов в список объектов. Каждый документ будет иметь два атрибута:
contentиmetadata. Объект metadata будет содержать весь JSON-объект с такими атрибутами, какname,description,sanskrit_nameи т. д.contentбудет представлять собой текстовую строку, являющуюся конкатенацией нескольких полей. - Получив список документов, мы будем использовать класс Vertex AI Embeddings для генерации встраивания для поля содержимого. Это встраивание будет добавлено к каждой записи документа, а затем мы будем использовать API Firestore для сохранения этого списка объектов документов в коллекции (мы используем переменную
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 Embedding.

Теперь, когда записи загружены в базу данных Firestore с уже созданными векторными представлениями, мы можем перейти к следующему шагу и посмотреть, как выполнить поиск векторного сходства в Firestore.
6. Импортируйте полные позы йоги в коллекцию базы данных Firestore.
Теперь мы создадим коллекцию poses , которая представляет собой полный список из 160 поз йоги. Для этого мы сгенерировали файл импорта в базу данных, который вы можете импортировать напрямую. Это сделано для экономии времени в лаборатории. Процесс генерации базы данных, содержащей описание и векторные представления, тот же, что мы рассматривали в предыдущем разделе.
Импортируйте базу данных, выполнив следующие действия:
- Создайте хранилище (bucket) в вашем проекте с помощью приведенной ниже команды
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 и коллекцией. После этого в коллекции вызывается метод 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 на стороне клиента. Затем он вызывает метод search, который выполняет поиск по векторному сходству, рассмотренный в предыдущем разделе.
Затем ответ отправляется обратно в 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-адресу, и вы увидите, что то же самое веб-приложение развернуто и успешно работает.

Вы также можете зайти в Cloud Run из консоли Google Cloud и увидеть список сервисов Cloud Run. Сервис yogaposes должен быть одним из перечисленных (если не единственным).

Вы можете просмотреть подробную информацию о сервисе, такую как URL-адрес, конфигурации, журналы и многое другое, щелкнув по названию конкретного сервиса (в нашем случае yogaposes ).

На этом завершается разработка и развертывание нашего веб-приложения для рекомендаций поз йоги на платформе Cloud Run.
10. Поздравляем!
Поздравляем, вы успешно создали приложение, которое загружает набор данных в Firestore, генерирует векторные представления и выполняет поиск векторного сходства на основе запроса пользователя.