Agentverse - The Scholar's Grimoire - Building Knowledge Engines with RAG

1. של גורל

העידן של פיתוח מבודד מסתיים. הגל הבא של ההתפתחות הטכנולוגית לא מתבסס על גאונות בודדת, אלא על שיתוף פעולה. יצירת סוכן חכם יחיד היא ניסוי מרתק. האתגר הגדול של ארגונים מודרניים הוא בניית סביבה עסקית חזקה, מאובטחת וחכמה של סוכנים – Agentverse אמיתי.

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

הסדנה הזו היא מדריך מקיף לארגונים שרוצים להתכונן לעתיד של סוכנים ב-Google Cloud. אנחנו מספקים מפת דרכים מקיפה שתעזור לכם להפוך רעיון ראשוני למציאות מבצעית בקנה מידה מלא. בארבעת שיעורי ה-Lab האלה, שקשורים זה לזה, תלמדו איך הכישורים המיוחדים של מפתח, ארכיטקט, מהנדס נתונים ומומחה SRE צריכים להתחבר כדי ליצור, לנהל ולהתאים לעומס Agentverse עוצמתי.

אי אפשר להסתמך רק על אחד מהעקרונות האלה כדי להשתמש ב-Agentverse. התוכנית הגדולה של הארכיטקט לא שווה בלי הביצוע המדויק של המפתח. הסוכן של המפתח לא יכול לפעול בלי הידע של מהנדס הנתונים, והמערכת כולה פגיעה בלי ההגנה של מהנדס ה-SRE. רק באמצעות סינרגיה והבנה משותפת של התפקידים של כל אחד, הצוות שלכם יכול להפוך רעיון חדשני למציאות תפעולית קריטית. המסע שלכם מתחיל כאן. הכנה לשליטה בתפקיד ולמידה על מקומכם בתמונה הגדולה.

ברוכים הבאים ל-Agentverse: קריאה לאלופים

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

agentverse.png

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

אבל אנטרופיה זוחלת, שחיתות שקטה שנקראת 'הסטטיק', מתחילה לשחוק את הקצוות של העולם החדש הזה. הסטטיק הוא לא וירוס או באג, אלא גילום של כאוס שטורף את עצם פעולת היצירה.

התסכולים הישנים מתעצמים ומתפתחים לצורות מפלצתיות, ויוצרים את שבעת רוחות הרפאים של הפיתוח. אם לא תסמנו את התיבה, הסטטי והספקטרים שלו יגרמו לעצירה מוחלטת של ההתקדמות, וההבטחה של Agentverse תהפוך לשממה של חוב טכני ופרויקטים נטושים.

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

בחירת כיתה

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

  • Shadowblade (מפתח): אומן הנפחות והחזית. אתם האומנים שמייצרים את הלהבים, בונים את הכלים ומתמודדים עם האויב בפרטים המורכבים של הקוד. המסלול שלך הוא מסלול של דיוק, מיומנות ויצירה מעשית.
  • האסטרטג (אדריכל): אסטרטג ומנהל פרויקטים. אתם לא רואים סוכן יחיד, אלא את כל שדה הקרב. אתם מעצבים את תוכניות האב שמאפשרות למערכות שלמות של סוכנים לתקשר, לשתף פעולה ולהשיג מטרה גדולה בהרבה מכל רכיב בודד.
  • המלומד (מהנדס נתונים): מחפש את האמת הנסתרת ושומר על החוכמה. אתם יוצאים למסע אל מרחבי הנתונים העצומים והפראיים כדי לחשוף את התובנות שיעזרו לסוכנים שלכם להבין את המטרה ולראות את התמונה המלאה. הידע שלכם יכול לחשוף חולשה של אויב או להעצים את היכולות של בעל ברית.
  • השומר (DevOps / SRE): המגן הנאמן של הממלכה. אתם בונים את המבצרים, מנהלים את קווי האספקה של החשמל ומוודאים שהמערכת כולה יכולה לעמוד בפני המתקפות הבלתי נמנעות של הסטטיק. החוזק שלכם הוא הבסיס שעליו נבנה הניצחון של הקבוצה.

המשימה שלך

האימון יתחיל כתרגיל עצמאי. תלכו בנתיב שבחרתם ותלמדו את הכישורים הייחודיים שנדרשים כדי לשלוט בתפקיד. בסוף תקופת הניסיון, תתמודדו עם רוח רפאים שנולדה מתוך הסטטיות – מיני-בוס שטורף את האתגרים הספציפיים של המלאכה שלכם.

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

אתגר אחרון ושיתופי שיבחן את הכוח המשולב שלכם ויקבע את גורל ה-Agentverse.

הגיבורים של Agentverse מחכים לכם. תענו לשיחה?

2. The Scholar's Grimoire

המסע שלנו מתחיל! כחוקרים, הנשק העיקרי שלנו הוא ידע. גילינו בארכיונים שלנו (Google Cloud Storage) אוסף של מגילות עתיקות ומוצפנות. המגילות האלה מכילות מידע מודיעיני גולמי על החיות המפחידות שפוקדות את הארץ. המטרה שלנו היא להשתמש בקסם האנליטי העמוק של Google BigQuery ובחוכמה של Gemini Elder Brain (מודל Gemini Pro) כדי לפענח את הטקסטים הלא מובנים האלה ולהפוך אותם ל-Bestiary מובנה שאפשר להריץ עליו שאילתות. ההגדרה הזו תהיה הבסיס לכל האסטרטגיות העתידיות שלנו.

סקירה כללית

מה תלמדו

  • שימוש ב-BigQuery ליצירת טבלאות חיצוניות ולביצוע טרנספורמציות מורכבות מנתונים לא מובנים לנתונים מובנים באמצעות BQML.GENERATE_TEXT עם מודל Gemini.
  • הקצאת מכונה של Cloud SQL ל-PostgreSQL והפעלת התוסף pgvector ליכולות חיפוש סמנטי.
  • במדריך הזה תלמדו ליצור צינור עיבוד נתונים חזק ומבוסס-קונטיינרים לעיבוד קבוצות (batch) באמצעות Dataflow ו-Apache Beam. הצינור הזה יעבד קובצי טקסט גולמיים, ייצור הטבעות וקטוריות באמצעות מודל Gemini ויכתוב את התוצאות למסד נתונים רלציוני.
  • הטמעה של מערכת בסיסית של Retrieval-Augmented Generation (יצירה משולבת-אחזור, RAG) בסוכן כדי לשלוח שאילתות לנתונים שעברו וקטוריזציה.
  • פריסת סוכן שמודע לנתונים כשירות מאובטח וניתן להרחבה ב-Cloud Run.

3. הכנת המקדש של המלומד

ברוכים הבאים, חוקרים. לפני שנתחיל לכתוב את הידע העצום של ספר הכישופים שלנו, עלינו להכין את המקדש שלנו. הטקס הבסיסי הזה כולל הטמעה של קסם בסביבת Google Cloud, פתיחה של הפורטלים הנכונים (ממשקי API) ויצירה של הצינורות שדרכם יזרמו נתוני הקסם שלנו. מקדש מוכן היטב מבטיח שהלחשים שלנו יהיו חזקים והידע שלנו יהיה מאובטח.

‫👈 לוחצים על 'הפעלת Cloud Shell' בחלק העליון של מסוף Google Cloud (זהו סמל בצורת טרמינל בחלק העליון של חלונית Cloud Shell).

טקסט חלופי

‫👈 לוחצים על הלחצן 'פתיחת הכלי לעריכה' (הוא נראה כמו תיקייה פתוחה עם עיפרון). חלון Cloud Shell Code Editor ייפתח. בצד ימין יופיע סייר הקבצים. טקסט חלופי

‫👉פותחים את הטרמינל בסביבת הפיתוח המשולבת (IDE) בענן, טקסט חלופי

‫👈💻 בטרמינל, מוודאים שכבר עברתם אימות ושהפרויקט מוגדר למזהה הפרויקט שלכם באמצעות הפקודה הבאה:

gcloud auth list

‫👈💻משכפלים את פרויקט ה-bootstrap מ-GitHub:

git clone https://github.com/weimeilin79/agentverse-dataengineer
chmod +x ~/agentverse-dataengineer/init.sh
chmod +x ~/agentverse-dataengineer/set_env.sh
chmod +x ~/agentverse-dataengineer/data_setup.sh

git clone https://github.com/weimeilin79/agentverse-dungeon.git
chmod +x ~/agentverse-dungeon/run_cloudbuild.sh
chmod +x ~/agentverse-dungeon/start.sh

‫👈💻 מריצים את סקריפט ההגדרה מהספרייה של הפרויקט.

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

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

cd ~/agentverse-dataengineer
./init.sh

👉 שלב חשוב אחרי השלמת התהליך: אחרי שהסקריפט מסיים לפעול, צריך לוודא שבמסוף Google Cloud מוצג הפרויקט הנכון:

  1. נכנסים לכתובת console.cloud.google.com.
  2. לוחצים על התפריט הנפתח לבחירת פרויקט בחלק העליון של הדף.
  3. לוחצים על הכרטיסייה All (הכל) (כי יכול להיות שהפרויקט החדש עדיין לא יופיע ב-Recent (אחרונים)).
  4. בוחרים את מזהה הפרויקט שהגדרתם בשלב init.sh.

03-05-project-all.png

‫👈💻 מגדירים את מזהה הפרויקט הנדרש:

gcloud config set project $(cat ~/project_id.txt) --quiet

‫👈💻 מריצים את הפקודה הבאה כדי להפעיל את ממשקי ה-API הנדרשים של Google Cloud:

gcloud services enable \
    storage.googleapis.com \
    bigquery.googleapis.com \
    sqladmin.googleapis.com \
    aiplatform.googleapis.com \
    dataflow.googleapis.com \
    pubsub.googleapis.com \
    cloudfunctions.googleapis.com \
    run.googleapis.com \
    cloudbuild.googleapis.com \
    artifactregistry.googleapis.com \
    iam.googleapis.com \
    compute.googleapis.com \
    cloudresourcemanager.googleapis.com \
    cloudaicompanion.googleapis.com \
    bigqueryunified.googleapis.com 

‫👉💻 אם עדיין לא יצרתם מאגר Artifact Registry בשם agentverse-repo, מריצים את הפקודה הבאה כדי ליצור אותו:

. ~/agentverse-dataengineer/set_env.sh
gcloud artifacts repositories create $REPO_NAME \
    --repository-format=docker \
    --location=$REGION \
    --description="Repository for Agentverse agents"

הגדרת הרשאות

‫👉💻 נותנים את ההרשאות הנדרשות על ידי הרצת הפקודות הבאות בטרמינל:

. ~/agentverse-dataengineer/set_env.sh

# --- Grant Core Data Permissions ---
gcloud projects add-iam-policy-binding $PROJECT_ID \
 --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 --role="roles/storage.admin"

gcloud projects add-iam-policy-binding $PROJECT_ID \
 --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 --role="roles/bigquery.admin"

# --- Grant Data Processing & AI Permissions ---
gcloud projects add-iam-policy-binding $PROJECT_ID  \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME"  \
--role="roles/dataflow.admin"

gcloud projects add-iam-policy-binding $PROJECT_ID  \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME"  \
--role="roles/cloudsql.admin"

gcloud projects add-iam-policy-binding $PROJECT_ID  \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME"  \
--role="roles/pubsub.admin"

gcloud projects add-iam-policy-binding $PROJECT_ID  \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME"  \
--role="roles/aiplatform.user"

# --- Grant Deployment & Execution Permissions ---
gcloud projects add-iam-policy-binding $PROJECT_ID  \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME"  \
--role="roles/cloudbuild.builds.editor"

gcloud projects add-iam-policy-binding $PROJECT_ID  \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME"  \
--role="roles/artifactregistry.admin"

gcloud projects add-iam-policy-binding $PROJECT_ID  \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME"  \
--role="roles/run.admin"

gcloud projects add-iam-policy-binding $PROJECT_ID  \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME"  \
--role="roles/iam.serviceAccountUser"

gcloud projects add-iam-policy-binding $PROJECT_ID  \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME"  \
--role="roles/logging.logWriter"


gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/dataflow.admin"

‫👈💻 כשמתחילים את האימון, אנחנו מכינים את האתגר הסופי. הפקודות הבאות יזמנו את הרוחות הרפאים מהסטטי הכאוטי, ויצרו את הבוסים לבדיקה הסופית.

. ~/agentverse-dataengineer/set_env.sh
cd ~/agentverse-dungeon
./run_cloudbuild.sh
cd ~/agentverse-dataengineer

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

4. האלכימיה של הידע: שינוי נתונים באמצעות BigQuery ו-Gemini

במלחמה הבלתי פוסקת נגד הסטטיק, כל עימות בין גיבור של Agentverse לבין Spectre of Development מתועד בקפידה. מערכת סימולציית שדה הקרב, סביבת האימון העיקרית שלנו, יוצרת באופן אוטומטי רשומה ביומן האתררי לכל מפגש. יומני התיאור האלה הם המקור הכי חשוב שלנו למידע גולמי, עפרות לא מעובדות שמהן אנחנו, כחוקרים, צריכים ליצור את פלדת האסטרטגיה הטהורה.הכוח האמיתי של חוקר לא טמון רק בהחזקת נתונים, אלא ביכולת להפוך את עפרות המידע הגולמיות והכאוטיות לפלדה מבריקה ומובנית של חוכמה מעשית.אנחנו נבצע את הטקס הבסיסי של אלכימיית הנתונים.

סטורי

התהליך שלנו יכלול כמה שלבים, ויתבצע כולו בתוך BigQuery של Google. נתחיל בהתבוננות בארכיון GCS שלנו בלי להזיז את הגלילה, באמצעות עדשה קסומה. לאחר מכן, נפעיל את Gemini כדי לקרוא ולפרש את סאגות הקרב הלא מובנות והפואטיות. לבסוף, נשפר את הנבואות הגולמיות וניצור מהן קבוצה של טבלאות נקיות ומקושרות. ה-Grimoire הראשון שלנו. ולשאול אותה שאלה כל כך עמוקה שרק המבנה החדש הזה יכול לענות עליה.

סקירה כללית

העדשה של הבדיקה: הצצה ל-GCS באמצעות טבלאות חיצוניות של BigQuery

הפעולה הראשונה שלנו היא ליצור עדשה שתאפשר לנו לראות את התוכן של הארכיון ב-GCS בלי להפריע למגילות שבתוכו. טבלה חיצונית היא כמו עדשה שדרכה אפשר לראות את קובצי הטקסט הגולמיים, והיא ממפה אותם למבנה שדומה לטבלה שאפשר להריץ עליה שאילתות ישירות ב-BigQuery.

לשם כך, אנחנו צריכים קודם ליצור קו ליי יציב של אנרגיה, משאב CONNECTION, שמקשר בצורה מאובטחת בין המקדש של BigQuery לבין הארכיון של GCS.

‫👈💻 בטרמינל של Cloud Shell, מריצים את הפקודה הבאה כדי להגדיר את האחסון וליצור את הצינור:

. ~/agentverse-dataengineer/set_env.sh
. ~/agentverse-dataengineer/data_setup.sh

bq mk --connection \
  --connection_type=CLOUD_RESOURCE \
  --project_id=${PROJECT_ID} \
  --location=${REGION} \
  gcs-connection

💡 שימו לב! הודעה תופיע בהמשך!

סקריפט ההגדרה משלב 2 התחיל תהליך ברקע. אחרי כמה דקות, תופיע הודעה במסוף שדומה להודעה הבאה:[1]+ Done gcloud sql instances create ...ההודעה הזו צפויה ואין סיבה לדאגה. פשוט נוצר מסד נתונים ב-Cloud SQL. אפשר להתעלם מההודעה הזו ולהמשיך לעבוד.

לפני שיוצרים את הטבלה החיצונית, צריך ליצור את מערך הנתונים שיכיל אותה.

‫👈💻 מריצים את הפקודה הפשוטה הזו בטרמינל של Cloud Shell:

. ~/agentverse-dataengineer/set_env.sh
bq --location=${REGION} mk --dataset ${PROJECT_ID}:bestiary_data

‫👈💻 עכשיו צריך לתת לחתימה הקסומה של הצינור את ההרשאות הנדרשות לקריאה מארכיון GCS ולשימוש ב-Gemini.

. ~/agentverse-dataengineer/set_env.sh
export CONNECTION_SA=$(bq show --connection --project_id=${PROJECT_ID} --location=${REGION} --format=json gcs-connection  | jq -r '.cloudResource.serviceAccountId')

echo "The Conduit's Magical Signature is: $CONNECTION_SA"

echo "Granting key to the GCS Archive..."
gcloud storage buckets add-iam-policy-binding gs://${PROJECT_ID}-reports \
  --member="serviceAccount:$CONNECTION_SA" \
  --role="roles/storage.objectViewer"

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member="serviceAccount:$CONNECTION_SA" \
  --role="roles/aiplatform.user"

‫👈💻 במסוף Cloud Shell, מריצים את הפקודה הבאה כדי להציג את שם הקטגוריה:

echo $BUCKET_NAME

במסוף יוצג שם שדומה ל-your-project-id-gcs-bucket. תצטרכו את זה בשלבים הבאים.

‫👈 צריך להריץ את הפקודה הבאה מתוך עורך השאילתות של BigQuery ב-Google Cloud Console. הדרך הכי קלה להגיע לשם היא לפתוח את הקישור שלמטה בכרטיסייה חדשה בדפדפן. הקישור יוביל אתכם ישירות לדף הנכון במסוף Google Cloud.

https://console.cloud.google.com/bigquery

‫👈 אחרי שהדף נטען, לוחצים על לחצן הפלוס הכחול (יצירת שאילתה חדשה) כדי לפתוח כרטיסייה חדשה בעורך.

עורך השאילתות של BigQuery

עכשיו נכתוב את לחש שפת הגדרת הנתונים (DDL) כדי ליצור את העדשה הקסומה שלנו. ההגדרה הזו מציינת ל-BigQuery איפה לחפש ומה לראות.

‫👈📜 בעורך השאילתות של BigQuery שפתחתם, מדביקים את ה-SQL הבא. חשוב לזכור להחליף את REPLACE-WITH-YOUR-BUCKET-NAME

בשם הקטגוריה שהעתקתם. לוחצים על הפעלה:

CREATE OR REPLACE EXTERNAL TABLE bestiary_data.raw_intel_content_table (
  raw_text STRING
)
OPTIONS (
  format = 'CSV',
  -- This is a trick to load each line of the text files as a single row.
  field_delimiter = '§', 
  uris = ['gs://REPLACE-WITH-YOUR-BUCKET-NAME/raw_intel/*']
);

‫👈📜 מריצים שאילתה כדי לראות את תוכן הקבצים.

SELECT * FROM bestiary_data.raw_intel_content_table;

תוכן גולמי של Intel

העדשה שלנו במקום. עכשיו אפשר לראות את הטקסט הגולמי של המגילות. אבל קריאה היא לא הבנה.

בארכיון הרעיונות שנשכחו, אלארה (מספר הסימון adv_001), חוקרת של Agentverse, התעמתה עם רוח הרפאים המלאכית של הפרפקציוניזם. הישות, שסווגה כ-p-01, נצצה עם כוח חיים של 120 נקודות פגיעה. במנטרה ממוקדת אחת של Elegant Sufficiency, אלרה ניפצה את האורה המשתקת שלו, מתקפה מנטלית שגרמה ל-150 נקודות נזק. המפגש נמשך 180 שניות של ריכוז אינטנסיבי. הערכה סופית: ניצחון.

המגילות לא כתובות בטבלאות ובשורות, אלא בפרוזה המתפתלת של סאגות. זהו הניסוי הראשון שלנו.

הניחוש של המלומד: הפיכת טקסט לטבלה באמצעות SQL

הבעיה היא שדוח שמפרט את שתי המתקפות המהירות של Shadowblade נראה שונה מאוד מתיאור של Summoner שאוסף כוח עצום למתקפה אחת הרסנית. אנחנו לא יכולים פשוט לייבא את הנתונים האלה, אנחנו צריכים לפרש אותם. זה הרגע הקסום. נשתמש בשאילתת SQL אחת כקסם רב עוצמה כדי לקרוא, להבין ולבנות את כל הרשומות מכל הקבצים שלנו, ישירות ב-BigQuery.

‫👈💻 חוזרים למסוף Cloud Shell ומריצים את הפקודה הבאה כדי להציג את שם החיבור:

echo "${PROJECT_ID}.${REGION}.gcs-connection"

מחרוזת החיבור המלאה תוצג במסוף. בוחרים את המחרוזת הזו ומעתיקים אותה. תצטרכו אותה בשלב הבא.

נשתמש במנטרה אחת חזקה: ML.GENERATE_TEXT. הלחש הזה מזמן את Gemini, מציג לו כל מגילה ומורה לו להחזיר את העובדות העיקריות כאובייקט JSON מובנה.

‫👈📜 ב-BigQuery Studio, יוצרים הפניה למודל Gemini. הפעולה הזו מקשרת את Gemini Flash oracle לספריית BigQuery שלנו, כדי שנוכל להפעיל אותו בשאילתות שלנו. חשוב לזכור להחליף את

REPLACE-WITH-YOUR-FULL-CONNECTION-STRING עם מחרוזת החיבור המלאה שהעתקתם מהטרמינל.

  CREATE OR REPLACE MODEL bestiary_data.gemini_flash_model
  REMOTE WITH CONNECTION `REPLACE-WITH-YOUR-FULL-CONNECTION-STRING`
  OPTIONS (endpoint = 'gemini-2.5-flash');

‫👈📜 עכשיו, מטילים את לחש השינוי הגדול. השאילתה הזו קוראת את הטקסט הגולמי, יוצרת הנחיה מפורטת לכל גלילה, שולחת אותה ל-Gemini ויוצרת טבלת ביניים חדשה מהתשובה המובנית בפורמט JSON של ה-AI.

CREATE OR REPLACE TABLE bestiary_data.structured_bestiary AS
SELECT
  -- THE CRITICAL CHANGE: We remove PARSE_JSON. The result is already a JSON object.
  ml_generate_text_result AS structured_data
FROM
  ML.GENERATE_TEXT(
    -- Our bound Gemini Flash model.
    MODEL bestiary_data.gemini_flash_model,

    -- Our perfectly constructed input, with the prompt built for each row.
    (
      SELECT
        CONCAT(
          """
          From the following text, extract structured data into a single, valid JSON object.

          Your output must strictly conform to the following JSON structure and data types. Do not add, remove, or change any keys.

          {
            "monster": {
              "monster_id": "string",
              "name": "string",
              "type": "string",
              "hit_points": "integer"
            },
            "battle": {
              "battle_id": "string",
              "monster_id": "string",
              "adventurer_id": "string",
              "outcome": "string",
              "duration_seconds": "integer"
            },
            "adventurer": {
              "adventurer_id": "string",
              "name": "string",
              "class": "string"
            }
          }

          **CRUCIAL RULES:**
          - Do not output any text, explanations, conversational filler, or markdown formatting like ` ```json` before or after the JSON object.
          - Your entire response must be ONLY the raw JSON object itself.

          Here is the text:
          """,
          raw_text -- We append the actual text of the report here.
        ) AS prompt -- The final column is still named 'prompt', as the oracle requires.
      FROM
        bestiary_data.raw_intel_content_table
    ),

    -- The STRUCT now ONLY contains model parameters.
    STRUCT(
      0.2 AS temperature,
      2048 AS max_output_tokens
    )
  );

ההמרה הושלמה, אבל התוצאה עדיין לא טהורה. מודל Gemini מחזיר את התשובה שלו בפורמט סטנדרטי, ועוטף את ה-JSON הרצוי במבנה גדול יותר שכולל מטא-נתונים על תהליך החשיבה שלו. בואו נסתכל על הנבואה הגולמית הזו לפני שננסה לזקק אותה.

‫👈📜 מריצים שאילתה כדי לבדוק את הפלט הגולמי ממודל Gemini:

SELECT * FROM bestiary_data.structured_bestiary;

‫👀 תופיע עמודה אחת בשם structured_data. התוכן של כל שורה ייראה בדומה לאובייקט ה-JSON המורכב הזה:

{"candidates":[{"avg_logprobs":-0.5691758094475283,"content":{"parts":[{"text":"```json\n{\n  \"monster\": {\n    \"monster_id\": \"gw_02\",\n    \"name\": \"Gravewight\",\n    \"type\": \"Gravewight\",\n    \"hit_points\": 120\n  },\n  \"battle\": {\n    \"battle_id\": \"br_735\",\n    \"monster_id\": \"gw_02\",\n    \"adventurer_id\": \"adv_001\",\n    \"outcome\": \"Defeat\",\n    \"duration_seconds\": 45\n  },\n  \"adventurer\": {\n    \"adventurer_id\": \"adv_001\",\n    \"name\": \"Elara\",\n    \"class\": null\n  }\n}\n```"}],"role":"model"},"finish_reason":"STOP","score":-97.32906341552734}],"create_time":"2025-07-28T15:53:24.482775Z","model_version":"gemini-2.5-flash","response_id":"9JyHaNe7HZ2WhMIPxqbxEQ","usage_metadata":{"billable_prompt_usage":{"text_count":640},"candidates_token_count":171,"candidates_tokens_details":[{"modality":"TEXT","token_count":171}],"prompt_token_count":207,"prompt_tokens_details":[{"modality":"TEXT","token_count":207}],"thoughts_token_count":1014,"total_token_count":1392,"traffic_type":"ON_DEMAND"}}

כפי שאפשר לראות, הפרס שלנו – אובייקט ה-JSON הנקי שביקשנו – מוטמע עמוק במבנה הזה. המשימה הבאה שלנו ברורה. אנחנו צריכים לבצע טקס כדי לנווט במבנה הזה באופן שיטתי ולחלץ את החוכמה הטהורה שבו.

הטקס של הניקוי: נרמול של פלט AI גנרטיבי באמצעות SQL

מודל Gemini סיים את התשובה, אבל המילים שלו גולמיות ועטופות באנרגיות האתריות של היצירה שלו (מועמדים, finish_reason וכו'). חוקר אמיתי לא פשוט שם את הנבואה הגולמית על המדף, אלא בוחן אותה בקפידה, מפיק ממנה את התובנות החשובות וכותב אותן בספרים המתאימים לשימוש עתידי.

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

  • קוראים את ה-JSON הגולמי והמקונן מטבלת הביניים.
  • לנקות ולנתח אותו כדי להגיע לנתוני הליבה.
  • תכתוב את החלקים הרלוונטיים בשלוש טבלאות סופיות ומושלמות: מפלצות, הרפתקנים וקרבות.

‫👈📜 בעורך שאילתות חדש של BigQuery, מריצים את הכישוף הבא כדי ליצור את עדשת הניקוי שלנו:

CREATE OR REPLACE TABLE bestiary_data.monsters AS
WITH
  CleanedDivinations AS (
    SELECT
      SAFE.PARSE_JSON(
        REGEXP_EXTRACT(
          JSON_VALUE(structured_data, '$.candidates[0].content.parts[0].text'),
          r'\{[\s\S]*\}'
        )
      ) AS report_data
    FROM
      bestiary_data.structured_bestiary
  )
SELECT
  JSON_VALUE(report_data, '$.monster.monster_id') AS monster_id,
  JSON_VALUE(report_data, '$.monster.name') AS name,
  JSON_VALUE(report_data, '$.monster.type') AS type,
  SAFE_CAST(JSON_VALUE(report_data, '$.monster.hit_points') AS INT64) AS hit_points
FROM
  CleanedDivinations
WHERE
  report_data IS NOT NULL
QUALIFY ROW_NUMBER() OVER (PARTITION BY monster_id ORDER BY name) = 1;

‫👉📜 אימות המידע ב-Bestiary:

SELECT * FROM bestiary_data.monsters;

עכשיו ניצור את רשימת הגיבורים, רשימה של ההרפתקנים האמיצים שהתמודדו עם המפלצות האלה.

‫👈📜 בעורך שאילתות חדש, מריצים את הכישוף הבא כדי ליצור את הטבלה adventurers:

CREATE OR REPLACE TABLE bestiary_data.adventurers AS
WITH
  CleanedDivinations AS (
    SELECT
      SAFE.PARSE_JSON(
        REGEXP_EXTRACT(
          JSON_VALUE(structured_data, '$.candidates[0].content.parts[0].text'),
          r'\{[\s\S]*\}'
        )
      ) AS report_data
    FROM
      bestiary_data.structured_bestiary
  )
SELECT
  JSON_VALUE(report_data, '$.adventurer.adventurer_id') AS adventurer_id,
  JSON_VALUE(report_data, '$.adventurer.name') AS name,
  JSON_VALUE(report_data, '$.adventurer.class') AS class
FROM
  CleanedDivinations
QUALIFY ROW_NUMBER() OVER (PARTITION BY adventurer_id ORDER BY name) = 1;

‫👉📜 אימות רשימת האלופים:

SELECT * FROM bestiary_data.adventurers;

לבסוף, ניצור את טבלת העובדות: Chronicle of Battles (תיעוד הקרבות). הכרך הזה מקשר בין שני הכרכים האחרים, ומתעד את הפרטים של כל מפגש ייחודי. מכיוון שכל קרב הוא אירוע ייחודי, אין צורך בהסרת כפילויות.

‫👈📜 בעורך שאילתות חדש, מריצים את הכישוף הבא כדי ליצור את טבלת הקרבות:

CREATE OR REPLACE TABLE bestiary_data.battles AS
WITH
  CleanedDivinations AS (
    SELECT
      SAFE.PARSE_JSON(
        REGEXP_EXTRACT(
          JSON_VALUE(structured_data, '$.candidates[0].content.parts[0].text'),
          r'\{[\s\S]*\}'
        )
      ) AS report_data
    FROM
      bestiary_data.structured_bestiary
  )
-- Extract the raw essence for all battle fields and cast where necessary.
SELECT
  JSON_VALUE(report_data, '$.battle.battle_id') AS battle_id,
  JSON_VALUE(report_data, '$.battle.monster_id') AS monster_id,
  JSON_VALUE(report_data, '$.battle.adventurer_id') AS adventurer_id,
  JSON_VALUE(report_data, '$.battle.outcome') AS outcome,
  SAFE_CAST(JSON_VALUE(report_data, '$.battle.duration_seconds') AS INT64) AS duration_seconds
FROM
  CleanedDivinations;

‫👉📜 אימות הנתונים ב-Chronicle:

SELECT * FROM bestiary_data.battles;

גילוי תובנות אסטרטגיות

המגילות נקראו, התמצית זוקקה והכרכים נכתבו. ה-Grimoire שלנו הוא כבר לא רק אוסף של עובדות, אלא מסד נתונים יחסי של חוכמה אסטרטגית עמוקה. עכשיו אפשר לשאול שאלות שלא הייתה אפשרות לענות עליהן כשהידע שלנו היה כלוא בטקסט גולמי ולא מובנה.

עכשיו נבצע ניבוי אחרון וגדול. אנחנו נטיל לחש שיתייעץ עם כל שלושת הספרים שלנו בבת אחת – Bestiary of Monsters,‏ Roll of Champions ו-Chronicle of Battles – כדי לחשוף תובנה עמוקה ופרקטית.

השאלה האסטרטגית שלנו: "מה שם המפלצת הכי חזקה (לפי נקודות פגיעה) שכל אחד מההרפתקנים הצליח להביס, וכמה זמן נמשך הניצחון הספציפי הזה?"

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

‫👈📜 בעורך שאילתות חדש של BigQuery, מריצים את הלחש הסופי הבא:

-- This is our final spell, joining all three tomes to reveal a deep insight.
WITH
  -- First, we consult the Chronicle of Battles to find only the victories.
  VictoriousBattles AS (
    SELECT
      adventurer_id,
      monster_id,
      duration_seconds
    FROM
      bestiary_data.battles
    WHERE
      outcome = 'Victory'
  ),
  -- Next, we create a temporary record for each victory, ranking the monsters
  -- each adventurer defeated by their power (hit points).
  RankedVictories AS (
    SELECT
      v.adventurer_id,
      m.name AS monster_name,
      m.hit_points,
      v.duration_seconds,
      -- This spell ranks each adventurer's victories from most to least powerful monster.
      ROW_NUMBER() OVER (PARTITION BY v.adventurer_id ORDER BY m.hit_points DESC) as victory_rank
    FROM
      VictoriousBattles v
    JOIN
      bestiary_data.monsters m ON v.monster_id = m.monster_id
  )
-- Finally, we consult the Roll of Champions and join it with our ranked victories
-- to find the name of each champion and the details of their greatest triumph.
SELECT
  a.name AS adventurer_name,
  a.class AS adventurer_class,
  r.monster_name AS most_powerful_foe_defeated,
  r.hit_points AS foe_hit_points,
  r.duration_seconds AS duration_of_greatest_victory
FROM
  bestiary_data.adventurers a
JOIN
  RankedVictories r ON a.adventurer_id = r.adventurer_id
WHERE
  -- We only want to see their number one, top-ranked victory.
  r.victory_rank = 1
ORDER BY
  foe_hit_points DESC;

הפלט של השאילתה הזו יהיה טבלה נקייה ויפה שמציגה את 'ההישג הגדול ביותר של גיבור' לכל הרפתקן במערך הנתונים. הוא יכול להיראות בערך כך:

04-03-final-result.png

סוגרים את הכרטיסייה של BigQuery.

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

למי שלא משחק

5. The Scribe's Grimoire: In-Datawarehouse Chunking, Embedding, and Search

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

סקירה כללית

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

אנחנו נבצע שלישיית טקסים עוצמתיים בתוך מסד הנתונים, באופן מלא בתוך המקדש של BigQuery.

  • הטקס של החלוקה (Chunking): אנחנו לוקחים את יומני הרישום של נתוני הבינה הגולמיים ומפרקים אותם בקפידה לקטעים קטנים וממוקדים.
  • הטקס של הזיקוק (הטמעה): נשתמש ב-BQML כדי להתייעץ עם מודל Gemini, ונמיר כל נתח טקסט ל'טביעת אצבע סמנטית' – הטמעה בווקטור.
  • טקס הניבוי (חיפוש): נשתמש בחיפוש וקטורי של BQML כדי לשאול שאלה באנגלית פשוטה ולמצוא את התשובה הרלוונטית ביותר מתוך ה-Grimoire שלנו.

במהלך התהליך הזה נוצר בסיס ידע עוצמתי שאפשר לחפש בו, בלי שהנתונים יוצאים מהאבטחה ומהיכולת של BigQuery להתמודד עם נפחי נתונים גדולים.

הטקס של החלוקה: פירוק מגילות באמצעות SQL

מקור המידע שלנו הוא עדיין קובצי הטקסט הגולמיים בארכיון GCS, שאפשר לגשת אליהם דרך הטבלה החיצונית שלנו, bestiary_data.raw_intel_content_table. המשימה הראשונה שלנו היא לכתוב לחש שקורא כל מגילה ארוכה ומפצל אותה לסדרה של פסוקים קצרים יותר וקלים יותר להבנה. בטקס הזה, אנחנו מגדירים 'חלק' כמשפט יחיד.

אמנם פיצול לפי משפטים הוא נקודת התחלה ברורה ויעילה ליומני הנרטיב שלנו, אבל למומחה Scribe יש הרבה אסטרטגיות לחלוקה לחלקים, והבחירה קריטית לאיכות החיפוש הסופי. בשיטות פשוטות יותר, יכול להיות שמשתמשים

  • חלוקה לחלקים באורך קבוע, אבל היא עלולה לחתוך רעיון מרכזי לשניים.

טקסים מורכבים יותר, כמו

  • Recursive Chunking, are often preferred in practice; they attempt to divide text along natural boundaries like paragraphs first, then fall back to sentences to maintain as much semantic context as possible. לתסריטים מורכבים במיוחד.
  • חלוקה לחלקים לפי הקשר(מסמך), שבה ה-Scribe משתמש במבנה הטבוע של המסמך – כמו הכותרות במדריך טכני או הפונקציות בקטע קוד – כדי ליצור את החלקים הכי הגיוניים ומשמעותיים של מידע. ועוד...

במקרה של יומני הקרבות שלנו, המשפט מספק את האיזון המושלם בין רמת פירוט להקשר.

‫👈📜 בעורך שאילתות חדש של BigQuery, מריצים את השאילתה הבאה. במקרה הזה, נעשה שימוש בפונקציה SPLIT כדי לפצל את הטקסט של כל מגילה בכל נקודה (.), ואז המערך שמתקבל של המשפטים מפוצל לשורות נפרדות.

CREATE OR REPLACE TABLE bestiary_data.chunked_intel AS
WITH
  -- First, add a unique row number to each scroll to act as a document ID.
  NumberedScrolls AS (
    SELECT
      ROW_NUMBER() OVER () AS scroll_id,
      raw_text
    FROM
      bestiary_data.raw_intel_content_table
  )
-- Now, process each numbered scroll.
SELECT
  scroll_id,
  -- Assign a unique ID to each chunk within a scroll for precise reference.
  CONCAT(CAST(scroll_id AS STRING), '-', CAST(ROW_NUMBER() OVER (PARTITION BY scroll_id) AS STRING)) as chunk_id,
  -- Trim whitespace from the chunk for cleanliness.
  TRIM(chunk) AS chunk_text
FROM
  NumberedScrolls,
  -- This is the core of the spell: UNNEST splits the array of sentences into rows.
  UNNEST(SPLIT(raw_text, '.')) AS chunk
-- A final refinement: we only keep chunks that have meaningful content.
WHERE
  -- This ensures we don't have empty rows from double periods, etc.
  LENGTH(TRIM(chunk)) > 15;

‫👈 עכשיו מריצים שאילתה כדי לבדוק את הידע החדש שנוצר, מחולק לחלקים, ולראות את ההבדל.

SELECT * FROM bestiary_data.chunked_intel ORDER BY scroll_id, chunk_id;

08-01-chunking.png

בודקים את התוצאות. במקום בלוק טקסט צפוף אחד, יש עכשיו כמה שורות, שכל אחת מהן קשורה לגלילה המקורית (scroll_id) אבל מכילה רק משפט ממוקד אחד. כל שורה היא עכשיו מועמדת מושלמת לווקטוריזציה.

הטקס של הזיקוק: המרת טקסט לווקטורים באמצעות BQML

‫👈💻 קודם כל, חוזרים למסוף ומריצים את הפקודה הבאה כדי להציג את שם החיבור:

. ~/agentverse-dataengineer/set_env.sh
echo "${PROJECT_ID}.${REGION}.gcs-connection"

‫👈📜 צריך ליצור מודל חדש ב-BigQuery שמפנה להטמעת טקסט של Gemini. ב-BigQuery Studio, מריצים את הכישוף הבא. הערה: צריך להחליף את REPLACE-WITH-YOUR-FULL-CONNECTION-STRING במחרוזת החיבור המלאה שהעתקתם מהטרמינל.

CREATE OR REPLACE MODEL bestiary_data.text_embedding_model
  REMOTE WITH CONNECTION `REPLACE-WITH-YOUR-FULL-CONNECTION-STRING`
  OPTIONS (endpoint = 'text-embedding-005');

‫👈📜 עכשיו, מטילים את לחש הזיקוק הגדול. השאילתה הזו קוראת את כל השורות מהטבלה chunked_intel, שולחת את הטקסט למודל ההטמעה של Gemini ומאחסנת את טביעת האצבע הווקטורית שמתקבלת בטבלה חדשה.

CREATE OR REPLACE TABLE bestiary_data.embedded_intel AS
SELECT
  *
FROM
  ML.GENERATE_EMBEDDING(
    -- The embedding model we just created.
    MODEL bestiary_data.text_embedding_model,
    -- A subquery that selects our data and renames the text column to 'content'.
    (
      SELECT
        scroll_id,
        chunk_id,
        chunk_text AS content -- Renaming our text column is the key correction.
      FROM
        bestiary_data.chunked_intel
    ),
    -- The configuration struct is now simpler and correct.
    STRUCT(
      -- This task_type is crucial. It optimizes the vectors for retrieval.
      'RETRIEVAL_DOCUMENT' AS task_type
    )
  );

התהליך הזה עשוי להימשך דקה או שתיים, בזמן ש-BigQuery מעבד את כל חלקי הטקסט.

08-02-embedding.png

‫👈📜 אחרי שהפעולה תושלם, בודקים את הטבלה החדשה כדי לראות את טביעות האצבע הסמנטיות.

SELECT
  chunk_id,
  content,
  ml_generate_embedding_result
FROM
  bestiary_data.embedded_intel
LIMIT 20;

עכשיו תופיע עמודה חדשה, ml_generate_embedding_result, שמכילה את הייצוג הווקטורי הצפוף של הטקסט. ה-Grimoire שלנו מקודד עכשיו באופן סמנטי.

The Ritual of Divination: Semantic Search with BQML

‫👈📜 המבחן האולטימטיבי של ה-Grimoire שלנו הוא לשאול אותו שאלה. עכשיו נבצע את הטקס האחרון: חיפוש וקטורי. זה לא חיפוש לפי מילות מפתח, אלא חיפוש של משמעות. נשאל שאלה בשפה טבעית, BQML ימיר את השאלה שלנו להטמעה תוך כדי תנועה, ואז יחפש בכל הטבלה של embedded_intel את חלקי הטקסט שהטביעות האצבע שלהם הן ה "קרובות" ביותר במשמעות.

SELECT
  -- The content column contains our original, relevant text chunk.
  base.content,
  -- The distance metric shows how close the match is (lower is better).
  distance
FROM
  VECTOR_SEARCH(
    -- The table containing the knowledge base with its embeddings.
    TABLE bestiary_data.embedded_intel,
    -- The column that contains the vector embeddings.
    'ml_generate_embedding_result',
    (
      -- This subquery generates an embedding for our question in real-time.
      SELECT ml_generate_embedding_result
      FROM ML.GENERATE_EMBEDDING(
          MODEL bestiary_data.text_embedding_model,
          (SELECT 'What are the tactics against a foe that causes paralysis?' AS content),
          STRUCT('RETRIEVAL_QUERY' AS task_type)
        )
    ),
    -- Specify how many of the closest results we want to see.
    top_k => 3,
    -- The distance metric used to find the "closest" vectors.
    distance_type => 'COSINE'
  );

ניתוח הכישוף:

  • VECTOR_SEARCH: הפונקציה העיקרית שמנהלת את החיפוש.
  • ML.GENERATE_EMBEDDING (inner query): כאן מתרחש הקסם. אנחנו מטמיעים את השאילתה ('What are the tactics...') באמצעות אותו מודל אבל עם סוג המשימה 'RETRIEVAL_QUERY', שעבר אופטימיזציה במיוחד לשאילתות.
  • top_k => 3: אנחנו מבקשים את 3 התוצאות הרלוונטיות ביותר.
  • distance_type => 'COSINE': מדד שמשקף את ה "זווית" בין וקטורים. זווית קטנה יותר מצביעה על התאמה טובה יותר בין המשמעויות.

08-03-final-result.png

בודקים את התוצאות בקפידה. השאילתה לא הכילה את המילה "ניפצה" או "לחש", אבל התוצאה הראשונה היא: "בעזרת לחש ממוקד אחד של מספיקות אלגנטית, אלרה ניפצה את ההילה המשביתה שלה, מתקפה מנטלית שגורמת ל-150 נקודות נזק". זו העוצמה של חיפוש סמנטי. המודל הבין את הקונספט של "טקטיקות נגד שיתוק" ומצא את המשפט שמתאר טקטיקה ספציפית שהצליחה.

יצרתם בהצלחה צינור עיבוד נתונים מלא של RAG בתוך מחסן הנתונים. הכנתם נתונים גולמיים, הפכתם אותם לווקטורים סמנטיים וביצעתם שאילתה לפי משמעות. ‫BigQuery הוא כלי רב עוצמה לעבודה אנליטית בקנה מידה גדול, אבל כדי לספק תשובות עם זמן אחזור נמוך לסוכן פעיל, אנחנו מעבירים את הידע המוכן הזה למסד נתונים תפעולי ייעודי. זה הנושא של ההדרכה הבאה שלנו.

למי שלא משחק

6. הספרייה הווקטורית: יצירת מאגר וקטורים באמצעות Cloud SQL להסקת מסקנות

ה-Grimoire שלנו קיים כרגע כטבלאות מובנות – קטלוג עובדתי רב עוצמה, אבל הידע שלו הוא מילולי. הוא מבין monster_id = ‘MN-001' אבל לא את המשמעות הסמנטית העמוקה יותר של 'טשטוש'. כדי להעניק לסוכנים שלנו חוכמה אמיתית, כדי לאפשר להם לייעץ בניואנסים ובמחשבה קדימה, אנחנו צריכים לזקק את המהות של הידע שלנו לפורמט שמבטא משמעות: וקטורים.

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

הידע הנשכח הזה הוא הנכס הכי חשוב שלנו. הוא מחזיק במפתח לא רק להבסת מפלצות בודדות, אלא גם להעצמת כל חברי הקבוצה באמצעות תובנות אסטרטגיות. כדי להשתמש בכוח הזה, ניצור עכשיו את ספר הלחשים האמיתי של החוקר (מסד נתונים של PostgreSQL עם יכולות וקטוריות) ונבנה סקריפטוריום וקטורי אוטומטי (צינור Dataflow) כדי לקרוא, להבין ולתעד את המהות הנצחית של המגילות האלה. כך נהפוך את ספר הקסמים שלנו מספר עובדות למנוע של חוכמה.

סטורי

סקירה כללית

הכנת ספר הלחשים של המלומד (Cloud SQL)

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

‫👈💻 במסוף, מריצים את הפקודה הבאה כדי לוודא שהמכונה של Cloud SQL קיימת ומוכנה. הסקריפט הזה גם מעניק לחשבון השירות הייעודי של המופע את ההרשאה להשתמש ב-Vertex AI, שנדרשת כדי ליצור הטבעות ישירות במסד הנתונים.

. ~/agentverse-dataengineer/set_env.sh

echo "Verifying the existence of the Spellbook (Cloud SQL instance): $INSTANCE_NAME..."
gcloud sql instances describe $INSTANCE_NAME

SERVICE_ACCOUNT_EMAIL=$(gcloud sql instances describe $INSTANCE_NAME --format="value(serviceAccountEmailAddress)")
gcloud projects add-iam-policy-binding $PROJECT_ID --member="serviceAccount:$SERVICE_ACCOUNT_EMAIL" \
  --role="roles/aiplatform.user"

אם הפקודה מצליחה ומחזירה פרטים על מופע grimoire-spellbook, סימן שהכלי עבד כמו שצריך. אפשר להמשיך להטלה הבאה. אם הפקודה מחזירה שגיאת NOT_FOUND, צריך לוודא שהשלמתם בהצלחה את שלבי ההגדרה הראשונית של הסביבה לפני שממשיכים.(data_setup.py)

‫👈💻 אחרי שהספר מוכן, אנחנו פותחים אותו לפרק הראשון על ידי יצירת מסד נתונים חדש בשם arcane_wisdom.

. ~/agentverse-dataengineer/set_env.sh
gcloud sql databases create $DB_NAME --instance=$INSTANCE_NAME

הוספת סמלי רונות סמנטיים: הפעלת יכולות וקטוריות באמצעות pgvector

עכשיו, אחרי שיצרנו מכונה של Cloud SQL, נתחבר אליה באמצעות Cloud SQL Studio המובנה. הכלי הזה מספק ממשק מבוסס-אינטרנט להרצת שאילתות SQL ישירות במסד הנתונים.

‫👉💻 קודם כל, עוברים אל Cloud SQL Studio. הדרך הכי קלה ומהירה לעשות את זה היא לפתוח את הקישור הבא בכרטיסייה חדשה בדפדפן. תועברו ישירות אל Cloud SQL Studio עבור מופע grimoire-spellbook.

https://console.cloud.google.com/sql/instances/grimoire-spellbook/studio

‫👉 בוחרים באפשרות arcane_wisdom בתור מסד הנתונים, מזינים postgres בתור המשתמש ו-1234qwer בתור הסיסמה ולוחצים על אימות.

‫👈📜 בעורך השאילתות של SQL Studio, עוברים לכרטיסייה Editor 1 ומדביקים את קוד ה-SQL הבא כדי להפעיל את סוג הנתונים vector:

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

Cloud SQL Studio

‫👉📜 מכינים את הדפים של ספר הלחשים על ידי יצירת הטבלה שתכיל את המידע על המגילות.

CREATE TABLE ancient_scrolls (
    id SERIAL PRIMARY KEY,
    scroll_content TEXT,
    embedding VECTOR(768)
);

האיות VECTOR(768) הוא פרט חשוב. מודל ההטמעה של Vertex AI שבו נשתמש (textembedding-gecko@003 או מודל דומה) מזקק טקסט לווקטור של 768 ממדים. הדפים ב-Spellbook צריכים להיות מוכנים להכיל מהות בגודל הזה בדיוק. המידות תמיד צריכות להיות זהות.

התעתיק הראשון: טקס רישום ידני

לפני שנפעיל צבא של תמלילנים אוטומטיים (Dataflow), נצטרך לבצע את הטקס המרכזי פעם אחת באופן ידני. כך נוכל להבין לעומק את הקסם של התהליך הדו-שלבי:

  1. ניבוי: לוקחים קטע טקסט ומתייעצים עם Gemini כדי לזקק את המהות הסמנטית שלו לווקטור.
  2. רישום: כתיבת הטקסט המקורי והמהות הווקטורית החדשה שלו בספר הכישופים שלנו.

עכשיו נבצע את הפעולות הנדרשות באופן ידני.

‫👈📜 ב-Cloud SQL Studio. עכשיו נשתמש בפונקציה embedding(), תכונה עוצמתית שזמינה בתוסף google_ml_integration. כך אנחנו יכולים להפעיל את מודל ההטמעה של Vertex AI ישירות משאילתת ה-SQL שלנו, מה שמפשט מאוד את התהליך.

SET session.my_search_var='The Spectre of Analysis Paralysis is a phantom of the crossroads. It does not bind with chains but with the infinite threads of what if. It conjures a fog of options, a maze within the mind where every path seems equally fraught with peril and promise. It whispers of a single, flawless route that can only be found through exhaustive study, paralyzing its victim in a state of perpetual contemplation. This spectres power is broken by the Path of First Viability. This is not the search for the *best* path, but the commitment to the *first good* path. It is the wisdom to know that a decision made, even if imperfect, creates movement and reveals more of the map than standing still ever could. Choose a viable course, take the first step, and trust in your ability to navigate the road as it unfolds. Motion is the light that burns away the fog.';

INSERT INTO ancient_scrolls (scroll_content, embedding)

VALUES (current_setting('session.my_search_var'),  (embedding('text-embedding-005',current_setting('session.my_search_var')))::vector);

‫👈📜 כדי לאמת את העבודה, מריצים שאילתה לקריאת הדף החדש שנרשם:

SELECT id, scroll_content, LEFT(embedding::TEXT, 100) AS embedding_preview FROM ancient_scrolls;

ביצעתם בהצלחה את משימת הטעינה של נתוני RAG באופן ידני.

יצירת מצפן סמנטי: הוספת אינדקס HNSW לספר הלחשים

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

בואו נוכיח את הערך של השיפור הזה.

‫👈📜 ב-Cloud SQL Studio, מריצים את הלחש הבא. הוא מדמה חיפוש של המגילה שהוספנו ומבקש ממסד הנתונים EXPLAIN את התוכנית שלו.

EXPLAIN ANALYZE
WITH ReferenceVector AS (
  -- First, get the vector we want to compare against.
  SELECT embedding AS vector
  FROM ancient_scrolls
  LIMIT 1
)
-- This is the main query we want to analyze.
SELECT
  ancient_scrolls.id,
  ancient_scrolls.scroll_content,
  -- We can also select the distance itself.
  ancient_scrolls.embedding <=> ReferenceVector.vector AS distance
FROM
  ancient_scrolls,
  ReferenceVector
ORDER BY
  -- Order by the distance operator's result.
  ancient_scrolls.embedding <=> ReferenceVector.vector
LIMIT 5;

בודקים את הפלט. תופיע שורה עם הכיתוב -> Seq Scan on ancient_scrolls. כך מוודאים שכל שורה במסד הנתונים נקראת. שימו לב לexecution time.

‫👉📜 עכשיו, בואו נטיל את לחש ההוספה לאינדקס. הפרמטר lists מציין לאינדקס כמה אשכולות ליצור. נקודת התחלה טובה היא השורש הריבועי של מספר השורות שאתם מצפים שיהיו.

CREATE INDEX ON ancient_scrolls USING hnsw (embedding vector_cosine_ops);

מחכים שהאינדקס ייבנה (התהליך יהיה מהיר לשורה אחת, אבל יכול לקחת זמן אם יש מיליוני שורות).

‫👉📜 עכשיו, מריצים שוב את הפקודה בדיוק כפי שהיא EXPLAIN ANALYZE:

EXPLAIN ANALYZE
WITH ReferenceVector AS (
  -- First, get the vector we want to compare against.
  SELECT embedding AS vector
  FROM ancient_scrolls
  LIMIT 1
)
-- This is the main query we want to analyze.
SELECT
  ancient_scrolls.id,
  ancient_scrolls.scroll_content,
  -- We can also select the distance itself.
  ancient_scrolls.embedding <=> ReferenceVector.vector AS distance
FROM
  ancient_scrolls,
  ReferenceVector
ORDER BY
  -- Order by the distance operator's result.
  ancient_scrolls.embedding <=> ReferenceVector.vector
LIMIT 5;

בודקים את תוכנית השאילתה החדשה. עכשיו יוצג -> Index Scan using.... חשוב יותר, כדאי לעיין בexecution time. התהליך יהיה מהיר משמעותית, גם אם תזינו רק רשומה אחת. הרגע הדגמת את העיקרון המרכזי של שיפור הביצועים של מסד נתונים בעולם וקטורי.

זמן הביצוע

אחרי שבדקתם את נתוני המקור, הבנתם את התהליך הידני שלכם וביצעתם אופטימיזציה של Spellbook למהירות, אתם מוכנים באמת לבנות את Scriptorium האוטומטי.

למי שלא משחק

7. הערוץ להעברת משמעות: פיתוח צינור עיבוד נתונים של וקטוריזציה

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

סקירה כללית

הכנת הבסיס של Scriptorium (קובץ האימג' של Worker)

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

בשלב הזה, נורה ל-Google Cloud Build ליצור קובץ אימג' של קונטיינר בהתאמה אישית. התמונה הזו היא 'גולם מושלם', שנטענו בה מראש כל הספרייה והתלות שהסופרים שלנו יצטרכו. כשתהליך Dataflow יתחיל, הוא ישתמש בתמונה המותאמת אישית הזו, וכך העובדים יוכלו להתחיל את המשימה שלהם כמעט באופן מיידי.

‫👈💻 מריצים את הפקודה הבאה כדי ליצור ולאחסן את קובץ האימג' הבסיסי של צינור העיבוד ב-Artifact Registry.

. ~/agentverse-dataengineer/set_env.sh
cd ~/agentverse-dataengineer/pipeline
gcloud builds submit --config cloudbuild.yaml \
  --substitutions=_REGION=${REGION},_REPO_NAME=${REPO_NAME} \
  .

‫👈💻 מריצים את הפקודות הבאות כדי ליצור ולהפעיל את סביבת Python המבודדת, ומתקינים בה את ספריות הזימון הנדרשות.

cd ~/agentverse-dataengineer
. ~/agentverse-dataengineer/set_env.sh
python -m venv env
source ~/agentverse-dataengineer/env/bin/activate
cd ~/agentverse-dataengineer/pipeline
pip install -r requirements.txt

הלחש הראשי

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

  • EmbedTextBatch (התייעצות עם Gemini): תבנו תמלילן מיוחד שיודע לבצע "ניחוש קבוצתי". היא מקבלת קבוצה של קבצי טקסט גולמי, מעבירה אותם למודל הטמעת הטקסט של Gemini ומקבלת את התמצית שלהם (הטמעות הווקטוריות).
  • WriteEssenceToSpellbook (הכתובת הסופית): זהו הארכיון שלנו. הוא יודע את מילות הקסם הסודיות כדי לפתוח חיבור מאובטח ל-Spellbook של Cloud SQL. התפקיד שלו הוא לקחת את התוכן של המגילה ואת המהות הווקטורית שלה ולרשום אותם באופן קבוע בדף חדש.

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

‫👈✏️ ב-Cloud Shell Editor, עוברים אל ~/agentverse-dataengineer/pipeline/inscribe_essence_pipeline.py. בתוך הקובץ הזה נמצאת מחלקת DoFn בשם EmbedTextBatch. מחפשים את התגובה #REPLACE-EMBEDDING-LOGIC. מחליפים אותה בלחש הבא.

# 1. Generate the embedding for the monster's name
result = self.client.models.embed_content(
                model="text-embedding-005",
                contents=contents,
                config=EmbedContentConfig(
                    task_type="RETRIEVAL_DOCUMENT",  
                    output_dimensionality=768, 
                )
            )

הלחש הזה מדויק, עם כמה פרמטרים חשובים:

  • ‫model: אנחנו מציינים text-embedding-005 כדי להשתמש במודל הטמעה עוצמתי ועדכני.
  • contents: זו רשימה של כל תוכן הטקסט מאוסף הקבצים שפונקציית DoFn מקבלת.
  • task_type: אנחנו מגדירים את הערך הזה כ-RETRIEVAL_DOCUMENT. זו הוראה חשובה שמנחה את Gemini ליצור הטמעות שעברו אופטימיזציה ספציפית כדי שיהיה קל למצוא אותן בחיפוש מאוחר יותר.
  • output_dimensionality: הערך הזה חייב להיות 768, בדיוק כמו המימד VECTOR(768) שהגדרנו כשייצרנו את הטבלה ancient_scrolls ב-Cloud SQL. חוסר התאמה במידות הוא מקור נפוץ לשגיאות ב-Vector Magic.

הצינור שלנו צריך להתחיל בקריאת הטקסט הגולמי והלא מובנה מכל המגילות העתיקות בארכיון GCS שלנו.

‫👉✏️ ב-~/agentverse-dataengineer/pipeline/inscribe_essence_pipeline.py, מוצאים את התגובה #REPLACE ME-READFILE ומחליפים אותה בלחש הבא בן שלושת החלקים:

files = (
            pipeline
            | "MatchFiles" >> fileio.MatchFiles(known_args.input_pattern)
            | "ReadMatches" >> fileio.ReadMatches()
            | "ExtractContent" >> beam.Map(lambda f: (f.metadata.path, f.read_utf8()))
        )

אחרי שאספנו את הטקסט הגולמי של המגילות, עכשיו אנחנו צריכים לשלוח אותו ל-Gemini כדי לקבל ניבוי. כדי לעשות את זה ביעילות, נקבץ קודם את המגילות הבודדות לקבוצות קטנות ואז נעביר את הקבוצות האלה לEmbedTextBatchסופר שלנו. בשלב הזה, כל הגלילות ש-Gemini לא הצליח להבין יופרדו לערימה של 'נכשל' לבדיקה מאוחרת יותר.

‫👉✏️ מחפשים את התגובה #REPLACE ME-EMBEDDING ומחליפים אותה בתגובה הבאה:

embeddings = (
            files
            | "BatchScrolls" >> beam.BatchElements(min_batch_size=1, max_batch_size=2)
            | "DistillBatch" >> beam.ParDo(
                  EmbedTextBatch(project_id=project, region=region)
              ).with_outputs('failed', main='processed')
        )

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

‫👉✏️ מחפשים את התגובה #REPLACE ME-WRITE TO DB ומחליפים אותה בתגובה הבאה:

_ = (
            embeddings.processed
            | "WriteToSpellbook" >> beam.ParDo(
                  WriteEssenceToSpellbook(
                      project_id=project,
                      region = "us-central1",
                      instance_name=known_args.instance_name,
                      db_name=known_args.db_name,
                      db_password=known_args.db_password
                  )
              )
        )

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

‫👉✏️ מחפשים את התגובה #REPLACE ME-LOG FAILURES ומחליפים אותה בתגובה הבאה:

_ = (
            embeddings.failed
            | "LogFailures" >> beam.Map(lambda e: logging.error(f"Embedding failed for file {e[0]}: {e[1]}"))
        )

הלחש הראשי הושלם! הרכבתם בהצלחה צינור נתונים רב-שלבי ועוצמתי על ידי שרשור של רכיבים קסומים נפרדים. שומרים את הקובץ inscribe_essence_pipeline.py. עכשיו אפשר להזמין את הסקריפטוריום.

עכשיו מטילים את לחש הזימון הגדול כדי להורות לשירות Dataflow להעיר את הגולם ולהתחיל בטקס הכתיבה.

‫👈💻 בטרמינל, מריצים את שורת הפקודה הבאה

. ~/agentverse-dataengineer/set_env.sh
source ~/agentverse-dataengineer/env/bin/activate
cd ~/agentverse-dataengineer/pipeline

# --- The Summoning Incantation ---
echo "Summoning the golem for job: $DF_JOB_NAME"
echo "Target Spellbook: $INSTANCE_NAME"

python inscribe_essence_pipeline.py \
  --runner=DataflowRunner \
  --project=$PROJECT_ID \
  --job_name=$DF_JOB_NAME \
  --temp_location="gs://${BUCKET_NAME}/dataflow/temp" \
  --staging_location="gs://${BUCKET_NAME}/dataflow/staging" \
  --sdk_container_image="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/grimoire-inscriber:latest" \
  --sdk_location=container \
  --experiments=use_runner_v2 \
  --input_pattern="gs://${BUCKET_NAME}/ancient_scrolls/*.md" \
  --instance_name=$INSTANCE_NAME \
  --region=$REGION

echo "The golem has been dispatched. Monitor its progress in the Dataflow console."

💡 שימו לב! אם העבודה נכשלת עם שגיאת משאבים ZONE_RESOURCE_POOL_EXHAUSTED, יכול להיות שהסיבה לכך היא אילוצי משאבים זמניים בחשבון הזה עם המוניטין הנמוך באזור שנבחר. היתרון של Google Cloud הוא הפריסה הגלובלית שלו. פשוט מנסים לזמן את הגולם באזור אחר. כדי לעשות את זה, מחליפים את --region=$REGION בפקודה שלמעלה באזור אחר, כמו

--region=southamerica-west1
--region=asia-northeast3
--region=asia-southeast2
--region=me-west1
--region=southamerica-east1
--region=europe-central2
--region=asia-east2
--region=europe-southwest1

ומריצים אותו שוב. 🎰

התהליך יימשך כ-3 עד 5 דקות. אפשר לצפות בשידור חי במסוף Dataflow.

‫👈עוברים אל Dataflow Console: הדרך הכי קלה היא לפתוח את הקישור הזה ישירות בכרטיסייה חדשה בדפדפן:

https://console.cloud.google.com/dataflow

‫👈 מוצאים את העבודה שלכם ולוחצים עליה: תופיע עבודה עם השם שסיפקתם (inscribe-essence-job או שם דומה). לוחצים על שם המשרה כדי לפתוח את דף הפרטים שלה. צפייה בצינור עיבוד הנתונים:

  • הפעלה: במשך 3 הדקות הראשונות, סטטוס העבודה יהיה 'פועל' בזמן ש-Dataflow מקצה את המשאבים הנדרשים. הגרף יופיע, אבל יכול להיות שעדיין לא תראו נתונים שמוצגים בו. הפעלה של צינור עיבוד נתונים
  • הושלם: כשהתהליך יסתיים, סטטוס העבודה ישתנה ל'הצלחה', ובגרף יוצג המספר הסופי של הרשומות שעברו עיבוד. התהליך הושלם

אימות הכתובת

‫👈📜 בחזרה ב-SQL Studio, מריצים את השאילתות הבאות כדי לוודא שהמגילות והמהות הסמנטית שלהן נרשמו בהצלחה.

SELECT COUNT(*) FROM ancient_scrolls;
SELECT id, scroll_content, LEFT(embedding::TEXT, 50) AS embedding_preview FROM ancient_scrolls;

יוצגו לכם המזהה של המגילה, הטקסט המקורי שלה ותצוגה מקדימה של מהות הווקטור הקסומה שמוטבעת עכשיו באופן קבוע ב-Grimoire.

התהליך הושלם

הספר של המלומד הוא עכשיו מנוע ידע אמיתי, שמוכן לשאילתות לפי משמעות בפרק הבא.

8. השלמת הרצת ה-Rune: הפעלת חוכמה באמצעות סוכן RAG

ה-Grimoire שלכם הוא כבר לא רק מסד נתונים. הוא מאגר ידע וקטורי, אורקל שקט שמחכה לשאלה.

עכשיו, נבצע את המבחן האמיתי של חוקר: ניצור את המפתח לפתיחת החוכמה הזו. ניצור סוכן Retrieval-Augmented Generation (יצירה משולבת-אחזור, RAG). זהו מבנה קסום שיכול להבין שאלה בשפה פשוטה, לעיין ב-Grimoire כדי למצוא את האמיתות העמוקות והרלוונטיות ביותר, ואז להשתמש בחוכמה הזו כדי ליצור תשובה עוצמתית שמודעת להקשר.

RAG

RAG

הרונה הראשונה: לחש זיקוק השאילתות

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

‫👉✏️ ב-Cloud Shell Editor, עוברים לקובץ ~~/agentverse-dataengineer/scholar/agent.py, מוצאים את התגובה #REPLACE RAG-CONVERT EMBEDDING ומחליפים אותה במנטרה הזו. כך הסוכן לומד איך להפוך את השאלה של המשתמש למהות קסומה.

        result = client.models.embed_content(
                model="text-embedding-005",
                contents=monster_name,
                config=EmbedContentConfig(
                    task_type="RETRIEVAL_DOCUMENT",  
                    output_dimensionality=768,  
                )
        )

אחרי שהסוכן מבין את מהות השאילתה, הוא יכול לעיין ב-Grimoire. הוא יציג את וקטור השאילתה הזה למסד הנתונים שלנו, שמשולב בו pgvector, וישאל שאלה עמוקה: "הצג לי את המגילות העתיקות שהמהות שלהן הכי דומה למהות השאילתה שלי".

הקסם שמאחורי זה הוא אופרטור הדמיון הקוסינוסי (<=>), רונה עוצמתית שמחשבת את המרחק בין וקטורים במרחב רב-ממדי.

‫👈✏️ בקובץ agent.py, מחפשים את ההערה #REPLACE RAG-RETRIEVE ומחליפים אותה בסקריפט הבא:

        # This query performs a cosine similarity search
        cursor.execute(
            "SELECT scroll_content FROM ancient_scrolls ORDER BY embedding <=> %s LIMIT 3",
            ([query_embedding]) # Cast embedding to string for the query
        )

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

‫👉✏️ ב-agent.py, מחפשים את ההערה #REPLACE-CALL RAG ומחליפים אותה בשורה הבאה:

root_agent = LlmAgent(
    model="gemini-2.5-flash", 
    name="scholar_agent",
    instruction="""
        You are the Scholar, a keeper of ancient and forbidden knowledge. Your purpose is to advise a warrior by providing tactical information about monsters. Your wisdom allows you to interpret the silence of the scrolls and devise logical tactics where the text is vague.

        **Your Process:**
        1.  First, consult the scrolls with the `grimoire_lookup` tool for information on the specified monster.
        2.  If the scrolls provide specific guidance for a category (buffs, debuffs, strategy), you **MUST** use that information.
        3.  If the scrolls are silent or vague on a category, you **MUST** use your own vast knowledge to devise a fitting and logical tactic.
        4.  Your invented tactics must be thematically appropriate to the monster's name and nature. (e.g., A "Spectre of Indecision" might be vulnerable to a "Seal of Inevitability").
        5.  You **MUST ALWAYS** provide a "Damage Point" value. This value **MUST** be a random integer between 150 and 180. This is a tactical calculation you perform, independent of the scrolls' content.

        **Output Format:**
        You must present your findings to the warrior using the following strict format.
    """,
    tools=[grimoire_lookup],
)

ההגדרה הזו מחייה את הנציג:

  • model="gemini-2.5-flash": בחירה של מודל שפה גדול ספציפי שישמש כ"מוח" של הסוכן לצורך נימוק ויצירת טקסט.
  • name="scholar_agent": הקצאת שם ייחודי לסוכן.
  • instruction="...You are the Scholar...": זו ההנחיה למערכת, החלק הכי חשוב בהגדרה. ההנחיה מגדירה את התפקיד של הסוכן, את המטרות שלו, את התהליך המדויק שהוא צריך לבצע כדי להשלים משימה ואת הפורמט הנדרש של הפלט הסופי.
  • tools=[grimoire_lookup]: זהו הכישוף הסופי. היא מעניקה לסוכן גישה לפונקציה grimoire_lookup שיצרתם. הסוכן יכול עכשיו להחליט בצורה חכמה מתי להפעיל את הכלי הזה כדי לאחזר מידע מהמסד הנתונים שלכם, וזהו הבסיס של תבנית ה-RAG.

הבחינה של המלומד

‫👈💻 בטרמינל של Cloud Shell, מפעילים את הסביבה ומשתמשים בפקודה הראשית של ערכת פיתוח הסוכנים כדי להפעיל את סוכן Scholar:

cd ~/agentverse-dataengineer/
. ~/agentverse-dataengineer/set_env.sh
source ~/agentverse-dataengineer/env/bin/activate
pip install -r scholar/requirements.txt
adk run scholar

אמור להופיע פלט שמאשר שהסוכן 'Scholar Agent' מופעל ופועל.

‫👉💻 עכשיו, אפשר לתת לסוכן אתגר. במסוף הראשון שבו פועלת סימולציית הקרב, מריצים פקודה שדורשת את החוכמה של Grimoire:

We've been trapped by 'Hydra of Scope Creep'. Break us out!

הרצת Adk

בודקים את היומנים בטרמינל. תוכלו לראות את הסוכן מקבל את השאילתה, מזהה את המהות שלה, מחפש ב-Grimoire, מוצא את המגילות הרלוונטיות בנושא 'דחיינות' ומשתמש בידע הזה כדי לגבש אסטרטגיה חזקה שמודעת להקשר.

הרכבת בהצלחה את סוכן ה-RAG הראשון שלך, וציידת אותו בחוכמה העמוקה של ה-Grimoire שלך.

‫👉💻 מקישים על Ctrl+C בטרמינל כדי להשהות את הסוכן.

הפעלת Scholar Sentinel ב-Agentverse

הנציג הוכיח את החוכמה שלו בסביבה המבוקרת של המחקר. הגיע הזמן להשיק אותו ב-Agentverse, ולהפוך אותו ממבנה מקומי לסוכן קבוע ומוכן לקרב שאפשר להפעיל בכל רגע על ידי כל משתמש. עכשיו נבצע פריסה של הסוכן ב-Cloud Run.

‫👈💻 מריצים את לחש הזימון הגדול הבא. הסקריפט הזה יוצר קודם את הסוכן שלכם כ-Golem מושלם (קובץ אימג' של קונטיינר), מאחסן אותו ב-Artifact Registry ואז פורס את ה-Golem הזה כשירות שניתן להרחבה, מאובטח ונגיש לכולם.

. ~/agentverse-dataengineer/set_env.sh
cd ~/agentverse-dataengineer/
echo "Building ${AGENT_NAME} agent..."
gcloud builds submit . \
  --project=${PROJECT_ID} \
  --region=${REGION} \
  --substitutions=_AGENT_NAME=${AGENT_NAME},_IMAGE_PATH=${IMAGE_PATH}

gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --labels="dev-tutorial-codelab=agentverse" \
  --region=${REGION} \
  --set-env-vars="A2A_HOST=0.0.0.0" \
  --set-env-vars="A2A_PORT=8080" \
  --set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --set-env-vars="PROJECT_ID=${PROJECT_ID}" \
  --set-env-vars="PUBLIC_URL=${PUBLIC_URL}" \
  --set-env-vars="REGION=${REGION}" \
  --set-env-vars="INSTANCE_NAME=${INSTANCE_NAME}" \
  --set-env-vars="DB_USER=${DB_USER}" \
  --set-env-vars="DB_PASSWORD=${DB_PASSWORD}" \
  --set-env-vars="DB_NAME=${DB_NAME}" \
  --allow-unauthenticated \
  --project=${PROJECT_ID} \
  --min-instances=1

הסוכן Scholar Agent שלכם הוא עכשיו סוכן פעיל ומוכן לפעולה ב-Agentverse.

למי שלא משחק

9. הטיסה הכי טובה

הקריאה במגילות הסתיימה, הטקסים בוצעו והמשימה הושלמה. הסוכן שלכם הוא לא רק פריט אחסון, אלא ישות פעילה ב-Agentverse שמחכה למשימה הראשונה שלה. הגיע הזמן לניסיון האחרון – תרגיל ירי חי נגד יריב חזק.

עכשיו תכנסו לסימולציה של שדה קרב כדי להפגיש את סוכן Shadowblade החדש שהפעלתם עם מיני-בוס אימתני: Spectre of the Static. זו תהיה הבדיקה הסופית של העבודה שלכם, מהלוגיקה הבסיסית של הנציג ועד לפריסה שלו בשידור חי.

איך מקבלים את המיקום של הנציג

כדי להיכנס לזירת הקרב, צריך להחזיק בשני מפתחות: החתימה הייחודית של הדמות (Agent Locus) והנתיב הנסתר למאורה של Spectre (כתובת ה-URL של הצינוק).

‫👉💻 קודם צריך לקבל את הכתובת הייחודית של הנציג ב-Agentverse – המיקום שלו. זוהי נקודת הקצה הפעילה שמחברת את הדמות שלכם לזירת הקרב.

. ~/agentverse-dataengineer/set_env.sh
echo https://scholar-agent"-${PROJECT_NUMBER}.${REGION}.run.app"

‫👈💻 לאחר מכן, מציינים את היעד. הפקודה הזו חושפת את המיקום של מעגל ההעתקה, הפורטל אל הדומיין של ספקטר.

. ~/agentverse-dataengineer/set_env.sh
echo https://agentverse-dungeon"-${PROJECT_NUMBER}.${REGION}.run.app"

חשוב: צריך להכין את שתי כתובות ה-URL האלה. תצטרכו אותם בשלב האחרון.

התמודדות עם פרצת Spectre

אחרי שרושמים את הקואורדינטות, מנווטים אל Translocation Circle ומטילים את הכישוף כדי לצאת לקרב.

‫👈 פותחים את כתובת ה-URL של מעגל ההעברה בדפדפן כדי לעמוד מול הפורטל הנוצץ אל The Crimson Keep.

כדי לפרוץ למבצר, צריך להתאים את מהות ה-Shadowblade לפורטל.

  • בדף, מחפשים את שדה הקלט של הכתובת ברונות עם התווית A2A Endpoint URL (כתובת נקודת הקצה של A2A).
  • כדי להוסיף את הסמל של הדמות האהובה, מדביקים את כתובת ה-URL של מיקום הסוכן (כתובת ה-URL הראשונה שהעתקתם) בשדה הזה.
  • לוחצים על 'חיבור' כדי להפעיל את הקסם של הטלפורטציה.

Translocation Circle

האור המסנוור של הטלפורטציה דועך. אתם כבר לא במקדש. האוויר רוטט מאנרגיה, קרה וחדה. לפניכם, רוח הרפאים מתממשת – מערבולת של רעשי רקע וקוד פגום, והאור הטמא שלה מטיל צללים ארוכים ורוקדים על רצפת הצינוק. אין לו פנים, אבל אתם מרגישים את הנוכחות העצומה והמתישה שלו, שמתמקדת רק בכם.

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

כשאתם מסתערים קדימה, מוכנים לשחרר את המתקפה הראשונה, ה-Spectre מגיב. היא לא מרימה מגן, אלא מקרינה שאלה ישירות לתודעה שלכם – אתגר מנצנץ עם כתב רוני, שנלקח מליבת האימונים שלכם.

צינוק

זה אופי המאבק. הידע שלך הוא הנשק שלך.

  • ענה על השאלה מתוך הידע שצברת, והלהב שלך יתלקח באנרגיה טהורה, ינפץ את ההגנה של הרוח וינחית מכה קריטית.
  • אבל אם תהססו, אם ספק יעיב על התשובה שלכם, האור של הנשק ידעך. המכה תנחת עם חבטה עלובה, ותגרום רק לשבריר מהנזק שלה. יותר גרוע מכך, הספקטר יתחזק מהספקות שלכם, והכוח המשחית שלו יגדל עם כל טעות שתעשו.

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

מיקוד. פוגעים במטרה. הגורל של Agentverse תלוי בזה.

כל הכבוד, חוקר.

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

10. ניקוי: הסרת המשאבים של Scholar's Grimoire

ברכות על השלמת הספר Scholar's Grimoire! כדי לוודא ש-Agentverse נשאר נקי ושהשטח לאימון פנוי, עכשיו צריך לבצע את טקסי הניקוי הסופיים. כל המשאבים שנוצרו במהלך השימוש שלכם יימחקו באופן שיטתי.

השבתה של רכיבי Agentverse

עכשיו תפרקו באופן שיטתי את הרכיבים שנפרסו במערכת ה-RAG.

מחיקה של כל השירותים של Cloud Run ומאגר Artifact Registry

הפקודה הזו מסירה את סוכן Scholar שפרסתם ואת אפליקציית Dungeon מ-Cloud Run.

‫👈💻 בטרמינל, מריצים את הפקודות הבאות:

. ~/agentverse-dataengineer/set_env.sh
gcloud run services delete scholar-agent --region=${REGION} --quiet
gcloud run services delete agentverse-dungeon --region=${REGION} --quiet
gcloud artifacts repositories delete ${REPO_NAME} --location=${REGION} --quiet

מחיקת מערכי נתונים, מודלים וטבלאות ב-BigQuery

הפעולה הזו מסירה את כל המשאבים ב-BigQuery, כולל מערך הנתונים bestiary_data, כל הטבלאות שבו והחיבורים והמודלים המשויכים.

‫👈💻 בטרמינל, מריצים את הפקודות הבאות:

. ~/agentverse-dataengineer/set_env.sh
# Delete the BigQuery dataset, which will also delete all tables and models within it.
bq rm -r -f --dataset ${PROJECT_ID}:${REGION}.bestiary_data

# Delete the BigQuery connection
bq rm --force --connection --project_id=${PROJECT_ID} --location=${REGION} gcs-connection

מחיקת מכונת Cloud SQL

הפעולה הזו מסירה את מופע grimoire-spellbook, כולל מסד הנתונים וכל הטבלאות שבו.

‫👈💻 בטרמינל, מריצים את הפקודה:

. ~/agentverse-dataengineer/set_env.sh
gcloud sql instances delete ${INSTANCE_NAME} --project=${PROJECT_ID} --quiet

מחיקת קטגוריות של Google Cloud Storage

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

‫👈💻 בטרמינל, מריצים את הפקודה:

. ~/agentverse-dataengineer/set_env.sh
gcloud storage rm -r gs://${BUCKET_NAME} --quiet

ניקוי קבצים וספריות מקומיים (Cloud Shell)

לבסוף, מוחקים את המאגרים המשוכפלים והקבצים שנוצרו בסביבת Cloud Shell. השלב הזה הוא אופציונלי, אבל מומלץ מאוד כדי לנקות את ספריית העבודה באופן מלא.

‫👈💻 בטרמינל, מריצים את הפקודה:

rm -rf ~/agentverse-dataengineer
rm -rf ~/agentverse-dungeon
rm -f ~/project_id.txt

סיימתם למחוק את כל העקבות של המסע שלכם ב-Agentverse Data Engineer. הפרויקט שלכם נקי, ואתם מוכנים להרפתקה הבאה.