‫Cymbal Transit: מערכת מרובת סוכנים שמשתמשת ב-LangChain4J וב-MCP Toolbox Java SDK

1. סקירה כללית

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

בשיעור ה-Lab הזה נבנה את Cymbal Transit Agent באמצעות:

  • LangChain4j: מסגרת Java מובילה לניהול AI.
  • AlloyDB: מסד נתונים בעל ביצועים גבוהים שתואם ל-PostgreSQL.
  • MCP Toolbox Java SDK: דרך סטנדרטית לקשר סוכני Java לכלים ולמקורות נתונים חיצוניים.

מה תפַתחו

e68388d533c9997e.png

‫Cymbal Bus Agent, אפליקציית Java Spring Boot שכוללת:

  1. מסד נתונים של AlloyDB ו-MCP Toolbox Java SDK לתיאום בין הכלים לבין הסוכנים.
  2. ‫Cloud Run לפריסת כלי ארגז הכלים ואפליקציות (פריסת סוכן).
  3. ספריית LangChain4J לסוכן ולמסגרת LLM באפליקציית Spring Boot עם Java 17.

מה תלמדו

  • איך משתמשים ב-LangChain4J כדי ליצור סוכנים ותת-סוכנים ייעודיים שמתואמים באמצעות MCP Toolbox for Databases Java SDK
  • איך מגדירים ומשתמשים ב-AlloyDB לנתונים ול-AI.
  • איך משתמשים ב-MCP Toolbox כדי לקשר סוכנים לכלי נתונים של AlloyDB.
  • איך פורסים את הפתרון באמצעות Cloud Run או מפעילים אותו באופן מקומי.

הארכיטקטורה

  1. AlloyDB ל-PostgreSQL: משמש כמסד נתונים תפעולי עם ביצועים גבוהים, שכולל את הרשומות של המסלולים, המדיניות וההזמנות שלנו. הוא מפעיל את החיפוש והאחזור של וקטורים.
  2. MCP Toolbox for Databases Java SDK: משמש כ "מנהל התזמור", ומציג נתונים של AlloyDB ככלים שניתנים להרצה והסוכנים יכולים להפעיל.

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

  1. LangChain4J: ספריית Java בקוד פתוח שמפשטת את השילוב של מודלים גדולים של שפה (LLM) באפליקציות Java. הוא מספק כלים והפשטות לבניית אפליקציות מבוססות-AI, כולל צ'אט בוטים, סוכנים ומערכות Retrieval-Augmented Generation (יצירה משולבת-אחזור, RAG).
  2. Cloud Run: פלטפורמה מנוהלת ללא שרת (serverless) שמאפשרת לכם לבנות ולפרוס בקלות אפליקציות או אתרים במהירות בכל שפה, בכל ספרייה ובכל קובץ בינארי. אתם יכולים לכתוב קוד באמצעות השפה, המסגרת והספריות המועדפות עליכם, לארוז אותו כקונטיינר, להריץ את הפקודה gcloud run deploy והאפליקציה שלכם תהיה פעילה – עם כל מה שהיא צריכה כדי לפעול בסביבת ייצור. יצירת מאגר תגים היא אופציונלית לחלוטין. אם אתם משתמשים ב-Go,‏ Node.js,‏ Python,‏ Java,‏ ‎.NET Core או Ruby, אתם יכולים להשתמש באפשרות הפריסה מבוססת-המקור שיוצרת את הקונטיינר בשבילכם, בהתאם לשיטות המומלצות לשפה שבה אתם משתמשים.

דרישות

  • דפדפן, כמו Chrome או Firefox.
  • פרויקט ב-Google Cloud שהחיוב בו מופעל.
  • היכרות בסיסית עם SQL ו-Java.

‫2. לפני שמתחילים

יצירת פרויקט

  1. ב-מסוף Google Cloud, בדף לבחירת הפרויקט, בוחרים או יוצרים פרויקט ב-Google Cloud.
  2. מוודאים שהחיוב מופעל בפרויקט ב-Cloud. כך בודקים אם החיוב מופעל בפרויקט.
  1. תשתמשו ב-Cloud Shell, סביבת שורת פקודה שפועלת ב-Google Cloud. לוחצים על 'הפעלת Cloud Shell' בחלק העליון של מסוף Google Cloud.

תמונה של לחצן ההפעלה של Cloud Shell

  1. אחרי שמתחברים ל-Cloud Shell, אפשר לבדוק שכבר בוצע אימות ושהפרויקט מוגדר למזהה הפרויקט שלכם באמצעות הפקודה הבאה:
gcloud auth list
  1. מריצים את הפקודה הבאה ב-Cloud Shell כדי לוודא שפקודת gcloud מכירה את הפרויקט.
gcloud config list project
  1. אם הפרויקט לא מוגדר, משתמשים בפקודה הבאה כדי להגדיר אותו:
gcloud config set project <YOUR_PROJECT_ID>
  1. מפעילים את ממשקי ה-API הנדרשים: לוחצים על הקישור ומפעילים את ממשקי ה-API.

אפשר גם להשתמש בפקודת gcloud. אפשר לעיין במאמרי העזרה בנושא פקודות gcloud ושימוש בהן.

נקודות חשובות ופתרון בעיות

תסמונת 'פרויקט הרפאים'

הפעלת את הפקודה gcloud config set project, אבל בפועל את מסתכלת על פרויקט אחר בממשק המשתמש של המסוף. צריך לבדוק את מזהה הפרויקט בתפריט הנפתח שבפינה הימנית העליונה.

מחסום החיוב

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

השהיה של הפצת API

לחצת על 'הפעלת ממשקי API', אבל בשורת הפקודה עדיין מופיעה ההודעה Service Not Enabled. מחכים 60 שניות. יעבור רגע עד שהנוירונים בענן יתעוררו.

מכסה Quags

אם אתם משתמשים בחשבון ניסיון חדש לגמרי, יכול להיות שתגיעו למכסה אזורית של מופעי AlloyDB. אם הפעולה us-central1 נכשלת, מנסים את הפעולה us-east1.

סוכן שירות 'מוסתר'

לפעמים סוכן השירות של AlloyDB לא מקבל אוטומטית את התפקיד aiplatform.user. בדרך כלל, אם שאילתות ה-SQL לא יכולות לתקשר עם Gemini בהמשך, זו הסיבה לכך.

3. הגדרת מסד נתונים

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

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

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

  1. לוחצים על הלחצן או מעתיקים את הקישור שלמטה לדפדפן שבו המשתמש מחובר למסוף Google Cloud.

לחלופין, אפשר לעבור אל Cloud Shell Terminal מהפרויקט שבו מימשתם את החשבון לחיוב, לשכפל את מאגר GitHub ולעבור אל הפרויקט באמצעות הפקודות הבאות:

git clone https://github.com/AbiramiSukumaran/easy-alloydb-setup

cd easy-alloydb-setup
  1. אחרי שתשלימו את השלב הזה, המאגר ישוכפל לעורך המקומי של Cloud Shell ותוכלו להריץ את הפקודה שלמטה מתוך תיקיית הפרויקט (חשוב לוודא שאתם בספריית הפרויקט):
sh run.sh
  1. עכשיו משתמשים בממשק המשתמש (לוחצים על הקישור במסוף או על הקישור 'תצוגה מקדימה באינטרנט' במסוף).
  2. כדי להתחיל, מזינים את הפרטים של מזהה הפרויקט, האשכול ושמות המופעים.
  3. אפשר ללכת לשתות קפה בזמן שהרישומים מתגללים, וכאן אפשר לקרוא איך זה קורה מאחורי הקלעים.

נקודות חשובות ופתרון בעיות

הבעיה של 'סבלנות'

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

חוסר התאמה באזור

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

Zombie Clusters

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

פסק זמן ב-Cloud Shell

אם הפסקת הקפה שלכם נמשכת 30 דקות, יכול להיות ש-Cloud Shell יעבור למצב שינה וינתק את התהליך sh run.sh. הכרטיסייה צריכה להיות פעילה.

4. הקצאת הרשאות לסכימה

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

1e3ac974b18a8113.png

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

  • שם משתמש : "postgres"
  • מסד נתונים : "postgres"
  • סיסמה: alloydb (או כל סיסמה אחרת שהגדרתם בזמן היצירה)

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

28cb9a8b6aa0789f.png

מזינים פקודות ל-AlloyDB בחלונות של כלי העריכה, ומשתמשים באפשרויות Run (הפעלה), Format (עיצוב) ו-Clear (ניקוי) לפי הצורך.

הפעלת תוספים

כדי ליצור את האפליקציה הזו, נשתמש בתוספים pgvector ו-google_ml_integration. התוסף pgvector מאפשר לכם לאחסן ולחפש הטמעות של וקטורים. התוסף google_ml_integration מספק פונקציות שמשמשות לגישה לנקודות קצה של חיזוי ב-Vertex AI כדי לקבל חיזויים ב-SQL. מפעילים את התוספים האלה על ידי הפעלת פקודות ה-DDL הבאות:

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

מתן הרשאה

מריצים את ההצהרה הבאה כדי להעניק הרשאת הפעלה לפונקציה embedding:

GRANT EXECUTE ON FUNCTION embedding TO postgres;

נותנים לחשבון השירות של AlloyDB את התפקיד Vertex AI User

במסוף IAM של Google Cloud, מעניקים לחשבון השירות של AlloyDB (שנראה כך: service-<<PROJECT_NUMBER>>@gcp-sa-alloydb.iam.gserviceaccount.com) גישה לתפקיד Vertex AI User. המשתנה PROJECT_NUMBER יכיל את מספר הפרויקט.

לחלופין, אפשר להריץ את הפקודה הבאה מ-Cloud Shell Terminal:

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"

צור טבלה

אתם יכולים ליצור טבלה באמצעות הצהרת ה-DDL שבהמשך ב-AlloyDB Studio:

DROP TABLE IF EXISTS transit_policies;
DROP TABLE IF EXISTS bus_schedules;
DROP TABLE IF EXISTS bookings;

-- Table 1: Transit Policies (Unstructured Data for RAG)
CREATE TABLE transit_policies (
    policy_id SERIAL PRIMARY KEY,
    category VARCHAR(50),
    policy_text TEXT,
    policy_embedding vector(768) 
);

-- Table 2: Intercity Bus Schedules (Structured Data)
CREATE TABLE bus_schedules (
    trip_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    origin_city VARCHAR(100),
    destination_city VARCHAR(100),
    departure_time TIMESTAMP,
    arrival_time TIMESTAMP,
    available_seats INT DEFAULT 50,
    ticket_price DECIMAL(6,2)
);

-- Table 3: Booking Ledger (Transactional Action Data)
CREATE TABLE bookings (
    booking_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    trip_id UUID REFERENCES bus_schedules(trip_id),
    passenger_id VARCHAR(100),
    status VARCHAR(20) DEFAULT 'CONFIRMED',
    booking_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

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

הטמעת נתונים

מריצים את קבוצת הצהרות ה-SQL שבהמשך כדי להוסיף רשומות בכמות גדולה לטבלאות המתאימות:

  1. הוספת מדיניות לא מובנית ויצירת הטמעות אמיתיות באופן מקורי ב-AlloyDB
-- 1. Insert Unstructured Policies and GENERATE REAL EMBEDDINGS natively in AlloyDB

INSERT INTO transit_policies (category, policy_text, policy_embedding) 
VALUES 
('Pets', 'Service animals are always welcome. Small pets (under 25 lbs) are allowed in secure carriers for a $25 fee. Large dogs are not permitted on standard coaches.', embedding('text-embedding-005', 'Service animals are always welcome. Small pets (under 25 lbs) are allowed in secure carriers for a $25 fee. Large dogs are not permitted on standard coaches.')),
('Luggage', 'Each passenger is allowed one carry-on (up to 15 lbs) and two stowed bags (up to 50 lbs each) free of charge. Additional bags cost $15 each.', embedding('text-embedding-005', 'Each passenger is allowed one carry-on (up to 15 lbs) and two stowed bags (up to 50 lbs each) free of charge. Additional bags cost $15 each.')),
('Refunds', 'Tickets are fully refundable up to 24 hours before departure. Within 24 hours, tickets can be exchanged for travel credit only.', embedding('text-embedding-005', 'Tickets are fully refundable up to 24 hours before departure. Within 24 hours, tickets can be exchanged for travel credit only.'));
  1. יצירת יותר מ-200 לוחות זמנים ריאליסטיים ל-7 ימים באמצעות generate_series
-- 2. Generate 200+ Realistic Schedules for the Next 7 Days using generate_series

INSERT INTO bus_schedules (origin_city, destination_city, departure_time, arrival_time, ticket_price, available_seats)
SELECT 
    origin,
    destination,
    -- Generate departures every 4 hours starting from tomorrow
    (CURRENT_DATE + 1) + (interval '4 hours' * seq) AS dep_time,
    (CURRENT_DATE + 1) + (interval '4 hours' * seq) + interval '4.5 hours' AS arr_time,
    ROUND((RANDOM() * 30 + 25)::numeric, 2) AS price, -- Random price between $25 and $55
    FLOOR(RANDOM() * 50 + 1) AS seats -- Random seats between 1 and 50
FROM 
    (VALUES 
        ('New York', 'Boston'), ('Boston', 'New York'),
        ('Philadelphia', 'Washington DC'), ('Washington DC', 'Philadelphia'),
        ('Seattle', 'Portland'), ('Portland', 'Seattle')
    ) AS routes(origin, destination)
CROSS JOIN generate_series(1, 40) AS seq; -- 6 routes * 40 time slots = 240 distinct trips ingested!

יצירת הטמעות

ההטמעה מכוסה אוטומטית בהצהרת ההוספה לטבלה transit_policies באמצעות הפונקציה embedding('text-embedding-005', '<<policytext>>').

נקודות חשובות ופתרון בעיות

הלולאה של 'שכחתי את הסיסמה'

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

השגיאה 'התוסף לא נמצא'

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

בעיות בהפצת הרשאות IAM

הפעלתם את פקודת gcloud IAM, אבל פקודת ה-SQL‏ CALL עדיין נכשלת עם שגיאת הרשאה. יכול לעבור זמן מה עד שהשינויים ב-IAM יתעדכנו בכל מערכות Google. כדאי לקחת רגע.***קריטי:

  1. לפעמים יכול להיות שחשבון השירות שלכם ב-AlloyDB ייראה שונה מהפורמט הקיים שבו השתמשנו בשלב ההרשאות. כדי לוודא באופן מלא שלחשבון השירות של AlloyDB יש את התפקיד Vertex AI User: נכנסים לדף AlloyDB Clusters במסוף Google Cloud. לוחצים על האשכול, ובכרטיסייה Overview מחפשים שדה עם התווית Service Account.
    מעתיקים את הערך ועוברים אל IAM כדי להוסיף את תפקיד המשתמש ב-Vertex AI.
  2. בנוסף, אם דילגתם על השלב 'הפעלת ה-API' בקטע 'לפני שמתחילים', תיתקלו בבעיות בגישה להטמעות מ-AlloyDB.

חוסר התאמה בין מאפייני וקטורים

העמודה policy_embedding בטבלה transit_policies מוגדרת כ-VECTOR(768). אם תנסו להשתמש במודל אחר (למשל מודל עם 1,536 מימדים) בהמשך, התוספים שלכם יתפוצצו. כדאי להשתמש ב-text-embedding-005.

שגיאת הקלדה במזהה הפרויקט

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

5. כלים והגדרת ארגז הכלים

‫MCP Toolbox for Databases הוא שרת MCP בקוד פתוח למסדי נתונים. היא מאפשרת לפתח כלים בקלות, במהירות ובאופן מאובטח יותר, כי היא מטפלת במורכבויות כמו איגום חיבורים, אימות ועוד. ערכת הכלים עוזרת לכם ליצור כלי AI גנרטיבי שמאפשרים לסוכנים שלכם לגשת לנתונים במסד הנתונים.

אנחנו משתמשים בארגז הכלים Model Context Protocol (MCP) for Databases בתור "המנצח". הוא משמש כתוכנת ביניים סטנדרטית בין הסוכנים שלנו לבין AlloyDB. הגדרת תצורה של tools.yaml מאפשרת ל-toolbox לחשוף באופן אוטומטי פעולות מורכבות במסד הנתונים ככלים נקיים וניתנים להרצה כמו find-bus-schedules and routes או query-schedules for specific routes, ולבצע פעולות אוטונומיות כמו book-ticket. כך אין צורך בניהול ידני של מאגר חיבורים או ב-SQL סטנדרטי בתוך הלוגיקה של הסוכן.

התקנת שרת Toolbox

במסוף Cloud Shell, יוצרים תיקייה לשמירת קובץ ה-YAML של כלי חדש וקובץ הבינארי של ארגז הכלים:

mkdir cymbal-bus-toolbox

cd cymbal-bus-toolbox

מתוך התיקייה החדשה, מריצים את קבוצת הפקודות הבאה:

# see releases page for other versions
export VERSION=0.27.0
curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
chmod +x toolbox

לאחר מכן, יוצרים את הקובץ tools.yaml בתוך התיקייה החדשה. כדי לעשות זאת, עוברים אל Cloud Shell Editor ומעתיקים את התוכן של קובץ repo אל הקובץ tools.yaml.

... (Refer to entire file in the repo)

tools:

   find-bus-schedules:
    kind: postgres-sql
    source: alloydb
    description: Find all available bus schedules.
    statement: |
      SELECT CAST(trip_id AS TEXT) trip_id, departure_time, arrival_time, ticket_price, available_seats , origin_city, destination_city 
      FROM bus_schedules;

   query-schedules:
    kind: postgres-sql
    source: alloydb
    description: Find available bus schedules between an origin and destination city.
    parameters:
      - name: origin
        type: string
        description: The departure city name.
      - name: destination
        type: string
        description: The arrival city name.
    statement: |
      SELECT CAST(trip_id AS TEXT) trip_id, departure_time, arrival_time, ticket_price, available_seats 
      FROM bus_schedules 
      WHERE lower(origin_city) = lower($1) 
        AND lower(destination_city) = lower($2) 
        AND available_seats > 0 
      ORDER BY departure_time ASC 
      LIMIT 5;

   book-ticket:
    kind: postgres-sql
    source: alloydb
    description: Books a ticket for a specific trip, decrementing available seats and generating a confirmed booking record.
    parameters:
      - name: trip_id
        type: string
        description: The UUID of the trip schedule to book.
      - name: passenger_name
        type: string
        description: Name or ID of the passenger (Bound securely via backend or AuthToken).
        authServices:
          - name: google_auth
            field: sub
    statement: |
      WITH updated_schedule AS (
          UPDATE bus_schedules 
          SET available_seats = available_seats - 1 
          WHERE trip_id = CAST($1 AS UUID) AND available_seats > 0
          RETURNING trip_id
      )
      INSERT INTO bookings (trip_id, passenger_id)
      SELECT trip_id, $2 
      FROM updated_schedule
      RETURNING CAST(booking_id as TEXT) as booking_id, trip_id, passenger_id, status, booking_time;

   search-policies:
    kind: postgres-sql
    source: alloydb
    description: Semantic search for transit policies regarding luggage, pets, refunds, and general rules.
    parameters:
      - name: search_query
        type: string
        description: The user's question about transit policies to be embedded and searched.
    statement: |
      SELECT category, policy_text 
      FROM transit_policies 
      ORDER BY policy_embedding <=> CAST(embedding('text-embedding-005', $1) AS vector(768))
      LIMIT 2;

הערה:

  1. בהגדרה של tools.yaml, חשוב לכלול את ipType: "private" בהגדרת המקור של AlloyDB.
  2. בנוסף, חשוב לזכור לכלול את כתובת ה-URL של שירות MCP Toolbox בפרמטר clientId להגדרת authServices. יכול להיות שתקבלו את הקישור רק אחרי הפריסה הראשונית – לכן תצטרכו להפעיל את שלבי הפריסה פעמיים כדי לוודא שתרחיש השימוש של כלים מאומתים פועל.
  3. האפשרויות הבאות לבדיקת ארגז הכלים באופן מקומי לא יפעלו אם החיבור ל-AlloyDB מוגדר כפרטי. כדי לבדוק אותו באופן מקומי, צריך להגדיר אותו כציבורי או להשתמש ב-proxy לחיבור. אבל אין לכם מה לדאוג. במקרה שלנו, נבצע פריסה ישירות ב-Cloud Run ואז נבדוק.

כדי לבדוק את הקובץ tools.yaml בשרת המקומי:

./toolbox --tools-file "tools.yaml"

אפשר גם לבדוק את זה בממשק המשתמש:

./toolbox --ui

בואו נראה איך פורסים אותו ב-Cloud Run.

פריסה ב-Cloud Run

  1. מגדירים את משתנה הסביבה PROJECT_ID:
export PROJECT_ID="my-project-id"
  1. מאתחלים את ה-CLI של gcloud:
gcloud init
gcloud config set project $PROJECT_ID
  1. צריך להפעיל את ממשקי ה-API הבאים:
gcloud services enable run.googleapis.com \
                       cloudbuild.googleapis.com \
                       artifactregistry.googleapis.com \
                       iam.googleapis.com \
                       secretmanager.googleapis.com
  1. אם עדיין אין לכם חשבון שירות לקצה העורפי, יוצרים חשבון כזה:
gcloud iam service-accounts create toolbox-identity
  1. נותנים הרשאות לשימוש במנהל הסודות:
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member serviceAccount:toolbox-identity@$PROJECT_ID.iam.gserviceaccount.com \
    --role roles/secretmanager.secretAccessor
  1. נותנים לחשבון השירות הרשאות נוספות שספציפיות למקור AlloyDB שלנו (roles/alloydb.client ו-roles/serviceusage.serviceUsageConsumer)
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member serviceAccount:toolbox-identity@$PROJECT_ID.iam.gserviceaccount.com \
    --role roles/alloydb.client


gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member serviceAccount:toolbox-identity@$PROJECT_ID.iam.gserviceaccount.com \
    --role roles/serviceusage.serviceUsageConsumer
  1. מעלים את הקובץ tools.yaml כסוד:
gcloud secrets create tools-cymbal-transit --data-file=tools.yaml
  1. אם כבר יש לכם סוד ואתם רוצים לעדכן את גרסת הסוד, מריצים את הפקודה הבאה:
gcloud secrets versions add tools-cymbal-transit --data-file=tools.yaml
  1. מגדירים משתנה סביבה לקובץ אימג' של קונטיינר שרוצים להשתמש בו ב-Cloud Run:
export IMAGE=us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:latest
  1. מפעילים את Toolbox ב-Cloud Run באמצעות הפקודה הבאה:

אם הפעלתם גישה ציבורית במופע AlloyDB, אתם יכולים להשתמש בפקודה הבאה כדי לפרוס את האפליקציה ב-Cloud Run:

gcloud run deploy toolbox-cymbal-transit \
    --image $IMAGE \
    --service-account toolbox-identity \
    --region us-central1 \
    --set-secrets "/app/tools.yaml=tools-cymbal-transit:latest" \
    --args="--tools-file=/app/tools.yaml","--address=0.0.0.0","--port=8080" \
    --allow-unauthenticated

אם אתם משתמשים ברשת VPC, משתמשים בפקודה הבאה:

gcloud run deploy toolbox-cymbal-transit \
    --image $IMAGE \
    --service-account toolbox-identity \
    --region us-central1 \
    --set-secrets "/app/tools.yaml=tools-cymbal-transit:latest" \
    --args="--tools-file=/app/tools.yaml","--address=0.0.0.0","--port=8080" \
    --network <<YOUR_NETWORK_NAME>> \
    --subnet <<YOUR_SUBNET_NAME>> \
    --allow-unauthenticated

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

6. הגדרת אפליקציית הסוכן

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

מאגר GitHub

כדי לשכפל את הפרויקט, מריצים את הפקודה הבאה בטרמינל של Cloud Shell (בספריית הבסיס או בכל מקום שבו רוצים ליצור את הפרויקט):

git clone https://github.com/googleapis/mcp-toolbox-sdk-java

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

cd mcp-toolbox-sdk-java/demo-applications/cymbal-transit
  1. הפעולה הזו אמורה ליצור את הפרויקט, ואפשר לוודא זאת בעורך Cloud Shell.

a494664032904c77.png

  1. פותחים את הקובץ CymbalTransitController.java ומגדירים את משתני הסביבה:
  2. GCP_PROJECT_ID
  3. GCP_REGION
  4. GEMINI_MODEL_NAME
  5. MCP_TOOLBOX_URL

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

7. הסבר מפורט על הקוד

CymbalTransitController משמש כנקודת הכניסה לשירות Cloud Run שלנו. הוא מנהל את רצף השיחה ומוודא שלסוכן יש גישה לבקשה הנוכחית של המשתמש.

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

1. הגדרת סוכן AI‏ (AgentConfiguration)

המחלקות האלה משתמשות ב-@Configuration של Spring כדי להפעיל את רכיבי ה-AI. הוא מאתחל את VertexAiGeminiChatModel ומקשר אותו לממשק Agent.

@Bean
ChatLanguageModel geminiChatModel() {
    return VertexAiGeminiChatModel.builder()
        .project(projectId)
        .location(region)
        .modelName(modelName)
        .build();
}

@Bean
TransitAgent transitAgent(ChatLanguageModel chatLanguageModel, TransitAgentTools tools) {
    return AiServices.builder(TransitAgent.class)
        .chatLanguageModel(chatLanguageModel)
        .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(20))
        .tools(tools) 
        .build();
}

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

2. ממשק סוכן ה-AI‏ (TransitAgent)

ההערה @SystemMessage מגדירה את ה'פרסונה' ואת המגבלות התפעוליות, ובאופן ספציפי אסטרטגיית ניתוב.

@SystemMessage({
    "You are the Cymbal Transit Concierge.",
    "CRITICAL INSTRUCTION: On your very first interaction, you MUST use the 'findAllSchedules' tool to fetch and memorize the broad bus routes.",
    "ONLY if the user asks a specifically narrowed-down question... should you route to the specific tools like 'querySchedules', 'bookTicket', 'searchPolicies'.",
    "Don't show any asterisks while listing results. Keep it formatted and numbered or bulleted."
})
String chat(@MemoryId String sessionId, @UserMessage String userMessage);

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

3. הגשר של כלי העריכה (TransitAgentTools)

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

@Tool("Fetches the initial, broad dataset of all available bus schedules and routes.")
public String findAllSchedules() {
    return mcpService.findAllSchedules().join();
}


@Tool("Book a ticket for a passenger using a specific trip ID.")
public String bookTicket(String tripId, String passengerName) {
    return mcpService.bookTicket(tripId, passengerName).join();
}

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

4. שירות MCP Toolbox (McpToolboxService)

זוהי שכבת התקשורת שמשתמשת ב-MCP Toolbox Java SDK כדי ליצור אינטראקציה עם הקצה העורפי של AlloyDB.

// Identity Management: Fetching OIDC ID Token for Auth
GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
this.idToken = ((IdTokenProvider) credentials)
    .idTokenWithAudience(targetUrl, Collections.emptyList())
    .getTokenValue();

// Dynamic Invocation: Executing a tool by name
public CompletableFuture<String> findAllSchedules() {
    return mcpClient.invokeTool("find-bus-schedules", Collections.emptyMap()).thenApply(result -> {
        return result.content().stream()
            .map(content -> content.text())
            .collect(Collectors.joining(", ", "[", "]"));
    });
}

משמעות: הספרייה McpToolboxClient מטפלת בעבודה המורכבת של תקשורת JSON-RPC. השיטה bookTicket מדגימה באופן ספציפי את היכולת של ה-SDK לקשור פרמטרים מורכבים באופן דינמי.

5. ה-REST Controller ‏ (TransitAgentController)

נקודת הקצה הסופית פשוטה מאוד כי LangChain4j מנהל את המצב והלוגיקה.

@PostMapping("/chat")
public ResponseEntity<String> handleUserChat(@RequestBody String userMessage, HttpSession session) {
    String sessionId = session.getId();
    String agentResponse = transitAgent.chat(sessionId, userMessage);
    return ResponseEntity.ok(agentResponse);
}

משמעות: על ידי מיפוי מזהה HttpSession ל-@MemoryId, אנחנו מוודאים שתוכניות הנסיעה של משתמשים שונים לא יתערבבו, וגם שומרים על קוד הבקרה נקי וקריא.

8. ערכת הכלים של MCP: חשיבות ו-Java SDK

מה זה MCP?

אפשר לחשוב על Model Context Protocol‏ (MCP) כמתרגם אוניברסלי ל-AI. הפרוטוקול נועד לתקנן את האופן שבו מודלים של AI מתחברים לכלים ולמערכי נתונים חיצוניים. הוא מחליף סקריפטים מותאמים אישית ומפוצלים של שילוב, ומציע פרוטוקול אוניברסלי ומאובטח. בין אם ה-Agent צריך להריץ שאילתת SQL טרנזקציונלית, לחפש באלפי מסמכי מדיניות או להפעיל API בארכיטקטורת REST,‏ MCP מספק ממשק יחיד ומאוחד.

ארגז הכלים של MCP למסדי נתונים

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

כדי להחליף את צווארי הבקבוק האלה שמוגדרים בהארדקוד במישור בקרה מאוחד ומאובטח, אנחנו שמחים להשיק את Java SDK ל-Model Context Protocol (MCP) Toolbox for Databases. הגרסה הזו מביאה לעולם תזמור סוכנים ברמה גבוהה ובטוחה מבחינת טיפוסים, למערכת האקולוגית הארגונית הנפוצה ביותר. הארכיטקטורה המפותחת של Java מותאמת במיוחד לדרישות המחמירות האלה, ומספקת את הריבוי הגבוה של פעולות בו-זמניות, את השלמות הקפדנית של העסקאות ואת ניהול המצב החזק שנדרשים כדי להרחיב בבטחה סוכני AI קריטיים לייצור.

למה כדאי להשתמש ב-Java SDK?

MCP Toolbox Java SDK מאפשר למפתחי Java:

  1. שימוש בכלים: התחברות לשרת MCP (כמו MCP Toolbox ל-AlloyDB) והפיכת היכולות שלו לשיטות Java שמערכת LangChain4j מבינה באופן אוטומטי.
  2. בטיחות סוגים: שימוש בהקלדה חזקה של Java לפרמטרים של כלים, כדי להפחית שגיאות של "הזיות" בזמן הריצה בקריאות לכלים.
  3. מוכנות לארגונים: שילוב קל עם Spring Boot, ‏ Quarkus, ‏ Micronaut וכו'.
  4. חיבור פשוט: לא צריך לכתוב קוד JSON-RPC סטנדרטי.
  5. תקנון האימות: תמיכה מובנית באסימוני OIDC של Google Cloud מבטיחה הפעלה מאובטחת של הכלי.

ועוד הרבה יותר.

יחסי תלות: הגדרת pom.xml

מוסיפים את התלות הבאה לפרויקט Maven כדי לכלול את הגרסה העדכנית של MCP Toolbox Java SDK:

   <dependency>
        <groupId>com.google.cloud.mcp</groupId>
        <artifactId>mcp-toolbox-sdk-java</artifactId>
        <version>0.2.0</version>
    </dependency>

כדי לכלול את ארטיפקט LangChain4j, מוסיפים את יחסי התלות הבאים לפרויקט Maven:

     <!-- LangChain4j Core & Gemini -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j</artifactId>
        <version>0.35.0</version>
    </dependency>

זהו!!! שיבטנו את הפרויקט בהצלחה והסברנו את הפרטים של הסוכן, של MCP Toolbox Java SDK ושל ההקשר.

9. הרצה באופן מקומי

כדי לבדוק את הסוכן במחשב, צריך להפנות אותו לשרת MCP Toolbox שנפרס.

  1. הגדרת משתני סביבה:
export GCP_PROJECT_ID="<<YOUR_PROJECT_ID>>"
export GCP_REGION="us-central1"
export GEMINI_MODEL_NAME="gemini-2.5-flash"
export MCP_TOOLBOX_URL="<<YOUR_TOOLBOX_ENDPOINT_URL>>/mcp"
  1. מריצים עם Maven:
mvn compile

mvn spring-boot:run

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

10. בואו נפרוס אותו ב-Cloud Run

פורסים אותו ב-Cloud Run על ידי הפעלת הפקודה הבאה מ-Cloud Shell Terminal שבו הפרויקט משוכפל, מוודאים שאתם בתוך תיקיית הבסיס של הפרויקט.

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

cd cymbal-transit

אם אתם כבר בספריית הבסיס cymbal-transit, מריצים את הפקודה הבאה כדי לפרוס את האפליקציה ישירות ב-Cloud Run:

gcloud run deploy cymbal-transit --source . --set-env-vars GCP_PROJECT_ID=<<YOUR_PROJECT_ID>>,GCP_REGION=us-central1,GEMINI_MODEL_NAME=gemini-2.5-flash,MCP_TOOLBOX_URL=<<YOUR_MCP_TOOLBOX_URL>> --allow-unauthenticated

מחליפים את הערכים של הפלייס הולדרים <<YOUR_PROJECT>> and <<YOUR_MCP_TOOLBOX_URL>>

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

מקצים לחשבון השירות של Cloud Run את התפקיד AlloyDB Client.כך האפליקציה בלי שרת (serverless) יכולה ליצור מנהרה מאובטחת למסד הנתונים.

מריצים את הפקודה הבאה בטרמינל Cloud Shell:

# 1. Get your Project ID and Project Number
PROJECT_ID=$(gcloud config get-value project)
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")

# 2. Grant the AlloyDB Client role
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
--role="roles/alloydb.client"

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

עכשיו משתמשים בכתובת ה-URL של השירות (נקודת הקצה של Cloud Run שהעתקתם קודם) ובודקים את האפליקציה.

הערה: אם נתקלתם בבעיה בשירות, והזיכרון מצוין כסיבה, נסו להגדיל את מגבלת הזיכרון שהוקצה ל-1GB כדי לבדוק את זה.

11. הדגמה (דמו)

שואלים את הסוכן: "I need to get from New York to Boston tomorrow morning. אפשר להביא גולדן רטריבר?" שימו לב איך הסוכן:

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

aa0408a81074d0fc.png

12. הסרת המשאבים

אחרי שמסיימים את ה-Lab הזה, חשוב למחוק את אשכול AlloyDB ואת המכונה.

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

13. מזל טוב

יצרת בהצלחה סוכן מעבר מתוחכם שמבוסס על Java. בעזרת LangChain4j לארגון ו-MCP Toolbox Java SDK לקישוריות נתונים, יצרתם מערכת שיכולה להסיק מסקנות על סמך סוכנים, כלים ומקורות נתונים. אם אתם רוצים להתחיל לתזמן את האפליקציות מבוססות הסוכנים שלכם באמצעות MCP Toolbox for Databases במספר מסדי נתונים, גם בפלטפורמות שונות, כדאי להתחיל להשתמש ב-Java SDK כבר היום. בבלוג הזה אפשר לקרוא את הודעת ההשקה ולקבל מידע מפורט יותר על הספרייה. אם אתם רוצים לבנות עוד אפליקציות כאלה באופן מעשי, בחינם, בקצב שלכם ובהדרכת מורה, אתם יכולים להירשם ל-Code Vipassana בכתובת https://codevipassana.dev.