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

1. บทนำ

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

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

  1. ใช้ชุดข้อมูลท่าโยคะของ Hugging Face ที่มีอยู่ (รูปแบบ JSON)
  2. ปรับปรุงชุดข้อมูลด้วยคำอธิบายฟิลด์เพิ่มเติมที่ใช้ Gemini เพื่อสร้างคำอธิบายสำหรับท่าทางแต่ละท่า
  3. ใช้ Langchain เพื่อสร้างเอกสาร ใช้การผสานรวม Firestore Langchain เพื่อสร้างคอลเล็กชันและการฝังใน 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
  • โปรเจ็กต์ Cloud ที่เปิดใช้การเรียกเก็บเงิน

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

สำหรับ Codelab นี้ เราจะใช้ค่าที่กำหนดค่าไว้ล่วงหน้า (ยกเว้น 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 ทำตามขั้นตอน 2-3 ขั้นตอนถัดไปเพื่อเลือกไฟล์ 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 ใน Cloud Console

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

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

61d0277510803c8d.png

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

เพียงเท่านี้ก็ตั้งค่าฐานข้อมูล Firestore เสร็จเรียบร้อย

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

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

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

298cfae7f23e4bef.png

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

3fe6e55abdc032ec.png

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

ไปที่ไฟล์ data/yoga_poses.json ใน Cloud Shell Editor แล้วดูรายการออบเจ็กต์ 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 Editor ให้ไปที่ไฟล์ 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 และที่สำคัญคือสร้างการฝังเวกเตอร์สำหรับแต่ละระเบียน Vector Embeddings จะมีประโยชน์ในภายหลังเมื่อเราต้องทำการค้นหาความคล้ายกันใน Vector Embeddings ด้วยคำค้นหาของผู้ใช้ที่ระบุในภาษาธรรมชาติ

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

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

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

504cabdb99a222a5.png

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

d0708499e403aebc.png

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

d67113e2dc63cd6b.png

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

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

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

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

  1. สร้าง Bucket ในโปรเจ็กต์ด้วยคำสั่ง gsutil ที่ระบุไว้ด้านล่าง แทนที่ตัวแปร <PROJECT_ID> ในคำสั่งด้านล่างด้วยรหัสโปรเจ็กต์ Google Cloud
gsutil mb -l us-central1 gs://<PROJECT_ID>-my-bucket
  1. เมื่อสร้าง 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) ที่เราสร้างขึ้นได้

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

การนำเข้าจะใช้เวลาไม่กี่วินาที และเมื่อพร้อมแล้ว คุณจะตรวจสอบฐานข้อมูล 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 ซึ่งรับพรอมต์ที่ส่งจากไฟล์ index.html 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 สร้างการฝัง และทำการค้นหาความคล้ายกันของเวกเตอร์ตามคำค้นหาของผู้ใช้ได้สำเร็จ

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