1. 簡介
在本程式碼研究室中,您將建構應用程式,使用向量搜尋功能推薦瑜珈姿勢。
您將透過本程式碼研究室,按照以下步驟操作:
- 使用現有的 Hugging Face 資料集 (JSON 格式) 中的瑜珈姿勢。
- 透過額外的欄位說明強化資料集,使用 Gemini 為每個姿勢產生說明。
- 使用 Langchain 建立文件,並使用 Firestore Langchain 整合功能在 Firestore 中建立集合和嵌入資料。
- 在 Firestore 中建立複合式索引,以便進行向量搜尋。
- 在 Flask 應用程式中使用向量搜尋,將所有內容整合在一起,如下所示:
執行步驟
- 設計、建構及部署採用 Vector Search 推薦瑜伽姿勢的網頁應用程式。
課程內容
- 如何使用 Gemini 產生文字內容,並在本程式碼研究室的內容中產生瑜珈姿勢的說明
- 如何使用 Firestore 專用 Langchain 文件載入器,將 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 視窗工具列中的「Open Editor」(開啟編輯器)。按一下左上角的選單列,然後依序選取「檔案」→「開啟資料夾」,如下所示:
選取 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.
- 位置:使用預設位置設定。
- 如要使用安全性規則,請選擇
Test rules
。 - 建立資料庫。
在下一節中,我們將在預設的 Firestore 資料庫中建立名為 poses
的集合。這個集合會儲存範例資料 (文件) 或瑜珈姿勢資訊,以便我們在應用程式中使用。
這就完成了 Firestore 資料庫設定的部分。
4. 準備瑜珈姿勢資料集
我們的第一個任務是準備要用於應用程式的瑜珈姿勢資料集。我們將從現有的 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()
這個應用程式會在每個瑜珈姿勢 JSON 記錄中新增 description
欄位。系統會透過呼叫 Gemini 模型來取得說明,並在該模型中提供必要的提示。欄位會新增至 JSON 檔案,新檔案會寫入 data/yoga_poses_with_descriptions.json
檔案。
以下是主要步驟:
- 在
main()
函式中,您會發現它會叫用add_descriptions_to_json
函式,並提供預期的輸入檔案和輸出檔案。 add_descriptions_to_json
函式會針對每個 JSON 記錄 (即 Yoga 貼文資訊) 執行下列操作:- 它會擷取
pose_name
、sanskrit_name
、expertise_level
和pose_types
。 - 這項函式會叫用 generate_description 函式,藉此建構提示,然後叫用 Langchain VertexAI 模型類別來取得回應文字。
- 然後將此回應文字新增至 JSON 物件。
- 然後將更新後的 JSON 物件清單寫入目的檔案。
讓我們執行這個應用程式。啟動新的終端機視窗 (Ctrl + Shift + C),然後輸入下列指令:
python generate-descriptions.py
如果系統要求你提供任何授權,請提供相關資訊。
您會發現應用程式開始執行。我們已在記錄之間加入 30 秒的延遲時間,以免新 Google Cloud 帳戶出現任何速率限制配額,因此請耐心等候。
以下是進行中的執行範例:
所有 3 筆記錄都已透過 Gemini 呼叫進行強化後,系統就會產生檔案 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
物件的清單後,我們會使用FirestoreVectorStore
Langchain 類別,特別是from_documents
方法,搭配這個文件清單、集合名稱 (我們使用指向test-poses
的TEST_COLLECTION
變數)、Vertex AI 嵌入類別和 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
指令,在專案中建立值區。請將下方指令中的<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.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、產生嵌入項目,並根據使用者查詢執行向量相似度搜尋。