1. はじめに
この Codelab では、ベクトル検索を使用してヨガのポーズをおすすめするアプリを作成します。
この Codelab では、次の手順で進めていきます。
- ヨガのポーズの既存の Hugging Face データセット(JSON 形式)を利用します。
- Gemini を使用して各ポーズの説明を生成するフィールドの説明を追加して、データセットを拡張します。
- 生成されたエンベディングを使用して、ヨガのポーズデータを Firestore コレクションのドキュメントのコレクションとして読み込みます。
- Firestore で複合インデックスを作成して、ベクトル検索を可能にします。
- 以下に示すように、Node.js アプリケーションでベクトル検索を使用して、すべてを統合します。
演習内容
- ベクトル検索を使用してヨガのポーズをおすすめするウェブ アプリケーションを設計、構築、デプロイします。
学習内容
- Gemini を使用してテキスト コンテンツを生成する方法と、この Codelab のコンテキスト内でヨガのポーズの説明を生成する方法
- Hugging Face の拡張データセットのレコードをベクトル エンベディングとともに Firestore に読み込む方法
- Firestore ベクトル検索を使用して自然言語クエリに基づいてデータを検索する方法
- Google Cloud Text to Speech API を使用して音声コンテンツを生成する方法
必要なもの
- Chrome ウェブブラウザ
- Gmail アカウント
- 課金が有効になっている Cloud プロジェクト
この Codelab は、初心者を含むあらゆるレベルのデベロッパーを対象としており、サンプル アプリケーションで JavaScript と Node.js を使用します。ただし、ここで説明するコンセプトを理解するために JavaScript と Node.js の知識は必要ありません。
2. 始める前に
プロジェクトを作成する
- Google Cloud コンソールのプロジェクト選択ページで、Google Cloud プロジェクトを選択または作成します。
- Cloud プロジェクトに対して課金が有効になっていることを確認します。詳しくは、プロジェクトで課金が有効になっているかどうかを確認する方法をご覧ください。
- Cloud Shell(Google Cloud で動作するコマンドライン環境)を使用します。この環境には bq がプリロードされています。Google Cloud コンソールの上部にある [Cloud Shell をアクティブにする] をクリックします。
- Cloud Shell に接続したら、次のコマンドを使用して、認証が完了していることと、プロジェクトがプロジェクト ID に設定されていることを確認します。
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 コマンドとその使用方法については、ドキュメントをご覧ください。
リポジトリのクローンを作成して環境設定を設定する
次のステップでは、Codelab の残りの部分で参照するサンプル リポジトリのクローンを作成します。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
Google Cloud プロジェクトと Firestore データベースのリージョンの作成時に選択した内容に従って、PROJECT_ID
と LOCATION
の値を更新してください。理想的には、LOCATION
の値は Google Cloud プロジェクトと Firestore データベースで同じにする必要があります(us-central1
など)。
この Codelab では、次の値を使用します(ただし、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-template
ファイルと同じフォルダに .env
として保存してください。
Cloud Shell IDE の左上にあるメインメニュー、Terminal → New Terminal
の順に移動します。
次のコマンドを使用して、クローンを作成したリポジトリのルートフォルダに移動します。
cd yoga-poses-recommender-nodejs
次のコマンドを使用して Node.js の依存関係をインストールします。
npm install
これで準備が整いました。これで、Firestore データベースの設定に進むことができます。
3. Firestore を設定する
Cloud Firestore は、アプリケーション データのバックエンドとして使用するフルマネージドのサーバーレス ドキュメント データベースです。Cloud Firestore のデータは、ドキュメントのコレクションで構成されます。
Firestore データベースの初期化
Cloud コンソールの [Firestore] ページに移動します。
プロジェクトで Firestore データベースを初期化したことがない場合は、[Create Database
] をクリックして default
データベースを作成します。データベースの作成時に、次の値を使用します。
- Firestore モード:
Native.
- 位置情報: デフォルトの位置情報設定を使用します。
- セキュリティ ルールには
Test rules
を選択します。 - データベースを作成します。
次のセクションでは、デフォルトの Firestore データベースに poses
という名前のコレクションを作成する準備を行います。このコレクションには、サンプルデータ(ドキュメント)またはヨガポーズに関する情報を格納します。これらのデータは、アプリケーションで使用します。
これで、Firestore データベースの設定セクションは完了です。
4. ヨガのポーズ データセットを準備する
まず、アプリケーションで使用するヨガポーズ データセットを準備します。まず、既存の Hugging Face データセットから始め、追加情報を追加して拡張します。
ヨガのポーズ用の Hugging Face データセットをご覧ください。この Codelab ではデータセットの 1 つを使用しますが、実際には他の任意のデータセットを使用して、ここで説明した手法に沿ってデータセットを拡張できます。
[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();
このアプリケーションは、各ヨガのポーズ JSON レコードに新しい description
フィールドを追加します。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
承認を求められた場合は、承認してください。
アプリケーションの実行が開始されます。新しい Google Cloud アカウントで発生する可能性があるレート制限の割り当てを回避するため、レコード間に 30 秒の遅延を追加しました。ご不便をおかけしますが、ご容赦ください。
実行中の実行例を以下に示します。
3 つのレコードがすべて Gemini 呼び出しで拡張されると、ファイル data/yoga_poses_with_description.json
が生成されます。ご確認いただけます。
これでデータファイルの準備が整いました。次のステップでは、エンベディングの生成とともに、Firestore データベースにデータを入力する方法について説明します。
5. データを Firestore にインポートしてベクトル エンベディングを生成する
data/yoga_poses_with_description.json
ファイルを作成したので、Firestore データベースにこのファイルを入力し、各レコードのベクトル エンベディングを生成する必要があります。ベクトル エンベディングは、後で自然言語で指定されたユーザーのクエリと類似性検索を行う際に役立ちます。
手順は次のとおりです。
- JSON オブジェクトのリストをオブジェクトのリストに変換します。各ドキュメントには、
content
とmetadata
の 2 つの属性があります。メタデータ オブジェクトには、name
、description
、sanskrit_name
などの属性を持つ JSON オブジェクト全体が含まれます。content
は、いくつかのフィールドを連結した文字列テキストです。 - ドキュメントのリストが作成されたら、Vertex AI Embeddings クラスを使用して、content フィールドのエンベディングを生成します。このエンベディングは各ドキュメント レコードに追加され、Firestore API を使用して、このドキュメント オブジェクトのリストをコレクションに保存します(
test-poses
を参照するTEST_COLLECTION
変数を使用しています)。
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.
レコードが正常に挿入され、エンベディングが生成されたかどうかを確認するには、Cloud コンソールの Firestore ページに移動します。
(デフォルトの)データベースをクリックすると、test-poses
コレクションとそのコレクション内の複数のドキュメントが表示されます。各ドキュメントは 1 つのヨガのポーズです。
ドキュメントをクリックして、フィールドを調べます。インポートしたフィールドに加えて、embedding
フィールド(ベクトル フィールド)もあります。このフィールドの値は、text-embedding-004
Vertex AI エンベディング モデルによって生成されています。
エンベディングが設定されたレコードが Firestore データベースにアップロードされたので、次のステップに進み、Firestore でベクトル類似度検索を行う方法を確認します。
6. ヨガのポーズ全体を Firestore データベース コレクションにインポートする
次に、160 種類のヨガポーズの完全なリストである poses
コレクションを作成します。このコレクション用に、直接インポートできるデータベース インポート ファイルを生成しました。これは、ラボでの時間を節約するためです。説明とエンベディングを含むデータベースを生成するプロセスは、前のセクションで説明したものと同じです。
次の手順に沿ってデータベースをインポートします。
- 次の
gsutil
コマンドを使用して、プロジェクトにバケットを作成します。次のコマンドの<PROJECT_ID>
変数を、Google Cloud プロジェクト ID に置き換えます。
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
インポートには数秒かかります。準備ができたら、https://console.cloud.google.com/firestore/databases にアクセスして、default
データベースと poses
コレクションを選択して、Firestore データベースとコレクションを検証します。
これで、アプリケーションで使用する 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
ファイルから始めます。このファイルには、フロントエンドの 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>>
アプリケーションのルートフォルダから上記のコマンドを実行します。Google Cloud APIs を有効にするよう求められたり、さまざまな権限について承認を求められたりします。その場合は、承認してください。
デプロイ プロセスが完了するまでに 5 ~ 7 分ほどかかりますので、しばらくお待ちください。
デプロイが正常に完了すると、デプロイの出力に Cloud Run サービス URL が表示されます。形式は次のようになります。
Service URL: https://yogaposes-<UNIQUEID>.us-central1.run.app
その公開 URL にアクセスすると、同じウェブ アプリケーションがデプロイされ、正常に実行されていることがわかります。
Google Cloud コンソールから Cloud Run にアクセスすると、Cloud Run のサービスが一覧表示されます。yogaposes
サービスが、そこに表示されるサービスの 1 つ(唯一のサービスではない)である必要があります。
特定のサービス名(この場合は yogaposes
)をクリックすると、URL、構成、ログなどのサービスの詳細を表示できます。
これで、Cloud Run でのヨガのポーズ レコメンデーター ウェブ アプリケーションの開発とデプロイが完了しました。
10.完了
これで、データセットを Firestore にアップロードし、エンベディングを生成し、ユーザーのクエリに基づいてベクトル類似度検索を行うアプリケーションを作成できました。