1. סקירה כללית
מהו ניהול נתוני בסיס?
ניהול נתוני בסיס (MDM) הוא תהליך שבו יוצרים מקור יחיד ומהימן של נתונים מהותיים לארגון. תארו לעצמכם ספרייה מאורגנת בקפידה שבה כל ספר (נקודה על הגרף) מתויג בצורה נכונה, מעודכן וקל לאיתור.
נתוני הבסיס מייצגים את ישויות הליבה העסקיות, שהן חיוניות לפעילות של החברה. אלה הרכיבים העיקריים של נתוני הבסיס:
- ישויות עסקיות: ישויות כמו לקוחות, מוצרים, ספקים, מיקומים ועובדים, שהן שמות העצם שהעסק מתבסס עליהם
- מזהים: מזהים ייחודיים שמבטיחים שכל ישות תהיה נפרדת וניתנת למעקב במערכות שונות
- מאפיינים: המאפיינים שמתארים כל ישות, לדוגמה, כתובת של לקוח, מחיר של מוצר וכו'.
כדי להבין טוב יותר את נתוני הבסיס, נשווה אותם לנתונים טרנזקציוניים. נתונים טרנזקציוניים מתעדים אירועים ספציפיים (רכישה, משלוח וכו'). לעומת זאת, נתוני הבסיס מספקים את ההקשר לאירועים האלה על ידי הגדרת הישויות שמעורבות בהם. לדוגמה, עסקת מכירה מקושרת לנתוני האב של הלקוח, המוצר והמוכר.
הטמעה של MDM חזק היא חיונית לקבלת החלטות אסטרטגיות, אבל היא יכולה להיות מורכבת ולדרוש משאבים רבים. כאן נכנס לתמונה הכוח הטרנספורמטיבי של AI גנרטיבי, במיוחד מודלים כמו Gemini 1.0 Pro, Gemini 1.0 Pro Vision ו-Gemini 1.5 Pro.
2. מטרה
ב-Codelab הזה תלמדו איך Gemini 1.0 Pro מפשט יישומים של ניהול נתוני בסיס, כמו העשרה וביטול כפילויות, עבור הנתונים של citibike_stations שזמינים במערך הנתונים הציבורי של BigQuery.
מה תצטרכו
- מערך נתונים ציבורי של BigQuery
bigquery-public-data.new_york_citibike. - הפעלת פונקציות ב-Gemini (פונקציית Java Cloud Function שמקבלת את פרטי הכתובת באמצעות Reverse Geocoding API לקואורדינטות שזמינות בנתונים של citibike_stations).
- Vertex AI Embeddings API ו-Vector Search ב-BigQuery כדי לזהות כפילויות.
מה תפַתחו
- תצטרכו ליצור מערך נתונים ב-BigQuery לתרחיש השימוש. במערך הנתונים הזה, תיצרו טבלת נחיתה עם נתונים מהטבלה
bigquery-public-data.new_york_citibike.citibike_stationsשל מערך הנתונים שגלוי לכולם. - תפרסו את הפונקציה של Cloud Functions שכוללת את התכונה 'בקשה להפעלת פונקציה' ב-Gemini לצורך סטנדרטיזציה של כתובות.
- תאחסנו את נתוני הכתובות המועשרים בטבלאות הנחיתה (משני המקורות שסופקו להדגמה הזו).
- תפעילו את Vertex AI Embeddings API מ-BigQuery על נתוני הכתובות.
- תשתמשו בחיפוש וקטורי ב-BigQuery כדי לזהות רשומות כפולות.
הדיאגרמה הבאה מייצגת את רצף הפעולות של הנתונים והשלבים שנדרשים להטמעה.

3. דרישות
4. לפני שמתחילים
- ב-מסוף 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, עוברים אל Gemini for Google Cloud Marketplace. אפשר גם להשתמש בפקודה הבאה במסוף של Cloud Shell:
gcloud services enable cloudaicompanion.googleapis.com --project PROJECT_ID
- מוודאים שממשקי ה-API של BigQuery, BigQuery Connection, Cloud Function, Cloud Run, Vertex AI ו-Cloud Build מופעלים. אפשרות נוספת היא להשתמש במסוף באמצעות הקישור הזה.
אפשר לעיין במאמרי העזרה בנושא פקודות gcloud ושימוש בהן.
5. יצירת מערך נתונים ב-BigQuery וחיבור חיצוני
נתחיל ביצירת מערך נתונים וקישור למשאב ב-Cloud.
מערך נתונים ב-BigQuery הוא מאגר של כל הטבלאות והאובייקטים של האפליקציה.
כדי ליצור מערך נתונים:
- נכנסים לדף BigQuery במסוף Google Cloud.
- בחלונית Explorer, בוחרים את הפרויקט שבו רוצים ליצור את מערך הנתונים.
- מרחיבים את האפשרות פעולות (סמל האליפסה האנכית) ולוחצים על יצירת מערך נתונים.

- מזינים
mdm_geminiבשדה Dataset ID (מזהה מערך הנתונים). - מגדירים את סוג המיקום כ-
Multi-regionומאשרים את ערך ברירת המחדל, שהואUS(multiple regions in United States. - לוחצים על יצירת מערך נתונים.
- בודקים שמערך הנתונים נוצר ומופיע מתחת למזהה הפרויקט בחלונית Explorer.
כדי ליצור אינטראקציה עם Cloud Function, צריך חיבור ל-BigQuery. כדי ליצור פונקציה מרוחקת, צריך ליצור חיבור ל-BigQuery. ב-codelab הזה נשתמש בחיבור BigLake כדי לגשת למודל מ-BigQuery דרך Cloud Function. חיבורי BigLake עוזרים לחבר את מקור הנתונים החיצוני תוך שמירה על בקרת גישה ואבטחה מדויקות של BigQuery, שבמקרה שלנו היא Vertex AI Gemini Pro API.
כדי ליצור את החיבור ל-BigLake:
- לוחצים על Add (הוספה) בחלונית Explorer (סייר) בדף BigQuery.

- לוחצים על חיבורים למקורות נתונים חיצוניים.
- ברשימה 'סוג החיבור', בוחרים באפשרות מודלים מרוחקים, פונקציות מרוחקות ו-BigLake של Vertex AI (משאב בענן).
- בשדה מזהה החיבור, מזינים את שם החיבור בתור
gemini-bq-conn. - מגדירים את סוג המיקום כ-
Multi-regionומאשרים את ערך ברירת המחדל, שהואUS(multiple regions in United States. - לוחצים על יצירת קישור.
- לוחצים על Go to connection (מעבר לקישור) ומעתיקים את מזהה חשבון השירות בחלונית Connection info (פרטי הקישור).

- עוברים לדף IAM ואדמין ולוחצים על Grant access (הענקת גישה).
- מדביקים את מזהה חשבון השירות בשדה New principles.
- בוחרים את התפקיד
Vertex AI userמרשימת התפקידים ולוחצים על שמירה.

יצרתם בהצלחה את מערך הנתונים ואת החיבור ל-BigQuery.
6. פריסת בקשות להפעלת פונקציות ב-Gemini (פונקציה ב-Cloud Functions ב-Java)
כדי לפרוס את פונקציית Cloud Functions ב-Java שכוללת בקשה להפעלת פונקציה ב-Gemini, פועלים לפי השלבים הבאים.
- משכפלים את מאגר GitHub מהטרמינל של Cloud Shell באמצעות הפקודה הבאה:
git clone https://github.com/AbiramiSukumaran/GeminiFunctionCalling
- מחליפים את ה-placeholders
YOUR_API_KEYו-YOUR_PROJECT_IDבערכים שלכם.
אם קראתם את הבלוג כאן, אתם יודעים שההטמעות של בקשות להפעלת פונקציות משתמשות ב-המרת קואורדינטות לכתובות (reverse geocoding) API. אפשר ליצור API_KEY משלכם לפי ההוראות שמופיעות כאן.
- בטרמינל של Cloud Shell, עוברים לספריית הפרויקט החדשה ששוכפלה GeminiFunctionCalling ומריצים את ההצהרה הבאה כדי ליצור ולפרוס את הפונקציה ב-Cloud Functions:
gcloud functions deploy gemini-fn-calling --gen2 --region=us-central1 --runtime=java11 --source=. --entry-point=cloudcode.helloworld.HelloWorld --trigger-http
אומרים "y" כשמוצגת השאלה "Allow unauthenticated invocations" (האם לאפשר הפעלות לא מאומתות). מומלץ להגדיר אימות לאפליקציות הארגוניות. אבל מכיוון שזו אפליקציית הדגמה, נמשיך ללא אימות.
הפלט הוא כתובת URL של REST בפורמט הבא:
https://us-central1-YOUR_PROJECT_ID.cloudfunctions.net/gemini-fn-calling
- מריצים את הפקודה הבאה במסוף כדי לבדוק את הפונקציה של Cloud Functions:
gcloud functions call gemini-fn-calling --region=us-central1 --gen2 --data '{"calls":[["40.714224,-73.961452"]]}'
תשובה להנחיה של מדגם אקראי:
'{"replies":["{ \"DOOR_NUMBER\": \"277\", \"STREET_ADDRESS\": \"Bedford Ave\", \"AREA\":
null, \"CITY\": \"Brooklyn\", \"TOWN\": null, \"COUNTY\": \"Kings County\", \"STATE\":
\"NY\", \"COUNTRY\": \"USA\", \"ZIPCODE\": \"11211\", \"LANDMARK\": null}}```"]}'
הפרמטרים של הבקשה והתגובה של פונקציה של Cloud Functions הזו מיושמים באופן שתואם להפעלת פונקציה מרחוק ב-BigQuery. אפשר להשתמש בו ישירות מנתוני BigQuery במקום. המשמעות היא שאם נתוני הקלט (נתוני קו רוחב וקו אורך) נמצאים ב-BigQuery, אפשר להפעיל את הפונקציה המרוחקת על הנתונים ולקבל את התגובה של הפונקציה, שאפשר לאחסן או לעבד אותה ישירות ב-BigQuery.
- מריצים את ה-DDL הבא מ-BigQuery כדי ליצור פונקציה מרוחקת שמפעילה את פונקציית Cloud Functions שפרסתם:
CREATE OR REPLACE FUNCTION
`mdm_gemini.MDM_GEMINI` (latlng STRING) RETURNS STRING
REMOTE WITH CONNECTION `us.gemini-bq-conn`
OPTIONS (
endpoint = 'https://us-central1-YOUR_PROJECT_ID.cloudfunctions.net/gemini-fn-calling', max_batching_rows = 1
);
בודקים את השאילתה כדי להשתמש בפונקציה החדשה שנוצרה מרחוק:
SELECT mdm_gemini.MDM_GEMINI(latlong) from mdm_gemini.CITIBIKE_STATIONS limit 1;
אם שאילתת הבדיקה שמשתמשת בפונקציה החדשה מרחוק שנוצרה ב-BigQuery נכשלת בגלל בעיה בהרשאות של Cloud Functions, עוברים אל Cloud Functions מ-מסוף Google Cloud ומאתרים את פונקציית Cloud Functions שנפרסה בשם gemini-fn-calling. עוברים לכרטיסייה 'הרשאות', מוסיפים את העיקרון בתור allUsers ומעניקים את התפקיד Cloud Functions Invoker כדי לוודא שכל המשתמשים יכולים לגשת ל-Cloud Functions (רק כי זו אפליקציית הדגמה).
7. פתרון עקיף
אם אין לכם את ה-API_KEY הנדרש לגישה לשיטת הקריאה לפונקציה של Reverse Geocoding, או אם לא פרסתם את Cloud Function, אתם יכולים לבצע את הפעולות הבאות כחלופה:
- מורידים את הקובץ CITIBIKE_STATIONS.csv ממאגר לתיקיית הפרויקט ב-Cloud Shell ומנווטים לתיקייה הזו.
- מייצאים את הנתונים מקובץ ה-CSV למערך הנתונים החדש ב-BigQuery
mdm_geminiבאמצעות הפקודה הבאה במסוף Cloud Shell:
bq load --source_format=CSV --skip_leading_rows=1 mdm_gemini.CITIBIKE_STATIONS ./CITIBIKE_STATIONS.csv \ name:string,latlng:string,capacity:numeric,num_bikes_available:numeric,num_docks_available:numeric,last_reported:timestamp,full_address_string:string
8. יצירת טבלה והעשרת נתוני כתובות
שלב 1: יוצרים את הטבלה
חשוב: אם השתמשתם בפתרון העקיף, אתם יכולים לדלג על השלב הזה כי כבר יצרתם את הטבלה.
אם לא השתמשתם בפתרון העקיף, מריצים את ה-DDL הבא בעורך ה-SQL של BigQuery:
CREATE TABLE mdm_gemini.CITIBIKE_STATIONS as (
select name, latitude || ',' || longitude as latlong, capacity, num_bikes_available, num_docks_available,last_reported,
'' as full_address_string
from bigquery-public-data.new_york_citibike.citibike_stations) ;
עכשיו נרחיב את נתוני הכתובות על ידי הפעלת הפונקציה המרוחקת על הקואורדינטות של קווי האורך והרוחב שזמינות בטבלה. מגדירים את התנאים הבאים לנתונים:
- דווחו בשנת 2024
- מספר האופניים הזמינים > 0
- הקיבולת גדולה מ-100
מריצים את השאילתה הבאה:
update `mdm_gemini.CITIBIKE_STATIONS`
set full_address_string = `mdm_gemini.MDM_GEMINI`(latlong)
where EXTRACT(YEAR FROM last_reported) = 2024 and num_bikes_available > 0 and capacity > 100;
שלב 2: יוצרים מקור שני לנתוני המיקום של תחנות האופניים
אל תדלגו על השלב הזה גם אם השתמשתם בפתרון העקיף כדי ליצור את הטבלה.
בשלב הזה תיצרו מקור שני של נתוני מיקום של תחנות השכרת אופניים לצורך ה-codelab הזה. הדוגמה הזו ממחישה שמערכת MDM מאגדת נתונים מכמה מקורות ומזהה את המקור המהימן.
מריצים את פקודות ה-DDL הבאות ב-BigQuery SQL Editor כדי ליצור את המקור השני של נתוני המיקום עם שני רשומות. נקרא לטבלה הזו mdm_gemini.CITIBIKE_STATIONS_SOURCE2 ונוסיף לה שתי רשומות.
CREATE TABLE mdm_gemini.CITIBIKE_STATIONS_SOURCE2 (name STRING(55), address STRING(1000), embeddings_src ARRAY<FLOAT64>);
insert into mdm_gemini.CITIBIKE_STATIONS_SOURCE2 VALUES ('Location broadway and 29','{ "DOOR_NUMBER": "1593", "STREET_ADDRESS": "Broadway", "AREA": null, "CITY": "New York", "TOWN": null, "COUNTY": "New York County", "STATE": "NY", "COUNTRY": "USA", "ZIPCODE": "10019", "LANDMARK": null}', null);
insert into mdm_gemini.CITIBIKE_STATIONS_SOURCE2 VALUES ('Allen St & Hester','{ "DOOR_NUMBER": "36", "STREET_ADDRESS": "Allen St", "AREA": null, "CITY": "New York", "TOWN": null, "COUNTY": "New York County", "STATE": "NY", "COUNTRY": "USA", "ZIPCODE": "10002", "LANDMARK": null}', null);
9. יצירת הטמעות לנתוני כתובות
הטמעה היא וקטור מספרי רב-ממדי שמייצג ישות נתונה, כמו קטע טקסט או קובץ אודיו. מודלים של למידת מכונה (ML) משתמשים בהטמעות כדי לקודד סמנטיקה לגבי ישויות כאלה, וכך להקל על ניתוח שלהן והשוואה ביניהן. לדוגמה, פעולה נפוצה במודלים של אשכולות, סיווג והמלצות היא מדידת המרחק בין וקטורים במרחב הטמעה כדי למצוא פריטים שהם הכי דומים מבחינה סמנטית. Vertex AI text-embeddings API מאפשר ליצור הטמעה של טקסט באמצעות AI גנרטיבי ב-Vertex AI. הטמעות טקסט הן ייצוגים מספריים של טקסט שמבטאים את הקשרים בין מילים וביטויים. כאן אפשר לקרוא מידע נוסף על Vertex AI Text Embeddings.
- מריצים את ה-DDL הבא כדי ליצור מודל מרוחק עבור Vertex AI text embeddings API:
CREATE OR REPLACE MODEL `mdm_gemini.CITIBIKE_STATIONS_ADDRESS_EMB`
REMOTE WITH CONNECTION `us.gemini-bq-conn`
OPTIONS (ENDPOINT = 'textembedding-gecko@latest');
- עכשיו, אחרי שהמודל המרוחק של הטמעות מוכן, ניצור הטמעות למקור הראשון ונאחסן אותן בטבלה באמצעות השאילתה הבאה:
CREATE TABLE `mdm_gemini.CITIBIKE_STATIONS_SOURCE1` AS (
SELECT *
FROM ML.GENERATE_EMBEDDING(
MODEL `mdm_gemini.CITIBIKE_STATIONS_ADDRESS_EMB`,
( select name, full_address_string as content from `mdm_gemini.CITIBIKE_STATIONS`
where full_address_string is not null )
)
);
במקום ליצור טבלה חדשה, אפשר גם לאחסן את שדה התוצאה של ההטמעות באותה טבלה mdm_gemini.CITIBIKE_STATIONS שיצרתם קודם.
- כדי ליצור הטמעות לנתוני כתובות בטבלה CITIBIKE_STATIONS_SOURCE2, מריצים את השאילתה הבאה:
update `mdm_gemini.CITIBIKE_STATIONS_SOURCE2` a set embeddings_src =
(
SELECT ml_generate_embedding_result
FROM ML.GENERATE_EMBEDDING(
MODEL `mdm_gemini.CITIBIKE_STATIONS_ADDRESS_EMB`,
( select name, address as content from `mdm_gemini.CITIBIKE_STATIONS_SOURCE2` ))
where name = a.name) where name is not null;
הפעולה הזו אמורה ליצור הטמעות למקור השני. שימו לב שיצרנו את שדה ההטמעות באותה טבלה CITIBIKE_STATIONS_SOURCE2.
- כדי להציג את הווקטורים שנוצרו עבור טבלאות 1 ו-2 של נתוני המקור, מריצים את השאילתה הבאה:
select name,address,embeddings_src from `mdm_gemini.CITIBIKE_STATIONS_SOURCE2`;
select name,content,ml_generate_embedding_result from `mdm_gemini.CITIBIKE_STATIONS_SOURCE1`;
עכשיו נבצע חיפוש וקטורי כדי לזהות כפילויות.
10. הרצת חיפוש וקטורי לסימון כתובות כפולות
בשלב הזה, תחפשו בעמודה ml_generate_embedding_result של הטבלה mdm_gemini.CITIBIKE_STATIONS_SOURCE1 את שני הווקטורים המובילים שמתאימים לכל שורה של נתונים בעמודה embeddings_src של הטבלה mdm_gemini.CITIBIKE_STATIONS_SOURCE2.
כדי לעשות זאת, מריצים את השאילתה הבאה:
select query.name name1,base.name name2,
/* (select address from mdm_gemini.CITIBIKE_STATIONS_SOURCE2 where name = query.name) content1, base.content content2, */
distance
from VECTOR_SEARCH(
TABLE mdm_gemini.CITIBIKE_STATIONS_SOURCE1,
'ml_generate_embedding_result',
(SELECT * FROM mdm_gemini.CITIBIKE_STATIONS_SOURCE2),
'embeddings_src',
top_k => 2
) where query.name <> base.name
order by distance desc;
הטבלה שאליה מתבצעת השאילתה: mdm_gemini.CITIBIKE_STATIONS_SOURCE1 בשדה ml_generate_embedding_result
טבלה שבה אנחנו משתמשים כבסיס: mdm_gemini.CITIBIKE_STATIONS_SOURCE2 בשדה embeddings_src
top_k: מציין את מספר השכנים הקרובים ביותר להחזרה. ערך ברירת המחדל הוא 10. ערך שלילי נחשב לאינסוף, כלומר כל הערכים נספרים ומוחזרים כשכנים.
distance_type: מציין את סוג המדד שבו יש להשתמש כדי לחשב את המרחק בין שני וקטורים. סוגי המרחקים הנתמכים הם Euclidean ו-Cosine. ברירת המחדל היא Euclidean.
תוצאת השאילתה היא:

כפי שאפשר לראות, בשתי השורות ב-CITIBIKE_STATIONS_SOURCE2 מ-CITIBIKE_STATIONS_SOURCE1 מופיעים שני השכנים הקרובים ביותר (כלומר, הכפילויות הכי קרובות). מכיוון ש-distance_type לא צוין, המערכת מניחה שמדובר ב-Euclidean והמרחק נקרא כמרחקים בערכי הטקסט של הכתובת בין שני המקורות, והנמוך ביותר הוא הטקסט של הכתובת הכי דומה.
נניח שאנחנו רוצים להגדיר את distance_type ל-Cosine באמצעות השאילתה הבאה:
select query.name name1,base.name name2,
/* (select address from mdm_gemini.CITIBIKE_STATIONS_SOURCE2 where name = query.name) content1, base.content content2, */
distance
from VECTOR_SEARCH(
TABLE mdm_gemini.CITIBIKE_STATIONS_SOURCE1,
'ml_generate_embedding_result',
(SELECT * FROM mdm_gemini.CITIBIKE_STATIONS_SOURCE2),
'embeddings_src',
top_k => 2,distance_type => 'COSINE'
) where query.name <> base.name
order by distance desc;
תוצאת השאילתה היא:

שתי השאילתות (של שני סוגי המרחקים) מסודרות לפי מרחק בסדר יורד, כלומר אנחנו רוצים להציג את התוצאות לפי מרחק הולך ופוחת. אבל אפשר לראות שבשאילתה השנייה סדר המרחקים הפוך. רוצים לנחש למה?
כן!! צדקת! בשיטת הדמיון הקוסינוסי, מספר גדול יותר מציין דמיון רב יותר ומרחק קטן יותר. במרחק אוקלידי, מספר גדול יותר מציין מרחק גדול יותר בין הערכים.
מידע נוסף על MDM וטיפים להבנת ההבדלים בין המרחק האוקלידי לבין דמיון הקוסינוס ועל השימושים בהם זמין בבלוג.
11. הסרת המשאבים
כדי לא לצבור חיובים לחשבון Google Cloud על המשאבים שבהם השתמשתם במאמר הזה:
- במסוף Google Cloud, עוברים לדף Manage resources.
- ברשימת הפרויקטים, בוחרים את הפרויקט שרוצים למחוק ולוחצים על Delete.
- כדי למחוק את הפרויקט, כותבים את מזהה הפרויקט בתיבת הדו-שיח ולוחצים על Shut down.
- אם רוצים לשמור את הפרויקט, מדלגים על השלבים שלמעלה ומוחקים את Cloud Function. כדי לעשות זאת, עוברים אל Cloud Functions, מסמנים את הפונקציה שרוצים למחוק מתוך רשימת הפונקציות ולוחצים על מחיקה.
12. מזל טוב
מעולה! הראית את העוצמה של השימוש ב-Gemini 1.0 Pro וב-Function Calling כדי להפוך כמה פעילויות של MDM ליכולות פשוטות אך עוצמתיות, דטרמיניסטיות ואמינות של AI גנרטיבי. עכשיו, אחרי שאתם יודעים את זה, אתם יכולים לנסות למצוא דרכים אחרות להטמיע את אותו תרחיש שימוש או פונקציות אחרות של MDM. האם יש מערכי נתונים שאפשר לאמת, פערים במידע שאפשר למלא או משימות שאפשר לבצע אוטומטית באמצעות קריאות מובנות שמוטמעות בתשובות של ה-AI הגנרטיבי? הנחיות מפורטות יותר זמינות במאמרי העזרה בנושא Vertex AI, פונקציות מרוחקות ב-BigQuery, Cloud Functions, הטמעות וחיפוש וקטורי. מאגר GitHub של הפרויקט הזה. נשמח לשמוע מה תבנו בעזרת הידע שרכשתם!