Codelab - Crea un'app di consigli contestuali sulle posizioni yoga con Firestore, Vector Search, Langchain e Gemini (versione Node.js)

1. Introduzione

In questo codelab, creerai un'applicazione che utilizza la ricerca di vettori per consigliare posizioni yoga.

Nel codelab, utilizzerai un approccio passo passo come segue:

  1. Utilizza un set di dati Hugging Face esistente di posizioni yoga (formato JSON).
  2. Migliora il set di dati con una descrizione del campo aggiuntiva che utilizza Gemini per generare descrizioni per ciascuna delle pose.
  3. Carica i dati sulle posizioni yoga come raccolta di documenti nella raccolta Firestore con gli embedding generati.
  4. Crea un indice composto in Firestore per consentire la ricerca vettoriale.
  5. Utilizza la ricerca di vettori in un'applicazione Node.js che riunisce tutto, come mostrato di seguito:

84e1cbf29cbaeedc.png

Attività previste

  • Progetta, crea ed esegui il deployment di un'applicazione web che utilizza la Ricerca vettoriale per consigliare posizioni yoga.

Cosa imparerai a fare

  • Come utilizzare Gemini per generare contenuti di testo e, nel contesto di questo codelab, generare descrizioni per le posizioni yoga
  • Come caricare in Firestore i record di un set di dati migliorato di Hugging Face insieme agli embedding vettoriali
  • Come utilizzare la ricerca vettoriale di Firestore per cercare dati in base a una query in linguaggio naturale
  • Come utilizzare l'API Google Cloud Text to Speech per generare contenuti audio

Che cosa ti serve

  • Browser web Chrome
  • Un account Gmail
  • Un progetto Cloud con la fatturazione abilitata

Questo codelab, progettato per sviluppatori di tutti i livelli (inclusi i principianti), utilizza JavaScript e Node.js nella sua applicazione di esempio. Tuttavia, la conoscenza di JavaScript e Node.js non è necessaria per comprendere i concetti presentati.

2. Prima di iniziare

Crea un progetto

  1. Nella console Google Cloud, nella pagina di selezione del progetto, seleziona o crea un progetto Google Cloud.
  2. Verifica che la fatturazione sia attivata per il tuo progetto Cloud. Scopri come verificare se la fatturazione è attivata per un progetto .
  3. Utilizzerai Cloud Shell, un ambiente a riga di comando in esecuzione in Google Cloud precaricato con bq. Fai clic su Attiva Cloud Shell nella parte superiore della console Google Cloud.

Immagine del pulsante Attiva Cloud Shell

  1. Una volta connesso a Cloud Shell, verifica di aver già eseguito l'autenticazione e che il progetto sia impostato sul tuo ID progetto utilizzando il seguente comando:
gcloud auth list
  1. Esegui il seguente comando in Cloud Shell per verificare che il comando gcloud conosca il tuo progetto.
gcloud config list project
  1. Se il progetto non è impostato, utilizza il seguente comando per impostarlo:
gcloud config set project <YOUR_PROJECT_ID>
  1. Abilita le API richieste tramite il comando mostrato di seguito. L'operazione potrebbe richiedere alcuni minuti.
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

Al termine dell'esecuzione del comando, dovresti visualizzare un messaggio simile a quello mostrato di seguito:

Operation "operations/..." finished successfully.

L'alternativa al comando gcloud è tramite la console cercando ciascun prodotto o utilizzando questo link.

Se manca un'API, puoi sempre attivarla durante l'implementazione.

Consulta la documentazione per i comandi e l'utilizzo di gcloud.

Clona il repository e configura le impostazioni dell'ambiente

Il passaggio successivo consiste nel clonare il repository di esempio a cui faremo riferimento nel resto del codelab. Supponendo che tu stia utilizzando Cloud Shell, dai il seguente comando dalla home directory:

git clone https://github.com/rominirani/yoga-poses-recommender-nodejs

Per avviare l'editor, fai clic su Apri editor nella barra degli strumenti della finestra di Cloud Shell. Fai clic sulla barra dei menu nell'angolo in alto a sinistra e seleziona File → Apri cartella come mostrato di seguito:

66221fd0d0e5202f.png

Seleziona la cartella yoga-poses-recommender-nodejs e dovresti visualizzare la cartella aperta con i seguenti file, come mostrato di seguito:

7dbe126ee112266d.png

Ora dobbiamo configurare le variabili di ambiente che utilizzeremo. Fai clic sul file env-template. Dovresti vedere i contenuti come mostrato di seguito:

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

Aggiorna i valori di PROJECT_ID e LOCATION in base a quanto selezionato durante la creazione del progetto Google Cloud e della regione del database Firestore. Idealmente, i valori di LOCATION dovrebbero essere gli stessi per il progetto Google Cloud e il database Firestore, ad esempio us-central1.

Ai fini di questo codelab, utilizzeremo i seguenti valori, ad eccezione di PROJECT_ID e LOCATION, che devi impostare in base alla tua configurazione.

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

Salva questo file come .env nella stessa cartella del file env-template.

Vai al menu principale in alto a sinistra nell'IDE Cloud Shell, quindi su Terminal → New Terminal.

Vai alla cartella principale del repository che hai clonato tramite il seguente comando:

cd yoga-poses-recommender-nodejs

Installa le dipendenze Node.js tramite il comando:

npm install

Ottimo. Ora è tutto pronto per passare alla configurazione del database Firestore.

3. Configura Firestore

Cloud Firestore è un database di documenti serverless completamente gestito che utilizzeremo come backend per i dati della nostra applicazione. I dati in Cloud Firestore sono strutturati in raccolte di documenti.

Inizialezzazione del database Firestore

Visita la pagina Firestore nella console Cloud.

Se non hai mai inizializzato un database Firestore nel progetto, crea il database default facendo clic su Create Database. Durante la creazione del database, utilizza i seguenti valori:

  • Modalità Firestore: Native.
  • Posizione: utilizza le impostazioni di geolocalizzazione predefinite.
  • Per le regole di sicurezza, scegli Test rules.
  • Crea il database.

504cabdb99a222a5.png

Nella sezione successiva, prepareremo le basi per creare una raccolta denominata poses nel nostro database Firestore predefinito. Questa raccolta conterrà dati di esempio (documenti) o informazioni sulle posizioni yoga, che utilizzeremo nella nostra applicazione.

Questa è la fine della sezione relativa alla configurazione del database Firestore.

4. Prepara il set di dati sulle posizioni yoga

Il nostro primo compito è preparare il set di dati sulle posizioni yoga che utilizzeremo per l'applicazione. Inizieremo con un set di dati Hugging Face esistente e lo miglioreremo con informazioni aggiuntive.

Consulta il set di dati Hugging Face per le posizioni yoga. Tieni presente che, anche se questo codelab utilizza uno dei set di dati, puoi utilizzare qualsiasi altro set di dati e seguire le stesse tecniche dimostrate per migliorarlo.

298cfae7f23e4bef.png

Se andiamo alla sezione Files and versions, possiamo ottenere il file di dati JSON per tutte le pose.

3fe6e55abdc032ec.png

Abbiamo scaricato il file yoga_poses.json e te lo abbiamo fornito. Questo file si chiama yoga_poses_alldata.json e si trova nella cartella /data.

Vai al file data/yoga_poses.json nell'editor Cloud Shell e dai un'occhiata all'elenco di oggetti JSON, in cui ogni oggetto JSON rappresenta una posa yoga. Abbiamo un totale di 3 record e di seguito è riportato un record di esempio:

{
   "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"]
 }

Questa è un'ottima opportunità per presentarti Gemini e come possiamo utilizzare il modello predefinito per generare un campo description.

Nell'editor di Cloud Shell, vai al file generate-descriptions.js. I contenuti di questo file sono riportati di seguito:

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();

Questa applicazione aggiungerà un nuovo campo description a ogni record JSON della posa yoga. Otterrà la descrizione tramite una chiamata al modello Gemini, a cui forniremo il prompt necessario. Il campo viene aggiunto al file JSON e il nuovo file viene scritto nel file data/yoga_poses_with_descriptions.json.

Vediamo i passaggi principali:

  1. Nella funzione main(), noterai che viene richiamata la funzione add_descriptions_to_json e vengono forniti il file di input e il file di output previsti.
  2. La funzione add_descriptions_to_json esegue le seguenti operazioni per ogni record JSON, ovvero le informazioni del post di yoga:
  3. Estrae pose_name, sanskrit_name, expertise_level e pose_types.
  4. Richiama la funzione callGemini che crea un prompt e poi la classe del modello LangchainVertexAI per ottenere il testo della risposta.
  5. Questo testo di risposta viene poi aggiunto all'oggetto JSON.
  6. L'elenco JSON aggiornato degli oggetti viene poi scritto nel file di destinazione.

Eseguiamo questa applicazione. Avvia una nuova finestra del terminale (Ctrl+Maiusc+C) e dai il seguente comando:

npm run generate-descriptions

Se ti viene richiesta un'autorizzazione, forniscila.

L'applicazione inizierà a essere eseguita. Abbiamo aggiunto un ritardo di 30 secondi tra i record per evitare eventuali quote di limite di frequenza nei nuovi account Google Cloud, quindi ti invitiamo a pazientare.

Di seguito è riportata un'esecuzione di esempio in corso:

469ede91ba007c1f.png

Una volta migliorati tutti e tre i record con la chiamata di Gemini, verrà generato un file data/yoga_poses_with_description.json. Dai un'occhiata.

Ora abbiamo il nostro file di dati e il passaggio successivo è capire come compilare un database Firestore con i dati e generare gli embedding.

5. Importa i dati in Firestore e genera embedding di vettori

Abbiamo il file data/yoga_poses_with_description.json e ora dobbiamo compilare il database Firestore con questo file e, soprattutto, generare gli embedding vettoriali per ciascuno dei record. Gli embedding vettoriali saranno utili in un secondo momento quando dovremo eseguire una ricerca di similarità con la query dell'utente fornita in linguaggio naturale.

I passaggi da seguire sono i seguenti:

  1. Convertiremo l'elenco di oggetti JSON in un elenco di oggetti. Ogni documento avrà due attributi: content e metadata. L'oggetto metadati conterrà l'intero oggetto JSON con attributi come name, description, sanskrit_name e così via. content sarà una stringa che sarà una concatenazione di alcuni campi.
  2. Una volta ottenuto un elenco di documenti, utilizzeremo la classe Embedding di Vertex AI per generare l'embedding per il campo dei contenuti. Questo incorporamento verrà aggiunto a ogni record del documento e poi utilizzeremo l'API Firestore per salvare questo elenco di oggetti documento nella raccolta (utilizziamo la variabile TEST_COLLECTION che punta a test-poses).

Di seguito è riportato il codice per import-data.js (parti del codice sono state troncate per brevità):

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();

Eseguiamo questa applicazione. Avvia una nuova finestra del terminale (Ctrl+Maiusc+C) e dai il seguente comando:

npm run import-data

Se tutto va bene, dovresti visualizzare un messaggio simile al seguente:

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.

Per verificare se i record sono stati inseriti correttamente e se gli embedding sono stati generati, visita la pagina Firestore nella console Cloud.

504cabdb99a222a5.png

Fai clic sul database (predefinito), che dovrebbe mostrare la raccolta test-poses e più documenti al suo interno. Ogni documento è una posa yoga.

9f37aa199c4b547a.png

Fai clic su uno dei documenti per esaminare i campi. Oltre ai campi che abbiamo importato, troverai anche il campo embedding, che è un campo vettoriale, il cui valore abbiamo generato tramite il modello di embedding Vertex AI text-embedding-004.

f0ed92124519beaf.png

Ora che abbiamo caricato i record nel database Firestore con gli incorporamenti, possiamo passare al passaggio successivo e scoprire come eseguire la ricerca di somiglianza vettoriale in Firestore.

6. Importa le pose yoga complete nella raccolta del database Firestore

Ora creeremo la raccolta poses, ovvero un elenco completo di 160 posizioni yoga, per la quale abbiamo generato un file di importazione del database che puoi importare direttamente. Questo viene fatto per risparmiare tempo nel lab. La procedura per generare il database contenente la descrizione e gli embedding è la stessa che abbiamo visto nella sezione precedente.

Importa il database seguendo i passaggi riportati di seguito:

  1. Crea un bucket nel tuo progetto con il comando gsutil riportato di seguito. Sostituisci la variabile <PROJECT_ID> nel comando seguente con il tuo ID progetto Google Cloud.
gsutil mb -l us-central1 gs://<PROJECT_ID>-my-bucket
  1. Ora che il bucket è stato creato, dobbiamo copiare l'esportazione del database che abbiamo preparato in questo bucket prima di poterla importare nel database Firebase. Utilizza il comando riportato di seguito:
gsutil cp -r gs://yoga-database-firestore-export-bucket/2025-01-27T05:11:02_62615  gs://<PROJECT_ID>-my-bucket

Ora che abbiamo i dati da importare, possiamo passare al passaggio finale dell'importazione dei dati nel database Firebase (default) che abbiamo creato.

  1. Utilizza il comando gcloud riportato di seguito:
gcloud firestore import gs://<PROJECT_ID>-my-bucket/2025-01-27T05:11:02_62615

L'importazione richiederà alcuni secondi. Una volta completata, puoi convalidare il database Firestore e la raccolta visitando la pagina https://console.cloud.google.com/firestore/databases, selezionando il database default e la raccolta poses come mostrato di seguito:

561f3cb840de23d8.png

La creazione della raccolta Firestore che utilizzeremo nella nostra applicazione è completata.

7. Eseguire una ricerca sulla similarità vettoriale in Firestore

Per eseguire la ricerca sulla similarità vettoriale, acquisiremo la query dall'utente. Un esempio di questa query può essere "Suggest me some exercises to relieve back pain".

Dai un'occhiata al file search-data.js. La funzione principale da esaminare è la funzione search, mostrata di seguito. A un livello generale, crea una classe di embedding che verrà utilizzata per generare l'embedding per la query dell'utente. Stabilisce quindi una connessione al database e alla raccolta Firestore. Quindi, sulla raccolta viene invocato il metodo findNearest, che esegue una ricerca di similarità vettoriale.

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}`);
 }
}

Prima di eseguirlo con alcuni esempi di query, devi prima generare un indice composto Firestore, necessario per il buon esito delle query di ricerca. Se esegui l'applicazione senza creare l'indice, viene visualizzato un errore che indica che devi prima creare l'indice, insieme al comando per creare l'indice.

Di seguito è riportato il comando gcloud per creare l'indice composito:

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

Il completamento dell'indice richiederà alcuni minuti perché nel database sono presenti più di 150 record. Al termine, puoi visualizzare l'indice tramite il comando mostrato di seguito:

gcloud firestore indexes composite list

Nell'elenco dovresti vedere l'indice che hai appena creato.

Prova subito il seguente comando:

node search-data.js --prompt "Recommend me some exercises for back pain relief"

Dovresti ricevere alcuni consigli. Di seguito è riportata un'esecuzione di esempio:

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']

Una volta che hai completato questa operazione, avrai capito come utilizzare il database vettoriale Firestore per caricare record, generare embedding ed eseguire una ricerca di somiglianza vettoriale. Ora possiamo creare un'applicazione web che integri la ricerca di vettori in un front-end web.

8. L'applicazione web

L'applicazione web Python Flask è disponibile nel file app.js e il file HTML frontend è presente in views/index.html.

Ti consigliamo di dare un'occhiata a entrambi i file. Inizia con il file app.js contenente l'handler /search, che prende il prompt passato dal file HTML index.html del front-end. Viene quindi invocato il metodo di ricerca, che esegue la ricerca di somiglianza di vettori che abbiamo esaminato nella sezione precedente.

La risposta viene quindi inviata al index.html con l'elenco dei consigli. index.html mostra quindi i consigli sotto forma di schede diverse.

Eseguire l'applicazione localmente

Avvia una nuova finestra del terminale (Ctrl+Maiusc+C) o qualsiasi finestra del terminale esistente e dai il seguente comando:

npm run start

Di seguito è riportata un'esecuzione di esempio:

...
Server listening on port 8080

Una volta completata la configurazione, visita l'URL di casa dell'applicazione facendo clic sul pulsante Anteprima web mostrato di seguito:

de297d4cee10e0bf.png

Dovresti vedere il file index.html pubblicato come mostrato di seguito:

20240a0e885ac17b.png

Fornisci una query di esempio (ad es. Provide me some exercises for back pain relief) e fai clic sul pulsante Search. Dovresti recuperare alcuni consigli dal database. Vedrai anche un pulsante Play Audio che genera uno stream audio basato sulla descrizione, che puoi ascoltare direttamente.

789b4277dc40e2be.png

9. (Facoltativo) Eseguire il deployment in Google Cloud Run

Il passaggio finale consisterà nell'eseguire il deployment di questa applicazione in Google Cloud Run. Il comando di deployment è mostrato di seguito. Prima di eseguirlo, assicurati di sostituire i valori in grassetto. Si tratta di valori che potrai recuperare dal file .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>>

Esegui il comando riportato sopra dalla cartella principale dell'applicazione. Ti potrebbe anche essere chiesto di abilitare le API Google Cloud, quindi conferma le varie autorizzazioni.

Il completamento del processo di deployment richiede circa 5-7 minuti, quindi attendi.

3a6d86fd32e4a5e.png

Una volta eseguito il deployment, l'output del deployment fornirà l'URL del servizio Cloud Run. Il formato sarà il seguente:

Service URL: https://yogaposes-<UNIQUEID>.us-central1.run.app

Visita l'URL pubblico e dovresti vedere la stessa applicazione web di cui è stato eseguito il deployment e che è in esecuzione correttamente.

84e1cbf29cbaeedc.png

Puoi anche visitare Cloud Run dalla console Google Cloud e visualizzare l'elenco dei servizi in Cloud Run. Il servizio yogaposes dovrebbe essere uno dei servizi (se non l'unico) elencati.

f2b34a8c9011be4c.png

Puoi visualizzare i dettagli del servizio, come URL, configurazioni, log e altro ancora, facendo clic sul nome del servizio specifico (yogaposes nel nostro caso).

faaa5e0c02fe0423.png

Lo sviluppo e il deployment della nostra applicazione web di consigli sulle posizioni yoga su Cloud Run sono stati completati.

10. Complimenti

Congratulazioni, hai creato correttamente un'applicazione che carica un set di dati in Firestore, genera gli embedding ed esegue una ricerca di somiglianza vettoriale in base alla query degli utenti.

Documentazione di riferimento