使用 AlloyDB 和 Vertex AI Agent Builder 打造智慧購物助理 - 第 1 部分

1. 總覽

在現今快速變化的零售環境中,提供卓越的客戶服務,同時提供個人化購物體驗,是至關重要的事。我們將帶您踏上技術旅程,建立知識導向的即時通訊應用程式,用於回答客戶問題、引導產品探索,以及提供客製化搜尋結果。這項創新解決方案結合了 AlloyDB 的資料儲存功能、內部分析引擎的內容理解功能、Gemini (大型語言模型) 的關聯性驗證功能,以及 Google 的 Agent Builder 快速啟動智慧對話式助理的功能。

挑戰:現代零售業的消費者希望能即時獲得解答,並獲得符合個人偏好的產品推薦。傳統搜尋方法通常無法提供這麼高的個人化服務。

解決方案:我們的知識導向即時通訊應用程式正面解決這個問題。這項服務會運用從零售資料衍生的豐富知識庫,瞭解客戶意圖、做出聰明回應,並提供極具相關性的結果。

建構項目

在本實驗室 (第 1 部分) 中,您將:

  1. 建立 AlloyDB 例項並載入電子商務資料集
  2. 在 AlloyDB 中啟用 pgvector 和生成式 AI 模型擴充功能
  3. 根據產品說明產生嵌入
  4. 針對使用者搜尋文字執行即時餘弦相似度搜尋
  5. 在無伺服器 Cloud Run 函式中部署解決方案

實驗室的第二部分將介紹 Agent Builder 的步驟。

需求條件

  • ChromeFirefox 等瀏覽器
  • 已啟用計費功能的 Google Cloud 專案。

2. 架構

資料流:讓我們進一步瞭解資料在系統中的移動歷程:

內容擷取

首先,我們會將零售資料 (商品目錄、產品說明、顧客互動) 匯入 AlloyDB。

數據分析引擎:

我們會使用 AlloyDB 做為分析引擎,執行下列操作:

  1. 擷取情境:引擎會分析 AlloyDB 中儲存的資料,以瞭解產品、類別、客戶行為等之間的關係。
  2. 嵌入建立:系統會為使用者的查詢和 AlloyDB 中儲存的資訊產生嵌入項目 (文字的數學表示法)。
  3. Vector Search:引擎會執行相似度搜尋,將查詢嵌入項目與產品說明、評論和其他相關資料的嵌入項目進行比較。這項功能會找出 25 個最相關的「最近鄰居」。

Gemini 驗證:

這些潛在回應會傳送至 Gemini 進行評估。Gemini 會判斷這些資訊是否確實相關且安全,可否與使用者分享。

回覆產生:

經過驗證的回應會以 JSON 陣列的形式建構,整個引擎會封裝為從代理程式建構工具叫用的無伺服器 Cloud Run 函式。

對話式互動:

代理程式建構工具會以自然語言格式向使用者呈現回應,方便雙方進行對話。這部分將在後續實驗室中說明。

3. 事前準備

建立專案

  1. Google Cloud 控制台的專案選取器頁面中,選取或建立 Google Cloud 專案
  2. 確認 Cloud 專案已啟用計費功能。瞭解如何檢查專案是否已啟用計費功能
  3. 您將使用 Cloud Shell,這是在 Google Cloud 中運作的指令列環境,並預先載入 bq。按一下 Google Cloud 控制台頂端的「啟用 Cloud Shell」。

「啟用 Cloud Shell」按鈕圖片

  1. 連線至 Cloud Shell 後,請使用下列指令確認您已通過驗證,且專案已設為您的專案 ID:
gcloud auth list
  1. 在 Cloud Shell 中執行下列指令,確認 gcloud 指令知道您的專案。
gcloud config list project
  1. 如果您尚未設定專案,請使用下列指令進行設定:
gcloud config set project <YOUR_PROJECT_ID>
  1. 啟用必要的 API。
gcloud services enable alloydb.googleapis.com \
                       compute.googleapis.com \
                       cloudresourcemanager.googleapis.com \
                       servicenetworking.googleapis.com \
                       run.googleapis.com \
                       cloudbuild.googleapis.com \
                       cloudfunctions.googleapis.com \
                       aiplatform.googleapis.com

您可以透過主控台搜尋每項產品,或使用這個連結,來代替 gcloud 指令。

如果遺漏任何 API,您隨時可以在實作期間啟用。

如要瞭解 gcloud 指令和用法,請參閱說明文件

4. 資料庫設定

在本實驗室中,我們會使用 AlloyDB 做為儲存零售資料的資料庫。它會使用叢集來保存所有資源,例如資料庫和記錄。每個叢集都有一個主要例項,可提供資料的存取點。資料表會儲存實際資料。

我們來建立 AlloyDB 叢集、例項和資料表,用於載入電子商務資料集。

建立叢集和執行個體

  1. 前往 Cloud 控制台的 AlloyDB 頁面。如要輕鬆在 Cloud Console 中找到大部分的頁面,請使用控制台的搜尋列進行搜尋。
  2. 在該頁面中選取「建立叢集」:

f76ff480c8c889aa.png

  1. 您會看到類似下方的畫面。使用下列值建立叢集和執行個體
  • 叢集 ID:「shopping-cluster
  • 密碼:「alloydb
  • 與 PostgreSQL 15 相容
  • 區域:「us-central1
  • 網路:default

538dba58908162fb.png

  1. 選取預設網路後,畫面會顯示如下圖所示畫面。選取「設定連線」。
    7939bbb6802a91bf.png
  2. 接著選取「使用系統自動分配的 IP 範圍」和「繼續」。查看相關資訊後,請選取「建立連線」。768ff5210e79676f.png
  3. 網路設定完成後,您可以繼續建立叢集。按一下「建立叢集」即可完成叢集設定,如下所示:

e06623e55195e16e.png

請務必將執行個體 ID 變更為「shopping-instance"」。

請注意,建立叢集大約需要 10 分鐘。成功後,您應該會看到類似下圖的畫面:

24eec29fa5cfdb3e.png

5. 資料擷取

接下來,我們要新增一個包含商店資料的表格。前往 AlloyDB,選取主要叢集,然後選取 AlloyDB Studio:

847e35f1bf8a8bd8.png

您可能需要等待執行個體建立完成。完成後,請使用建立叢集時建立的憑證登入 AlloyDB。使用下列資料驗證 PostgreSQL:

  • 使用者名稱:"postgres"
  • 資料庫:postgres
  • 密碼:"alloydb"

成功驗證 AlloyDB Studio 後,您就可以在編輯器中輸入 SQL 指令。您可以使用最後一個視窗右側的加號新增多個編輯器視窗。

91a86d9469d499c4.png

您會在編輯器視窗中輸入 AlloyDB 指令,並視需要使用「Run」、「Format」和「Clear」選項。

啟用擴充功能

我們將使用擴充功能 pgvectorgoogle_ml_integration 來建構這個應用程式。pgvector 擴充功能可讓您儲存及搜尋向量嵌入項目。google_ml_integration 擴充功能提供的函式可用於存取 Vertex AI 預測端點,以便在 SQL 中取得預測結果。執行下列 DDL 啟用這些擴充功能:

CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE;
CREATE EXTENSION IF NOT EXISTS vector;

如要檢查資料庫已啟用的擴充功能,請執行下列 SQL 指令:

select extname, extversion from pg_extension;

建立表格

使用下列 DDL 陳述式建立資料表:

CREATE TABLE
 apparels ( id BIGINT,
   category VARCHAR(100),
   sub_category VARCHAR(50),
   uri VARCHAR(200),
   image VARCHAR(100),
   content VARCHAR(2000),
   pdt_desc VARCHAR(5000),
   embedding vector(768) );

執行上述指令成功後,您應該就能在資料庫中查看該資料表。以下是螢幕截圖範例:

908e33bbff58a6d.png

擷取資料

在本實驗室中,我們在這個 SQL 檔案中提供約 200 筆記錄的測試資料。其中包含 id, category, sub_category, uri, imagecontent。我們會在稍後的實驗室課程中填入其他欄位。

複製這裡的 20 行/插入陳述式,然後將這些行貼到空白編輯器分頁中,然後選取「RUN」。

如要查看資料表內容,請展開「Explorer」部分,直到看到名為 apparels 的資料表。選取三橫線 (⋮) 即可查看「查詢表格」選項。系統會在新編輯器分頁中開啟 SELECT 陳述式。

b31ece70e670ab89.png

授予權限

執行下列陳述式,將 embedding 函式的執行權限授予使用者 postgres

GRANT EXECUTE ON FUNCTION embedding TO postgres;

將 Vertex AI 使用者角色授予 AlloyDB 服務帳戶

前往 Cloud Shell 終端機,並輸入下列指令:

PROJECT_ID=$(gcloud config get-value project)

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"

6. 背景資訊

返回 AlloyDB 執行個體頁面。

如要建立嵌入項目,我們需要有 context,也就是我們想在單一欄位中納入的所有資訊。我們會透過建立產品說明 (稱為 pdt_desc) 來完成這項操作。在本例中,我們會使用每項產品的所有資訊,但如果您要使用自己的資料執行這項操作,可以自由運用任何對自家業務有意義的資料處理方式。

在您新建立的執行個體的 AlloyDB Studio 中執行下列陳述式。這會使用情境資料更新 pdt_desc 欄位:

UPDATE
 apparels
SET
 pdt_desc = CONCAT('This product category is: ', category, ' and sub_category is: ', sub_category, '. The description of the product is as follows: ', content, '. The product image is stored at: ', uri)
WHERE
 id IS NOT NULL;

這段 DML 會使用資料表中所有欄位和其他依附元件 (如果有) 的資訊,建立簡單的背景摘要。如要更精確地分類資訊和建立情境,您可以運用任何對業務有意義的方式來設計資料。

7. 為情境建立嵌入

電腦處理數字比處理文字容易得多。嵌入系統會將文字轉換為一系列浮點數,這些數字應可代表文字,無論文字的措詞、使用的語言為何等。

建議您描述海邊景色。這些詞彙可能會稱為「on the water」、「beachfront」、「walk from your room to the ocean」、「sur la mer」、「на берегу океана」等,這些詞彙看起來都不同,但其語意或機器學習術語中的嵌入值應會非常接近。

資料和背景資訊都準備就緒後,我們會執行 SQL 指令碼,將產品說明的嵌入資料新增至 embedding 欄位中的資料表。您可以使用各種嵌入模型。我們使用 Vertex AI 的 text-embedding-004。請務必在整個專案中使用相同的嵌入模型!

注意:如果您使用的是已建立一段時間的現有 Google Cloud 專案,可能需要繼續使用舊版文字嵌入模型,例如 textembedding-gecko。

UPDATE
 apparels
SET
 embedding = embedding( 'text-embedding-004',
   pdt_desc)
WHERE
 TRUE;

再次查看 apparels 表格,看看其中的嵌入資料。請務必重新執行 SELECT 陳述式,查看變更內容。

SELECT
 id,
 category,
 sub_category,
 content,
 embedding
FROM
 apparels;

這項操作應會針對查詢中的範例文字傳回嵌入向量,如下所示,這類似於浮點陣列:

c69c08d085389f74.png

注意:如果新建立的 Google Cloud 專案屬於免費方案,每秒允許嵌入模型的嵌入要求數量可能會出現配額問題。建議您使用 ID 篩選器查詢,然後在產生嵌入內容時,選擇 1 到 5 筆記錄等。

8. 執行向量搜尋

表格、資料和嵌入項目都已準備就緒,現在讓我們針對使用者的搜尋字串執行即時向量搜尋。

假設使用者詢問:

「我想買女性上衣,粉紅色休閒款,只限純棉。」

您可以執行下列查詢,找出相符項目:

SELECT
id,
category,
sub_category,
content,
pdt_desc AS description
FROM
apparels
ORDER BY
embedding <=> embedding('text-embedding-004',
  'I want womens tops, pink casual only pure cotton.')::vector
LIMIT
5;

讓我們進一步瞭解這個查詢:

在這個查詢中,

  1. 使用者的搜尋文字為:「我想要女性上衣,粉紅色休閒服,只限純棉。」
  2. 我們會使用模型 text-embedding-004,將其轉換為 embedding() 方法中的嵌入資料。在上一節中,我們將嵌入函式套用至資料表中的所有項目,因此這個步驟應該會讓您感到熟悉。
  3. <=>」代表使用 COSINE SIMILARITY 距離方法。如要查看所有可用的相似度評估指標,請參閱 pgvector 的說明文件
  4. 我們會將嵌入方法的結果轉換為向量類型,以便與資料庫中儲存的向量相容。
  5. LIMIT 5 代表我們要擷取搜尋字串的 5 個最相近項目。

結果如下所示:

4193a68737400535.png

如您在結果中看到的,相符項目與搜尋文字非常相近。嘗試變更顏色,看看結果會如何變化。

9. 使用 LLM 進行比對驗證

在繼續建立服務,為應用程式傳回最相符的內容之前,我們先使用生成式 AI 模型,驗證這些潛在回覆是否確實相關,且可安全地與使用者分享。

確認已為 Gemini 設定執行個體

請先確認叢集和執行個體是否已啟用 Google ML 整合功能。在 AlloyDB Studio 中輸入下列指令:

show google_ml_integration.enable_model_support;

如果顯示的值為「on」,您可以略過接下來的 2 個步驟,直接設定 AlloyDB 和 Vertex AI 模型整合。

  1. 前往 AlloyDB 叢集的主要執行個體,然後按一下「編輯主要執行個體」

456ffdf292d3c0e0.png

  1. 前往「進階設定選項」中的「標記」部分。並確認 google_ml_integration.enable_model_support flag 已設為「on」,如下所示:

6a59351fcd2a9d35.png

如果未設為「開啟」,請將其設為「開啟」,然後按一下「更新執行個體」按鈕。這項步驟需要幾分鐘的時間。

AlloyDB 與 Vertex AI 模型整合

您現在可以連線至 AlloyDB Studio,並執行下列 DML 陳述式,透過 AlloyDB 設定 Gemini 模型存取權,並在指定位置使用專案 ID。執行指令前,系統可能會警告您語法錯誤,但指令應該會正常執行。

首先,我們會建立 Gemini 1.5 模型連線,如下所示。請記得將下方指令中的 $PROJECT_ID 替換為您的 Google Cloud 專案 ID。

CALL
 google_ml.create_model( model_id => 'gemini-1.5',
   model_request_url => 'https://us-central1-aiplatform.googleapis.com/v1/projects/$PROJECT_ID/locations/us-central1/publishers/google/models/gemini-1.5-pro:streamGenerateContent',
   model_provider => 'google',
   model_auth_type => 'alloydb_service_agent_iam');

您可以透過 AlloyDB Studio 中的下列指令,檢查已設定存取權限的模型:

select model_id,model_type from google_ml.model_info_view;        

最後,我們需要授予資料庫使用者權限,讓他們執行 ml_predict_row 函式,透過 Google Vertex AI 模型執行預測。執行下列指令:

GRANT EXECUTE ON FUNCTION ml_predict_row to postgres;

注意:如果您使用的是現有的 Google Cloud 專案,以及先前建立的 AlloyDB 叢集/例項,則可能需要刪除對 gemini-1.5 模型的舊參照,並使用上述 CALL 陳述式重新建立,並再次對 ml_predict_row 函式執行授權執行作業,以防在之後的 gemini-1.5 叫用作業中遇到問題。

評估回覆

雖然我們會在下一節中使用一個大型查詢,確保查詢的回應合理,但這項查詢可能難以理解。我們現在就來看看這些片段,看看它們在幾分鐘內如何組合在一起。

  1. 首先,我們會向資料庫傳送要求,取得與使用者查詢最相符的 5 個項目。我們會將查詢硬式編碼,以便簡化操作。不過別擔心,我們稍後會將其插入查詢中。我們會納入 apparels 資料表中的產品說明,並新增兩個欄位:一個將說明與索引結合,另一個則與原始要求結合。所有資料都會儲存在名為 xyz 的資料表中 (只是暫時性資料表名稱)。
CREATE TABLE
 xyz AS
SELECT
 id || ' - ' || pdt_desc AS literature,
 pdt_desc AS content,
 'I want womens tops, pink casual only pure cotton.' AS  user_text
FROM
 apparels
ORDER BY
 embedding <=> embedding('text-embedding-004',
   'I want womens tops, pink casual only pure cotton.')::vector
LIMIT
 5;

這項查詢的輸出結果會是與使用者查詢最相似的 5 列。新的 xyz 表格將包含 5 列,每列都包含下列資料欄:

  • literature
  • content
  • user_text
  1. 為了判斷回應是否有效,我們會使用複雜的查詢,說明如何評估回應。它會使用 xyz 資料表中的 user_textcontent 做為查詢的一部分。
"Read this user search text: ', user_text, 
' Compare it against the product inventory data set: ', content, 
' Return a response with 3 values: 1) MATCH: if the 2 contexts are at least 85% matching or not: YES or NO 2) PERCENTAGE: percentage of match, make sure that this percentage is accurate 3) DIFFERENCE: A clear short easy description of the difference between the 2 products. Remember if the user search text says that some attribute should not be there, and the record has it, it should be a NO match."
  1. 接著,我們會使用該查詢,查看 xyz 資料表中回應的「好壞程度」。
CREATE TABLE
  x AS
SELECT
  json_array_elements( google_ml.predict_row( model_id => 'gemini-1.5',
      request_body => CONCAT('{
 "contents": [ 
        { "role": "user", 
          "parts": 
             [ { "text": "Read this user search text: ', user_text, ' Compare it against the product inventory data set: ', content, ' Return a response with 3 values: 1) MATCH: if the 2 contexts are at least 85% matching or not: YES or NO 2) PERCENTAGE: percentage of match, make sure that this percentage is accurate 3) DIFFERENCE: A clear short easy description of the difference between the 2 products. Remember if the user search text says that some attribute should not be there, and the record has it, it should be a NO match." 
             } ]
         } 
] }'
)::json))-> 'candidates' -> 0 -> 'content' -> 'parts' -> 0 -> 'text'
AS LLM_RESPONSE
FROM
    xyz;
  1. predict_row 會以 JSON 格式傳回結果。程式碼「-> 'candidates' -> 0 -> 'content' -> 'parts' -> 0 -> 'text'"」用於從該 JSON 中擷取實際文字。如要查看實際傳回的 JSON,您可以移除這段程式碼。
  2. 最後,如要取得 LLM 欄位,您只需從 x 表格中擷取即可:
SELECT 
LLM_RESPONSE 
FROM 
        x;
  1. 這可以結合為單一下一個查詢,如下所示。

如果您已執行上述查詢來檢查中間結果,請先從 AlloyDB 資料庫中刪除/移除 xyz 和 x 資料表,再執行這項操作。

SELECT
 LLM_RESPONSE
FROM (
 SELECT
 json_array_elements( google_ml.predict_row( model_id => 'gemini-1.5',
     request_body => CONCAT('{
     "contents": [
       { "role": "user",
         "parts":
            [ { "text": "Read this user search text: ', user_text, ' Compare it against the product inventory data set: ', content, ' Return a response with 3 values: 1) MATCH: if the 2 contexts are at least 85% matching or not: YES or NO 2) PERCENTAGE: percentage of match, make sure that this percentage is accurate 3) DIFFERENCE: A clear short easy description of the difference between the 2 products. Remember if the user search text says that some attribute should not be there, and the record has it, it should be a NO match."
            } ]
        }
] }'
)::json))-> 'candidates' -> 0 -> 'content' -> 'parts' -> 0 -> 'text'
AS LLM_RESPONSE
   FROM (
         SELECT
           id || ' - ' || pdt_desc AS literature,
           pdt_desc AS content,
         'I want womens tops, pink casual only pure cotton.' user_text
         FROM
           apparels
         ORDER BY
             embedding <=> embedding('text-embedding-004',
             'I want womens tops, pink casual only pure cotton.')::vector
         LIMIT
           5 ) AS xyz ) AS X;

雖然這可能仍令人卻步,但希望您能對這項功能有更深入的瞭解。結果會指出是否有相符項目、相符程度,以及分數的相關說明。

請注意,Gemini 模型預設為開啟串流功能,因此實際回應會分散在多行中:14e74d71293b7b9.png

10. 將應用程式發布到網路

準備好將這個應用程式移至網路了嗎?請按照下列步驟,使用 Cloud Run 函式建構無伺服器知識引擎:

  1. 前往 Google Cloud 控制台的 Cloud Run 函式頁面,建立新的 Cloud Run 函式,或使用以下連結:https://console.cloud.google.com/functions/add
  2. 選取「Cloud Run 函式」做為環境。請提供「retail-engine」做為函式名稱,並選擇「us-central1」做為地區。將「驗證」設為「允許未經驗證的叫用」,然後按一下「NEXT」。選擇「Java 17」做為執行階段,並為原始碼選擇「Inline Editor」
  3. 根據預設,系統會將進入點設為「gcfv2.HelloHttpFunction」。請分別將 Cloud Run 函式的 HelloHttpFunction.javapom.xml 中的預留位置程式碼,替換為 Java 檔案XML 中的程式碼。
  4. 請記得將 Java 檔案中的 $PROJECT_ID 預留位置和 AlloyDB 連線憑證,改為您的值。AlloyDB 憑證是我們在本程式碼研究室一開始時使用的憑證。如果您使用了不同的值,請在 Java 檔案中進行相同的修改。
  5. 按一下「Deploy」

部署完成後,我們會建立 VPC 連接器,讓 Cloud 函式可以存取 AlloyDB 資料庫執行個體。

重要步驟:

設定部署作業後,您應該會在 Google Cloud Run 函式控制台中看到函式。搜尋新建立的函式 (retail-engine),然後按一下該函式,接著點選「編輯」,然後變更下列項目:

  1. 前往「執行階段、建構作業、連線和安全性設定」
  2. 將逾時時間提高至 180 秒
  3. 前往「連線設定」分頁:

4e83ec8a339cda08.png

  1. 在「Ingress」設定下方,確認已選取「Allow all traffic」(允許所有流量)。
  2. 在「Egress」設定下,按一下「Network」下拉式選單,然後選取「Add New VPC Connector」選項,並按照彈出式對話方塊中的操作說明進行:

8126ec78c343f199.png

  1. 請為 VPC 連接器提供名稱,並確認區域與執行個體相同。將「Network」值保留為預設值,並將「Subnet」設為「Custom IP Range」,IP 範圍為 10.8.0.0 或可用的類似值。
  2. 展開「顯示縮放設定」,並確認設定已正確設為下列項目:

7baf980463a86a5c.png

  1. 按一下「建立」,這個連接器現在應該會列在輸出設定中。
  2. 選取新建的連接器
  3. 選擇將所有流量轉送至這個虛擬私有雲連接器。
  4. 依序點選「NEXT」和「DEPLOY」

11. 測試應用程式

更新後的 Cloud Function 部署完成後,您應該會看到以下格式的端點:

https://us-central1-YOUR_PROJECT_ID.cloudfunctions.net/retail-engine

您可以透過 Cloud Shell 終端機執行下列指令來測試:

gcloud functions call retail-engine --region=us-central1 --gen2 --data '{"search": "I want some kids clothes themed on Disney"}'

您也可以按照下列步驟測試 Cloud Run 函式:

PROJECT_ID=$(gcloud config get-value project)

curl -X POST https://us-central1-$PROJECT_ID.cloudfunctions.net/retail-engine \
  -H 'Content-Type: application/json' \
  -d '{"search":"I want some kids clothes themed on Disney"}' \
  | jq .

結果如下:

88bc1ddfb5644a28.png

大功告成!在 AlloyDB 資料上使用嵌入模型執行相似度向量搜尋,就這麼簡單。

建構對話式服務專員!

這個實驗室的第二部分會說明如何建構服務專員。

12. 清理

如果您打算完成本實驗室的第 2 部分,請略過這個步驟,因為這會刪除目前的專案。

如要避免系統向您的 Google Cloud 帳戶收取這篇文章所用資源的費用,請按照下列步驟操作:

  1. 在 Google Cloud 控制台中前往「管理資源」頁面。
  2. 在專案清單中選取要刪除的專案,然後點按「刪除」。
  3. 在對話方塊中輸入專案 ID,然後按一下「Shut down」(關閉) 即可刪除專案。

13. 恭喜

恭喜!您已成功使用 AlloyDB、pgvector 和向量搜尋功能執行相似度搜尋。結合 AlloyDBVertex AIVector Search 的功能,我們已大幅提升內容和向量搜尋的易用性、效率和意義。本實驗室的下一節將介紹建立服務項目的步驟。