1. מבוא
בשיעור Codelab הזה תלמדו איך לקבל את התוצאות הכי טובות באמצעות QueryData API. לומדים איך ליצור תוצאות מדויקות שניתנות לשחזור כשמגישים בקשות בשפה טבעית למסד הנתונים. כדי לעזור לכם להשיג את המטרה הזו, במדריך הזה מפורטות שש דרכים שונות, שמוסברות בכמה פרקים, לבחינת היבטים שונים של המרת שפה טבעית ל-SQL (NL2SQL) והיישומים המעשיים שלה.
דרישות מוקדמות
- הבנה בסיסית של Google Cloud ומסוף Google Cloud
- מיומנויות בסיסיות בממשק שורת הפקודה וב-Cloud Shell
מה תלמדו
- איך פורסים אשכול ומכונה ראשית של AlloyDB
- איך מפעילים את QueryData, פונקציות AI ותצוגות מאובטחות עם פרמטרים
- איך יוצרים קבוצת הקשר QueryData
- איך משתמשים בתבניות ובהקשרים שונים ב-QueryData API
- איך להשתמש בהיבטים כדי להגדיל את הגמישות של תבניות QueryData
- איך לפתור בעיות של עמימות נתונים באמצעות חיפוש ערכים ואינדוקס טריגרם
- איך משתמשים בפונקציות AI לחיפוש סמנטי באמצעות תבניות
- איך משתמשים ב-Parameterized secure views עם QueryData API
הדרישות
- חשבון ב-Google Cloud ופרויקט בענן של Google
- דפדפן אינטרנט כמו Chrome שתומך ב-Google Cloud Console וב-Cloud Shell
2. הגדרה ודרישות
הגדרת הפרויקט
- נכנסים למסוף Google Cloud. אם עדיין אין לכם חשבון Gmail או Google Workspace, אתם צריכים ליצור חשבון.
משתמשים בחשבון לשימוש אישי במקום בחשבון לצורכי עבודה או בחשבון בית ספרי.
- יוצרים פרויקט חדש או משתמשים בפרויקט קיים. כדי ליצור פרויקט חדש במסוף Google Cloud, לוחצים בכותרת על הלחצן 'בחירת פרויקט' שפותח חלון קופץ.

בחלון Select a project (בחירת פרויקט), לוחצים על New Project (פרויקט חדש) כדי לפתוח תיבת דו-שיח לפרויקט החדש.

בתיבת הדו-שיח, מזינים שם לפרויקט ובוחרים את המיקום.

- שם הפרויקט הוא השם המוצג למשתתפים בפרויקט. השם של הפרויקט לא משמש את Google APIs, ואפשר לשנות אותו בכל שלב.
- מזהה הפרויקט הוא ייחודי לכל הפרויקטים ב-Google Cloud ואי אפשר לשנות אותו אחרי שהוא מוגדר. מסוף Google Cloud יוצר באופן אוטומטי מזהה ייחודי, אבל אפשר להתאים אותו אישית. אם לא אהבתם את המזהה שנוצר, אתם יכולים ליצור מזהה אקראי אחר או לספק מזהה משלכם כדי לבדוק אם הוא זמין. ברוב ה-codelabs, תצטרכו להתייחס למזהה הפרויקט שלכם, שבדרך כלל מזוהה באמצעות placeholder בשם PROJECT_ID.
- לידיעתכם, יש ערך שלישי, מספר פרויקט, שחלק מממשקי ה-API משתמשים בו. מידע נוסף על שלושת הערכים האלה מופיע במאמרי העזרה.
הפעלת חיוב
הגדרה של חשבון לחיוב לשימוש אישי
אם הגדרתם חיוב באמצעות קרדיטים ב-Google Cloud, אתם יכולים לדלג על השלב הזה.
כדי להגדיר חשבון לחיוב לשימוש אישי, עוברים לכאן כדי להפעיל את החיוב ב-Cloud Console.
הערות:
- העלות של השלמת ה-Lab הזה במשאבי Cloud צריכה להיות פחות מ-5 דולר ארה"ב.
- כדי למנוע חיובים נוספים, אפשר למחוק את המשאבים לפי השלבים שמפורטים בסוף ה-Lab הזה.
- משתמשים חדשים זכאים לתקופת ניסיון בחינם בשווי 300$.
הפעלת Cloud Shell
אפשר להפעיל את Google Cloud מרחוק מהמחשב הנייד, אבל ב-Codelab הזה נשתמש ב-Google Cloud Shell, סביבת שורת פקודה שפועלת בענן.
ב-מסוף Google Cloud, לוחצים על סמל Cloud Shell בסרגל הכלים שבפינה הימנית העליונה:

אפשר גם ללחוץ על G ואז על S. אם אתם במסוף Google Cloud, או אם אתם משתמשים בקישור הזה, הרצף הזה יפעיל את Cloud Shell.
הקצאת המשאבים והחיבור לסביבה יימשכו רק כמה רגעים. בסיום התהליך, אמור להופיע משהו כזה:

המכונה הווירטואלית הזו כוללת את כל הכלים שדרושים למפתחים. יש בה ספריית בית בנפח מתמיד של 5GB והיא פועלת ב-Google Cloud, מה שמשפר מאוד את הביצועים והאימות ברשת. אפשר לבצע את כל העבודה ב-codelab הזה בדפדפן. לא צריך להתקין שום דבר.
3. לפני שמתחילים
הפעלת ה-API
כדי להשתמש ב-AlloyDB, ב-Compute Engine, בשירותי רשת ובפלטפורמת הסוכנים, צריך להפעיל את ממשקי ה-API שלהם בפרויקט Google Cloud.
במסוף של Cloud Shell, מוודאים שמזהה הפרויקט מוגדר:
gcloud config get-value project
הפקודה אמורה להחזיר את מזהה הפרויקט ב-Google Cloud.
מגדירים את משתנה הסביבה PROJECT_ID:
PROJECT_ID=$(gcloud config get-value project)
מפעילים את ממשקי ה-API הנדרשים:
gcloud services enable alloydb.googleapis.com \
compute.googleapis.com \
cloudresourcemanager.googleapis.com \
servicenetworking.googleapis.com \
geminidataanalytics.googleapis.com \
cloudaicompanion.googleapis.com \
aiplatform.googleapis.com \
dataplex.googleapis.com
הפלט אמור להיראות כך:
student@cloudshell:~ (test-project-001-402417)$ gcloud config set project test-project-001-402417
Updated property [core/project].
student@cloudshell:~ (test-project-001-402417)$ PROJECT_ID=$(gcloud config get-value project)
Your active configuration is: [cloudshell-14650]
student@cloudshell:~ (test-project-001-402417)$
student@cloudshell:~ (test-project-001-402417)$ gcloud services enable alloydb.googleapis.com \
compute.googleapis.com \
cloudresourcemanager.googleapis.com \
servicenetworking.googleapis.com \
geminidataanalytics.googleapis.com \
cloudaicompanion.googleapis.com \
aiplatform.googleapis.com \
dataplex.googleapis.com
Operation "operations/acat.p2-4470404856-1f44ebd8-894e-4356-bea7-b84165a57442" finished successfully.
מידע על כל API שמופעל זמין בתיעוד.
4. פריסת AlloyDB
יצירת אשכול AlloyDB ומכונה ראשית. אפשר לפרוס אותו באמצעות סקריפט מוכן שיפרוס את כל המשאבים הנדרשים, או לבצע את הפריסה שלב אחר שלב בעצמכם לפי המסמכים של AlloyDB.
פריסת AlloyDB באמצעות סקריפט אוטומטי
בגישה הזו נעשה שימוש בסקריפט אוטומטי כדי לפרוס את אשכול AlloyDB, ומסופקים פרטי ההגדרה הנדרשים כדי להתחיל לעבוד עם המשאבים שנפרסו.
בטרמינל של Cloud Shell, מריצים את הפקודה כדי לשכפל את סקריפט הפריסה מהמאגר:
REPO_NAME="codelabs"
REPO_URL="https://github.com/GoogleCloudPlatform/$REPO_NAME"
SOURCE_DIR="alloydb-ai-nla"
git clone --no-checkout --filter=blob:none --depth=1 $REPO_URL
cd $REPO_NAME
git sparse-checkout set $SOURCE_DIR
git checkout
cd $SOURCE_DIR
מריצים את סקריפט הפריסה:
./deploy_alloydb.sh
הסקריפט יימשך חמש עד שבע דקות. בפלט מוצגים פרטי הפריסה של האשכול. חשוב לדעת שהסיסמה תהיה שונה – כדאי לרשום אותה לשימוש עתידי.
... <redacted> ... Creating primary instance: alloydb-aip-01-pr (8 vCPUs for TRIAL cluster) Operation ID: operation-1765988049916-646282264938a-bddce198-9f248715 Creating instance...done. ---------------------------------------- Deployment Process Completed Cluster: alloydb-aip-01 (TRIAL) Instance: alloydb-aip-01-pr Region: us-central1 Initial Password: JBBoDTgixzYwYpkF (if new cluster) ----------------------------------------
אפשר לראות את האשכול והמופע החדשים בדף AlloyDB Clusters במסוף Google Cloud.

5. הכנת מסד הנתונים
ליצור מסד נתונים, להפעיל את השילוב של Gemini Enterprise Agent Platform, ליצור אובייקטים של מסד נתונים ולהוסיף נתונים לדוגמה.
מתן ההרשאות הנדרשות ל-AlloyDB
מקצים לסוכן השירות של AlloyDB את תפקידי ה-IAM הנדרשים:
פותחים כרטיסייה חדשה ב-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"
הפלט הצפוי בקונסולה:
student@cloudshell:~ (test-project-001-402417)$ PROJECT_ID=$(gcloud config get-value project) Your active configuration is: [cloudshell-11039] student@cloudshell:~ (test-project-001-402417)$ 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" Updated IAM policy for project [test-project-001-402417]. bindings: - members: - serviceAccount:service-4470404856@gcp-sa-alloydb.iam.gserviceaccount.com role: roles/aiplatform.user - members: ... etag: BwYIEbe_Z3U= version: 1
הפעלת Data Access API
מפעילים את Data Access API באשכול AlloyDB.
באותו כרטיסייה בטרמינל, מריצים את הפקודה הבאה:
PROJECT_ID=$(gcloud config get-value project)
REGION=us-central1
ADBCLUSTER=alloydb-aip-01
curl -X PATCH \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
https://alloydb.googleapis.com/v1alpha/projects/$PROJECT_ID/locations/$REGION/clusters/$ADBCLUSTER/instances/$ADBCLUSTER-pr?updateMask=dataApiAccess \
-d '{
"dataApiAccess": "ENABLED",
}'
עדכון דגלים לניהול מסד נתונים
לפני שמפעילים את הדגלים ברמת המופע, צריך להמתין עד שהשלב של Data Access API יסתיים. מוודאים שבסטטוס המכונה מופיע סימן וי ירוק, כמו בתמונה הבאה.

כדי להשתמש בתוספי AI ובאימות IAM עבור כלים מבוססי-סוכן, צריך להפעיל בדוגמה (instance) דגלים ספציפיים של מסד הנתונים ולהוסיף את עצמכם כמשתמש במסד הנתונים. מפעילים את התכונות הניסיוניות הבאות:
- alloydb.iam_authentication
- password.enforce_complexity
- google_ml_integration.enable_ai_query_engine
- parameterized_views
באותו כרטיסייה בטרמינל, מריצים את הפקודות הבאות:
PROJECT_ID=$(gcloud config get-value project)
REGION=us-central1
ADBCLUSTER=alloydb-aip-01
gcloud beta alloydb instances update $ADBCLUSTER-pr \
--database-flags password.enforce_complexity=on,alloydb.iam_authentication=on,google_ml_integration.enable_ai_query_engine=on,parameterized_views.enabled=on \
--region=$REGION \
--cluster=$ADBCLUSTER \
--project=$PROJECT_ID \
--update-mode=FORCE_APPLY
יוצרים משתמש IAM ב-AlloyDB לחשבון שלכם:
REGION=us-central1
ADBCLUSTER=alloydb-aip-01
gcloud alloydb users create $(gcloud config get-value account) \
--cluster=$ADBCLUSTER \
--superuser=true \
--region=$REGION \
--type=IAM_BASED
סוגרים את הכרטיסייה על ידי הרצת פקודת היציאה בטרמינל:
exit
התחברות ל-AlloyDB Studio
בפרקים הבאים תוכלו להריץ את כל פקודות ה-SQL שדורשות חיבור למסד נתונים ב-AlloyDB Studio.
כדי לפתוח את AlloyDB Studio, מבצעים את השלבים הבאים:
- במסוף Google Cloud, עוברים אל הדף Clusters של AlloyDB ל-PostgreSQL.
- בוחרים את המופע הראשי כדי לפתוח את ממשק האינטרנט שלו.

- אחר כך לוחצים על AlloyDB Studio בצד ימין:

- בוחרים את מסד הנתונים
postgres, בוחרים באפשרות 'אימות IAM' ולוחצים על 'אימות'.

פעולה זו תפתח את הממשק של AlloyDB Studio.
- בוחרים בכרטיסייה Untitled Query (שאילתה ללא שם) כדי לפתוח את עורך ה-SQL.

ייפתח עורך שבו אפשר להריץ פקודות SQL, כמו שמוצג בתמונה הבאה:

יצירת מסד נתונים
יצירת מסד הנתונים למדריך למתחילים
בעורך של AlloyDB Studio, מריצים את הפקודה הבאה כדי ליצור את מסד הנתונים:
CREATE DATABASE quickstart_db
הפלט אמור להיראות כך:
Statement executed successfully
התחברות אל quickstart_db
לוחצים על Change database/user (שינוי מסד נתונים או משתמש) ב-AlloyDB Studio.

בוחרים את quickstart_db מרשימת מסדי הנתונים, בוחרים באפשרות 'אימות IAM' ומבצעים אימות.

ייפתח חיבור חדש שבו תוכלו לעבוד עם אובייקטים ממסד הנתונים quickstart_db.
הפעלת תוספים
בודקים את כל התוספים הנדרשים כדי לוודא שהם מופעלים במסד הנתונים ושהגרסאות שלהם הן הגרסאות המינימליות הנכונות.
מוודאים שכל התוספים הנדרשים קיימים במסד הנתונים:
SELECT extname, extversion FROM pg_extension WHERE extname IN ('vector', 'google_ml_integration', 'parameterized_views');
הפלט אמור להיראות כך:
extname | extversion -----------------------+---------------- google_ml_integration | 1.5.9 parameterized_views | 1.0 vector | 0.8.2.google-1
אם לא מוחזרות כל השורות שלמעלה, המשמעות היא שחלק מהתוספים לא מופעלים.
לפני שמפעילים את התוספים, צריך לוודא שכל הדגלים של מסד הנתונים ,google_ml_integration.enable_ai_query_engine ו-parameterized_views.enabled מופעלים במופע.
ב-AlloyDB Studio, כשאתם מחוברים ל-quickstart_db, מריצים את הפקודה:
show google_ml_integration.enable_ai_query_engine;
show parameterized_views.enabled;
אם אחת מהאפשרויות האלה מופיעה כ'מושבתת', צריך לנסות לחזור על השלב 'עדכון של דגלים במסד הנתונים' ולבדוק את הסטטוס באמצעות הפקודות 'show...' שוב.
מפעילים את התוספים הנדרשים.
ב-AlloyDB Studio, כשאתם מחוברים ל-quickstart_db, מריצים את הפקודה:
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS parameterized_views;
בודקים את גרסאות התוספים parameterized_views ו-google_ml.
ב-AlloyDB Studio, כשאתם מחוברים ל-quickstart_db, מריצים את הפקודה:
SELECT extname, extversion FROM pg_extension WHERE extname IN ('vector', 'google_ml_integration', 'parameterized_views');
הפלט אמור להיראות כך:
extname | extversion -----------------------+---------------- google_ml_integration | 1.5.9 parameterized_views | 1.0 vector | 0.8.2.google-1
אם הגרסה נמוכה מהגרסה הנדרשת, צריך לנסות לעדכן את התוסף parameterized_views.
ל-parameterized_views אפשר לנסות:
ALTER EXTENSION parameterized_views UPDATE;
בודקים שוב את הגרסה.
ב-AlloyDB Studio, כשאתם מחוברים ל-quickstart_db, מריצים את הפקודה:
SELECT extname, extversion FROM pg_extension WHERE extname IN ('vector', 'google_ml_integration', 'parameterized_views');
הפלט אמור להיראות כך:
extname | extversion -----------------------+---------------- google_ml_integration | 1.5.9 parameterized_views | 1.0 vector | 0.8.2.google-1
6. נתונים לדוגמה
עכשיו צריך ליצור אובייקטים במסד הנתונים ולטעון נתונים. תשתמשו במערך נתונים פיקטיבי של חברת נדל"ן. הוא כולל נתונים פיקטיביים על מכירות של נכסים ועל סוכנים. הנתונים מופיעים בסקריפט SQL real_estate_data.sql במאגר המשוכפל.
יצירת קטגוריית אחסון
משתמשים ב-Google SDK (gcloud) כדי לייבא נתונים מהמאגר המשוכפל למסד הנתונים של AlloyDB. לשם כך, צריך ליצור קטגוריה של Cloud Storage ולהעניק גישה לחשבון השירות של AlloyDB. אפשרות נוספת היא לנסות לעשות את זה באמצעות מסוף האינטרנט, כמו שמתואר במסמכי התיעוד.
בטרמינל של Google Cloud Shell, מריצים את הפקודה:
PROJECT_ID=$(gcloud config get-value project)
REGION=us-central1
gcloud storage buckets create gs://$PROJECT_ID-import --project=$PROJECT_ID --location=$REGION
gcloud storage buckets add-iam-policy-binding gs://$PROJECT_ID-import --member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" --role=roles/storage.objectViewer
טען נתונים
קובץ ה-SQL של מסד הנתונים נמצא בתיקיית המאגר המשוכפל. הפקודה הבאה מניחה שהשתמשתם בספריית הבית כנקודת התחלה כששיכפלתם את המאגר בשלב הקודם במהלך יצירת אשכול AlloyDB.
מעתיקים את קובץ ה-dump של מסד הנתונים לקטגוריה של Cloud Storage:
REPO_NAME="codelabs"
SOURCE_DIR="alloydb-ai-nla"
cd ~/$REPO_NAME/$SOURCE_DIR
gcloud storage cp ~/$REPO_NAME/$SOURCE_DIR/real_estate_data.sql gs://$PROJECT_ID-import
מייבאים את מערך הנתונים אל quickstart_db:
PROJECT_ID=$(gcloud config get-value project)
CLUSTER_NAME=alloydb-aip-01
REGION=us-central1
IAM_USER=$(gcloud config get-value account)
gcloud alloydb clusters import $CLUSTER_NAME --region=us-central1 --database=quickstart_db --gcs-uri=gs://$PROJECT_ID-import/real_estate_data.sql --project=$PROJECT_ID --sql --user=$IAM_USER
הפקודה תטען את מערך הנתונים לדוגמה למסד הנתונים quickstart_db.
כדי לאמת את הייבוא, סופרים את הרשומות בטבלה real_estate.agents באמצעות AlloyDB Studio:
SELECT COUNT(*) FROM real_estate.agents;
7. הכנת סכימת אונטולוגיה
מגדירים את קבוצת ההקשר QueryData, מספקים מטא-נתונים של סכימה ומריצים את סוכן ה-ADK.
יצירת מטא-נתונים
מטא-נתונים מורכבים מהערות SQL שחלות על טבלאות ועמודות במסד נתונים. ההערות האלה עוזרות למנוע QueryData לבחור אובייקטים במסד הנתונים.
אם ייבאתם את סכימת הנתונים באמצעות קובץ ה-SQL dump שסופק, כל המטא-נתונים כבר נוצרו בסכימה.
דוגמה לקטע קוד של מטא-נתונים שמוחל על סכימת real_estate:
COMMENT ON TABLE real_estate.agents IS 'Stores profile, contact, brokerage affiliation, and active status for individual real estate agents.'; COMMENT ON COLUMN real_estate.agents.agent_id IS 'Primary key uniquely identifying each real estate agent in the database.'; ...
אם אתם עובדים עם מערך נתונים משלכם, אתם יכולים לפעול לפי המדריך כדי להוסיף את המטא-נתונים לסכימה.
יצירת הקשר לאונטולוגיה של סכימה
קבוצת הקשרים מכילה תבניות של שאילתות, היבטים וחיפושים של ערכים שעוזרים למודל ה-AI ליצור שאילתות. אם לא מגדירים את ההקשר, סוכן AI ינסה ליצור שאילתה באמצעות המידע הזמין מסכימת public, בעיקר על סמך שמות הטבלאות, העמודות והמטא-נתונים, וינסה להסיק אילו אובייקטים מתאימים כדי לענות על בקשת שאילתה בשפה טבעית.
הקשר QueryData הוא קובץ JSON עם תבניות שאילתות, היבטים וחיפושי ערכים. אפשר לעיין בדוגמאות שונות בתיעוד.
טעינת QueryData Context Set במסוף האינטרנט
כדי לטעון קבוצת הקשר, פותחים את AlloyDB Studio ובחלונית הימנית בתחתית מופיעים Context sets ושלוש נקודות.

לוחצים על שלוש הנקודות ובוחרים באפשרות Create context set.
בתיבת הדו-שיח שנפתחת, מזינים את הערכים הבאים:
- שם:
sixrecipes - תיאור:
Real Estate Query Data - העלאת קובץ הקשר: לא ממלאים אותו
כשלוחצים על הלחצן Create, יכול להיות שיעבור זמן עד שאחסון ההקשר יאותחל, אם זו הפעם הראשונה שעושים זאת.
בודקים את מזהה קבוצת ההקשר QueryData. לשם כך, לוחצים שוב על סמל האפשרויות הנוספות (3 נקודות) משמאל לשם קבוצת ההקשר ובוחרים באפשרות Edit context set.

המזהה של קבוצת ההקשר מופיע בחלק העליון של הכרטיסייה שנפתחה בצד שמאל.

שומרים את המזהה הזה לשלבים הבאים.
התחלת שיחה עם סוכן AI
כדי ליצור ולהריץ שאילתה באמצעות האינטראקציה עם הקצה העורפי של QueryData, אפשר להשתמש בממשק של AlloyDB Studio, כפי שמתואר במאמר בנושא. אבל בשיעור ה-Lab הזה נשתמש בסוכן AI ככלי התקשורת העיקרי עם מסד הנתונים. סוכני ה-AI הם תרחיש מציאותי יותר לעומסי עבודה עתידיים, ומסגרת ADK של Google מספקת ממשק אינטרנט מצוין שבו אפשר לראות את כל השלבים והאינטראקציות בין מודלים, ממשקי API ואפליקציות.
בודקים את הגדרות המקור של הסוכן ב-Cloud Shell:
REPO_NAME="codelabs"
SOURCE_DIR="alloydb-ai-nla"
cd ~/$REPO_NAME/$SOURCE_DIR/data_agent
edit agent.py
בקובץ אפשר לראות איפה אנחנו מגדירים את רשימת הטבלאות בסכימה. נחזור לזה באחד מהשלבים הבאים.
# List of database tables included in QueryData API requests
TABLE_IDS = [
"real_estate.municipalities",
"real_estate.cities",
"real_estate.agents",
"real_estate.properties",
"real_estate.schools",
"real_estate.school_to_property",
"real_estate.property_transactions",
"real_estate.secure_offers",
]
יש גם הליך call_querydata_api לעבודה עם QueryData API וערכת כלים לעבודה עם מיומנויות של QueryData API.
def call_querydata_api(
prompt: str,
target_project_id: str = project_id,
target_location: str = location,
target_cluster_id: str = cluster_name,
target_instance_id: str = instance_name,
target_database_id: str = database_name,
target_context_set_id: str = context_set_id,
target_table_ids: list[str] = TABLE_IDS,
) -> dict:
"""Calls the Gemini Data Analytics QueryData API to translate natural ...
<Redacted>
...
# ADK File-Based Skill
database_query_skill = load_skill_from_dir(
pathlib.Path(__file__).parent / "skills" / "database-query-skill"
)
skill_toolset = SkillToolset(
skills=[database_query_skill]
)
כאן מופיע הסוכן הראשי עצמו עם הנחיה וכלים זמינים.
# Agent configuration
root_agent = Agent(
model=MODEL_ID,
name='root_agent',
description='A helpful assistant for analyst requests.',
instruction=f"""
Answer user questions to the best of your knowledge using provided tools.
Do not try to generate non-existent data but use the grounded data from the database.
When you answer questions about database contents, real estate properties, or sales activity:
1. Refer to database-query-skill for instructions on querying data using QueryData API.
2. Use `call_querydata_api` to submit the natural language prompt to the QueryData API.
CRITICAL: Call `call_querydata_api` EXACTLY ONCE per user request. Do NOT make retries, follow-up calls, or rephrased calls to `call_querydata_api`, even if the result contains zero rows or empty data.
3. Immediately parse the JSON response from that first `call_querydata_api` call.
4. Format your final response to the user containing:
- The generated SQL query (formatted in a Markdown SQL code block).
- The query results formatted as a Markdown table (constructed from `queryResult.columns` and `queryResult.rows`).
- The natural language answer (`naturalLanguageAnswer`).
""",
tools=[call_querydata_api, skill_toolset],
)
כדאי לעיין בקובץ data_agent/SKILL.md כדי להבין את הוראות המערכת של הסוכן:
REPO_NAME="codelabs"
SOURCE_DIR="alloydb-ai-nla"
cd ~/$REPO_NAME/$SOURCE_DIR/data_agent
edit SKILL.md
לפני שמפעילים את הסוכן, צריך להכין קובץ .env עם פרמטרים לבקשות של Agent API ו-QueryData API.
מייצאים את מזהה קבוצת ההקשר למשתנה סביבה:
export QUERYDATA_CONTEXTSET=<context set id from the Create Context Set chapter>
יוצרים את קובץ ההגדרות של סביבת זמן הריצה (.env) בספרייה data_agent:
PROJECT_ID=$(gcloud config get-value project)
REGION=us-central1
REPO_NAME="codelabs"
SOURCE_DIR="alloydb-ai-nla"
cd ~/$REPO_NAME/$SOURCE_DIR/data_agent
echo "GOOGLE_GENAI_USE_ENTERPRISE=true" > .env
echo "GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project -q)" >> .env
echo "GOOGLE_CLOUD_LOCATION=global" >> .env
echo "QUERYDATA_CONTEXTSET=$QUERYDATA_CONTEXTSET" >> .env
echo "ALLOYDB_CLUSTER=alloydb-aip-01" >> .env
echo "ALLOYDB_INSTANCE=alloydb-aip-01-pr" >> .env
echo "ALLOYDB_DATABASE=quickstart_db" >> .env
echo "ALLOYDB_LOCATION=$REGION" >> .env
התקנת יחסי תלות והפעלת שירות האינטרנט של הסוכן:
cd ~/$REPO_NAME/$SOURCE_DIR/
uv sync
מפעילים את הנציג:
uv run adk web --allow_origins 'regex:https://.*\.cloudshell\.dev'
הפלט אמור להיראות כך, עם נקודת הקצה http://127.0.0.1:8000 .
INFO: Started server process [83608] INFO: Waiting for application startup. +-----------------------------------------------------------------------------+ | ADK Web Server started | | | | For local testing, access at http://127.0.0.1:8000. | +-----------------------------------------------------------------------------+ INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

לוחצים על כתובת ה-URL של שרת האינטרנט שנוצרה (לדוגמה, http://127.0.0.1:8000) ב-Cloud Shell כדי להפעיל את הממשק של הסוכן. אם זה לא עובד, ב-Cloud Shell, לוחצים על Web Preview (תצוגה מקדימה באינטרנט) -> Preview on port 8000 (תצוגה מקדימה ביציאה 8000) כדי לפתוח את ממשק הסוכן.
מוודאים שהאפשרות data_agent נבחרה בתפריט הנפתח בפינה הימנית העליונה.

יצירה והרצה של שאילתות
תריץ את השאילתה Q1
כדי להריץ את ההנחיה הזו בשפה טבעית בממשק הצ'אט של הסוכן כדי להעריך את הביצועים של QueryData בלי תבניות או היבטים מותאמים אישית:
How many properties are currently for sale in San Diego?
לוחצים על לחצן החץ.

הוא יתחיל את תהליך העבודה של הסוכן כשהוא יבחר את הסקיל המתאים וישתמש בו ובפונקציה כדי לשלוח את הבקשה לנקודת קצה ל-API ולקבל בחזרה את השאילתה שנוצרה ואת התוצאות.

אם מעבירים את סמן העכבר מעל השלבים, אפשר לראות בדיוק מה השלבים האלה עשו. בנוסף, אפשר לראות את זה ועוד הרבה יותר בחלונית הימנית כשבוחרים שלב מסוים. כדאי להבין איך סוכנים עובדים עם כישורים וממשקי API.

שליחת שאילתת המשך (Q2):
List agents who sold a property for over $1000000.
אפשר לראות שהסוכן עובר ישירות למיומנות QueryData API, כי הוא כבר כתב בזיכרון את המיומנויות הזמינות ואין צורך לעבור שוב את שלב הגילוי.
קוד ה-SQL שנוצר דומה להצהרה הזו:
SELECT DISTINCT "a"."agent_id", "a"."firstname", "a"."lastname", "a"."email", "a"."brokerage" FROM "real_estate"."agents" AS "a" INNER JOIN "real_estate"."property_transactions" AS "t" ON "a"."agent_id" = "t"."seller_agent_id" WHERE "t"."sale_price" > 1000000 AND "a"."agent_id" IS NOT NULL ORDER BY "a"."lastname" ASC NULLS LAST;
אפשר לנסות שאילתה אחרת (Q3):
What is the average price of a single-family home with a garage in San Diego?
הפונקציה QueryData יוצרת שאילתות רגילות עם קבוצת הקשר ריקה, אבל משימות עסקיות דורשות לרוב לוגיקה עסקית ספציפית והתנהגות דטרמיניסטית.
8. שיפור הדיוק והרלוונטיות לעסק באמצעות הוספת פרשנות
החדרת פרשנות להנחיות מבטיחה שהשאילתות שנוצרות יתאימו ללוגיקה עסקית ספציפית.
תנסו דוגמה כזו עם לקוח שמבקש לקבל בתים עם מפלס אחד בדל מאר. השאלה כוללת שני מדדים חשובים ומייצגת דפוס נפוץ לבחירה.
אפשר לנסות את ההנחיה הבאה בשפה טבעית (Q4) בסוכן.
Single level Del Mar houses
המנוע מחזיר שאילתה כללית, אבל יכול לכלול כמה תנאים נוספים כמו is_single_family = TRUE:
SELECT p."property_id", p."address_street", p."price", p."bedrooms", p."bathrooms", p."listing_status", p."is_single_family", p."num_stories", c."city_name" FROM "real_estate"."properties" AS p JOIN "real_estate"."cities" AS c ON p."city_id" = c."city_id" WHERE c."city_name" ILIKE 'Del Mar' AND p."num_stories" = 1 AND p."is_single_family" = TRUE ORDER BY p."property_id" ASC NULLS LAST;
התוצאה היא שורה אחת:
Natural Language Answer The database contains one single-level, single-family house in Del Mar: Address: 999 Main Ave (Postal Code: 90024) Price: $3,800,000.00 Bedrooms: 4 Bathrooms: 3.5 Listing Status: Active Features: Single-family home (1 story), has a garage, and no condo fees.
בדיקת פרומפט אחר (שאלה 5):
Del Mar houses close to good schools
הוא חכם מספיק כדי להשתמש בעמודות proximity_miles ו-school_ranking כדי למיין את הנכסים, אבל הוא לא מעריך את המשקל והחשיבות של כל מדד.
SELECT "p"."property_id", "p"."address_street", "p"."price", "p"."bedrooms", "p"."bathrooms", "s"."school_name", "s"."school_ranking", "sp"."proximity_miles" FROM "real_estate"."properties" AS "p" INNER JOIN "real_estate"."cities" AS "c" ON "p"."city_id" = "c"."city_id" INNER JOIN "real_estate"."school_to_property" AS "sp" ON "p"."property_id" = "sp"."property_id" INNER JOIN "real_estate"."schools" AS "s" ON "sp"."school_id" = "s"."school_id" WHERE "c"."city_name" = 'Del Mar' AND "sp"."proximity_miles" IS NOT NULL AND "s"."school_ranking" IS NOT NULL ORDER BY "sp"."proximity_miles" ASC NULLS LAST, "s"."school_ranking" ASC NULLS LAST;
הפיכת שאילתה לדוגמה לתבנית
הלוגיקה העסקית דורשת:
- קרבה בטווח של 2 מיילים
- דירוג בית הספר הוא 5 או פחות
- סדר משוקלל: 80% דירוג בית הספר + 20% קרבה (ציון = 0.8 * school_ranking + 0.2 * proximity_miles)
זו הנוסחה לחישוב הדירוג המשוקלל:
Score = 0.8 * school_ranking + 0.2 * proximity_miles
מורידים את קובץ ההקשר sixrecipe_context1_1.json מ-Cloud Shell באמצעות ממשק שורת הפקודה ב-Cloud Shell:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context1_1.json
או באמצעות האפשרות Download בתפריט Cloud Shell:

כדי להגדיר את המבנה הסטנדרטי הזה, מעלים את sixrecipe_context1_1.json לתוך קבוצת ההקשר sixrecipe באמצעות AlloyDB Studio. לוחצים על שלוש הנקודות משמאל להקשר sixrecipes ומשתמשים באפשרות 'עריכת קבוצת ההקשרים' כדי להעלות את הקובץ החדש. הוא למעשה יחליף את ההקשר הישן והריק בהקשר החדש.
בתבנית מוגדרים כללים מפורשים של תבניות SQL לכללי קרבה טובים לבתי ספר:
{
"facets": [],
"templates": [
{
"nl_query": "Riverside houses close to good schools.",
"sql": "SELECT \"p\".\"address_street\", \"p\".\"address_postal_code\", \"s\".\"school_name\", \"stp\".\"proximity_miles\" FROM \"real_estate\".\"properties\" AS \"p\" JOIN \"real_estate\".\"cities\" AS \"c\" ON \"p\".\"city_id\" = \"c\".\"city_id\" JOIN \"real_estate\".\"school_to_property\" AS \"stp\" ON \"p\".\"property_id\" = \"stp\".\"property_id\" JOIN \"real_estate\".\"schools\" AS \"s\" ON \"stp\".\"school_id\" = \"s\".\"school_id\" WHERE \"c\".\"city_name\" = 'Riverside' AND \"s\".\"school_ranking\" <= 5 AND \"stp\".\"proximity_miles\" <= 2 ORDER BY 0.8 * \"s\".\"school_ranking\" + 0.2 * \"stp\".\"proximity_miles\"",
"intent": "Houses in Riverside close to good schools (within 2 miles of schools with a rank of at least 5).",
"manifest": "A given city houses close to good schools.",
"parameterized": {
"parameterized_intent": "Houses in $1 close to good schools (within 2 miles of schools with a rank of at least 5).",
"parameterized_sql": "SELECT \"p\".\"address_street\", \"p\".\"address_postal_code\", \"s\".\"school_name\", \"stp\".\"proximity_miles\" FROM \"real_estate\".\"properties\" AS \"p\" JOIN \"real_estate\".\"cities\" AS \"c\" ON \"p\".\"city_id\" = \"c\".\"city_id\" JOIN \"real_estate\".\"school_to_property\" AS \"stp\" ON \"p\".\"property_id\" = \"stp\".\"property_id\" JOIN \"real_estate\".\"schools\" AS \"s\" ON \"stp\".\"school_id\" = \"s\".\"school_id\" WHERE \"c\".\"city_name\" = $1 AND \"s\".\"school_ranking\" <= 5 AND \"stp\".\"proximity_miles\" <= 2 ORDER BY 0.8 * \"s\".\"school_ranking\" + 0.2 * \"stp\".\"proximity_miles\""
}
}
]
}
מריצים מחדש את הפרומפט:
Del Mar houses close to good schools
השאילתה שמתקבלת תואמת עכשיו ללוגיקה העסקית שמוגדרת בתבנית:
SELECT "p"."address_street", "p"."address_postal_code", "s"."school_name", "stp"."proximity_miles" FROM "real_estate"."properties" AS "p" JOIN "real_estate"."cities" AS "c" ON "p"."city_id" = "c"."city_id" JOIN "real_estate"."school_to_property" AS "stp" ON "p"."property_id" = "stp"."property_id" JOIN "real_estate"."schools" AS "s" ON "stp"."school_id" = "s"."school_id" WHERE "c"."city_name" = 'Del Mar' AND "s"."school_ranking" <= 5 AND "stp"."proximity_miles" <= 2 ORDER BY 0.8 * "s"."school_ranking" + 0.2 * "stp"."proximity_miles"
בנוסף להצהרת ה-SQL שנוצרה, נוצרת הכוונה הבאה:
intentExplanation: "Houses in Del Mar close to good schools (within 2 miles of schools with a rank of at least 5)."
אפשר לראות את הכוונה בחלונית הימנית של ממשק האינטרנט של ADK, כמו שמוצג כאן:

בתוצאות מוצגים שני נכסים שממוינים לפי הניקוד שלנו:
Query Results address_street address_postal_code school_name proximity_miles 200 Coast Blvd 92014 Del Mar Heights Elementary 0.50 100 Del Mar Heights Rd 92014 Del Mar Heights Elementary 1.00 Answer Based on the data, here are the properties in Del Mar located within 2 miles of a top-rated school (ranking of 5 or better): 200 Coast Blvd (92014) School: Del Mar Heights Elementary Proximity: 0.50 miles 100 Del Mar Heights Rd (92014) School: Del Mar Heights Elementary Proximity: 1.00 miles
בפרק הבא תרחיבו את ההיקף של התבנית.
הוספת פרשנות על ידי צילום ופרשנות של כמה שאלות
אתם יכולים להרחיב את היקף השימוש בתבנית שלנו כך שהיא תשמש לא רק לחיפוש 'בתי ספר טובים בקרבת מקום', אלא גם להוספת תנאים אחרים למניפסט, למשל לחיפוש 'בתים למשפחות עם ילדים'.
מורידים את קובץ ההקשר המעודכן:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context1_2.json
מעלים את קובץ sixrecipe_context1_2.json המעודכן למערך ההקשרים.
בקובץ החדש של הגדרות ההקשר, אנחנו מחליפים את המניפסט מ: "manifest": "A given city houses close to good schools." למניפסט: "manifest": "A given city houses close to good schools, or are great for families with kids.". היא מאפשרת להשתמש בתבנית הזו ביותר ממקרה אחד.
שליחת השאילתה הזו (Q6):
Houses in Waterloo great for families with kids.
הסוכן משתמש במניפסט התבנית המעודכן כדי להחיל את לוגיקת השאילתה הסטנדרטית שלך, ומחזיר את אותה שאילתה כמו למעלה, עם הכוונה:
intentExplanation : "Houses in Waterloo close to good schools (within 2 miles of schools with a rank of at least 5)."
9. שימוש בתבניות עם היבטים שונים כדי להגיע לרמת דיוק גבוהה תוך שמירה על גמישות
ההיבטים הם תנאי SQL מודולריים שמשולבים באופן דינמי עם תבניות כדי לטפל במאפיינים (למשל, 'חבילה למשפחות', 'נוף לים') בלי ליצור תבניות נפרדות לכל פרמוטציה.
במקום ליצור תבנית לכל שאילתה או תנאי, אפשר להכין קטעי קוד שאפשר להחיל על תבניות קיימות כדי להוסיף עוד מאפיינים לשאילתה.
מגדירים היבט למאפיינים עם המאפיינים הבאים:
- בית פרטי
- סטטוס פעיל של כרטיס מוצר
- שלושה עד ארבעה חדרי שינה ולפחות שני חדרי רחצה
- מיקום ברשות מקומית עם שיעור פשיעה מתחת ל-500 לכל 100,000 תושבים
מורידים את קובץ ההקשר המפורט:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context2_1.json
מעלים את הקובץ sixrecipe_context2_1.json לסט ההקשר sixrecipes ב-AlloyDB Studio כדי להגדיר את ההיבטים.
"facets": [
{
"sql_snippet": "real_estate.properties.is_single_family = TRUE AND real_estate.properties.listing_status = 'Active' AND real_estate.properties.bedrooms BETWEEN 3 AND 4 AND real_estate.properties.bathrooms >= 2.0 AND 500 > (SELECT m.crime_rate_per_100k FROM real_estate.cities c JOIN real_estate.municipalities m ON c.municipality_id = m.municipality_id AND c.city_id = real_estate.properties.city_id)",
"intent": "for a starter family (active single-family home listings with 3 to 4 bedrooms, 2+ bathrooms, and a crime rate under 500 per 100,000 residents)",
"manifest": "for a starter family",
"parameterized": {
"parameterized_intent": "for a starter family (active single-family home listings with 3 to 4 bedrooms, 2+ bathrooms, and a crime rate under 500 per 100,000 residents)",
"parameterized_sql_snippet": "real_estate.properties.is_single_family = TRUE AND real_estate.properties.listing_status = 'Active' AND real_estate.properties.bedrooms BETWEEN 3 AND 4 AND real_estate.properties.bathrooms >= 2.0 AND 500 > (SELECT m.crime_rate_per_100k FROM real_estate.cities c JOIN real_estate.municipalities m ON c.municipality_id = m.municipality_id AND c.city_id = real_estate.properties.city_id)"
}
}
]
שאילתה באמצעות חיפוש עם היבטים
מריצים את השאילתה הבאה בשפה טבעית (Q7):
Del Mar houses close to good schools for a starter family.
המנוע מוסיף את תנאי הפנים ישירות לשאילתת הבסיס:
SELECT "p"."address_street", "p"."address_postal_code", "s"."school_name", "stp"."proximity_miles"
FROM "real_estate"."properties" AS "p"
JOIN "real_estate"."cities" AS "c" ON "p"."city_id" = "c"."city_id"
JOIN "real_estate"."school_to_property" AS "stp" ON "p"."property_id" = "stp"."property_id"
JOIN "real_estate"."schools" AS "s" ON "stp"."school_id" = "s"."school_id"
WHERE "c"."city_name" = 'Del Mar'
AND "s"."school_ranking" <= 5
AND "stp"."proximity_miles" <= 2
AND "p"."is_single_family" = TRUE
AND "p"."listing_status" = 'Active'
AND "p"."bedrooms" BETWEEN 3 AND 4
AND "p"."bathrooms" >= 2.0
AND 500 > (
SELECT m.crime_rate_per_100k
FROM real_estate.cities c
JOIN real_estate.municipalities m ON c.municipality_id = m.municipality_id
AND c.city_id = p.city_id
)
ORDER BY 0.8 * "s"."school_ranking" + 0.2 * "stp"."proximity_miles"
אלה התוצאות:
Query Results address_street address_postal_code school_name proximity_miles 200 Coast Blvd 92014 Del Mar Heights Elementary 0.50 Natural Language Answer Based on your criteria, there is an active listing for a single-family house in Del Mar that fits your needs: Address: 200 Coast Blvd, Del Mar, 92014 Nearby School: Del Mar Heights Elementary (ranked within the top 5) Proximity to School: 0.50 miles
הוספת היבטים נוספים
מה קורה אם רוצים להוסיף עוד מאפיין על סמך הדפוס החדש שמתגלה – כמו תצוגת חלון של הנכס?
מוסיפים התאמה סמנטית של 'נוף לאוקיינוס' בהגדרת היבט.
{
"sql_snippet": "((real_estate.properties.description_embedding <=> google_ml.embedding('gemini-embedding-001', 'unobstructed, panoramic, ocean vista, vast Pacific, breathtaking, commanding view, deep blue horizon, massive, sweeping view')::vector) < 0.4)",
"intent": "with ocean view",
"manifest": "with ocean view",
"parameterized": {
"parameterized_intent": "with ocean view",
"parameterized_sql_snippet": "((real_estate.properties.description_embedding <=> google_ml.embedding('gemini-embedding-001', 'unobstructed, panoramic, ocean vista, vast Pacific, breathtaking, commanding view, deep blue horizon, massive, sweeping view')::vector) < 0.4)"
}
הערך 0.4 בהגדרת ההיבט שלמעלה הוא סף שנקבע בניסוי. מורידים את קובץ ההקשר הרב-פנים:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context2_2.json
מעלים את sixrecipe_context2_2.json לסט ההקשר ומריצים את ההנחיה הזו (Q8):
Ocean view Del Mar homes near good schools.
הפלט מכיל תנאים רגילים בשילוב עם התאמה של דמיון וקטורי באמצעות google_ml.embedding:
SELECT "p"."address_street", "p"."address_postal_code", "s"."school_name", "stp"."proximity_miles"
FROM "real_estate"."properties" AS "p"
JOIN "real_estate"."cities" AS "c" ON "p"."city_id" = "c"."city_id"
JOIN "real_estate"."school_to_property" AS "stp" ON "p"."property_id" = "stp"."property_id"
JOIN "real_estate"."schools" AS "s" ON "stp"."school_id" = "s"."school_id"
WHERE "c"."city_name" = 'Del Mar' AND "s"."school_ranking" <= 5 AND "stp"."proximity_miles" <= 2
AND (((p.description_embedding <=> google_ml.embedding('gemini-embedding-001', 'unobstructed, panoramic, ocean vista, vast Pacific, breathtaking, commanding view, deep blue horizon, massive, sweeping view')::vector) <= 0.4))
ORDER BY 0.8 * "s"."school_ranking" + 0.2 * "stp"."proximity_miles";
השאילתה מחזירה נכס אחד:
Query Results address_street address_postal_code school_name proximity_miles 100 Del Mar Heights Rd 92014 Del Mar Heights Elementary 1.00 Answer The ocean-view home in Del Mar near a top-ranked school is located at 100 Del Mar Heights Rd (92014). It is situated just 1.00 mile away from Del Mar Heights Elementary, which is one of the area's highly-ranked schools (ranking of 5 or better).
כדי לאמת את התוצאות, בודקים את התיאור של הנכס ב-AlloyDB Studio ומריצים את השאילתה הבאה:
select description from real_estate.properties where address_postal_code='92014' and description is not null;
יופיע התיאור 'מחזה ימי מרהיב'.
Well-located 2-bedroom condo with excellent proximity to coastal shopping and dining. The balcony provides a magnificent marine spectacle against an azure backdrop. Under contract, high monthly HOA fee.
10. הסרת דו-משמעות מהנתונים הפרטיים באמצעות חיפושי ערכים
בקשות לא ברורות יכולות להתאים לכמה ישויות, למשל אם יש בלבול בין שם של מיקום לבין שם של עסק. חיפושי ערכים ממפים מונחים למושגים במסד הנתונים כדי לעזור לפתור את ההפניות האלה.
החיפוש אחר ערכים מספק קשרים בין שם העמודה בתוספת הערכים בעמודה לבין סוג של מושג, שיכול להיות למשל עיר, מדינה או שם. בנוסף, מכיוון שהמודל אינדקס לא רק מטא-נתונים אלא גם דוגמאות מייצגות של הנתונים בעמודה, הוא יכול לשייך ערך שמשמש בבקשה בשפה טבעית לסוג מושג פוטנציאלי ולציין באיזו טבלה ועמודה אפשר להשתמש ב-SQL שיתקבל.
דו-משמעות בשאילתה
במציאות, יכול להיות שיהיה קשה יותר בגלל שמות ומיקומים שונים שנכתבים בצורה שגויה. מריצים את ההנחיה הזו (Q9) שמכילה שגיאת כתיב ומונח דו-משמעי:
Westwod's sold properties in the last 1 month.
יש כאן שתי בעיות – השם מאוית בצורה שגויה ולא ברור אם מדובר בנכסים שנמכרים במיקום עם שמות דומים או בסוכנות נדל"ן. כשמריצים את השאילתה, היא מחזירה שאילתה שדומה לזו שבהמשך.
SELECT "p"."property_id", "p"."address_street", "p"."address_postal_code", "p"."price" AS "listing_price", "p"."bedrooms", "p"."bathrooms", "p"."listing_status", "pt"."sale_date", "pt"."sale_price", "c"."city_name" FROM "real_estate"."properties" AS "p" INNER JOIN "real_estate"."property_transactions" AS "pt" ON "p"."property_id" = "pt"."property_id" INNER JOIN "real_estate"."cities" AS "c" ON "p"."city_id" = "c"."city_id" WHERE ("c"."city_name" ILIKE '%Westwood%' OR "c"."city_name" ILIKE '%Westwod%') AND "pt"."sale_date" >= '2025-09-01'::date AND "pt"."sale_date" <= '2025-09-30'::date ORDER BY "pt"."sale_date" DESC NULLS LAST, "pt"."sale_price" DESC NULLS LAST
הוא תיקן את השם והניח שהשאלה הייתה לגבי מיקום הנכס, בניסיון להתאים נכסים בעיר ווסטווד.
הפעלת התוסף pg_trgm ב-AlloyDB Studio כדי לתמוך בהשוואות מחרוזות משוערות:
CREATE EXTENSION IF NOT EXISTS pg_trgm;
לאחר מכן, נוכל להשתמש בתוסף כדי להוסיף חיפושים של ערכים לקבוצת ההקשר QueryData.
זו רשימת החיפושים לפי ערך שבהם אנחנו רוצים להשתמש:
"value_searches": [
{
"conceptType": "Municipality name",
"query": "/* Requires extension: pg_trgm */ WITH TrigramMetrics AS (SELECT T.municipality_name AS original_value, (T.municipality_name % $value::text) AS normalized_dist FROM real_estate.municipalities T WHERE T.municipality_name % $value::text) SELECT original_value AS value, 'real_estate.municipalities.municipality_name' AS columns, 'Name of the municipality' AS concept_type, normalized_dist AS distance, ''::text AS context FROM TrigramMetrics",
"description": "Trigram match for municipality name"
},
{
"conceptType": "City name",
"query": "/* Requires extension: pg_trgm */ WITH TrigramMetrics AS (SELECT T.city_name AS original_value, (T.city_name % $value::text) AS normalized_dist FROM real_estate.cities T WHERE T.city_name % $value::text) SELECT original_value AS value, 'real_estate.cities.city_name' AS columns, 'Name of the city' AS concept_type, normalized_dist AS distance, ''::text AS context FROM TrigramMetrics",
"description": "Trigram match for city name"
},
{
"conceptType": "Brokerage name",
"query": "/* Requires extension: pg_trgm */ WITH TrigramMetrics AS (SELECT T.brokerage AS original_value, (T.brokerage % $value::text) AS normalized_dist FROM real_estate.agents T WHERE T.brokerage % $value::text) SELECT original_value AS value, 'real_estate.agents.brokerage' AS columns, 'Name of the brokerage' AS concept_type, normalized_dist AS distance, ''::text AS context FROM TrigramMetrics",
"description": "Trigram match for brokerage name"
}
]
מורידים את קובץ ההקשר של חיפוש הערכים:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context3_1.json
מעלים את הקובץ sixrecipe_context3_1.json לקבוצת ההקשר sixrecipes של הכלי QueryData ושומרים אותו.
מריצים מחדש את ההנחיה (Q9):
Westwod's sold properties in the last 1 month.
התשובה שקיבלת היא הבהרה כי בחיפוש הערכים נמצאו 3 ישויות שונות עם ערכים דומים:
The database requires some clarification to provide the exact information you need: The search term 'Westwod' (Westwood) is ambiguous because it can refer to: A municipality name (e.g., the municipality of Westwood) A city name (e.g., the city of Westwood) A brokerage name (e.g., properties sold by agents affiliated with the Westwood brokerage) Could you please clarify which of these you are referring to?
המעקב נותן לנו רמז למה נבחר הנתיב הספציפי הזה

הנה הטקסט המלא:
"[NOT_ENOUGH_INFO] The search term 'Westwod' is ambiguous because it can refer to a municipality name, a city name, or a brokerage name. Could you please clarify if you are referring to properties sold within the municipality of Westwood, the city of Westwood, or properties sold by agents affiliated with the Westwood brokerage?"
הסרת דו-משמעות מהשאילתה
שיפור ההנחיה (Q10):
Westwood city location properties sold in September 2025.
עכשיו יש לנו את השאילתה ואת התוצאות:
Generated SQL SELECT "p"."property_id", "p"."address_street", "p"."address_postal_code", "p"."price", "pt"."sale_date", "pt"."sale_price" FROM "real_estate"."properties" AS "p" INNER JOIN "real_estate"."cities" AS "c" ON "p"."city_id" = "c"."city_id" INNER JOIN "real_estate"."property_transactions" AS "pt" ON "p"."property_id" = "pt"."property_id" WHERE "c"."city_name" = 'Westwood' AND "pt"."sale_date" >= '2025-09-01'::date AND "pt"."sale_date" <= '2025-09-30'::date ORDER BY "pt"."sale_date" ASC NULLS LAST Query Results property_id address_street address_postal_code price sale_date sale_price 32 999 Main St 99999 3800000.00 2025-09-12 1000000.00
11. חיפוש סמנטי ופונקציות AI
הפונקציות של AlloyDB AI מאפשרות סינון סמנטי של נתונים לא מובנים. אפשר להשתמש בפונקציות כמו ai.if ו-google_ml.embedding ישירות בשאילתות SQL לצד אופרטורים סטנדרטיים של מסד נתונים.
תבנית עם אופרטורים של ILIKE
השלב הראשון הוא להעלות את קובץ ההקשר החדש עם אופרטורים של ILIKE.
מורידים את קובץ ההקשר של תבנית ILIKE:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context4_1.json
מעלים את הקובץ sixrecipe_context4_1.json לערכת ההקשר כדי להעריך התאמה לתבנית ILIKE רגילה לבתים שהם "מוכנים למגורים".
{
"nl_query": "Move-in ready houses",
"sql": "SELECT \"p\".\"address_street\", \"p\".\"address_postal_code\" FROM \"real_estate\".\"properties\" AS \"p\" WHERE \"p\".listing_status = 'Active' AND \"p\".\"description\" IS NOT NULL AND (\"p\".description ILIKE '%turnkey%' OR \"p\".description ILIKE '%move-in%')",
"intent": "Move-in ready houses",
"manifest": "Move-in ready houses",
"parameterized": {
"parameterized_intent": "Move-in ready houses",
"parameterized_sql": "SELECT \"p\".\"address_street\", \"p\".\"address_postal_code\" FROM \"real_estate\".\"properties\" AS \"p\" WHERE \"p\".listing_status = 'Active' AND \"p\".\"description\" IS NOT NULL AND (\"p\".description ILIKE '%turnkey%' OR \"p\".description ILIKE '%move-in%')"
}
}
שליחת השאילתה הזו (Q11):
List move-in ready houses.
השאילתה שמתקבלת:
SELECT "p"."address_street", "p"."address_postal_code"
FROM "real_estate"."properties" AS "p"
WHERE "p".listing_status = 'Active'
AND "p"."description" IS NOT NULL
AND ("p".description ILIKE '%turnkey%' OR "p".description ILIKE '%move-in%')
היא מחזירה רשומה אחת תואמת.
Query Results address_street address_postal_code 40 Main St, Unit 8 N2H 2G8
תבנית עם פונקציית AI.IF
מורידים את קובץ ההקשר של תבנית AI.IF:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context4_2.json
מעלים את הקובץ sixrecipe_context4_2.json לסט ההקשר כדי להעריך את פונקציית הסינון הסמנטי ai.if.
{
"nl_query": "List Move-in ready houses",
"sql": "SELECT \"p\".\"address_street\", \"p\".\"address_postal_code\" FROM \"real_estate\".\"properties\" AS \"p\" WHERE \"p\".listing_status = 'Active' AND \"p\".\"description\" IS NOT NULL AND ai.if('Does this description represent a move-in ready house? Description: ' || \"p\".\"description\")",
"intent": "List Move-in ready houses",
"manifest": "List Move-in ready houses",
"parameterized": {
"parameterized_intent": "List Move-in ready houses",
"parameterized_sql": "SELECT \"p\".\"address_street\", \"p\".\"address_postal_code\" FROM \"real_estate\".\"properties\" AS \"p\" WHERE \"p\".listing_status = 'Active' AND \"p\".\"description\" IS NOT NULL AND ai.if('Does this description represent a move-in ready house? Description: ' || \"p\".\"description\")"
}
}
חזרה על ההנחיה (שאלה 12):
List move-in ready houses.
קוד ה-SQL שנוצר:
SELECT "p"."address_street", "p"."address_postal_code"
FROM "real_estate"."properties" AS "p"
WHERE "p".listing_status = 'Active'
AND "p"."description" IS NOT NULL
AND ai.if('Does this description represent a move-in ready house? Description: ' || "p"."description")
כך מקבלים את אותו מאפיין בתוצאה:
Query Results Address Street Address Postal Code 40 Main St, Unit 8 N2H 2G8 Natural Language Answer The move-in ready home currently available is located at: Address: 40 Main St, Unit 8 Postal Code: N2H 2G8
היבט עם חיפוש וקטורי
מורידים את קובץ ההקשר של החיפוש בהטמעה:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context4_3.json
מעלים את הקובץ sixrecipe_context4_3.json לסט ההקשרים כדי להעריך חיפוש וקטורי סמנטי באמצעות google_ml.embedding וחיפוש בתים עם מכשירי מטבח חדשים.
מעדכנים את קבוצת ההקשר באמצעות הקובץ sixrecipe_context4_3.json עם היבט נוסף באמצעות חיפוש וקטורי ב-AlloyDB.
{
"sql_snippet": "((real_estate.properties.description_embedding <=> google_ml.embedding('gemini-embedding-001', 'Upgraded kitchen appliances')::vector) < 0.4)",
"intent": "with upgraded kitchen appliances",
"manifest": "with upgraded kitchen appliances",
"parameterized": {
"parameterized_intent": "with upgraded kitchen appliances",
"parameterized_sql_snippet": "((real_estate.properties.description_embedding <=> google_ml.embedding('gemini-embedding-001', 'Upgraded kitchen appliances')::vector) < 0.4)"
}
}
הערך 0.4 בהגדרת ההיבט שלמעלה הוא סף שנקבע בניסוי.
מריצים את השאילתה הזו (Q13):
List move-in ready houses with upgraded kitchen appliances.
קוד ה-SQL שנוצר:
SELECT "p"."address_street", "p"."address_postal_code" FROM "real_estate"."properties" AS "p" WHERE "p"."listing_status" = 'Active' AND "p"."description" IS NOT NULL AND ai.if('Does this description represent a Move-in ready house? Description: ' || "p"."description") AND ((("p"."description_embedding" <=> google_ml.embedding('gemini-embedding-001', 'Upgraded kitchen appliances')::vector) < 0.4))
הנה תוצאות השאילתה וההמלצה בשפה טבעית:
Query Results Address Postal Code 40 Main St, Unit 8 N2H 2G8 Natural Language Answer The move-in ready house with upgraded kitchen appliances is located at: Address: 40 Main St, Unit 8 Postal Code: N2H 2G8
12. אבטחה ופרטיות
בכל פעם ש-QueryData API יוצר שאילתות, קיים סיכון לפרטיות, כי השאילתות שנוצרות עלולות לחשוף נתונים ששייכים למשתמשים אחרים. תצוגות מאובטחות עם פרמטרים (PSV) אוכפות גבולות אבטחה ברמת השורה במהלך יצירת NL2SQL.
יצירת תצוגה מאובטחת עם פרמטרים
יש לנו טבלה בשם real_estate.offers עם מידע על הצעות לכל עסקאות הנדל"ן שלנו. אנחנו רוצים שהסוכנים שלנו יראו רק את העסקאות שלהם, ולא את העסקאות של סוכנים אחרים. אנחנו יוצרים PSV עם אפשרות לסנן שורות לפי agent_id.
יוצרים תצוגה מאובטחת עם פרמטרים לטבלת המבצעים ב-AlloyDB Studio:
CREATE VIEW real_estate.secure_offers
WITH (security_barrier) AS
SELECT offer_id, property_id, buyer_agent_id,
seller_agent_id, offer_amount, offer_date,
offer_status, offer_expiration_date,
contingencies
FROM real_estate.offers
WHERE buyer_agent_id = $@agent_id;
מאמתים את ההגדרה של תצוגת האבטחה עם הפרמטרים:
SELECT definition
FROM parameterized_views.all_parameterized_views
WHERE schemaname = 'real_estate' AND viewname = 'secure_offers';
What about real_estate.offers table?
הגבלת הגישה לנתונים של QueryData באמצעות תצוגות מאובטחות עם פרמטרים ב-AlloyDB
ב-agent.py, התצוגה המאובטחת עם הפרמטרים real_estate.secure_offers רשומה ב-TABLE_IDS במקום ב-real_estate.offers:
קוד המקור של agent.py:
List of database tables included in QueryData API requests
TABLE_IDS = [
"real_estate.municipalities",
"real_estate.cities",
"real_estate.agents",
"real_estate.properties",
"real_estate.schools",
"real_estate.school_to_property",
"real_estate.property_transactions",
"real_estate.secure_offers",
]
ומעביר הקשר של זמן ריצה של סשן לסוכן עם agent_id=1:
"parameterized_secure_view_parameters": {
"parameters": {
"key": "agent_id",
"value": "1"
}
}
},
כתוצאה מכך, כל בקשת API של QueryData תעביר את הפרמטר הזה, וכשהתצוגה המאובטחת תיכלל בבקשה, יוחל סינון שיחזיר רק תוצאות עבור הסוכן הספציפי הזה.
מריצים את השאילתה הזו (Q14):
List the offer id and listing price in 2025 where the offer was within 5% of the final selling price.
קוד ה-SQL שנוצר:
SELECT "o"."offer_id", "p"."price" AS "listing_price"
FROM "real_estate"."secure_offers" AS "o"
INNER JOIN "real_estate"."properties" AS "p" ON "o"."property_id" = "p"."property_id"
INNER JOIN "real_estate"."property_transactions" AS "t" ON "o"."property_id" = "t"."property_id"
WHERE "o"."offer_date" >= '2025-01-01'
AND "o"."offer_date" <= '2025-12-31'
AND "o"."offer_amount" IS NOT NULL
AND "t"."sale_price" IS NOT NULL
AND "p"."price" IS NOT NULL
AND ABS("o"."offer_amount" - "t"."sale_price") <= (0.05 * "t"."sale_price")
ORDER BY "o"."offer_id" ASC NULLS LAST
השאילתה על התצוגה מסננת את התוצאות לפי הפרמטר של סשן הסוכן הפעיל, כדי להגן על נתוני רשומות לא מורשים ולהחזיר רק שתי רשומות.
Query Results Offer ID Listing Price 7 $750,000.00 8 $510,000.00 Natural Language Answer Here are the offers from 2025 where the offer amount was within 5% of the final selling price: Offer ID 7: Listing Price of $750,000.00 Offer ID 8: Listing Price of $510,000.00
אם מריצים את השאילתה הבאה על טבלת הבסיס real_estate.offers ב-AlloyDB Studio:
SELECT "o"."offer_id", "p"."price" AS "listing_price"
FROM "real_estate"."offers" AS "o"
INNER JOIN "real_estate"."properties" AS "p" ON "o"."property_id" = "p"."property_id"
INNER JOIN "real_estate"."property_transactions" AS "t" ON "o"."property_id" = "t"."property_id"
WHERE "o"."offer_date" >= '2025-01-01'
AND "o"."offer_date" <= '2025-12-31'
AND "o"."offer_amount" IS NOT NULL
AND "t"."sale_price" IS NOT NULL
AND "p"."price" IS NOT NULL
AND ABS("o"."offer_amount" - "t"."sale_price") <= (0.05 * "t"."sale_price")
ORDER BY "o"."offer_id" ASC NULLS LAST
הפונקציה מחזירה יותר משני רשומות, עם נתונים לגבי כל הסוכנים.
סיימנו את שיעור ה-Lab! למדתם איך להשתמש ב-QueryData API, בפונקציות AI ובתצוגות מאובטחות עם פרמטרים של AlloyDB.
13. ניקוי הסביבה
בסיום שיעור ה-Lab, משמידים את המופעים ואת האשכול של AlloyDB.
מחיקת אשכול AlloyDB וכל המכונות
האפשרות force משמידה את האשכול וגם מוחקת את כל המכונות ששייכות לאשכול.
אם החיבור לטרמינל נותק, מתחברים מחדש ומגדירים את הפרויקט ואת משתני הסביבה ב-Cloud Shell:
gcloud config set project <YOUR_PROJECT_ID>
export REGION=us-central1
export ADBCLUSTER=alloydb-aip-01
export PROJECT_ID=$(gcloud config get-value project)
מחיקת האשכול:
gcloud alloydb clusters delete $ADBCLUSTER --region=$REGION --force
הפלט הצפוי בקונסולה:
student@cloudshell:~ (test-project-001-402417)$ gcloud alloydb clusters delete $ADBCLUSTER --region=$REGION --force All of the cluster data will be lost when the cluster is deleted. Do you want to continue (Y/n)? Y Operation ID: operation-1697820178429-6082890a0b570-4a72f7e4-4c5df36f Deleting cluster...done.
מחיקת גיבויים של AlloyDB
מחיקת כל הגיבויים של AlloyDB לאשכול:
for i in $(gcloud alloydb backups list \
--filter="CLUSTER_NAME: projects/$PROJECT_ID/locations/$REGION/clusters/$ADBCLUSTER" \
--format="value(name)" \
--sort-by=~createTime) ; do \
gcloud alloydb backups delete $(basename $i) --region $REGION --quiet; done
הפלט הצפוי בקונסולה:
student@cloudshell:~ (test-project-001-402417)$ for i in $(gcloud alloydb backups list --filter="CLUSTER_NAME: projects/$PROJECT_ID/locations/$REGION/clusters/$ADBCLUSTER" --format="value(name)" --sort-by=~createTime) ; do gcloud alloydb backups delete $(basename $i) --region $REGION --quiet; done Operation ID: operation-1697826266108-60829fb7b5258-7f99dc0b-99f3c35f Deleting backup...done.
מחיקת קטגוריה של Cloud Storage
כדי למנוע חיובים שוטפים על אחסון, מוחקים את קטגוריית הייבוא של Cloud Storage:
gcloud storage rm -r gs://${PROJECT_ID}-import
14. מזל טוב
כל הכבוד, סיימתם את ה-Codelab!
מה נכלל
- איך פורסים אשכול ומכונה ראשית של AlloyDB
- איך מפעילים את QueryData, פונקציות AI ותצוגות מאובטחות עם פרמטרים ב-AlloyDB
- איך יוצרים קבוצת הקשר QueryData
- איך משתמשים בתבניות ובהקשרים שונים ב-QueryData API
- איך להשתמש בהיבטים כדי להגדיל את הגמישות של תבניות QueryData
- איך לפתור בעיות של עמימות נתונים באמצעות חיפוש ערכים ואינדוקס טריגרם
- איך משתמשים בפונקציות AI לחיפוש סמנטי באמצעות תבניות
- איך משתמשים בתצוגות מאובטחות עם פרמטרים באמצעות QueryData API
סקר
פלט: