Codelab - สร้างแอปแนะนำท่าโยคะตามบริบทด้วย Firestore, Vector Search, Langchain และ Gemini (เวอร์ชัน Python)

1. บทนำ

ในโค้ดแล็บนี้ คุณจะได้สร้างแอปพลิเคชันที่ใช้การค้นหาเวกเตอร์เพื่อแนะนำท่าโยคะ

คุณจะใช้แนวทางแบบทีละขั้นตอนต่อไปนี้ผ่าน Codelab

  1. ใช้ชุดข้อมูลท่าโยคะ (รูปแบบ JSON) ที่มีอยู่ของ Hugging Face
  2. ปรับปรุงชุดข้อมูลด้วยคำอธิบายช่องเพิ่มเติมที่ใช้ Gemini เพื่อสร้างคำอธิบายสำหรับท่าทางแต่ละท่า
  3. ใช้ Langchain เพื่อสร้างเอกสาร ใช้การผสานรวม Langchain ของ Firestore เพื่อสร้างคอลเล็กชันและการฝังใน Firestore
  4. สร้างดัชนีผสมใน Firestore เพื่ออนุญาตการค้นหาเวกเตอร์
  5. ใช้การค้นหาเวกเตอร์ในแอปพลิเคชัน Flask ที่รวมทุกอย่างเข้าด้วยกันดังที่แสดงด้านล่าง

84e1cbf29cbaeedc.png

สิ่งที่ต้องทำ

  • ออกแบบ สร้าง และทำให้เว็บแอปพลิเคชันที่ใช้การค้นหาเวกเตอร์เพื่อแนะนำท่าโยคะใช้งานได้

สิ่งที่คุณจะได้เรียนรู้

  • วิธีใช้ Gemini เพื่อสร้างเนื้อหาข้อความและสร้างคำอธิบายสำหรับท่าโยคะในบริบทของโค้ดแล็บนี้
  • วิธีใช้ Langchain Document Loader สำหรับ Firestore เพื่อโหลดระเบียนจากชุดข้อมูลที่ปรับปรุงแล้วจาก Hugging Face ลงใน Firestore พร้อมกับการฝังเวกเตอร์
  • วิธีใช้ Langchain Vector Store สำหรับ Firestore เพื่อค้นหาข้อมูลตามการค้นหาด้วยภาษาที่เป็นธรรมชาติ
  • วิธีใช้ Google Cloud Text to Speech API เพื่อสร้างเนื้อหาเสียง

สิ่งที่ต้องมี

  • เว็บเบราว์เซอร์ Chrome
  • บัญชี Gmail
  • โปรเจ็กต์ที่อยู่ในระบบคลาวด์ที่เปิดใช้การเรียกเก็บเงิน

Codelab นี้ออกแบบมาสำหรับนักพัฒนาซอฟต์แวร์ทุกระดับ (รวมถึงผู้เริ่มต้น) โดยใช้ Python ในแอปพลิเคชันตัวอย่าง อย่างไรก็ตาม คุณไม่จำเป็นต้องมีความรู้เกี่ยวกับ Python เพื่อทําความเข้าใจแนวคิดที่นำเสนอ

2. ก่อนเริ่มต้น

สร้างโปรเจ็กต์

  1. ในคอนโซล Google Cloud ให้เลือกหรือสร้างโปรเจ็กต์ Google Cloud ในหน้าตัวเลือกโปรเจ็กต์
  2. ตรวจสอบว่าเปิดใช้การเรียกเก็บเงินสำหรับโปรเจ็กต์ Cloud แล้ว ดูวิธีตรวจสอบว่าเปิดใช้การเรียกเก็บเงินในโปรเจ็กต์หรือไม่
  3. คุณจะใช้ Cloud Shell ซึ่งเป็นสภาพแวดล้อมบรรทัดคำสั่งที่ทำงานใน Google Cloud และโหลด bq ไว้ล่วงหน้า คลิก "เปิดใช้งาน Cloud Shell" ที่ด้านบนของคอนโซล Google Cloud

รูปภาพปุ่มเปิดใช้งาน Cloud Shell

  1. เมื่อเชื่อมต่อกับ Cloud Shell แล้ว ให้ตรวจสอบว่าคุณได้รับการตรวจสอบสิทธิ์แล้วและโปรเจ็กต์ได้รับการตั้งค่าเป็นรหัสโปรเจ็กต์ของคุณโดยใช้คําสั่งต่อไปนี้
gcloud auth list
  1. เรียกใช้คำสั่งต่อไปนี้ใน Cloud Shell เพื่อยืนยันว่าคำสั่ง gcloud รู้จักโปรเจ็กต์ของคุณ
gcloud config list project
  1. หากยังไม่ได้ตั้งค่าโปรเจ็กต์ ให้ใช้คําสั่งต่อไปนี้เพื่อตั้งค่า
gcloud config set project <YOUR_PROJECT_ID>
  1. เปิดใช้ 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 คลิกแถบเมนูที่มุมซ้ายบน แล้วเลือก "ไฟล์" → "เปิดโฟลเดอร์" ดังที่แสดงด้านล่าง

66221fd0d0e5202f.png

เลือกโฟลเดอร์ yoga-poses-recommender-python แล้วคุณควรเห็นโฟลเดอร์เปิดขึ้นพร้อมไฟล์ต่อไปนี้ดังที่แสดงด้านล่าง

44699efc7fb1b911.png

ตอนนี้เราต้องตั้งค่าตัวแปรสภาพแวดล้อมที่จะใช้ คลิกไฟล์ 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"

โปรดอัปเดตค่าสำหรับ project_id และ location ตามที่คุณเลือกไว้ขณะสร้างโปรเจ็กต์ Google Cloud และภูมิภาคฐานข้อมูล Firestore เราต้องการให้ค่าของ location เหมือนกันสำหรับโปรเจ็กต์ Google Cloud และฐานข้อมูล Firestore เช่น 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 ที่มีไลบรารีของแพ็กเกจ Python ใน requirements.txt เพื่อติดตั้งในสภาพแวดล้อมเสมือน โดยไปที่ Command Palette (Ctrl+Shift+P) ใน Cloud Shell IDE แล้วพิมพ์ 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 คือฐานข้อมูลเอกสารแบบ Serverless ที่มีการจัดการครบวงจรซึ่งเราจะใช้เป็นแบ็กเอนด์สำหรับข้อมูลแอปพลิเคชัน ข้อมูลใน Cloud Firestore มีโครงสร้างเป็นคอลเล็กชันของเอกสาร

การเริ่มต้นฐานข้อมูล Firestore

ไปที่หน้า Firestore ในคอนโซลระบบคลาวด์

หากคุณไม่เคยเริ่มต้นฐานข้อมูล Firestore ในโปรเจ็กต์มาก่อน ให้สร้างฐานข้อมูล default โดยคลิก Create Database ในระหว่างการสร้างฐานข้อมูล ให้ใช้ค่าต่อไปนี้

  • โหมด Firestore: Native.
  • ตำแหน่ง: ใช้การตั้งค่าตำแหน่งเริ่มต้น
  • สําหรับกฎความปลอดภัย ให้เลือก Test rules
  • สร้างฐานข้อมูล

504cabdb99a222a5.png

ในส่วนถัดไป เราจะวางรากฐานสําหรับการสร้างคอลเล็กชันชื่อ poses ในฐานข้อมูล Firestore เริ่มต้น คอลเล็กชันนี้จะเก็บข้อมูลตัวอย่าง (เอกสาร) หรือข้อมูลท่าโยคะ ซึ่งเราจะนำไปใช้ในแอปพลิเคชัน

ในส่วนการตั้งค่าฐานข้อมูล Firestore เสร็จสมบูรณ์แล้ว

4. เตรียมชุดข้อมูลท่าโยคะ

งานแรกของเราคือเตรียมชุดข้อมูลท่าโยคะที่จะใช้กับแอปพลิเคชัน เราจะเริ่มจากชุดข้อมูล Hugging Face ที่มีอยู่ แล้วปรับปรุงด้วยข้อมูลเพิ่มเติม

ดูชุดข้อมูล Hugging Face สำหรับท่าโยคะ โปรดทราบว่าแม้ว่าโค้ดแล็บนี้จะใช้ชุดข้อมูลชุดใดชุดหนึ่ง แต่คุณก็ใช้ชุดข้อมูลอื่นๆ และทำตามเทคนิคเดียวกันที่แสดงเพื่อปรับปรุงชุดข้อมูลได้

298cfae7f23e4bef.png

หากไปที่ส่วน Files and versions เราจะดูไฟล์ข้อมูล JSON สำหรับท่าทางทั้งหมดได้

3fe6e55abdc032ec.png

เราได้ดาวน์โหลด yoga_poses.json และส่งไฟล์ดังกล่าวให้คุณแล้ว ไฟล์นี้มีชื่อว่า yoga_poses_alldata.json และอยู่ในโฟลเดอร์ /data

ไปที่ไฟล์ data/yoga_poses.json ในเครื่องมือแก้ไข Cloud Shell แล้วดูรายการออบเจ็กต์ 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()

แอปพลิเคชันนี้จะเพิ่มช่อง description ใหม่ลงในระเบียน JSON ของท่าโยคะแต่ละรายการ โดยระบบจะรับคำอธิบายผ่านการเรียกใช้โมเดล Gemini ซึ่งเราจะให้พรอมต์ที่จำเป็นแก่โมเดล ระบบจะเพิ่มช่องลงในไฟล์ JSON และเขียนไฟล์ใหม่ลงในไฟล์ data/yoga_poses_with_descriptions.json

มาดูขั้นตอนหลักๆ กัน

  1. ในฟังก์ชัน main() คุณจะเห็นว่ามันเรียกใช้ฟังก์ชัน add_descriptions_to_json และให้ไฟล์อินพุตและไฟล์เอาต์พุตตามที่คาดไว้
  2. ฟังก์ชัน add_descriptions_to_json จะทำสิ่งต่อไปนี้กับแต่ละระเบียน JSON เช่น ข้อมูลโพสต์เกี่ยวกับโยคะ
  3. โดยจะดึงข้อมูล pose_name, sanskrit_name, expertise_level และ pose_types ออกมา
  4. โดยจะเรียกใช้ฟังก์ชัน generate_description ที่สร้างพรอมต์ จากนั้นเรียกใช้คลาสโมเดล Langchain VertexAI เพื่อรับข้อความตอบกลับ
  5. จากนั้นระบบจะเพิ่มข้อความตอบกลับนี้ลงในออบเจ็กต์ JSON
  6. จากนั้นระบบจะเขียนรายการออบเจ็กต์ JSON ที่อัปเดตแล้วลงในไฟล์ปลายทาง

เรามาเรียกใช้แอปพลิเคชันนี้กัน เปิดหน้าต่างเทอร์มินัลใหม่ (Ctrl+Shift+C) แล้วป้อนคำสั่งต่อไปนี้

python generate-descriptions.py

หากระบบขอสิทธิ์ ให้ดำเนินการตามคำขอ

คุณจะเห็นแอปพลิเคชันเริ่มทำงาน เราได้เพิ่มการหน่วงเวลา 30 วินาทีระหว่างระเบียนเพื่อหลีกเลี่ยงโควต้าการจำกัดอัตราที่อาจอยู่ในบัญชี Google Cloud ใหม่ ดังนั้นโปรดอดทนรอ

ตัวอย่างการเรียกใช้ที่อยู่ระหว่างดำเนินการแสดงอยู่ด้านล่าง

8e830d9ea9b6c60.png

เมื่อเพิ่มประสิทธิภาพระเบียนทั้ง 3 รายการด้วยคอล Gemini แล้ว ระบบจะสร้างไฟล์ data/yoga_poses_with_description.json คุณดูข้อมูลดังกล่าวได้

ตอนนี้เราพร้อมใช้งานไฟล์ข้อมูลแล้ว ขั้นตอนถัดไปคือทำความเข้าใจวิธีป้อนข้อมูลไฟล์ลงในฐานข้อมูล Firestore พร้อมกับการสร้างการฝัง

5. นําเข้าข้อมูลไปยัง Firestore และสร้างการฝังเวกเตอร์

เรามีไฟล์ data/yoga_poses_with_description.json แล้ว ตอนนี้ต้องป้อนข้อมูลไฟล์ลงในฐานข้อมูล Firestore และที่สำคัญคือสร้างการฝังเวกเตอร์สำหรับแต่ละระเบียน การฝังเวกเตอร์จะมีประโยชน์ในภายหลังเมื่อเราต้องทำการค้นหาความคล้ายคลึงกับคำค้นหาของผู้ใช้ที่ระบุเป็นภาษาธรรมชาติ

เราจะใช้คอมโพเนนต์ Langchain Firestore เพื่อใช้กระบวนการข้างต้น

ขั้นตอนในการดำเนินการมีดังนี้

  1. เราจะแปลงรายการออบเจ็กต์ JSON เป็นรายการออบเจ็กต์ Langchain Document เอกสารแต่ละรายการจะมีแอตทริบิวต์ 2 รายการ ได้แก่ page_content และ metadata ออบเจ็กต์ข้อมูลเมตาจะมีออบเจ็กต์ JSON ทั้งหมดที่มีแอตทริบิวต์ เช่น name, description, sanskrit_name เป็นต้น page_content จะเป็นสตริงข้อความที่ต่อเชื่อมช่องต่างๆ
  2. เมื่อเรามีรายการออบเจ็กต์ Document แล้ว เราจะใช้คลาส Langchain FirestoreVectorStore และโดยเฉพาะเมธอด from_documents กับรายการเอกสารนี้ ชื่อคอลเล็กชัน (เราใช้ตัวแปร TEST_COLLECTION ที่ชี้ไปยัง test-poses) คลาสการฝัง 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.

หากต้องการตรวจสอบว่าได้แทรกระเบียนและสร้างการฝังเรียบร้อยแล้วหรือไม่ ให้ไปที่หน้า Firestore ในคอนโซลระบบคลาวด์

504cabdb99a222a5.png

คลิกฐานข้อมูล (เริ่มต้น) ซึ่งจะแสดงคอลเล็กชัน test-poses และเอกสารหลายรายการในคอลเล็กชันนั้น เอกสารแต่ละรายการคือท่าโยคะ 1 ท่า

d0708499e403aebc.png

คลิกเอกสารใดก็ได้เพื่อตรวจสอบช่อง นอกจากช่องที่เรานําเข้าแล้ว คุณจะเห็นช่อง embedding ซึ่งเป็นช่องเวกเตอร์ที่สร้างขึ้นโดยอัตโนมัติสําหรับคุณผ่านคลาส Langchain VertexAIEmbeddings ที่เราใช้ ซึ่งเราได้ระบุโมเดลการฝัง Vertex AI text-embedding-004

d67113e2dc63cd6b.png

เมื่ออัปโหลดระเบียนลงในฐานข้อมูล Firestore พร้อมการฝังแล้ว เราจึงไปยังขั้นตอนถัดไปเพื่อดูวิธีทําการค้นหาความคล้ายคลึงของเวกเตอร์ใน Firestore

6. นําเข้าท่าโยคะทั้งหมดไปยังคอลเล็กชันฐานข้อมูล Firestore

ตอนนี้เราจะสร้างคอลเล็กชัน poses ซึ่งเป็นรายการท่าโยคะทั้งหมด 160 ท่า ซึ่งเราได้สร้างไฟล์การนำเข้าฐานข้อมูลที่คุณสามารถนำเข้าได้โดยตรง การดำเนินการนี้ช่วยประหยัดเวลาในแล็บ กระบวนการสร้างฐานข้อมูลที่มีคําอธิบายและการฝังนั้นเหมือนกับที่เราเห็นในส่วนก่อนหน้า

นําเข้าฐานข้อมูลโดยทําตามขั้นตอนด้านล่าง

  1. สร้างที่เก็บข้อมูลในโปรเจ็กต์ด้วยคำสั่ง gsutil ที่ระบุไว้ด้านล่าง แทนที่ตัวแปร <PROJECT_ID> ในคําสั่งด้านล่างด้วยรหัสโปรเจ็กต์ Google Cloud
gsutil mb -l us-central1 gs://<PROJECT_ID>-my-bucket
  1. เมื่อสร้างที่เก็บข้อมูลแล้ว เราจะต้องคัดลอกการส่งออกฐานข้อมูลที่เตรียมไว้ไปยังที่เก็บข้อมูลนี้ก่อนจึงจะนําเข้าไปยังฐานข้อมูล Firebase ได้ ใช้คำสั่งด้านล่าง
gsutil cp -r gs://yoga-database-firestore-export-bucket/2025-01-27T05:11:02_62615  gs://<PROJECT_ID>-my-bucket

เมื่อเรามีข้อมูลที่จะนําเข้าแล้ว เราจึงไปยังขั้นตอนสุดท้ายของการนําเข้าข้อมูลไปยังฐานข้อมูล Firebase (default) ที่เราสร้างขึ้น

  1. ใช้คำสั่ง gcloud ที่ระบุไว้ด้านล่าง
gcloud firestore import gs://<PROJECT_ID>-my-bucket/2025-01-27T05:11:02_62615

การนําเข้าจะใช้เวลา 2-3 วินาที เมื่อพร้อมแล้ว คุณสามารถตรวจสอบฐานข้อมูล Firestore และคอลเล็กชันได้โดยไปที่ https://console.cloud.google.com/firestore/databases เลือกฐานข้อมูล default และคอลเล็กชัน poses ดังที่แสดงด้านล่าง

a8f5a6ba69bec69b.png

การสร้างคอลเล็กชัน 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)

ก่อนเรียกใช้ตัวอย่างการค้นหา 2-3 รายการ คุณต้องสร้างดัชนีคอมโพสิท 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"

คุณควรได้รับคําแนะนํา 2-3 รายการ ตัวอย่างการเรียกใช้แสดงอยู่ด้านล่าง

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.

เราขอแนะนำให้คุณดูทั้ง 2 ไฟล์ เริ่มต้นด้วยไฟล์ main.py ที่มีตัวแฮนเดิล /search ซึ่งจะใช้พรอมต์ที่ส่งผ่านจากไฟล์ 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

เมื่อแอปพลิเคชันพร้อมใช้งานแล้ว ให้ไปที่ URL หน้าแรกของแอปพลิเคชันโดยคลิกปุ่มตัวอย่างเว็บที่แสดงด้านล่าง

de297d4cee10e0bf.png

ซึ่งควรแสดงไฟล์ index.html ที่แสดงดังที่แสดงด้านล่าง

20240a0e885ac17b.png

ระบุคำค้นหาตัวอย่าง (เช่น Provide me some exercises for back pain relief) แล้วคลิกปุ่ม Search ซึ่งจะดึงข้อมูลคําแนะนําบางส่วนจากฐานข้อมูล นอกจากนี้ คุณจะเห็นปุ่ม Play Audio ซึ่งจะสร้างสตรีมเสียงตามคำอธิบาย ซึ่งคุณจะฟังได้โดยตรง

789b4277dc40e2be.png

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 นาที จึงโปรดอดทนรอ

3a6d86fd32e4a5e.png

เมื่อทําให้ใช้งานได้สําเร็จ เอาต์พุตของการสร้างจะแสดง URL ของบริการ Cloud Run โดยจะมีรูปแบบดังนี้

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

โปรดไปที่ URL สาธารณะดังกล่าว แล้วคุณจะเห็นเว็บแอปพลิเคชันเดียวกันนี้ที่ติดตั้งใช้งานและทํางานสําเร็จ

84e1cbf29cbaeedc.png

นอกจากนี้ คุณยังไปที่ Cloud Run จากคอนโซล Google Cloud ได้ด้วย และจะเห็นรายการบริการใน Cloud Run บริการ yogaposes ควรเป็นหนึ่งในบริการ (หากไม่ใช่บริการเดียว) ที่แสดงอยู่ในนั้น

f2b34a8c9011be4c.png

คุณสามารถดูรายละเอียดของบริการ เช่น URL, การกําหนดค่า, บันทึก และอื่นๆ ได้โดยคลิกที่ชื่อบริการที่ต้องการ (ในกรณีนี้คือ yogaposes)

faaa5e0c02fe0423.png

การดำเนินการนี้ถือเป็นการพัฒนาและทำให้เว็บแอปพลิเคชันแนะนำท่าโยคะใน Cloud Run ใช้งานได้อย่างสมบูรณ์

10. ขอแสดงความยินดี

ยินดีด้วย คุณสร้างแอปพลิเคชันที่อัปโหลดชุดข้อมูลไปยัง Firestore, สร้างการฝัง และทำการค้นหาความคล้ายคลึงของเวกเตอร์ตามการค้นหาของผู้ใช้เรียบร้อยแล้ว

เอกสารอ้างอิง