1. 簡介
在本程式碼研究室中,您將建構應用程式,使用向量搜尋功能推薦瑜珈姿勢。
在本程式碼研究室中,您將逐步完成下列步驟:
- 使用現有的 Hugging Face 瑜珈姿勢資料集 (JSON 格式)。
- 使用 Gemini 為每個姿勢生成說明,並新增欄位說明來強化資料集。
- 使用 Langchain 建立文件,並使用 Firestore Langchain 整合功能在 Firestore 中建立集合和嵌入。
- 在 Firestore 中建立複合式索引,以啟用向量搜尋功能。
- 在 Flask 應用程式中使用向量搜尋,將所有內容整合在一起,如下所示:

執行步驟
- 設計、建構及部署網頁應用程式,運用 Vector Search 推薦瑜珈體式。
課程內容
- 如何使用 Gemini 生成文字內容,以及在本程式碼研究室的脈絡中,生成瑜珈姿勢的說明
- 如何使用 Langchain Document Loader for Firestore,將 Hugging Face 強化資料集的記錄連同向量嵌入載入 Firestore
- 如何使用 Langchain Vector Store for Firestore,根據自然語言查詢搜尋資料
- 如何使用 Google Cloud Text-to-Speech API 產生音訊內容
軟硬體需求
- Chrome 網路瀏覽器
- Gmail 帳戶
- 已啟用計費功能的 Cloud 專案
本程式碼研究室適合各種程度的開發人員 (包括初學者),並使用 Python 撰寫範例應用程式。不過,您不需要具備 Python 知識,也能瞭解本文介紹的概念。
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 指令和用法,請參閱說明文件。
複製存放區並設定環境設定
下一步是複製範例存放區,我們會在程式碼研究室的其餘部分參照這個存放區。假設您位於 Cloud Shell 中,請從主目錄執行下列指令:
git clone https://github.com/rominirani/yoga-poses-recommender-python
如要啟動編輯器,請點選 Cloud Shell 視窗工具列中的「開啟編輯器」。按一下左上角的選單列,然後選取「檔案」→「開啟資料夾」,如下所示:

選取 yoga-poses-recommender-python 資料夾,您應該會看到資料夾開啟,並顯示下列檔案,如下所示:

現在需要設定要使用的環境變數。按一下 config.template.yaml 檔案,您應該會看到如下所示的內容:
project_id: your-project-id
location: us-central1
gemini_model_name: gemini-1.5-flash-002
embedding_model_name: text-embedding-004
image_generation_model_name: imagen-3.0-fast-generate-002
database: (default)
collection: poses
test_collection: test-poses
top_k: "3"
請根據您建立 Google Cloud 專案和 Firestore 資料庫區域時選取的項目,更新 project_id 和 location 的值。理想情況下,Google Cloud 專案和 Firestore 資料庫的 location 值應相同,例如 us-central1。
在本程式碼研究室中,我們將使用預先設定的值 (當然,project_id 和 location 除外,您需要根據設定進行設定)。
請將這個檔案儲存為 config.yaml,並放在 config.template.yaml 檔案所在的資料夾中。
最後一個步驟是建立 Python 環境,我們將在本機使用這個環境,並設定所有 Python 依附元件。請查看包含相同詳細資料的 pyproject.toml 檔案,內容如下所示:
dependencies = [
"datasets>=3.2.0",
"flask>=3.1.0",
"google-cloud-aiplatform>=1.78.0",
"google-cloud-texttospeech>=2.24.0",
"langchain-community>=0.3.15",
"langchain-core>=0.3.31",
"langchain-google-community>=2.0.4",
"langchain-google-firestore>=0.5.0",
"langchain-google-vertexai>=2.0.7",
"pydantic-settings>=2.7.1",
"pyyaml>=6.0.2",
"tenacity>=9.0.0",
]
這些依附元件已在 requirements.txt. 中鎖定版本。總而言之,我們需要建立虛擬 Python 環境,並在虛擬環境中安裝 requirements.txt 中的 Python 套件依附元件。如要執行這項操作,請前往 Cloud Shell IDE 中的 Command Palette (Ctrl+Shift+P),然後輸入 Python: Create Environment。請按照下列步驟選取 Virtual Environment(venv)、Python 3.x interpreter 和 requirements.txt 檔案。
環境建立完成後,請執行下列指令來啟用建立的環境
source .venv/bin/activate
控制台中應該會顯示 (.venv)。例如:-> (.venv) yourusername@cloudshell:
太棒了!現在一切就緒,可以繼續設定 Firestore 資料庫。
3. 設定 Firestore
Cloud Firestore 是全代管的無伺服器文件資料庫,我們將用來做為應用程式資料的後端。Cloud Firestore 中的資料會以文件集合的形式建構。
初始化 Firestore 資料庫
前往 Cloud 控制台的 Firestore 頁面。
如果專案先前未初始化 Firestore 資料庫,請點選 Create Database 建立 default 資料庫。建立資料庫時,請使用下列值:
- Firestore 模式:
Native. - 將「位置類型」選為
Region,然後選取區域的us-central1位置。 - 在「安全性規則」部分,選擇
Test rules。 - 建立資料庫。

在下一節中,我們將為在預設 Firestore 資料庫中建立名為 poses 的集合奠定基礎。這個集合會保存範例資料 (文件) 或瑜珈姿勢資訊,我們稍後會在應用程式中使用這些資料。
這樣就完成 Firestore 資料庫的設定部分。
4. 準備瑜珈姿勢資料集
我們的第一項工作是準備應用程式要使用的 Yoga Poses 資料集。我們會先使用現有的 Hugging Face 資料集,然後加入額外資訊來強化資料集。
請參閱 Hugging Face 的瑜珈姿勢資料集。請注意,雖然本程式碼研究室使用其中一個資料集,但您其實可以使用任何其他資料集,並按照示範的相同技巧來強化資料集。

前往 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.py 檔案。這個檔案的內容如下所示:
import json
import time
import logging
import vertexai
from langchain_google_vertexai import VertexAI
from tenacity import retry, stop_after_attempt, wait_exponential
from settings import get_settings
settings = get_settings()
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
)
# Initialize Vertex AI SDK
vertexai.init(project=settings.project_id, location=settings.location)
logging.info("Done Initializing Vertex AI SDK")
@retry(
stop=stop_after_attempt(5),
wait=wait_exponential(multiplier=1, min=4, max=10),
)
def generate_description(pose_name, sanskrit_name, expertise_level, pose_types):
"""Generates a description for a yoga pose using the Gemini API."""
prompt = f"""
Generate a concise description (max 50 words) for the yoga pose: {pose_name}
Also known as: {sanskrit_name}
Expertise Level: {expertise_level}
Pose Type: {", ".join(pose_types)}
Include key benefits and any important alignment cues.
"""
try:
model = VertexAI(model_name=settings.gemini_model_name, verbose=True)
response = model.invoke(prompt)
return response
except Exception as e:
logging.info(f"Error generating description for {pose_name}: {e}")
return ""
def add_descriptions_to_json(input_file, output_file):
"""Loads JSON data, adds descriptions, and saves the updated data."""
with open(input_file, "r") as f:
yoga_poses = json.load(f)
total_poses = len(yoga_poses)
processed_count = 0
for pose in yoga_poses:
if pose["name"] != " Pose":
start_time = time.time() # Record start time
pose["description"] = generate_description(
pose["name"],
pose["sanskrit_name"],
pose["expertise_level"],
pose["pose_type"],
)
end_time = time.time() # Record end time
processed_count += 1
end_time = time.time() # Record end time
time_taken = end_time - start_time
logging.info(
f"Processed: {processed_count}/{total_poses} - {pose['name']} ({time_taken:.2f} seconds)"
)
else:
pose["description"] = ""
processed_count += 1
logging.info(
f"Processed: {processed_count}/{total_poses} - {pose['name']} ({time_taken:.2f} seconds)"
)
# Adding a delay to avoid rate limit
time.sleep(30)
with open(output_file, "w") as f:
json.dump(yoga_poses, f, indent=2)
def main():
# File paths
input_file = "./data/yoga_poses.json"
output_file = "./data/yoga_poses_with_descriptions.json"
# Add descriptions and save the updated JSON
add_descriptions_to_json(input_file, output_file)
if __name__ == "__main__":
main()
這個應用程式會在每個 Yoga 姿勢 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。 - 這個函式會叫用 generate_description 函式來建構提示詞,然後叫用 Langchain VertexAI 模型類別來取得回覆文字。
- 然後將這段回覆文字新增至 JSON 物件。
- 更新後的物件 JSON 清單隨即會寫入目的地檔案。
讓我們執行這個應用程式。啟動新的終端機視窗 (Ctrl+Shift+C),然後輸入下列指令:
python generate-descriptions.py
如果系統要求授權,請提供授權。
您會發現應用程式開始執行。為避免新 Google Cloud 帳戶可能出現任何速率限制配額,我們在記錄之間新增了 30 秒的延遲時間,請耐心等候。
下方顯示執行中的範例:

使用 Gemini 呼叫功能強化所有 3 筆記錄後,系統會生成 data/yoga_poses_with_description.json 檔案。你可以參考這篇文章。
現在我們已準備好資料檔案,下一步是瞭解如何使用該檔案填入 Firestore 資料庫,以及如何產生嵌入。
5. 將資料匯入 Firestore 並生成向量嵌入
我們已有 data/yoga_poses_with_description.json 檔案,現在需要使用該檔案填入 Firestore 資料庫,並為每筆記錄產生向量嵌入。稍後,當我們必須對使用者以自然語言提供的查詢執行相似度搜尋時,向量嵌入就會派上用場。
我們將使用 Langchain Firestore 元件實作上述程序。
具體操作步驟如下:
- 我們會將 JSON 物件清單轉換為 Langchain Document 物件清單。每個文件都會有兩個屬性:
page_content和metadata。中繼資料物件會包含整個 JSON 物件,其中具有name、description、sanskrit_name等屬性。page_content會是字串文字,由幾個欄位串連而成。 - 取得
Document物件清單後,我們會使用FirestoreVectorStoreLangchain 類別,並特別使用from_documents方法,搭配這份文件清單、集合名稱 (我們使用指向test-poses的TEST_COLLECTION變數)、Vertex AI Embedding 類別和 Firestore 連線詳細資料 (PROJECT_ID和DATABASE名稱)。這樣就會建立集合,並為每個屬性產生embedding欄位。
import-data.py 的程式碼如下所示 (為求簡潔,部分程式碼已截斷):
...
def create_langchain_documents(poses):
"""Creates a list of Langchain Documents from a list of poses."""
documents = []
for pose in poses:
# Convert the pose to a string representation for page_content
page_content = (
f"name: {pose.get('name', '')}\n"
f"description: {pose.get('description', '')}\n"
f"sanskrit_name: {pose.get('sanskrit_name', '')}\n"
f"expertise_level: {pose.get('expertise_level', 'N/A')}\n"
f"pose_type: {pose.get('pose_type', 'N/A')}\n"
).strip()
# The metadata will be the whole pose
metadata = pose
document = Document(page_content=page_content, metadata=metadata)
documents.append(document)
logging.info(f"Created {len(documents)} Langchain documents.")
return documents
def main():
all_poses = load_yoga_poses_data_from_local_file(
"./data/yoga_poses_with_descriptions.json"
)
documents = create_langchain_documents(all_poses)
logging.info(
f"Successfully created langchain documents. Total documents: {len(documents)}"
)
embedding = VertexAIEmbeddings(
model_name=settings.embedding_model_name,
project=settings.project_id,
location=settings.location,
)
client = firestore.Client(project=settings.project_id, database=settings.database)
vector_store = FirestoreVectorStore.from_documents(
client=client,
collection=settings.test_collection,
documents=documents,
embedding=embedding,
)
logging.info("Added documents to the vector store.")
if __name__ == "__main__":
main()
讓我們執行這個應用程式。啟動新的終端機視窗 (Ctrl+Shift+C),然後輸入下列指令:
python import-data.py
如果一切順利,您應該會看到類似以下的訊息:
2025-01-21 14:50:06,479 - INFO - Added documents to the vector store.
如要確認記錄是否已順利插入,以及是否已產生嵌入內容,請前往 Cloud 控制台的 Firestore 頁面。

按一下 (預設) 資料庫,這時應該會顯示 test-poses 集合,以及該集合下的多個文件。每份文件都是一個瑜珈姿勢。

點選任一文件即可調查欄位。除了匯入的欄位,您也會看到 embedding 欄位,這是透過我們使用的 Langchain VertexAIEmbeddings 類別自動產生的向量欄位,其中我們提供了 text-embedding-004 Vertex AI 嵌入模型。

現在記錄已上傳至 Firestore 資料庫,且嵌入項目也已就位,我們可以前往下一個步驟,瞭解如何在 Firestore 中執行向量相似度搜尋。
6. 將完整瑜珈姿勢匯入 Firestore 資料庫集合
現在我們要建立 poses 集合,其中包含 160 個瑜珈姿勢的完整清單。我們已產生資料庫匯入檔案,您可以直接匯入。這是為了節省實驗室時間。產生包含說明和嵌入內容的資料庫時,所用的程序與上一節所述相同。
請按照下列步驟匯入資料庫:
- 使用下列
gsutil指令,在專案中建立 bucket。在下方指令中,將<PROJECT_ID>變數替換為您的 Google Cloud 專案 ID。
gsutil mb -l us-central1 gs://<PROJECT_ID>-my-bucket
- 建立好 bucket 後,我們需要將準備好的資料庫匯出內容複製到這個 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.py 檔案。要查看的主要函式是搜尋函式,如下所示。從高層次來看,這會建立嵌入類別,用於產生使用者查詢的嵌入內容。接著,它會使用 FirestoreVectorStore 類別叫用 similarity_search 函式。
def search(query: str):
"""Executes Firestore Vector Similarity Search"""
embedding = VertexAIEmbeddings(
model_name=settings.embedding_model_name,
project=settings.project_id,
location=settings.location,
)
client = firestore.Client(project=settings.project_id, database=settings.database)
vector_store = FirestoreVectorStore(
client=client, collection=settings.collection, embedding_service=embedding
)
logging.info(f"Now executing query: {query}")
results: list[Document] = vector_store.similarity_search(
query=query, k=int(settings.top_k), include_metadata=True
)
for result in results:
print(result.page_content)
使用幾個查詢範例執行這項作業前,請先產生 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
清單中應該會顯示您剛建立的索引。
現在請試試下列指令:
python search-data.py --prompt "Recommend me some exercises for back pain relief"
系統會提供幾項建議。以下是執行範例:
2025-01-21 15:48:51,282 - INFO - Now executing query: Recommend me some exercises for back pain relief
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 the spine.
sanskrit_name: Supta Matsyendrasana
expertise_level: Beginner
pose_type: ['Supine', 'Twist']
name: Cow Pose
description: Cow Pose (Bitilasana) is a gentle backbend, stretching the chest, shoulders, and abdomen. Maintain a neutral spine, lengthen the tailbone, and avoid hyperextension. Benefits include improved posture and stress relief.
sanskrit_name: Bitilasana
expertise_level: Beginner
pose_type: ['Arm Leg Support', 'Back Bend']
name: Locust I Pose
description: Locust Pose I (Shalabhasana A) strengthens the back, glutes, and shoulders. Lie prone, lift chest and legs simultaneously, engaging back muscles. Keep hips grounded and gaze slightly forward.
sanskrit_name: Shalabhasana A
expertise_level: Intermediate
pose_type: ['Prone', 'Back Bend']
完成上述步驟後,您就瞭解如何使用 Firestore 向量資料庫上傳記錄、產生嵌入內容,以及執行向量相似度搜尋。現在可以建立網頁應用程式,將向量搜尋整合至網路前端。
8. 網頁應用程式
Python Flask 網頁應用程式位於 main.py 檔案中,前端 HTML 檔案則位於 templates/index.html.
建議您查看這兩個檔案。首先,請從包含 /search 處理常式的 main.py 檔案開始,該處理常式會接收從前端 HTML index.html 檔案傳遞的提示。接著,這會叫用搜尋方法,執行上一節中介紹的向量相似度搜尋。
接著,系統會將回應連同建議清單傳回給 index.html。index.html 接著會以不同資訊卡的形式顯示建議。
在本機執行應用程式
啟動新的終端機視窗 (Ctrl+Shift+C) 或任何現有的終端機視窗,然後輸入下列指令:
python main.py
執行範例如下所示:
* Serving Flask app 'main'
* Debug mode: on
2025-01-21 16:02:37,473 - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:8080
* Running on http://10.88.0.4:8080
2025-01-21 16:02:37,473 - INFO - Press CTRL+C to quit
2025-01-21 16:02:37,474 - INFO - * Restarting with stat
2025-01-21 16:02:41,462 - WARNING - * Debugger is active!
2025-01-21 16:02:41,484 - INFO - * Debugger PIN: 440-653-555
啟動並執行後,按一下下方顯示的「網頁預覽」按鈕,即可前往應用程式的首頁網址:

如下所示,畫面應會顯示所提供的 index.html 檔案:

提供範例查詢 (例如:Provide me some exercises for back pain relief),然後按一下「Search」按鈕。這應該會從資料庫擷取一些建議。你也會看到 Play Audio 按鈕,點選後系統會根據說明生成音訊串流,你可以直接聆聽。

9. (選用) 部署至 Google Cloud Run
最後一個步驟是將這個應用程式部署至 Google Cloud Run。部署指令如下所示,請務必先將變數 (<<YOUR_PROJECT_ID>>) 的值替換成專案專屬的值,再進行部署。這些值可從 config.yaml 檔案擷取。
gcloud run deploy yogaposes --source . \
--port=8080 \
--allow-unauthenticated \
--region=us-central1 \
--platform=managed \
--project=<<YOUR_PROJECT_ID>> \
--env-vars-file=config.yaml
從應用程式的根資料夾執行上述指令。系統也可能會要求您啟用 Google Cloud API,並確認各種權限,請按照指示操作。
部署程序大約需要 5 到 7 分鐘才能完成,請耐心等候。

成功部署後,部署輸出內容會提供 Cloud Run 服務網址。這是測試。
Service URL: https://yogaposes-<<UNIQUEID>.us-central1.run.app
前往該公開網址,您應該會看到已部署且成功執行的相同網頁應用程式。

您也可以從 Google Cloud 控制台前往 Cloud Run,查看 Cloud Run 中的服務清單。yogaposes 服務應是列於該處的服務之一 (如果不是唯一服務)。

按一下特定服務名稱 (在本例中為 yogaposes),即可查看服務詳細資料,例如網址、設定、記錄等。

這樣就完成了在 Cloud Run 上開發及部署瑜珈姿勢建議網頁應用程式。
10. 恭喜
恭喜!您已成功建構應用程式,可將資料集上傳至 Firestore、產生嵌入項目,並根據使用者查詢執行向量相似度搜尋。