1. Einführung
In diesem Codelab erstellen Sie eine Anwendung, die mithilfe der Vektorsuche Yoga-Posen empfiehlt.
Im Codelab gehen Sie so vor:
- Verwenden Sie einen vorhandenen Hugging Face-Datensatz mit Yoga-Posen (JSON-Format).
- Ergänzen Sie den Datensatz um eine zusätzliche Feldbeschreibung, mit der mit Gemini Beschreibungen für jede der Posen generiert werden.
- Laden Sie die Daten zu Yoga-Posen als Sammlung von Dokumenten in die Firestore-Sammlung mit generierten Einbettungen.
- Erstellen Sie einen zusammengesetzten Index in Firestore, um die Vektorsuche zu ermöglichen.
- Verwenden Sie die Vektorsuche in einer Node.js-Anwendung, die alles zusammenführt, wie unten dargestellt:
Aufgabe
- Eine Webanwendung entwerfen, erstellen und bereitstellen, die Yoga-Posen anhand der Vektorsuche empfiehlt.
Aufgaben in diesem Lab
- Mit Gemini Textinhalte generieren und im Rahmen dieses Codelabs Beschreibungen für Yoga-Posen generieren
- Datensätze aus einem erweiterten Hugging Face-Dataset zusammen mit Vektoreinbettungen in Firestore laden
- Mit der Firestore-Vektorsuche nach Daten anhand einer Abfrage in natürlicher Sprache suchen
- Audioinhalte mit der Google Cloud Text-to-Speech API generieren
Voraussetzungen
- Chrome-Webbrowser
- Ein Gmail-Konto
- Ein Cloud-Projekt mit aktivierter Abrechnung
Dieses Codelab richtet sich an Entwickler aller Stufen (einschließlich Anfänger) und verwendet in der Beispielanwendung JavaScript und Node.js. Kenntnisse in JavaScript und Node.js sind jedoch nicht erforderlich, um die vorgestellten Konzepte zu verstehen.
2. Hinweis
Projekt erstellen
- Wählen Sie in der Google Cloud Console auf der Seite der Projektauswahl ein Google Cloud-Projekt aus oder erstellen Sie eines.
- Die Abrechnung für das Cloud-Projekt muss aktiviert sein. So prüfen Sie, ob die Abrechnung für ein Projekt aktiviert ist.
- Sie verwenden Cloud Shell, eine Befehlszeilenumgebung, die in Google Cloud ausgeführt wird und bq bereits vorinstalliert hat. Klicken Sie oben in der Google Cloud Console auf „Cloud Shell aktivieren“.
- Nachdem Sie eine Verbindung zu Cloud Shell hergestellt haben, prüfen Sie mit dem folgenden Befehl, ob Sie bereits authentifiziert sind und das Projekt auf Ihre Projekt-ID festgelegt ist:
gcloud auth list
- Führen Sie in Cloud Shell den folgenden Befehl aus, um zu prüfen, ob der gcloud-Befehl Ihr Projekt kennt.
gcloud config list project
- Wenn Ihr Projekt nicht festgelegt ist, verwenden Sie den folgenden Befehl, um es festzulegen:
gcloud config set project <YOUR_PROJECT_ID>
- Aktivieren Sie die erforderlichen APIs mit dem folgenden Befehl. Dies kann einige Minuten dauern.
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
Wenn der Befehl erfolgreich ausgeführt wurde, sollte eine Meldung ähnlich der folgenden angezeigt werden:
Operation "operations/..." finished successfully.
Alternativ können Sie in der Console nach den einzelnen Produkten suchen oder diesen Link verwenden.
Wenn eine API fehlt, können Sie sie jederzeit während der Implementierung aktivieren.
Weitere Informationen zu gcloud-Befehlen und deren Verwendung finden Sie in der Dokumentation.
Repository klonen und Umgebungseinstellungen einrichten
Im nächsten Schritt klonen wir das Beispiel-Repository, auf das wir im Rest des Codelabs verweisen werden. Angenommen, Sie befinden sich in Cloud Shell, geben Sie in Ihrem Basisverzeichnis den folgenden Befehl ein:
git clone https://github.com/rominirani/yoga-poses-recommender-nodejs
Klicken Sie zum Starten des Editors in der Symbolleiste des Cloud Shell-Fensters auf „Editor öffnen“. Klicken Sie links oben auf die Menüleiste und wählen Sie „Datei“ > „Ordner öffnen“ aus (siehe Abbildung unten).
Wählen Sie den Ordner yoga-poses-recommender-nodejs
aus. Der Ordner sollte sich mit den folgenden Dateien öffnen:
Jetzt müssen wir die Umgebungsvariablen einrichten, die wir verwenden werden. Klicken Sie auf die Datei env-template
. Der Inhalt sollte wie unten dargestellt aussehen:
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
Bitte aktualisieren Sie die Werte für PROJECT_ID
und LOCATION
gemäß den Angaben, die Sie beim Erstellen des Google Cloud-Projekts und der Firestore-Datenbankregion ausgewählt haben. Idealerweise sollten die Werte von LOCATION
für das Google Cloud-Projekt und die Firestore-Datenbank gleich sein, z.B. us-central1
.
Für dieses Codelab verwenden wir die folgenden Werte, mit Ausnahme von PROJECT_ID
und LOCATION
, die Sie entsprechend Ihrer Konfiguration festlegen müssen.
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
Speichern Sie diese Datei als .env
im selben Ordner wie die Datei env-template
.
Klicken Sie in der Cloud Shell IDE links oben auf das Dreistrich-Menü und dann auf Terminal → New Terminal
.
Rufen Sie mit dem folgenden Befehl das Stammverzeichnis des geklonten Repositorys auf:
cd yoga-poses-recommender-nodejs
Installieren Sie die Node.js-Abhängigkeiten mit dem Befehl:
npm install
Sehr gut! Jetzt können wir mit der Einrichtung der Firestore-Datenbank fortfahren.
3. Firestore einrichten
Cloud Firestore ist eine vollständig verwaltete serverlose Dokumentendatenbank, die wir als Backend für unsere Anwendungsdaten verwenden. Daten in Cloud Firestore sind in Sammlungen von Dokumenten strukturiert.
Firestore-Datenbank initialisieren
Rufen Sie in der Cloud Console die Seite „Firestore“ auf.
Wenn Sie noch keine Firestore-Datenbank im Projekt initialisiert haben, erstellen Sie die Datenbank default
, indem Sie auf Create Database
klicken. Verwenden Sie beim Erstellen der Datenbank die folgenden Werte:
- Firestore-Modus:
Native.
- Standort: Verwenden Sie die Standardeinstellungen für die Standortermittlung.
- Verwenden Sie für die Sicherheitsregeln
Test rules
. - Erstellen Sie die Datenbank.
Im nächsten Abschnitt legen wir die Grundlagen für die Erstellung einer Sammlung mit dem Namen poses
in unserer Standard-Firestore-Datenbank. Diese Sammlung enthält Beispieldaten (Dokumente) oder Informationen zu Yoga-Posen, die wir dann in unserer Anwendung verwenden.
Damit ist der Abschnitt zur Einrichtung der Firestore-Datenbank abgeschlossen.
4. Yoga-Pose-Dataset vorbereiten
Als Erstes müssen wir das Dataset mit Yoga-Posen vorbereiten, das wir für die Anwendung verwenden werden. Wir beginnen mit einem vorhandenen Hugging Face-Datensatz und ergänzen ihn dann um zusätzliche Informationen.
Sehen Sie sich den Hugging Face-Dataset für Yoga-Posen an. In diesem Codelab wird zwar einer der Datasets verwendet, Sie können aber auch ein beliebiges anderes Dataset verwenden und dieselben Techniken anwenden, um es zu optimieren.
Im Bereich Files and versions
können wir die JSON-Datendatei für alle Posen abrufen.
Wir haben die Datei yoga_poses.json
heruntergeladen und Ihnen zur Verfügung gestellt. Diese Datei heißt yoga_poses_alldata.json
und befindet sich im Ordner /data
.
Rufen Sie im Cloud Shell-Editor die Datei data/yoga_poses.json
auf und sehen Sie sich die Liste der JSON-Objekte an. Jedes JSON-Objekt steht für eine Yoga-Pose. Es gibt insgesamt drei Einträge. Unten sehen Sie ein Beispiel:
{
"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"]
}
Das ist eine gute Gelegenheit, Gemini vorzustellen und zu zeigen, wie wir mit dem Standardmodell ein description
-Feld generieren können.
Rufen Sie im Cloud Shell-Editor die Datei generate-descriptions.js
auf. Der Inhalt dieser Datei ist unten zu sehen:
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();
Mit dieser Anwendung wird jedem JSON-Eintrag für Yoga-Posen ein neues Feld description
hinzugefügt. Die Beschreibung wird über einen Aufruf des Gemini-Modells abgerufen, in dem wir den erforderlichen Prompt bereitstellen. Das Feld wird der JSON-Datei hinzugefügt und die neue Datei wird in die Datei data/yoga_poses_with_descriptions.json
geschrieben.
Sehen wir uns die wichtigsten Schritte an:
- In der Funktion
main()
wird die Funktionadd_descriptions_to_json
aufgerufen und die erwartete Eingabe- und Ausgabedatei angegeben. - Die Funktion
add_descriptions_to_json
führt für jeden JSON-Datensatz, also für die Informationen zum Yoga-Beitrag, Folgendes aus: - Es werden
pose_name
,sanskrit_name
,expertise_level
undpose_types
extrahiert. - Sie ruft die Funktion
callGemini
auf, die einen Prompt erstellt, und ruft dann die Modellklasse „LangchainVertexAI“ auf, um den Antworttext abzurufen. - Dieser Antworttext wird dann dem JSON-Objekt hinzugefügt.
- Die aktualisierte JSON-Liste der Objekte wird dann in die Zieldatei geschrieben.
Führen wir diese Anwendung aus. Öffnen Sie ein neues Terminalfenster (Strg + Umschalt + C) und geben Sie den folgenden Befehl ein:
npm run generate-descriptions
Wenn Sie um eine Autorisierung gebeten werden, stellen Sie diese bitte zur Verfügung.
Die Anwendung wird ausgeführt. Wir haben eine Verzögerung von 30 Sekunden zwischen den Einträgen hinzugefügt, um Ratenlimitquoten zu vermeiden, die bei neuen Google Cloud-Konten auftreten können. Bitte haben Sie etwas Geduld.
Unten sehen Sie ein Beispiel für einen laufenden Test:
Sobald alle drei Einträge mit dem Gemini-Aufruf erweitert wurden, wird die Datei data/yoga_poses_with_description.json
generiert. Sie können sich das ansehen.
Wir haben jetzt unsere Datendatei. Im nächsten Schritt erfahren Sie, wie Sie damit eine Firestore-Datenbank füllen und Einbettungen generieren.
5. Daten in Firestore importieren und Vektoreinbettungen generieren
Wir haben die Datei data/yoga_poses_with_description.json
und müssen sie jetzt in die Firestore-Datenbank einfügen und vor allem die Vektor-Embeddings für jeden der Einträge generieren. Die Vektor-Embeddings sind später nützlich, wenn wir eine Ähnlichkeitssuche mit der Nutzerabfrage in natürlicher Sprache durchführen müssen.
Gehen Sie dazu so vor:
- Wir konvertieren die Liste der JSON-Objekte in eine Liste von Objekten. Jedes Dokument hat zwei Attribute:
content
undmetadata
. Das Metadatenobjekt enthält das gesamte JSON-Objekt mit Attributen wiename
,description
undsanskrit_name
.content
ist ein Stringtext, der aus mehreren Feldern zusammengesetzt ist. - Sobald wir eine Liste mit Dokumenten haben, verwenden wir die Vertex AI Embeddings-Klasse, um die Einbettung für das Inhaltsfeld zu generieren. Diese Einbettung wird jedem Dokumentendatensatz hinzugefügt. Anschließend speichern wir diese Liste von Dokumentenobjekten mit der Firestore API in der Sammlung. Dazu verwenden wir die Variable
TEST_COLLECTION
, die auftest-poses
verweist.
Der Code für import-data.js
ist unten zu sehen (Teile des Codes wurden aus Gründen der Übersichtlichkeit gekürzt):
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();
Führen wir diese Anwendung aus. Öffnen Sie ein neues Terminalfenster (Strg + Umschalt + C) und geben Sie den folgenden Befehl ein:
npm run import-data
Wenn alles wie geplant ausgeführt wird, sollte eine Meldung wie die folgende angezeigt werden:
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.
Ob die Einträge erfolgreich eingefügt und die Einbettungen generiert wurden, können Sie in der Cloud Console auf der Firestore-Seite prüfen.
Klicken Sie auf die (Standard-)Datenbank. Daraufhin sollten die Sammlung test-poses
und mehrere Dokumente in dieser Sammlung angezeigt werden. Jedes Dokument ist eine Yoga-Pose.
Klicken Sie auf eines der Dokumente, um die Felder zu untersuchen. Neben den importierten Feldern finden Sie auch das Feld embedding
. Dies ist ein Vektorfeld, dessen Wert wir über das Vertex AI-Einbettungsmodell text-embedding-004
generiert haben.
Nachdem wir die Einträge mit den Einbettungen in die Firestore-Datenbank hochgeladen haben, können wir mit dem nächsten Schritt fortfahren und uns ansehen, wie eine Vektorähnlichkeitssuche in Firestore funktioniert.
6. Vollständige Yoga-Posen in die Firestore-Datenbanksammlung importieren
Wir erstellen jetzt die Sammlung poses
, eine vollständige Liste von 160 Yoga-Posen, für die wir eine Datenbankimportdatei generiert haben, die Sie direkt importieren können. So sparen Sie Zeit im Lab. Das Erstellen der Datenbank mit Beschreibung und Einbettungen erfolgt wie im vorherigen Abschnitt beschrieben.
So importieren Sie die Datenbank:
- Erstellen Sie mit dem Befehl
gsutil
unten einen Bucket in Ihrem Projekt. Ersetzen Sie die Variable<PROJECT_ID>
im folgenden Befehl durch Ihre Google Cloud-Projekt-ID.
gsutil mb -l us-central1 gs://<PROJECT_ID>-my-bucket
- Nachdem der Bucket erstellt wurde, müssen wir den vorbereiteten Datenbankexport in diesen Bucket kopieren, bevor wir ihn in die Firebase-Datenbank importieren können. Verwenden Sie den folgenden Befehl:
gsutil cp -r gs://yoga-database-firestore-export-bucket/2025-01-27T05:11:02_62615 gs://<PROJECT_ID>-my-bucket
Nachdem wir die Daten zum Importieren haben, können wir mit dem letzten Schritt fortfahren, bei dem die Daten in die von uns erstellte Firebase-Datenbank (default
) importiert werden.
- Verwenden Sie den folgenden gcloud-Befehl:
gcloud firestore import gs://<PROJECT_ID>-my-bucket/2025-01-27T05:11:02_62615
Der Import dauert einige Sekunden. Sobald er abgeschlossen ist, können Sie Ihre Firestore-Datenbank und die Sammlung validieren. Rufen Sie dazu https://console.cloud.google.com/firestore/databases auf und wählen Sie die Datenbank default
und die Sammlung poses
aus (siehe Abbildung unten):
Damit ist die Erstellung der Firestore-Sammlung abgeschlossen, die wir in unserer Anwendung verwenden werden.
7. Suche nach Vektorähnlichkeiten in Firestore
Für die Suche nach Vektorähnlichkeiten nehmen wir die Suchanfrage des Nutzers auf. Ein Beispiel für diese Abfrage ist "Suggest me some exercises to relieve back pain"
.
Sehen Sie sich die Datei search-data.js
an. Die wichtigste Funktion ist die Funktion search
, die unten dargestellt ist. Im Wesentlichen wird eine Einbettungsklasse erstellt, die zum Generieren der Einbettung für die Nutzerabfrage verwendet wird. Anschließend wird eine Verbindung zur Firestore-Datenbank und -Sammlung hergestellt. Anschließend wird die Methode „findNearest“ auf der Sammlung aufgerufen, die eine Suche nach Vektorähnlichkeit ausführt.
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}`);
}
}
Bevor Sie dies mit einigen Abfragebeispielen ausführen, müssen Sie zuerst einen Firestore-zusammengesetzten Index generieren, der für erfolgreiche Suchanfragen erforderlich ist. Wenn Sie die Anwendung ausführen, ohne den Index zu erstellen, wird ein Fehler angezeigt, der darauf hinweist, dass Sie den Index zuerst erstellen müssen.
Unten sehen Sie den Befehl gcloud
zum Erstellen des zusammengesetzten Index:
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
Die Indexierung dauert einige Minuten, da sich mehr als 150 Einträge in der Datenbank befinden. Sobald der Vorgang abgeschlossen ist, können Sie den Index mit dem folgenden Befehl aufrufen:
gcloud firestore indexes composite list
Der gerade erstellte Index sollte in der Liste angezeigt werden.
Probieren Sie jetzt den folgenden Befehl aus:
node search-data.js --prompt "Recommend me some exercises for back pain relief"
Sie sollten einige Empfehlungen erhalten. Unten sehen Sie ein Beispiel für einen Testlauf:
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']
Sobald das funktioniert, wissen Sie, wie Sie mit der Firestore-Vektordatenbank Datensätze hochladen, Einbettungen generieren und eine Vektorähnlichkeitssuche durchführen. Wir können jetzt eine Webanwendung erstellen, in der die Vektorsuche in ein Web-Frontend eingebunden wird.
8. Die Webanwendung
Die Python Flask-Webanwendung ist in der Datei app.js
verfügbar und die Front-End-HTML-Datei befindet sich in views/index.html.
.
Wir empfehlen, sich beide Dateien anzusehen. Beginnen Sie mit der Datei app.js
, die den Handler /search
enthält, der den Prompt übernimmt, der von der Front-End-HTML-Datei index.html
übergeben wurde. Dadurch wird die Suchmethode aufgerufen, die die im vorherigen Abschnitt beschriebene Suche nach Vektorähnlichkeit durchführt.
Die Antwort wird dann mit der Liste der Empfehlungen an die index.html
zurückgesendet. Über das Symbol index.html
werden die Empfehlungen dann als verschiedene Karten angezeigt.
Anwendung lokal ausführen
Öffnen Sie ein neues Terminalfenster (Strg + Umschalt + C) oder ein vorhandenes Terminalfenster und geben Sie den folgenden Befehl ein:
npm run start
Eine Beispielausführung ist unten dargestellt:
...
Server listening on port 8080
Wenn die Anwendung einsatzbereit ist, rufen Sie die Start-URL der Anwendung auf, indem Sie auf die Schaltfläche „Webvorschau“ unten klicken:
Es sollte die index.html
-Datei angezeigt werden, die wie unten dargestellt bereitgestellt wird:
Geben Sie eine Beispielabfrage ein (z. B. Provide me some exercises for back pain relief
) und klicken Sie auf die Schaltfläche Search
. Dadurch sollten einige Empfehlungen aus der Datenbank abgerufen werden. Außerdem wird die Schaltfläche Play Audio
angezeigt. Wenn Sie darauf klicken, wird anhand der Beschreibung ein Audiostream generiert, den Sie direkt anhören können.
9. Optional: In Google Cloud Run bereitstellen
Im letzten Schritt stellen wir diese Anwendung in Google Cloud Run bereit. Der Bereitstellungsbefehl wird unten angezeigt. Ersetzen Sie vor der Bereitstellung die Werte, die unten fett dargestellt sind. Diese Werte können Sie aus der Datei .env
abrufen.
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>>
Führen Sie den Befehl oben im Stammverzeichnis der Anwendung aus. Möglicherweise werden Sie auch aufgefordert, Google Cloud APIs zu aktivieren und Ihre Einwilligung für verschiedene Berechtigungen zu erteilen. Bitte tun Sie dies.
Die Bereitstellung dauert etwa 5 bis 7 Minuten. Bitte haben Sie etwas Geduld.
Nach der erfolgreichen Bereitstellung wird in der Bereitstellungsausgabe die URL des Cloud Run-Dienstes angezeigt. Sie hat folgendes Format:
Service URL: https://yogaposes-<UNIQUEID>.us-central1.run.app
Wenn Sie diese öffentliche URL aufrufen, sollte dieselbe Webanwendung bereitgestellt und erfolgreich ausgeführt werden.
Sie können auch Cloud Run in der Google Cloud Console aufrufen. Dort sehen Sie eine Liste der Cloud Run-Dienste. Der Dienst yogaposes
sollte einer der dort aufgeführten Dienste sein (falls nicht der einzige).
Sie können Details zum Dienst wie URL, Konfigurationen und Protokolle aufrufen, indem Sie auf den jeweiligen Dienstnamen klicken (in unserem Fall yogaposes
).
Damit ist die Entwicklung und Bereitstellung unserer Webanwendung für Yoga-Pose-Empfehlungen in Cloud Run abgeschlossen.
10. Glückwunsch
Herzlichen Glückwunsch! Sie haben eine Anwendung erstellt, die einen Datensatz in Firestore hochlädt, die Einbettungen generiert und eine Vektorähnlichkeitssuche basierend auf der Nutzerabfrage durchführt.