1. סקירה כללית
תארו לעצמכם שאתם נכנסים לחנות צעצועים באופן וירטואלי או פיזי, ומוצאים בקלות את המתנה המושלמת. אתם יכולים לתאר את מה שאתם מחפשים, להעלות תמונה של צעצוע או אפילו לעצב יצירה משלכם, והחנות תבין מיד את הצרכים שלכם ותספק חוויה מותאמת אישית. זה לא פנטזיה עתידנית, אלא מציאות שמונעת על ידי AI, טכנולוגיית ענן וחזון של מסחר אלקטרוני מותאם אישית.
האתגר: לפעמים קשה למצוא את המוצר המושלם שמתאים בדיוק למה שדמיינתם. חיפושים כלליים, מילות מפתח וחיפושים משוערים לרוב לא מספיקים, גלישה באינסוף דפים יכולה להיות מייגעת, והפער בין מה שאתם מדמיינים לבין מה שזמין יכול להוביל לתסכול.
הפתרון: אפליקציית ההדגמה מתמודדת עם האתגר הזה באופן ישיר, ומנצלת את היכולות של ה-AI כדי לספק חוויה מותאמת אישית וחלקה באמת, עם חיפוש לפי הקשר ויצירה בהתאמה אישית של המוצר שתואם להקשר החיפוש.
מה תפַתחו
במסגרת ה-Lab הזה:
- יצירת מכונת AlloyDB וטעינת מערך הנתונים Toys
- הפעלת התוספים pgvector ומודל AI גנרטיבי ב-AlloyDB
- יצירת הטמעות מתיאור המוצר וביצוע חיפוש בזמן אמת של דמיון קוסינוס לטקסט החיפוש של המשתמש
- הפעלת Gemini 2.0 Flash כדי לתאר את התמונה שהמשתמש העלה לחיפוש צעצועים לפי הקשר
- הפעלת Imagen 3 כדי ליצור צעצוע בהתאמה אישית על סמך תחומי העניין של המשתמש
- הפעלת כלי לחיזוי מחירים שנוצר באמצעות Gen AI Toolbox for Databases כדי לקבל פרטי מחיר של צעצוע שנוצר בהתאמה אישית
- פריסת הפתרון בפונקציות ללא שרת (serverless) ב-Cloud Run
דרישות
2. ארכיטקטורה
זרימת הנתונים: נבחן מקרוב את האופן שבו הנתונים עוברים במערכת שלנו:
- חיפוש הקשרי באמצעות RAG (Retrieval Augmented Generation) מבוסס-AI
כך זה עובד: במקום לחפש רק 'מכונית אדומה', המערכת מבינה את הדברים הבאים:
"small vehicle suitable for a 3-year-old boy."
AlloyDB כבסיס: אנחנו משתמשים ב-AlloyDB, מסד נתונים מנוהל של Google Cloud שתואם ל-PostgreSQL, כדי לאחסן את נתוני הצעצועים שלנו, כולל תיאורים, כתובות URL של תמונות ומאפיינים רלוונטיים אחרים.
pgvector לחיפוש סמנטי: pgvector, תוסף ל-PostgreSQL, מאפשר לנו לאחסן הטמעות וקטוריות של תיאורי צעצועים ושל שאילתות חיפוש של משתמשים. כך מתאפשר חיפוש סמנטי, כלומר המערכת מבינה את המשמעות של המילים, ולא רק את מילות המפתח המדויקות.
דמיון קוסינוס לרלוונטיות: אנחנו משתמשים בדמיון קוסינוס כדי למדוד את הדמיון הסמנטי בין וקטור החיפוש של המשתמש לבין וקטורי תיאור הצעצוע, וכך להציג את התוצאות הרלוונטיות ביותר.
אינדקס ScaNN למהירות ודיוק: כדי להבטיח תוצאות מהירות ומדויקות, במיוחד כשהמלאי של הצעצועים שלנו גדל, אנחנו משלבים את אינדקס ScaNN (Scalable Nearest Neighbors). השיפור הזה מגדיל באופן משמעותי את היעילות של חיפוש הווקטורים ואת היכולת שלו לאחזר מידע.
- חיפוש והבנה מבוססי תמונות באמצעות Gemini 2.0 Flash
במקום להקליד את ההקשר כטקסט, נניח שהמשתמש רוצה להעלות תמונה של צעצוע מוכר שהוא רוצה לחפש איתה. המשתמשים יכולים להעלות תמונה של צעצוע שהם אוהבים ולקבל מידע רלוונטי עליו. אנחנו משתמשים במודל Gemini 2.0 Flash של Google, שמופעל באמצעות LangChain4j, כדי לנתח את התמונה ולחלץ הקשר רלוונטי, כמו הצבע, החומר, הסוג וקבוצת הגיל שהצעצוע מיועד לה.
- איך יוצרים את צעצוע החלומות בהתאמה אישית באמצעות AI גנרטיבי: Imagen 3
הקסם האמיתי קורה כשהמשתמשים מחליטים ליצור צעצוע משלהם. באמצעות Imagen 3, אנחנו מאפשרים להם לתאר את צעצוע החלומות שלהם באמצעות הנחיות טקסט פשוטות. תארו לעצמכם שאתם אומרים: "אני רוצה דרקון פרוותי עם כנפיים סגולות ופנים חמודות" ורואים את הדרקון הזה קם לתחייה על המסך! Imagen 3 יוצר תמונה של הצעצוע המותאם אישית, וכך המשתמש יכול לראות בבירור את היצירה שלו.
- חיזוי מחירים באמצעות סוכנים ו-MCP Toolbox for Databases
הטמענו תכונה של חיזוי מחירים, שמעריכה את העלות של ייצור הצעצוע המותאם אישית. התכונה הזו מבוססת על סוכן שכולל כלי מתוחכם לחישוב מחירים.
MCP Toolbox for Databases: הסוכן הזה משולב בצורה חלקה עם מסד הנתונים שלנו באמצעות הכלי החדש של Google בקוד פתוח, MCP Toolbox for Databases. כך הסוכן יכול לגשת לנתונים בזמן אמת על עלויות חומרים, תהליכי ייצור וגורמים רלוונטיים אחרים כדי לספק הערכת מחיר מדויקת. מידע נוסף זמין כאן.
- Java Spring Boot, Gemini Code Assist ו-Cloud Run לפיתוח יעיל ופריסה ללא שרת
האפליקציה כולה מבוססת על Java Spring Boot, framework חזק וניתן להרחבה. השתמשנו ב-Gemini Code Assist לאורך תהליך הפיתוח, במיוחד לפיתוח חזיתי, וכך קיצרנו משמעותית את מחזור הפיתוח ושיפרנו את איכות הקוד. השתמשנו ב-Cloud Run כדי לפרוס את כל האפליקציה וב-Cloud Run Functions כדי לפרוס את מסד הנתונים ואת הפונקציות של הסוכן כנקודות קצה עצמאיות.
3. לפני שמתחילים
יצירת פרויקט
- ב-מסוף Google Cloud, בדף לבחירת הפרויקט, בוחרים או יוצרים פרויקט ב-Google Cloud.
- הקפידו לוודא שהחיוב מופעל בפרויקט שלכם ב-Cloud. כך בודקים אם החיוב מופעל בפרויקט
- תשתמשו ב-Cloud Shell, סביבת שורת פקודה שפועלת ב-Google Cloud ומגיעה עם bq שנטען מראש. לוחצים על 'הפעלת Cloud Shell' בחלק העליון של מסוף Google Cloud.

- אחרי שמתחברים ל-Cloud Shell, אפשר לבדוק שכבר בוצע אימות ושהפרויקט מוגדר לפי מזהה הפרויקט באמצעות הפקודה הבאה:
gcloud auth list
- מריצים את הפקודה הבאה ב-Cloud Shell כדי לוודא שפקודת gcloud מכירה את הפרויקט.
gcloud config list project
- אם הפרויקט לא מוגדר, משתמשים בפקודה הבאה כדי להגדיר אותו:
gcloud config set project <YOUR_PROJECT_ID>
- מפעילים את ממשקי ה-API הנדרשים על ידי הרצת הפקודות הבאות אחת אחרי השנייה בטרמינל של Cloud Shell:
יש גם פקודה אחת להפעלת הפעולות שבהמשך, אבל אם יש לכם חשבון ניסיון, יכול להיות שתיתקלו בבעיות שקשורות למכסה כשאתם מנסים להפעיל את הפעולות האלה בכמות גדולה. לכן הפקודות מופיעות בשורה נפרדת.
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. הגדרת מסד נתונים
בשיעור ה-Lab הזה נשתמש ב-AlloyDB כמסד הנתונים שיכיל את הנתונים של חנות הצעצועים. הוא משתמש באשכולות כדי להכיל את כל המשאבים, כמו מסדי נתונים ויומנים. לכל אשכול יש מופע ראשי שמספק נקודת גישה לנתונים. הטבלאות יכילו את הנתונים בפועל.
ניצור אשכול, מכונה וטבלה של AlloyDB שבהם ייטען מערך הנתונים של המסחר האלקטרוני.
יצירת אשכול ומופע
- עוברים לדף AlloyDB במסוף Cloud. דרך קלה למצוא את רוב הדפים ב-Cloud Console היא לחפש אותם באמצעות סרגל החיפוש של המסוף.
- בדף הזה, לוחצים על יצירת אשכול:

- יוצג מסך כמו זה שבהמשך. יוצרים אשכול ומופע עם הערכים הבאים (אם משכפלים את קוד האפליקציה מהמאגר, חשוב לוודא שהערכים זהים):
- מזהה האשכול: "
vector-cluster" - password: "
alloydb" - תואם ל-PostgreSQL 15
- אזור: "
us-central1" - רשת: "
default"

- כשבוחרים את רשת ברירת המחדל, מוצג מסך כמו זה שבהמשך.
לוחצים על הגדרת חיבור.
- משם, בוחרים באפשרות שימוש בטווח כתובות IP שהוקצה באופן אוטומטי ולוחצים על 'המשך'. אחרי שבודקים את המידע, לוחצים על CREATE CONNECTION (יצירת חיבור).

- אחרי שמגדירים את הרשת, אפשר להמשיך ליצור את האשכול. לוחצים על CREATE CLUSTER (יצירת אשכול) כדי להשלים את הגדרת האשכול, כמו שמוצג בהמשך:

חשוב לשנות את מזהה המופע ל
vector-instance
אם אי אפשר לשנות את המזהה, חשוב לשנות את מזהה המופע בכל ההפניות הבאות.
שימו לב: תהליך יצירת האשכול יימשך כ-10 דקות. אחרי שהפעולה תסתיים בהצלחה, יוצג מסך עם סקירה כללית של האשכול שיצרתם.
5. הטמעת נתונים
עכשיו צריך להוסיף טבלה עם הנתונים על החנות. עוברים אל AlloyDB, בוחרים את האשכול הראשי ואז את AlloyDB Studio:

יכול להיות שתצטרכו לחכות עד שהמופע שלכם יסיים את תהליך היצירה. אחרי שזה קורה, נכנסים ל-AlloyDB באמצעות פרטי הכניסה שיצרתם כשנוצר האשכול. משתמשים בנתונים הבאים כדי לבצע אימות ב-PostgreSQL:
- שם משתמש : "
postgres" - מסד נתונים : "
postgres" - סיסמה : "
alloydb"
אחרי שתעברו בהצלחה את תהליך האימות ב-AlloyDB Studio, תוכלו להזין פקודות SQL בכלי העריכה. אפשר להוסיף כמה חלונות של Editor באמצעות סימן הפלוס שמימין לחלון האחרון.

מזינים פקודות ל-AlloyDB בחלונות העריכה, ומשתמשים באפשרויות Run (הפעלה), Format (עיצוב) ו-Clear (ניקוי) לפי הצורך.
הפעלת תוספים
כדי ליצור את האפליקציה הזו, נשתמש בתוספים pgvector ו-google_ml_integration. התוסף pgvector מאפשר לכם לאחסן ולחפש הטמעות של וקטורים. התוסף google_ml_integration מספק פונקציות שמשמשות לגישה לנקודות קצה (endpoints) של חיזוי ב-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)) ;
אחרי שהפקודה שלמעלה תופעל בהצלחה, תוכלו לראות את הטבלה במסד הנתונים.
הטמעת נתונים
בשיעור ה-Lab הזה נשתמש בנתוני בדיקה של כ-72 רשומות בקובץ ה-SQL הזה. הוא מכיל את השדות id, name, description, quantity, price, image_url. את שאר השדות ממלאים בהמשך שיעור ה-Lab.
מעתיקים רק את 5 השורות הראשונות או את הצהרות ההוספה, מדביקים אותן בכרטיסייה ריקה בעורך ובוחרים באפשרות 'הפעלה'. אם אין לכם חשבון לחיוב של תקופת ניסיון, כנראה שתוכלו להעתיק את כל הצהרות ההוספה ולהפעיל אותן.
כדי לראות את תוכן הטבלה, מרחיבים את הקטע 'כלי המחקר' עד שרואים את הטבלה שנקראת apparels. לוחצים על סמל האפשרויות הנוספות (⋮) כדי לראות את האפשרות 'שאילתת הטבלה'. הוראת SELECT תיפתח בכרטיסייה חדשה של Editor.

מתן הרשאה
מריצים את ההצהרה הבאה כדי להעניק הרשאות הרצה בפונקציה embedding למשתמש postgres:
GRANT EXECUTE ON FUNCTION embedding TO postgres;
נותנים לחשבון השירות של AlloyDB את התפקיד Vertex AI User
עוברים לטרמינל של 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. יצירת הטמעות להקשר
למחשבים קל יותר לעבד מספרים מאשר לעבד טקסט. מערכת הטמעה ממירה טקסט לסדרה של מספרים עם נקודה עשרונית, שאמורים לייצג את הטקסט, לא משנה איך הוא מנוסח, באיזו שפה הוא כתוב וכו'.
אפשר לתאר מיקום ליד הים. יכול להיות שהם יופיעו עם תיאורים כמו 'על המים', 'מול החוף', 'הליכה מהחדר לאוקיינוס', 'sur la mer', 'на берегу океана' וכו'. המונחים האלה נראים שונים, אבל המשמעות הסמנטית שלהם, או במינוח של למידת מכונה, ההטמעות שלהם, צריכה להיות דומה מאוד.
עכשיו, כשהנתונים וההקשר מוכנים, נריץ את ה-SQL כדי להוסיף את ההטמעות של תיאור המוצר לטבלה בשדה embedding. יש מגוון של מודלים להטמעה שאפשר להשתמש בהם. אנחנו משתמשים ב-text-embedding-005 מ-Vertex AI. חשוב להשתמש באותו מודל הטמעה בכל הפרויקט.
הערה: אם אתם משתמשים בפרויקט קיים ב-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;
הפונקציה אמורה להחזיר את וקטור ההטמעה, שנראה כמו מערך של מספרים עשרוניים, עבור תיאור הצעצוע כמו שמוצג בהמשך:

הערה: יכול להיות שבפרויקטים חדשים ב-Google Cloud במסגרת התוכנית החינמית תהיה בעיה עם המכסה של מספר בקשות ההטמעה שמותרות בשנייה למודלים של הטמעה. מומלץ להשתמש בשאילתת סינון למזהה, ואז לבחור באופן סלקטיבי 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 2;
בואו נבחן את השאילתה הזו בפירוט:
בשאילתה הזו,
- טקסט החיפוש של המשתמש הוא: "
I want a white plush teddy bear toy with a floral pattern." - אנחנו ממירים אותו להטמעות בשיטה
embedding()באמצעות המודל:text-embedding-005. השלב הזה אמור להיראות מוכר אחרי השלב הקודם, שבו הפעלנו את פונקציית ההטמעה על כל הפריטים בטבלה. -
<=>מייצג את השימוש בשיטת המרחק COSINE SIMILARITY. אפשר למצוא את כל מדדי הדמיון שזמינים בתיעוד של pgvector. - אנחנו ממירים את התוצאה של שיטת ההטמעה לסוג וקטור כדי שתהיה תואמת לווקטורים שמאוחסנים במסד הנתונים.
- הערך LIMIT 5 מציין שאנחנו רוצים לחלץ 5 שכנים קרובים ביותר לטקסט החיפוש.
התוצאה נראית כך:

כפי שאפשר לראות בתוצאות, ההתאמות קרובות מאוד לטקסט החיפוש. נסו לשנות את הטקסט כדי לראות איך התוצאות משתנות.
הערה חשובה:
נניח שאנחנו רוצים לשפר את הביצועים (זמן השאילתה), היעילות וההחזרה של תוצאת החיפוש הווקטורי הזו באמצעות אינדקס ScaNN. כדאי לקרוא את השלבים שמופיעים בבלוג הזה כדי להשוות את ההבדל בתוצאות עם האינדקס ובלי האינדקס.
שלב אופציונלי: שיפור היעילות וההחזרה באמצעות אינדקס ScaNN
אפשר לדלג על השלב הזה אם מספר הרשומות קטן מ-100.
לנוחותכם, ריכזנו כאן את השלבים ליצירת אינדקס:
- מכיוון שכבר יצרנו את האשכול, המופע, ההקשר וההטמעות, אנחנו צריכים רק להתקין את התוסף ScaNN באמצעות ההצהרה הבאה:
CREATE EXTENSION IF NOT EXISTS alloydb_scann;
- בשלב הבא ניצור את האינדקס (ScaNN):
CREATE INDEX toysearch_index ON toys
USING scann (text_embeddings cosine)
WITH (num_leaves=9);
בדוגמת ה-DDL שלמעלה, apparel_index הוא שם האינדקס
'toys' היא הטבלה שלי
scann היא שיטת האינדקס
'embedding' היא העמודה בטבלה שאני רוצה ליצור לה אינדקס
'cosine' היא שיטת המרחק שבה אני רוצה להשתמש עם האינדקס
'8' הוא מספר המחיצות שיש להחיל על האינדקס הזה. הערך יכול להיות בין 1 ל-1,048,576. מידע נוסף על קביעת הערך הזה זמין במאמר בנושא התאמה של אינדקס ScaNN.
השתמשתי בשורש הריבועי של מספר נקודות הנתונים, כמומלץ במאגר ScaNN (בחלוקה למחיצות, הערך של num_leaves צריך להיות בערך השורש הריבועי של מספר נקודות הנתונים).
- בודקים אם האינדקס נוצר באמצעות השאילתה:
SELECT * FROM pg_stat_ann_indexes;
- מבצעים חיפוש וקטורי באמצעות אותה שאילתה שבה השתמשנו בלי האינדקס:
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. אבל עכשיו השדה הזה מתווסף לאינדקס.
- כדי לבדוק, מריצים שאילתת חיפוש פשוטה עם האינדקס ובלי האינדקס (על ידי השמטת האינדקס):
בתרחיש השימוש הזה יש רק 72 רשומות, ולכן האינדקס לא באמת משפיע. בדיקה שנערכה בתרחיש לדוגמה אחר הניבה את התוצאות הבאות:
אותה שאילתת חיפוש וקטורי על נתוני ההטמעות שעברו אינדוקס מובילה לתוצאות חיפוש איכותיות ויעילות. היעילות משתפרת באופן משמעותי (במונחים של זמן ביצוע: 10.37 אלפיות השנייה ללא ScaNN ו-0.87 אלפיות השנייה עם ScaNN) עם האינדקס. מידע נוסף על הנושא הזה זמין בבלוג.
8. אימות ההתאמה באמצעות מודל שפה גדול (LLM)
לפני שנמשיך וניצור שירות להחזרת ההתאמות הטובות ביותר לאפליקציה, נשתמש במודל AI גנרטיבי כדי לוודא שהתשובות הפוטנציאליות האלה באמת רלוונטיות ובטוחות לשיתוף עם המשתמש.
איך מוודאים שהמופע מוגדר ל-Gemini
קודם צריך לבדוק אם השילוב של Google ML כבר מופעל באשכול ובמופע שלכם. ב-AlloyDB Studio, מזינים את הפקודה הבאה:
show google_ml_integration.enable_model_support;
אם הערך שמוצג הוא on, אפשר לדלג על 2 השלבים הבאים ולעבור ישירות להגדרת השילוב של AlloyDB ו-Vertex AI Model.
- עוברים למופע הראשי של אשכול AlloyDB ולוחצים על EDIT PRIMARY INSTANCE (עריכת המופע הראשי).

- עוברים לקטע Flags (דגלים) באפשרויות ההגדרה המתקדמות. מוודאים שהערך של
google_ml_integration.enable_model_support flagמוגדר ל-on, כמו שמוצג בהמשך:

אם האפשרות לא מוגדרת כ'מופעלת', מגדירים אותה כ'מופעלת' ואז לוחצים על הלחצן UPDATE INSTANCE (עדכון המופע). השלב הזה יימשך כמה דקות.
שילוב של מודלים של AlloyDB ו-Vertex AI
עכשיו אפשר להתחבר ל-AlloyDB Studio ולהריץ את פקודת ה-DML הבאה כדי להגדיר גישה למודל Gemini מ-AlloyDB, באמצעות מזהה הפרויקט במקום שצוין. יכול להיות שתופיע אזהרה על שגיאת תחביר לפני הרצת הפקודה, אבל היא אמורה לפעול בצורה תקינה.
קודם כל, יוצרים את החיבור למודל Gemini 1.5 כמו שמוצג למטה. חשוב להחליף את $PROJECT_ID בפקודה שלמטה במזהה הפרויקט ב-Google Cloud.
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 שלמעלה. בנוסף, יכול להיות שתצטרכו להריץ שוב את הפקודה grant execute on function ml_predict_row במקרה שתיתקלו בבעיות בהפעלות הקרובות של gemini-1.5.
בדיקת התשובות
בקטע הבא נשתמש בשאילתה גדולה אחת כדי לוודא שהתשובות שמתקבלות מהשאילתה סבירות, אבל יכול להיות שיהיה קשה להבין את השאילתה. עכשיו נבחן את החלקים ונראה איך הם מתחברים יחד בעוד כמה דקות.
- קודם נשלח בקשה למסד הנתונים כדי לקבל את 10 ההתאמות הכי קרובות לשאילתת משתמש.
- כדי לקבוע את מידת התוקף של התשובות, נשתמש בשאילתה חיצונית שבה נסביר איך להעריך את התשובות. היא משתמשת בשדה
recommended_textשהוא טקסט החיפוש ובשדהcontent(שהוא שדה תיאור הצעצוע) של הטבלה הפנימית כחלק מהשאילתה. - על סמך זה, נבדוק את איכות התשובות שמתקבלות.
- הפונקציה
predict_rowמחזירה את התוצאה שלה בפורמט JSON. הקוד-> 'candidates' -> 0 -> 'content' -> 'parts' -> 0 -> 'text'"משמש לחילוץ הטקסט בפועל מ-JSON. כדי לראות את ה-JSON בפועל שמוחזר, אפשר להסיר את הקוד הזה. - לבסוף, כדי לקבל את התשובה של ה-LLM, אנחנו מחלצים אותה באמצעות
REGEXP_REPLACE(gemini_validation,'[^a-zA-Z,: ]','','g')
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 1) 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 מוגדר כברירת מחדל להזרמה, ולכן התשובה בפועל מפוזרת על פני כמה שורות:

9. העברת אפליקציית חיפוש הצעצועים לשרת בענן ללא שרת
רוצה להעביר את האפליקציה הזו לאינטרנט? כדי להפוך את מנוע הידע הזה ל-Serverless באמצעות Cloud Run Functions:
- נכנסים אל Cloud Run Functions במסוף Google Cloud כדי ליצור פונקציית Cloud Run חדשה, או משתמשים בקישור: https://console.cloud.google.com/functions/add.
- בוחרים באפשרות פונקציית Cloud Run בתור הסביבה. מזינים את שם הפונקציה get-toys-alloydb ובוחרים באזור us-central1. מגדירים את האימות ל'מתן הרשאה להפעלות לא מאומתות' ולוחצים על הבא. בוחרים באפשרות Java 17 כסביבת זמן ריצה ובאפשרות Inline Editor לקוד המקור.
- כברירת מחדל, נקודת הכניסה מוגדרת כ-
gcfv2.HelloHttpFunction. מחליפים את קוד placeholder ב-HelloHttpFunction.javaוב-pom.xmlשל פונקציית Cloud Run בקוד מ-HelloHttpFunction.java ומ-pom.xml בהתאמה. - חשוב לזכור לשנות את ה-placeholder <<YOUR_PROJECT>> ואת פרטי הכניסה לחיבור ל-AlloyDB לערכים שלכם בקובץ Java. פרטי הכניסה ל-AlloyDB הם אותם פרטי כניסה שבהם השתמשנו בתחילת ה-codelab. אם השתמשתם בערכים שונים, עליכם לשנות אותם בקובץ Java.
- לוחצים על פריסה.
אחרי הפריסה, כדי לאפשר ל-Cloud Function לגשת למכונת מסד הנתונים של AlloyDB, ניצור את מחבר ה-VPC.
שלב חשוב:
אחרי שמתחילים את הפריסה, הפונקציות אמורות להופיע ב-Cloud Run Functions console של Google. מחפשים את הפונקציה החדשה שנוצרה (get-toys-alloydb), לוחצים עליה ואז על עריכה ומשנים את הפרטים הבאים:
- מעבר להגדרות של זמן ריצה, build, חיבורים ואבטחה
- הגדלת משך הזמן הקצוב לתפוגה ל-180 שניות
- עוברים לכרטיסייה CONNECTIONS (חיבורים):

- בהגדרות של Ingress, מוודאים שהאפשרות Allow all traffic (התרת כל התנועה) נבחרה.
- בקטע Egress settings (הגדרות יציאה), לוחצים על התפריט הנפתח Network (רשת) ובוחרים באפשרות Add New VPC Connector (הוספת מחבר VPC חדש). פועלים לפי ההוראות שמופיעות בתיבת הדו-שיח שקופצת:

- נותנים שם למחבר ה-VPC ומוודאים שהאזור זהה למופע. משאירים את ערך הרשת כברירת מחדל ומגדירים את תת-הרשת כטווח כתובות IP מותאם אישית עם טווח כתובות ה-IP 10.8.0.0 או טווח דומה שזמין.
- מרחיבים את האפשרות SHOW SCALING SETTINGS (הצגת הגדרות שינוי הגודל) ומוודאים שההגדרה מוגדרת בדיוק כמו שמופיע כאן:

- לוחצים על CREATE (יצירה). עכשיו המחבר הזה אמור להופיע בהגדרות היציאה.
- בוחרים את המחבר החדש שנוצר
- בוחרים שכל התנועה תנותב דרך מחבר ה-VPC הזה.
- לוחצים על הבא ואז על פריסה.
10. בדיקת פונקציית Cloud Run
אחרי שפורסים את הפונקציה המעודכנת ב-Cloud Functions, אמורה להופיע נקודת הקצה שנוצרה. מעתיקים את הערך הזה ומחליפים אותו בפקודה הבאה:
אפשר גם לבדוק את הפונקציה של Cloud Run באופן הבא:
PROJECT_ID=$(gcloud config get-value project)
curl -X POST <<YOUR_ENDPOINT>> \
-H 'Content-Type: application/json' \
-d '{"search":"I want a standing pink panther toy"}' \
| jq .
והתוצאה:

זהו! זה כל מה שצריך כדי לבצע חיפוש וקטורי של דמיון באמצעות מודל ההטמעות בנתוני AlloyDB.
11. פיתוח לקוח לאפליקציית אינטרנט
בחלק הזה, ניצור אפליקציית אינטרנט שבה המשתמש יוכל ליצור אינטראקציה ולמצוא צעצועים מתאימים על סמך טקסט, תמונה ואפילו ליצור צעצוע חדש על סמך הצרכים שלו. מכיוון שהאפליקציה כבר בנויה, אפשר לפעול לפי השלבים הבאים כדי להעתיק אותה אל סביבת הפיתוח המשולבת ולהפעיל את האפליקציה.
- אנחנו משתמשים ב-Gemini 2.0 Flash כדי לתאר את התמונה שהמשתמש עשוי להעלות כדי למצוא צעצועים תואמים, ולכן אנחנו צריכים לקבל את מפתח ה-API של האפליקציה הזו. כדי לעשות זאת, עוברים אל https://aistudio.google.com/apikey ומקבלים את מפתח ה-API של פרויקט Google Cloud הפעיל שבו אתם מטמיעים את האפליקציה הזו, ושומרים את המפתח במקום כלשהו:

- מעבר לטרמינל Cloud Shell
- משכפלים את המאגר באמצעות הפקודה הבאה:
git clone https://github.com/AbiramiSukumaran/toysearch
cd toysearch
- אחרי שיבוט המאגר, אמורה להיות לכם גישה לפרויקט מהכלי לעריכה ב-Cloud Shell.
- צריך למחוק את התיקיות get-toys-alloydb ו-toolbox-toys מהפרויקט המשוכפל, כי הן מכילות קוד של Cloud Run Functions שאפשר להפנות אליו מהמאגר כשצריך.
- עוברים אל GenerateToy.java בתיקיית האינטרנט, מוצאים את השורה הבאה ומסירים אותה כי יכול להיות שנדרשת הרשאה מיוחדת כדי לאפשר תוכן למבוגרים, וההרשאה הזו לא זמינה בחלק מהחשבונות לחיוב בתקופת הניסיון:
paramsMap.put("personGeneration", "allow_adult");
- לפני שיוצרים את האפליקציה ומפעילים אותה, צריך לוודא שכל משתני הסביבה הנדרשים מוגדרים. עוברים אל Cloud Shell Terminal ומריצים את הפקודה הבאה:
PROJECT_ID=$(gcloud config get-value project)
export PROJECT_ID=$PROJECT_ID
export GOOGLE_API_KEY=<YOUR API KEY that you saved>
- פיתוח והרצה של האפליקציה באופן מקומי:
מוודאים שאתם נמצאים בספריית הפרויקט ומריצים את הפקודות הבאות:
mvn package
mvn spring-boot:run
- פריסה ב-Cloud Run
gcloud run deploy --source .
12. הסבר על פרטי ה-AI הגנרטיבי
אין צורך בפעולה נוספת. חשוב שתדעו:
עכשיו, אחרי שהבנתם איך האפליקציה פועלת, כדאי להקדיש רגע כדי להבין איך ביצענו את החיפוש (טקסט ותמונה) והיצירה.
- חיפוש וקטור שמבוסס על טקסט של משתמש:
הבעיה הזו כבר נפתרה בפונקציות Cloud Run שפרסנו בקטע 'הפעלת אפליקציית חיפוש וקטורי באינטרנט'.
- חיפוש וקטורי על סמך העלאת תמונה:
במקום להקליד את ההקשר כטקסט, נניח שהמשתמש רוצה להעלות תמונה של צעצוע מוכר שהוא רוצה לחפש איתה. המשתמשים יכולים להעלות תמונה של צעצוע שהם אוהבים ולקבל מידע רלוונטי עליו.
אנחנו משתמשים במודל Gemini 2.0 Flash של Google, שמופעל באמצעות LangChain4j, כדי לנתח את התמונה ולחלץ הקשר רלוונטי, כמו הצבע, החומר, הסוג וקבוצת הגיל שהצעצוע מיועד לה.
ב-5 שלבים בלבד, לקחנו קלט נתונים מולטימודאלי של משתמשים והתאמנו אותו לתוצאות תואמות באמצעות הפעלה של מודל שפה גדול (LLM) עם מסגרת קוד פתוח. למידע נוסף:
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;
}
}
- הסבר על השימוש ב-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 הצעצועים הכי דומים (מבחינת תיאור) לצעצוע שהמשתמש מעצב.
חיזוי המחיר של הצעצוע שנוצר הוא חלק חשוב באפליקציה הזו, והשתמשנו בגישה מבוססת-סוכן כדי ליצור אותו. חדש: Gen AI Toolbox for Databases (ערכת כלים לבינה מלאכותית גנרטיבית למסדי נתונים).
13. ערכת כלים של AI גנרטיבי למסדי נתונים
Gen AI Toolbox for Databases הוא שרת קוד פתוח מבית Google שמקל על יצירת כלי AI גנרטיבי לאינטראקציה עם מסדי נתונים. הוא מאפשר לכם לפתח כלים בקלות, במהירות ובאופן מאובטח יותר, כי הוא מטפל במורכבויות כמו איגום חיבורים, אימות ועוד. הוא עוזר לכם ליצור כלים של AI גנרטיבי שמאפשרים לנציגים שלכם לגשת לנתונים במסד הנתונים.
כדי להכין את הכלי ולהפוך את סוכן האפליקציה שלנו לסוכן עצמאי, צריך לפעול לפי השלבים הבאים: קישור ל-Codelab של Toolbox
האפליקציה יכולה עכשיו להשתמש בנקודת הקצה של פונקציית Cloud Run שפרסתם כדי לאכלס את המחיר יחד עם התוצאה שנוצרה על ידי Imagen לתמונת הצעצוע בהזמנה אישית.
14. בדיקת אפליקציית האינטרנט
עכשיו, אחרי שכל הרכיבים של האפליקציה נוצרו ונפרסו, היא מוכנה להצגה בענן. בודקים את האפליקציה בכל התרחישים. הנה קישור לסרטון שמציג את מה שצפוי לכם:
https://www.youtube.com/shorts/ZMqUAWsghYQ
כך נראה דף הנחיתה:

15. הסרת המשאבים
כדי לא לצבור חיובים לחשבון Google Cloud על המשאבים שבהם השתמשתם במאמר הזה:
- במסוף Google Cloud, עוברים לדף Manage resources.
- ברשימת הפרויקטים, בוחרים את הפרויקט שרוצים למחוק ולוחצים על Delete.
- כדי למחוק את הפרויקט, כותבים את מזהה הפרויקט בתיבת הדו-שיח ולוחצים על Shut down.
16. מזל טוב
מעולה! ביצעתם בהצלחה חיפוש והפקת תוכן בהקשר של חנות צעצועים באמצעות AlloyDB, pgvector, Imagen ו-Gemini 2.0, תוך שימוש בספריות קוד פתוח כדי ליצור שילובים חזקים. שילוב היכולות של AlloyDB, Vertex AI ו-Vector Search מאפשר לנו לעשות קפיצת מדרגה משמעותית ולהפוך את החיפושים ההקשריים והווקטוריים לנגישים, יעילים ומבוססי-משמעות.