iT邦幫忙

2026 iThome 鐵人賽

DAY 7
0
Build on Google AI

將考國際證照的應用程式變成開源系列 第 8

使用 Vector Search,以 Firestore 為代理建立基準

  • 分享至 

  • xImage
  •  

https://github.com/alan-nanpie/coffee-barista-agent/tree/main

在實際的生產環境中,將菜單寫死在本地的 menu.json 檔案中並不是理想的做法,因為只要菜單有任何更動,你就必須重新建立並部署整個應用程式。

為了解決這個問題,我們可以加入 選用:使用 Vector Search,以 Firestore 為代理建立基準 的進階挑戰。這個步驟會將菜單資料遷移至雲端資料庫 Cloud Firestore,並運用 Vector Search(向量搜尋) 技術,透過語意相似度讓 AI 精準撈取最相關的品項。

以下是結合 Firestore 向量搜尋的完整 CLI 建置步驟與詳細中文說明:


1. 啟用 Firestore API 並初始化資料庫

首先,我們需要啟用 Firestore 服務,並建立一個名為 coffee-menu 的原生模式資料庫。

# 1. 啟用 Firestore API
gcloud services enable firestore.googleapis.com

# 2. 建立資料庫 (請確保您已設定 $REGION 變數,例如 export REGION="asia-east1")
gcloud firestore databases create --database="coffee-menu" --location=$REGION

💡 提示:啟用 API 後可能需要 1~2 分鐘才會生效。如果出現錯誤,請稍候再試或輸入 Y 繼續。

2. 將菜單資料寫入 Firestore (資料播種 Seed)

我們需要寫一支 Python 腳本,將原本 menu.json 的資料轉換成向量(Embeddings)並存入 Firestore。

# 1. 安裝 Firestore 與 GenAI 的 Python 用戶端套件
pip3 install google-cloud-firestore==2.27.0 google-genai==2.11.0

# 2. 建立 seed.py 檔案
cat << 'EOF' > seed.py
import json
import os
from google import genai
from google.cloud import firestore
from google.cloud.firestore_v1.vector import Vector

db = firestore.Client(database="coffee-menu")
client = genai.Client(
    vertexai=True,
    project=os.environ.get("PROJECT_ID"),
    location=os.environ.get("REGION", "us-central1")
)

with open("menu.json", "r") as f:
    menu_items = json.load(f)

for item in menu_items:
    # 使用名稱做為文件 ID
    doc_id = item["name"].lower().replace(" ", "-")
    
    # 使用 Vertex AI text-embedding-004 模型生成文字向量 (Embedding)
    text_to_embed = f"{item['name']}: {item['description']}"
    response = client.models.embed_content(
        model="text-embedding-004",
        contents=text_to_embed,
    )
    embedding = response.embeddings[0].values
    
    # 將向量資料加入菜單項目中
    item["embedding"] = Vector(embedding)
    db.collection("menu").document(doc_id).set(item)

print("Firestore menu collection seeded with vector embeddings successfully!")
EOF

# 3. 執行播種腳本
python3 seed.py

3. 建立 Firestore 向量索引 (Vector Index)

為了讓資料庫能進行向量搜尋,必須針對 embedding 欄位建立複合向量索引。

gcloud firestore indexes composite create \
  --collection-group=menu \
  --query-scope=COLLECTION \
  --database="coffee-menu" \
  --field-config=field-path=embedding,vector-config='{"dimension":"768", "flat": "{}"}'

💡 提示:這個索引會在背景建立,可能需要幾分鐘的時間,你可以直接進行下一步。

4. 授予服務帳戶存取權

你的 Cloud Run 應用程式需要有權限讀取 Firestore,因此要賦予其服務帳戶 datastore.user 的角色。

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:barista-agent-sa@$PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/datastore.user"

5. 更新應用程式程式碼

接下來,我們要修改原本的程式碼,讓 AI 代理程式改從 Firestore 讀取資料。

1. 更新 requirements.txt
將這兩行加入你的 requirements.txt 中:

google-cloud-firestore==2.27.0
google-genai==2.11.0

2. 更新 agent.py (AI 代理邏輯)
將讀取菜單的邏輯改為向量搜尋:

# [START get_menu]
from google import genai
from google.cloud import firestore
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector
import json

def get_menu(query: str) -> str:
    """檢索與使用者查詢相符的咖啡廳菜單項目。"""
    try:
        db = firestore.Client(database="coffee-menu")
        client = genai.Client()
        
        # 將使用者的問題轉換成向量
        response = client.models.embed_content(
            model="text-embedding-004",
            contents=query,
        )
        query_vector = response.embeddings[0].values
        
        # 在 Firestore 中使用向量搜尋尋找最相近的 3 個品項 (Cosine 相似度)
        results = db.collection("menu").find_nearest(
            vector_field="embedding",
            query_vector=Vector(query_vector),
            distance_measure=DistanceMeasure.COSINE,
            limit=3,
        ).stream()
        
        menu_data = []
        for doc in results:
            item = doc.to_dict()
            item.pop("embedding", None) # 移除向量欄位以節省 Token 消耗
            menu_data.append(item)
            
        return json.dumps(menu_data)
    except Exception as e:
        return json.dumps({"error": f"Could not retrieve menu: {str(e)}"})
# [END get_menu]

3. 更新 app.py (Streamlit 介面)
讓 Streamlit 側邊欄從 Firestore 抓取現有菜單:

# [START load_menu]
from google.cloud import firestore
import streamlit as st

try:
    db = firestore.Client(database="coffee-menu")
    docs = db.collection("menu").stream()
    menu_items = []
    for doc in docs:
        item = doc.to_dict()
        item.pop("embedding", None)
        menu_items.append(item)
except Exception as e:
    st.error(f"Error loading menu from Firestore: {e}")
    menu_items = []
# [END load_menu]

6. 重新部署至 Cloud Run

程式碼修改完成後,我們將新版本重新部署上去。

gcloud run deploy coffee-barista \
  --source . \
  --region $REGION \
  --allow-unauthenticated \
  --command "/cnb/lifecycle/launcher" \
  --args "sh,-c,python3 -m streamlit run app.py --server.port=\$PORT --server.address=0.0.0.0 --server.enableCORS=false --server.enableXsrfProtection=false" \
  --service-account "barista-agent-sa@$PROJECT_ID.iam.gserviceaccount.com" \
  --set-env-vars GOOGLE_GENAI_USE_VERTEXAI=TRUE,GOOGLE_CLOUD_PROJECT=$PROJECT_ID,GOOGLE_CLOUD_LOCATION=global

7. 驗證測試 (即時新增品項)

為了證明資料庫現在是動態的,我們透過 Python 腳本直接在 Firestore 塞入一個新產品「抹茶拿鐵」,而完全不需要重新部署 Cloud Run

python3 -c "
import os
from google import genai
from google.cloud import firestore
from google.cloud.firestore_v1.vector import Vector

db = firestore.Client(database='coffee-menu')
client = genai.Client(
    vertexai=True,
    project=os.environ.get('PROJECT_ID'),
    location=os.environ.get('REGION', 'us-central1')
)

name = 'Matcha Green Tea Latte'
desc = 'Creamy steamed milk infused with premium Japanese matcha powder.'
res = client.models.embed_content(
    model='text-embedding-004',
    contents=f'{name}: {desc}'
)
embedding = res.embeddings[0].values

db.collection('menu').document('matcha-latte').set({
    'name': name,
    'description': desc,
    'price': 5.50,
    'tags': ['sweet', 'hot', 'dairy-free'],
    'allergens': [],
    'embedding': Vector(embedding)
})
print('Successfully added Matcha Latte with vector embeddings!')
"

執行完畢後,直接重新整理你的 Streamlit 網頁。你會發現側邊欄自動出現了「抹茶拿鐵」,並且在聊天室詢問「你們有抹茶飲品嗎?」時,AI 代理程式就能透過 Vector Search 準確地推薦它給你!


上一篇
使用 Google ADK 和 Cloud Run,在 Streamlit 中部署 RAG AI 代理程式
下一篇
ui-components , automation-scripts
系列文
將考國際證照的應用程式變成開源12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言