使用 Cloud 資料庫、無伺服器執行階段和開放原始碼整合功能的玩具店搜尋應用程式

1. 總覽

想像一下,你可以透過虛擬方式或親臨玩具店,輕鬆找到最適合的禮物。你可以描述自己想要的東西、上傳玩具相片,甚至設計自己的創作,商店就能立即瞭解你的需求,並提供客製化體驗。這並非未來的幻想,而是 AI、雲端技術和個人化電子商務願景的實際應用。

挑戰:要找到符合你想像的完美產品並不容易。一般搜尋字詞、關鍵字和模糊搜尋通常無法滿足需求,瀏覽無止盡的網頁可能會讓人感到厭煩,而且你想像的內容與實際可用的內容之間的落差,可能會讓你感到沮喪。

解決方案:這個示範應用程式正面解決這個問題,運用 AI 技術的強大功能,提供真正個人化且流暢的體驗,包括情境搜尋和符合搜尋情境的產品自訂生成。

建構項目

本實驗室的學習內容如下:

  1. 建立 AlloyDB 執行個體並載入 Toys 資料集
  2. 在 AlloyDB 中啟用 pgvector 和生成式 AI 模型擴充功能
  3. 根據產品說明產生嵌入資料,並針對使用者搜尋文字執行即時餘弦相似度搜尋
  4. 叫用 Gemini 2.0 Flash,為使用者上傳的圖片提供描述,以便進行內容相關玩具搜尋
  5. 叫用 Imagen 3,根據使用者的興趣自訂建立玩具
  6. 叫用使用資料庫版 Gen AI Toolbox 建立的價格預測工具,以取得自訂玩具的價格詳細資料
  7. 在無伺服器 Cloud Run 函式中部署解決方案

需求條件

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

2. 架構

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

  1. 使用 AI 輔助的 RAG (檢索增強生成) 技術進行情境搜尋

舉例來說,系統不會只尋找「紅色汽車」,而是會理解以下內容:

「適合 3 歲男童使用的小型車輛。」

以 AlloyDB 做為基礎:我們使用 AlloyDB 這項 Google Cloud 全代管的 PostgreSQL 相容資料庫,儲存玩具資料,包括說明、圖片網址和其他相關屬性。

用於語意搜尋的 pgvector:pgvector 是 PostgreSQL 擴充功能,可讓我們儲存玩具說明和使用者搜尋查詢的向量嵌入項目。這可啟用語意搜尋功能,讓系統瞭解字詞背後的含意,而非只比對關鍵字。

關於關聯性的餘弦相似度:我們會使用餘弦相似度來評估使用者搜尋向量和玩具說明向量之間的語意相似度,並顯示最相關的結果。

ScaNN 索引可提升速度和準確度:為了確保快速且準確的結果,尤其是在玩具商品目錄增加的情況下,我們整合了 ScaNN (可擴充的近似近鄰) 索引。這麼做可大幅提升向量搜尋的效率和回溯率。

  1. 使用 Gemini 2.0 Flash 進行以圖像為基礎的搜尋與理解

假設使用者想上傳熟悉的玩具相片,並以此進行搜尋,而非輸入文字做為背景資訊。使用者可以上傳自己喜歡的玩具圖片,並透過這項功能取得相關功能。我們會運用 Google 的 Gemini 2.0 Flash 模型 (使用 LangChain4j 叫用) 來分析圖片,並擷取相關的背景資訊,例如玩具的顏色、材質、類型和適用年齡層。

  1. 使用生成式 AI 打造夢幻玩具:第 3 集:Imagen

使用者決定自行製作玩具時,才是真正的魔法所在。我們使用 Imagen 3,讓他們透過簡單的文字提示描述夢想中的玩具。想像一下,當你說出「我想要一隻紫色翅膀和友善表情的龍娃娃」時,就能在螢幕上看到栩栩如生的龍娃娃!接著,Imagen 3 會產生自訂玩具的圖片,讓使用者清楚看到自己的創作。

  1. 價格預測功能,採用代理程式和資料庫專用的 Gen AI Toolbox

我們已導入價格預測功能,可預估自訂設計玩具的生產成本。這項功能由代理程式提供,其中包含精密的價格計算工具。

資料庫專用 Gen AI Toolbox:這個代理程式會使用 Google 全新的開放原始碼工具資料庫專用 Gen AI Toolbox,與資料庫完美整合。這樣一來,代理人就能存取材料成本、製造程序和其他相關因素的即時資料,提供準確的預估價格。詳情請參閱這篇文章

  1. Java Spring Boot、Gemini Code Assist 和 Cloud Run,可簡化開發作業並進行無伺服器部署

整個應用程式都是使用 Java Spring Boot 建構,這是一個可靠且可擴充的架構。我們在整個開發過程中都運用了 Gemini Code Assist,特別是在前端開發作業中,大幅加快開發週期並改善程式碼品質。我們使用 Cloud Run 部署整個應用程式,並使用 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. 在 Cloud Shell 終端機中逐一執行下列指令,啟用必要的 API:

您也可以使用單一指令執行下列操作,但如果您是試用版帳戶使用者,可能會在嘗試大量啟用這些功能時遇到配額問題。因此,指令會以每行一項的方式輸入。

gcloud services enable alloydb.googleapis.com
gcloud services enable compute.googleapis.com 
gcloud services enable cloudresourcemanager.googleapis.com 
gcloud services enable servicenetworking.googleapis.com 
gcloud services enable run.googleapis.com 
gcloud services enable cloudbuild.googleapis.com 
gcloud services enable cloudfunctions.googleapis.com 
gcloud services enable aiplatform.googleapis.com

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

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

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

4. 資料庫設定

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

我們來建立 AlloyDB 叢集、執行個體和資料表,用於載入電子商務資料集。

建立叢集和執行個體

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

f76ff480c8c889aa.png

  1. 您會看到類似下方的畫面。使用下列值建立叢集和執行個體 (如果您要從存放區複製應用程式程式碼,請務必確認這些值相符):
  • 叢集 ID:"vector-cluster"
  • 密碼:"alloydb"
  • PostgreSQL 15 相容
  • 區域:「us-central1
  • 網路default

538dba58908162fb.png

  1. 選取預設網路後,畫面會顯示如下圖所示畫面。

選取「設定連線」
7939bbb6802a91bf.png

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

e06623e55195e16e.png

請務必將執行個體 ID 變更為「

vector-instance"

請注意,建立叢集大約需要 10 分鐘。成功後,畫面上會顯示剛剛建立的叢集總覽。

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 toys ( id VARCHAR(25), name VARCHAR(25), description VARCHAR(20000), quantity INT, price FLOAT, image_url VARCHAR(200), text_embeddings vector(768)) ;

執行上述指令成功後,您應該就能在資料庫中查看該資料表。

擷取資料

在本實驗室中,我們在這個 SQL 檔案中提供約 72 筆記錄的測試資料。其中包含 id, name, description, quantity, price, image_url 欄位。其他欄位會在稍後的實驗室中填入。

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

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

cfaa52b717f9aaed.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. 為背景資訊建立嵌入

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

建議您描述海邊景色。例如「on the water」、「beachfront」、「walk from your room to the ocean」、「sur la mer」、「на берегу океана」等。這些字詞看起來都不同,但語意或機器學習術語中的嵌入值應該都很相近。

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

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

返回 AlloyDB Studio 分頁,然後輸入下列 DML:

UPDATE toys set text_embeddings = embedding( 'text-embedding-005', description);

再次查看 toys 表格,即可看到一些嵌入項目。請務必重新執行 SELECT 陳述式,查看變更內容。

SELECT id, name, description, price, quantity, image_url, text_embeddings FROM toys;

這應該會傳回玩具說明的嵌入向量,如下所示,這類似於浮點陣列:

7d32f7cd7204e1f3.png

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

7. 執行向量搜尋

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

假設使用者詢問:

I want a white plush teddy bear toy with a floral pattern」。

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

select * from toys
ORDER BY text_embeddings <=> CAST(embedding('text-embedding-005', 'I want a white plush teddy bear toy with a floral pattern') as vector(768))
LIMIT 5;

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

在這個查詢中,

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

結果如下所示:

fa7f0fc3a4c68804.png

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

重要注意事項:

假設我們想使用 ScaNN 索引提升向量搜尋結果的效能 (查詢時間)、效率和回憶率。請參閱這篇網誌中的步驟,比較有無索引的結果差異。

選用步驟:使用 ScaNN 索引改善效率和回憶率

為了方便您操作,我們在此列出建立索引的步驟:

  1. 由於我們已建立叢集、例項、情境和嵌入,因此只需使用以下陳述式安裝 ScaNN 擴充功能:
CREATE EXTENSION IF NOT EXISTS alloydb_scann;
  1. 接下來,我們將建立索引 (ScaNN):
CREATE INDEX toysearch_index ON toys
USING scann (text_embeddings cosine)
WITH (num_leaves=9);

在上述 DDL 中,apparel_index 是索引的名稱

「toys」是我的表格

「scann」是索引方法

「嵌入」是指要建立索引的表格欄

「cosine」是我要與索引一起使用的距離方法

「8」是指要套用至此索引的分區數量。請將其設為 1 到 1048576 之間的任一值。如要進一步瞭解如何決定這個值,請參閱「調整 ScaNN 索引」。

我使用資料點數的平方根,如 ScaNN 存放區中所建議 (當進行分割時,num_leaves 應大致等於資料點數的平方根)。

  1. 檢查是否使用以下查詢建立索引:
SELECT * FROM pg_stat_ann_indexes;
  1. 使用與前述相同的查詢執行向量搜尋,但不使用索引:
select * from toys
ORDER BY text_embeddings <=> CAST(embedding('text-embedding-005', 'I want a white plush teddy bear toy with a floral pattern') as vector(768))
LIMIT 5;

上述查詢與實驗室步驟 8 中使用的查詢相同。不過,現在我們已將欄位編入索引。

  1. 使用簡單的搜尋查詢進行測試,並在有和沒有索引的情況下進行測試 (透過捨棄索引):

這個用途只有 72 筆記錄,因此索引不會真正生效。在其他用途中進行的測試結果如下:

針對已編入索引的嵌入資料執行相同的 Vector Search 查詢,可取得高品質的搜尋結果,並提高效率。使用索引後,效率大幅提升 (執行時間:不使用 ScaNN 為 10.37 毫秒,使用 ScaNN 為 0.87 毫秒)。如要進一步瞭解這個主題,請參閱這篇網誌文章

8. 使用 LLM 進行比對驗證

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

確認已為 Gemini 設定執行個體

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

show google_ml_integration.enable_model_support;

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

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

cb76b934ba3735bd.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. 首先,我們會向資料庫傳送要求,取得與使用者查詢最相符的 10 個項目。
  2. 為了判斷回應是否有效,我們會使用外部查詢,說明如何評估回應。它會使用內部資料表的 recommended_text 欄位 (即搜尋文字) 和 content (即玩具說明欄位) 做為查詢的一部分。
  3. 然後,我們會根據這項資訊,評估回傳的回覆是否「正確」。
  4. predict_row 會以 JSON 格式傳回結果。程式碼「-> 'candidates' -> 0 -> 'content' -> 'parts' -> 0 -> 'text'"」用於從該 JSON 中擷取實際文字。如要查看實際傳回的 JSON,您可以移除這段程式碼。
  5. 最後,我們使用 REGEXP_REPLACE(gemini_validation, '[^a-zA-Z,: ]', '', 'g') 擷取 LLM 回應。
SELECT id,
       name,
       content,
       quantity,
       price,
       image_url,
       recommended_text,
       REGEXP_REPLACE(gemini_validation, '[^a-zA-Z,: ]', '', 'g') AS gemini_validation
  FROM (SELECT id,
               name,
               content,
               quantity,
               price,
               image_url,
               recommended_text,
               CAST(ARRAY_AGG(LLM_RESPONSE) AS TEXT) AS gemini_validation
          FROM (SELECT id,
                       name,
                       content,
                       quantity,
                       price,
                       image_url,
                       recommended_text,
                       json_array_elements(google_ml.predict_row(model_id => 'gemini-1.5',
                                                                   request_body => CONCAT('{ "contents": [ { "role": "user", "parts": [ { "text": "User wants to buy a toy and this is the description of the toy they wish to buy: ',                                                                                              recommended_text,                                                                                              '. Check if the following product items from the inventory are close enough to really, contextually match the user description. Here are the items: ',                                                                                         content,                                                                                         '. Return a ONE-LINE response with 3 values: 1) MATCH: if the 2 contexts are reasonably matching in terms of any of the color or color family specified in the list, approximate style match with any of the styles mentioned in the user search text: This should be a simple YES or NO. Choose NO only if it is completely irrelevant to users search criteria. 2) PERCENTAGE: percentage of match, make sure that this percentage is accurate 3) DIFFERENCE: A clear one-line 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' :: TEXT AS LLM_RESPONSE
                  FROM (SELECT id,
                               name,
                               description AS content,
                               quantity,
                               price,
                               image_url,
                               'Pink panther standing' AS recommended_text
                          FROM toys
                         ORDER BY text_embeddings <=> embedding('text-embedding-005',
                                                                'Pink panther standing')::VECTOR
                         LIMIT 10) AS xyz) AS X
         GROUP BY id,
                  name,
                  content,
                  quantity,
                  price,
                  image_url,
                  recommended_text) AS final_matches
 WHERE REGEXP_REPLACE(gemini_validation, '[^a-zA-Z,: ]', '', 'g') LIKE '%MATCH%:%YES%';

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

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

c2b006aeb3f3a2fc.png

9. 將 Toy Search 無伺服器化

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

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

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

重要步驟:

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

  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」

10. 測試 Cloud Run 函式

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

https://us-central1-YOUR_PROJECT_ID.cloudfunctions.net/get-toys-alloydb

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

PROJECT_ID=$(gcloud config get-value project)

curl -X POST https://us-central1-$PROJECT_ID.cloudfunctions.net/get-toys-alloydb \
  -H 'Content-Type: application/json' \
  -d '{"search":"I want a standing pink panther toy"}' \
  | jq .

結果如下:

23861e9091565a64.png

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

11. 建構網頁應用程式用戶端!

在本節中,我們將建立一個網頁應用程式,讓使用者可以根據文字、圖片與使用者互動,甚至根據需求建立新玩具。由於應用程式已建構完成,您可以按照下列步驟將應用程式複製到 IDE,並讓應用程式開始運作。

  1. 我們使用 Gemini 2.0 Flash 描述使用者可能上傳的圖片,以便找到相符的玩具,因此需要取得此應用程式的 API 金鑰。如要這麼做,請前往 https://aistudio.google.com/apikey,為您要實作此應用程式的有效 Google Cloud 專案取得 API 金鑰,並將金鑰儲存在某處:

ae2db169e6a94e4a.png

  1. 前往 Cloud Shell 終端機
  2. 使用下列指令複製存放區:
git clone https://github.com/AbiramiSukumaran/toysearch

cd toysearch
  1. 複製存放區後,您應該就能透過 Cloud Shell 編輯器存取專案。
  2. 您需要從複製的專案中刪除「get-toys-alloydb」和「toolbox-toys」資料夾,因為這兩個資料夾是 Cloud Run 函式程式碼,可在需要時從存放區參照。
  3. 請務必先設定所有必要的環境變數,再建構及部署應用程式。請前往 Cloud Shell 終端機並執行以下操作:
PROJECT_ID=$(gcloud config get-value project)

export PROJECT_ID $PROJECT_ID

export GOOGLE_API_KEY <YOUR API KEY that you saved>
  1. 在本機建構並執行應用程式:

請確認您位於專案目錄中,然後執行下列指令:

mvn package

mvn spring-boot:run 
  1. 在 Cloud Run 上部署
gcloud run deploy --source .

12. 瞭解生成式 AI 的詳細資訊

您不需要採取任何行動。請注意:

您已準備好要部署的應用程式,現在請花點時間瞭解我們如何執行搜尋 (文字和圖片) 和產生作業。

  1. 使用者以文字為基礎的向量搜尋:

我們在「使用 Vector Search 應用程式網頁」一節中部署的 Cloud Run 函式已解決這個問題。

  1. 圖片上傳向量搜尋:

假設使用者想上傳熟悉的玩具相片,並以此進行搜尋,而非輸入文字做為背景資訊。使用者可以上傳自己喜歡的玩具圖片,並透過這項功能取得相關功能。

我們會運用 Google 的 Gemini 2.0 Flash 模型 (使用 LangChain4j 叫用) 來分析圖片,並擷取相關的背景資訊,例如玩具的顏色、材質、類型和適用年齡層。

我們只需透過 5 個步驟,就能使用開放原始碼架構,將使用者的多模態資料輸入與大型語言模型叫用相符的結果進行比對。瞭解如何:

package cloudcode.helloworld.web;

import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.googleai.GoogleAiGeminiChatModel;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.data.message.ImageContent;
import dev.langchain4j.data.message.TextContent;
import java.util.Base64;
import java.util.Optional;

public class GeminiCall {
  public String imageToBase64String(byte[] imageBytes) {
    String base64Img = Base64.getEncoder().encodeToString(imageBytes);
    return base64Img;
  }

  public String callGemini(String base64ImgWithPrefix) throws Exception {
    String searchText = "";

    // 1. Remove the prefix
    String base64Img = base64ImgWithPrefix.replace("data:image/jpeg;base64,", "");

    // 2. Decode base64 to bytes
    byte[] imageBytes = Base64.getDecoder().decode(base64Img);
    String image = imageToBase64String(imageBytes);

    // 3. Get API key from environment variable
        String apiKey = Optional.ofNullable(System.getenv("GOOGLE_API_KEY"))
                .orElseThrow(() -> new IllegalArgumentException("GOOGLE_API_KEY environment variable not set"));

    // 4. Invoke Gemini 2.0
    ChatLanguageModel gemini = GoogleAiGeminiChatModel.builder()
        .apiKey(apiKey)
        .modelName("gemini-2.0-flash-001")
        .build();

    Response<AiMessage> response = gemini.generate(
        UserMessage.from(
            ImageContent.from(image, "image/jpeg"),
            TextContent.from(
                "The picture has a toy in it. Describe the toy in the image in one line. Do not add any prefix or title to your description. Just describe that toy that you see in the image in one line, do not describe the surroundings and other objects around the toy in the image. If you do not see any toy in the image, send  response stating that no toy is found in the input image.")));
   
    // 5. Get the text from the response and send it back to the controller
    searchText = response.content().text().trim();
    System.out.println("searchText inside Geminicall: " + searchText);
    return searchText;
  }
}
  1. 瞭解我們如何使用 Imagen 3 搭配生成式 AI,根據使用者要求建構客製化玩具。

接著,Imagen 3 會產生自訂玩具的圖片,讓使用者清楚看到自己的創作。我們只需 5 個步驟就能完成:

// Generate an image using a text prompt using an Imagen model
    public String generateImage(String projectId, String location, String prompt)
        throws ApiException, IOException {
      final String endpoint = String.format("%s-aiplatform.googleapis.com:443", location);
      PredictionServiceSettings predictionServiceSettings =
      PredictionServiceSettings.newBuilder().setEndpoint(endpoint).build();
     
      // 1. Set up the context and prompt
      String context = "Generate a photo-realistic image of a toy described in the following input text from the user. Make sure you adhere to all the little details and requirements mentioned in the prompt. Ensure that the user is only describing a toy. If it is anything unrelated to a toy, politely decline the request stating that the request is inappropriate for the current context. ";
      prompt = context + prompt;

      // 2. Initialize a client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      try (PredictionServiceClient predictionServiceClient =
          PredictionServiceClient.create(predictionServiceSettings)) {
 
      // 3. Invoke Imagen 3
        final EndpointName endpointName =
            EndpointName.ofProjectLocationPublisherModelName(
                projectId, location, "google", "imagen-3.0-generate-001"); //"imagegeneration@006"; imagen-3.0-generate-001
        Map<String, Object> instancesMap = new HashMap<>();
        instancesMap.put("prompt", prompt);
        Value instances = mapToValue(instancesMap);
        Map<String, Object> paramsMap = new HashMap<>();
        paramsMap.put("sampleCount", 1);
        paramsMap.put("aspectRatio", "1:1");
        paramsMap.put("safetyFilterLevel", "block_few");
        paramsMap.put("personGeneration", "allow_adult");
        paramsMap.put("guidanceScale", 21);
        paramsMap.put("imagenControlScale", 0.95); //Setting imagenControlScale
        Value parameters = mapToValue(paramsMap);
       
      // 4. Get prediction response image
        PredictResponse predictResponse =
            predictionServiceClient.predict(
                endpointName, Collections.singletonList(instances), parameters);

      // 5. Return the Base64 Encoded String to the controller
        for (Value prediction : predictResponse.getPredictionsList()) {
          Map<String, Value> fieldsMap = prediction.getStructValue().getFieldsMap();
          if (fieldsMap.containsKey("bytesBase64Encoded")) {
            bytesBase64EncodedOuput = fieldsMap.get("bytesBase64Encoded").getStringValue();
        }
      }
      return bytesBase64EncodedOuput.toString();
    }
  }

價格預測

在上述前一個部分,我們討論了 Imagen 如何產生使用者想自行設計的玩具圖像。為了讓他們能夠購買,應用程式需要設定價格,我們採用了直覺的邏輯,為客製化訂製玩具定價。邏輯是使用使用者設計的玩具中,最相符 (說明方面) 的前 5 個玩具的平均價格。

生成玩具的價格預測是此應用程式的重要部分,我們使用代理人方法產生這項資訊。隆重推出資料庫專用的生成式 AI 工具箱

13. 資料庫適用的生成式 AI 工具箱

資料庫專用的生成式 AI 工具箱是 Google 推出的開放原始碼伺服器,可讓您更輕鬆地建構生成式 AI 工具,與資料庫互動。它可處理連線資源池、驗證等複雜作業,讓您更輕鬆、快速且安全地開發工具。這項服務可協助您建構生成式 AI 工具,讓服務專員存取資料庫中的資料。

請按照下列步驟設定,讓工具做好準備,並讓應用程式成為代理程式:Toolbox 程式碼研究室連結

應用程式現在可以使用這個已部署的 Cloud Run 函式端點,為自訂玩具圖片填入價格,以及產生的 Imagen 結果。

14. 測試網頁應用程式

應用程式的所有元件都已建構及部署完成,現在可在雲端提供服務。請針對所有情境測試應用程式。以下是影片連結,您可以瞭解可能會發生的情況:

https://www.youtube.com/shorts/ZMqUAWsghYQ

到達網頁如下所示:

241db19e7176e93e.png

15. 清理

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

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

16. 恭喜

恭喜!您已成功使用 AlloyDB、pgvector、Imagen 和 Gemini 2.0 執行玩具店的內容搜尋和產生作業,並利用開放原始碼程式庫建構穩健的整合功能。結合 AlloyDBVertex AIVector Search 的功能,我們已大幅改善內容搜尋和向量搜尋的易用性、效率和意義。