מסד נתונים ככלי: RAG מבוסס-סוכן עם ADK, ערכת הכלים MCP ו-Cloud SQL

1. מבוא

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

ב-Codelab הזה מוצגת גישה שונה. מצהירים על כלי מסד הנתונים בקובץ YAML – שאילתות SQL סטנדרטיות, חיפוש דמיון וקטורי ואפילו יצירה אוטומטית של הטמעה – ו-MCP Toolbox for Databases מטפל בכל פעולות מסד הנתונים כשרת MCP. קוד הסוכן נשאר מינימלי: טוענים את הכלים ומאפשרים ל-Gemini להחליט באיזה כלי להשתמש.

מה תפַתחו

עוזר חכם ללוח משרות בשם TechJobs – סוכן ADK שמבוסס על Gemini ועוזר למפתחים לעיין ברשימות של משרות בתחום הטכנולוגיה באמצעות מסננים סטנדרטיים (תפקיד, סטאק תוכנות) ולגלות משרות באמצעות תיאורים בשפה טבעית, כמו "אני רוצה משרה עם עבודה מרחוק בתחום של צ'אטבוטים מבוססי-AI". הסוכן קורא ממסד נתונים של Cloud SQL PostgreSQL וכותב אליו נתונים באופן מלא דרך MCP Toolbox for Databases, שמטפל בכל הגישה למסד הנתונים – כולל יצירה אוטומטית של הטמעה לחיפוש וקטורי. בסיום התהליך, גם ה-Toolbox וגם הסוכן יפעלו ב-Cloud Run.

eb6de681c40990c1.jpeg

מה תלמדו

  • איך תקן MCP‏ (Model Context Protocol) מאפשר לסוכני AI לגשת לכלים בצורה אחידה, ואיך MCP Toolbox for Databases מיישם את זה בפעולות שקשורות למסדי נתונים
  • הגדרת MCP Toolbox for Databases כתוכנת ביניים בין סוכן ADK לבין Cloud SQL PostgreSQL
  • הגדרת כלי מסד נתונים באופן הצהרתי ב-tools.yaml – אין קוד מסד נתונים בסוכן
  • יצירת סוכן ADK שמעמיס כלים משרת Toolbox פעיל באמצעות ToolboxToolset
  • יצירת הטמעות וקטורים באמצעות הפונקציה המובנית embedding() של Cloud SQL והפעלת חיפוש סמנטי באמצעות pgvector
  • שימוש בתכונה valueFromParam להוספה אוטומטית של וקטורים בפעולות כתיבה
  • פריסה של השרת Toolbox וסוכן ה-ADK ב-Cloud Run

דרישות מוקדמות

  • חשבון Google Cloud עם חשבון לחיוב בתקופת ניסיון
  • היכרות בסיסית עם Python ו-SQL
  • ניסיון קודם עם מסד נתונים בענן ו-ADK יכול לעזור

2. הגדרת הסביבה

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

פתיחת Cloud Shell

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

אחר כך לוחצים על View (תצוגה) -> Terminal (טרמינל) כדי לפתוח את הטרמינל.הממשק אמור להיראות בערך כך:

86307fac5da2f077.png

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

הגדרת ספריית העבודה

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

mkdir -p ~/build-agent-adk-toolbox-cloudsql
cloudshell workspace ~/build-agent-adk-toolbox-cloudsql && cd ~/build-agent-adk-toolbox-cloudsql

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

mkdir -p ~/build-agent-adk-toolbox-cloudsql/scripts
mkdir -p ~/build-agent-adk-toolbox-cloudsql/logs

הגדרת הפרויקט ב-Google Cloud

יוצרים את קובץ .env עם משתני המיקום:

# For Vertex AI / Gemini API calls
echo "GOOGLE_CLOUD_LOCATION=global" > .env
# For Cloud SQL, Cloud Run, Artifact Registry
echo "REGION=us-central1" >> .env

כדי לפשט את הגדרת הפרויקט במסוף, מורידים את סקריפט הגדרת הפרויקט הזה לספריית העבודה:

curl -sL https://raw.githubusercontent.com/alphinside/cloud-trial-project-setup/main/setup_verify_trial_project.sh -o setup_verify_trial_project.sh

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

bash setup_verify_trial_project.sh && source .env

הסקריפט:

  1. אימות שיש לכם חשבון לחיוב עם תקופת ניסיון פעילה
  2. בודקים אם יש פרויקט קיים ב-.env (אם יש)
  3. יוצרים פרויקט חדש או משתמשים בפרויקט קיים
  4. קישור החשבון לחיוב בתקופת הניסיון לפרויקט
  5. שומרים את מזהה הפרויקט ב-.env
  6. הגדרת הפרויקט כפרויקט פעיל ב-gcloud

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

dcba35ce1389f313.png

הפעלת API נדרש

לאחר מכן, צריך להפעיל כמה ממשקי API למוצר שאיתו תהיה אינטראקציה:

gcloud services enable \
  aiplatform.googleapis.com \
  sqladmin.googleapis.com \
  compute.googleapis.com \
  run.googleapis.com \
  cloudbuild.googleapis.com \
  artifactregistry.googleapis.com
  • Vertex AI API (aiplatform.googleapis.com) – הסוכן משתמש במודלים של Gemini, וכלי העזר משתמש ב-Embedding API לחיפוש וקטורי.
  • Cloud SQL Admin API ‏ (sqladmin.googleapis.com) – אתם מקצים ומנהלים מופע PostgreSQL.
  • Compute Engine API ‏ (compute.googleapis.com) – נדרש ליצירת מופעים של Cloud SQL.
  • Cloud Run, ‏ Cloud Build, ‏ Artifact Registry – שירותים שבהם נשתמש בשלב הפריסה בהמשך שיעור ה-Codelab הזה

3. הכנת סקריפטים לאתחול מסד נתונים

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

קודם צריך להוסיף את הסיסמה של מסד הנתונים לקובץ .env ולטעון אותו מחדש:

echo "DB_PASSWORD=techjobs-pwd" >> .env
echo "DB_INSTANCE=jobs-instance" >> .env
echo "DB_NAME=jobs_db" >> .env
source .env

יצירת סקריפט Bash ליצירת מכונה ומסד נתונים

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

mkdir -p ~/build-agent-adk-toolbox-cloudsql/scripts
cloudshell edit scripts/setup_database.sh

לאחר מכן, מעתיקים את הקוד הבא לקובץ scripts/setup_database.sh

#!/bin/bash
set -e
source .env

echo "================================================"
echo "Database Setup"
echo "================================================"
echo ""

# Step 1: Create Cloud SQL instance
echo "[1/5] Creating Cloud SQL instance..."

# Check if instance already exists
if gcloud sql instances describe "$DB_INSTANCE" --quiet >/dev/null 2>&1; then
    echo "      Instance already exists"
else
    echo "      Creating instance (takes 5-10 minutes)..."
    gcloud sql instances create "$DB_INSTANCE" \
        --database-version=POSTGRES_17 \
        --tier=db-custom-1-3840 \
        --edition=ENTERPRISE \
        --region="$REGION" \
        --root-password="$DB_PASSWORD" \
        --enable-google-ml-integration \
        --database-flags cloudsql.enable_google_ml_integration=on \
        --quiet
fi
echo "      ✓ Instance ready"
echo ""

# Step 2: Verify instance is ready
echo "[2/5] Verifying instance state..."

STATE=$(gcloud sql instances describe "$DB_INSTANCE" --format='value(state)')

if [ "$STATE" != "RUNNABLE" ]; then
    echo "ERROR: Instance not ready (state: $STATE)"
    exit 1
fi
echo "      ✓ Instance is RUNNABLE"
echo ""

# Step 3: Grant IAM permissions
echo "[3/5] Granting Vertex AI permissions..."

SERVICE_ACCOUNT=$(gcloud sql instances describe "$DB_INSTANCE" \
    --format='value(serviceAccountEmailAddress)')

if [ -z "$SERVICE_ACCOUNT" ]; then
    echo "ERROR: Could not retrieve service account"
    exit 1
fi

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

echo "      ✓ Permissions granted"
echo ""

# Step 4: Create database
echo "[4/5] Creating database..."

# Check if database already exists
if gcloud sql databases describe "$DB_NAME" \
    --instance="$DB_INSTANCE" --quiet >/dev/null 2>&1; then
    echo "      Database already exists"
else
    gcloud sql databases create "$DB_NAME" \
        --instance="$DB_INSTANCE" \
        --quiet
fi

echo "      ✓ Database '$DB_NAME' ready"
echo ""

# Step 5: Seed database and generate embeddings
echo "[5/5] Seeding database and generating embeddings..."

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SETUP_SCRIPT="${SCRIPT_DIR}/setup_jobs_db.py"

if [ ! -f "$SETUP_SCRIPT" ]; then
    echo "ERROR: Setup script not found: $SETUP_SCRIPT"
    exit 1
fi

uv run "$SETUP_SCRIPT"

echo ""
echo "================================================"
echo "Setup complete!"
echo "================================================"
echo ""

יצירת סקריפט Python לאכלוס נתונים

לאחר מכן, יוצרים את קובץ ה-Python של סקריפט ההרצה scripts/setup_jobs_db.py באמצעות הפקודה הבאה

cloudshell edit scripts/setup_jobs_db.py

לאחר מכן, מעתיקים את הקוד הבא לקובץ scripts/setup_jobs_db.py

import os
import sys
from pathlib import Path
from dotenv import load_dotenv
from google.cloud.sql.connector import Connector
import pg8000
import time

# Load environment variables from .env file
env_path = Path(__file__).parent.parent / '.env'
load_dotenv(env_path)
EMBEDDING_MODEL='gemini-embedding-001'

# Verify required environment variables
required_vars = ['GOOGLE_CLOUD_PROJECT', 'REGION', 'DB_PASSWORD']
missing_vars = [var for var in required_vars if not os.environ.get(var)]

if missing_vars:
    print(f"ERROR: Missing required environment variables: {', '.join(missing_vars)}", file=sys.stderr)
    print(f"", file=sys.stderr)
    print(f"Expected .env file location: {env_path}", file=sys.stderr)
    if not env_path.exists():
        print(f"✗ File not found at that location", file=sys.stderr)
    else:
        print(f"✓ File exists but is missing the variables above", file=sys.stderr)
    print(f"", file=sys.stderr)
    print(f"Make sure your .env file contains:", file=sys.stderr)
    for var in missing_vars:
        print(f"  {var}=<value>", file=sys.stderr)
    sys.exit(1)

# Job listings data (fictional, for tutorial purposes only)
JOBS = [
    ("Senior Backend Engineer", "Stripe", "Backend", "Go, PostgreSQL, gRPC, Kubernetes", "$180-250K/year", "San Francisco, Hybrid", 3,
     "Design and build high-throughput microservices powering payment infrastructure for millions of businesses. Optimize Go services for sub-100ms latency at scale, work with PostgreSQL and Redis for data persistence, and deploy on Kubernetes clusters handling billions of API calls."),
    ("Machine Learning Engineer", "Spotify", "Data/AI", "Python, TensorFlow, BigQuery, Vertex AI", "$170-230K/year", "Stockholm, Remote", 2,
     "Build and deploy ML models for music recommendation and personalization systems serving hundreds of millions of listeners. Design feature pipelines in BigQuery, train models using distributed computing, and serve predictions through real-time APIs processing thousands of requests per second."),
    ("Frontend Engineer", "Vercel", "Frontend", "React, TypeScript, Next.js", "$140-190K/year", "Remote", 4,
     "Build developer-facing dashboard interfaces and deployment tools used by millions of developers worldwide. Create responsive, accessible React components for project management, analytics, and real-time deployment monitoring with a focus on developer experience."),
    ("DevOps Engineer", "Datadog", "DevOps", "Terraform, GCP, Docker, Kubernetes, ArgoCD", "$160-220K/year", "New York, Hybrid", 2,
     "Manage cloud infrastructure powering an observability platform used by thousands of engineering teams. Automate deployment pipelines with ArgoCD, manage multi-cloud Kubernetes clusters, and implement infrastructure-as-code with Terraform across production environments."),
    ("Mobile Engineer (Android)", "Grab", "Mobile", "Kotlin, Jetpack Compose, GraphQL", "$120-170K/year", "Singapore, Hybrid", 3,
     "Develop features for a super-app serving millions of users across Southeast Asia. Build modern Android UIs with Jetpack Compose, integrate GraphQL APIs, and optimize app performance for diverse device capabilities and network conditions."),
    ("Data Engineer", "Airbnb", "Data", "Python, Apache Spark, Airflow, BigQuery", "$160-210K/year", "San Francisco, Hybrid", 2,
     "Build data pipelines that process booking, search, and pricing data for a global travel marketplace. Design ETL workflows with Apache Spark and Airflow, maintain data warehouses in BigQuery, and ensure data quality for analytics and machine learning teams."),
    ("Full Stack Engineer", "Revolut", "Full Stack", "TypeScript, Node.js, React, PostgreSQL", "$130-180K/year", "London, Remote", 5,
     "Build the next generation of financial products making banking accessible to millions of users across 35 countries. Develop real-time trading interfaces with React and WebSockets, build Node.js APIs handling market data streams, and design PostgreSQL schemas for financial transactions."),
    ("Site Reliability Engineer", "Cloudflare", "SRE", "Go, Prometheus, Grafana, GCP, Terraform", "$170-230K/year", "Austin, Hybrid", 2,
     "Ensure 99.99% uptime for a global network handling millions of requests per second. Define SLOs, build monitoring dashboards with Prometheus and Grafana, manage incident response, and automate infrastructure scaling across 300+ data centers worldwide."),
    ("Cloud Architect", "Google Cloud", "Cloud", "GCP, Terraform, Kubernetes, Python", "$200-280K/year", "Seattle, Hybrid", 1,
     "Help enterprises modernize their infrastructure on Google Cloud. Design multi-region architectures, lead migration projects from on-premises to GKE, and build reference implementations using Terraform and Cloud Foundation Toolkit."),
    ("Backend Engineer (Payments)", "Square", "Backend", "Java, Spring Boot, PostgreSQL, Kafka", "$160-220K/year", "San Francisco, Hybrid", 3,
     "Build payment processing systems handling millions of transactions for businesses of all sizes. Design event-driven architectures using Kafka, implement idempotent payment flows with Spring Boot, and ensure PCI-DSS compliance across all services."),
    ("AI Engineer", "Hugging Face", "Data/AI", "Python, LangChain, Vertex AI, FastAPI, PostgreSQL", "$150-210K/year", "Paris, Remote", 2,
     "Build AI-powered tools for the largest open-source ML community. Develop RAG pipelines that index and search model documentation, create conversational agents using LangChain, and deploy AI services with FastAPI on cloud infrastructure."),
    ("Platform Engineer", "Coinbase", "Platform", "Rust, Kubernetes, AWS, Terraform", "$180-250K/year", "Remote", 0,
     "Build the infrastructure platform for a leading cryptocurrency exchange. Develop high-performance matching engines in Rust, manage Kubernetes clusters for microservices, and design CI/CD pipelines that enable rapid feature deployment with zero downtime."),
    ("QA Automation Engineer", "Shopify", "QA", "Python, Selenium, Cypress, Jenkins", "$110-160K/year", "Toronto, Hybrid", 3,
     "Design and maintain automated test suites for a commerce platform powering millions of merchants. Build end-to-end test frameworks with Cypress and Selenium, integrate tests into Jenkins CI pipelines, and establish quality gates that prevent regressions in checkout and payment flows."),
    ("Security Engineer", "CrowdStrike", "Security", "Python, SIEM, Kubernetes, Penetration Testing", "$170-240K/year", "Austin, On-site", 1,
     "Protect enterprise customers from cyber threats on a leading endpoint security platform. Conduct penetration testing, design security monitoring with SIEM tools, implement zero-trust networking in Kubernetes environments, and lead incident response for security events."),
    ("Product Engineer", "GitLab", "Full Stack", "Go, React, PostgreSQL, Redis, GCP", "$140-200K/year", "Remote", 4,
     "Own features end-to-end for an all-in-one DevSecOps platform used by millions of developers. Build Go microservices for CI/CD pipelines, create React frontends for code review and project management, and collaborate with product managers to iterate on user-facing features using data-driven development."),
]


def get_connection():
    """Create a connection to Cloud SQL using the connector."""
    project = os.environ['GOOGLE_CLOUD_PROJECT']
    region = os.environ['REGION']
    password = os.environ['DB_PASSWORD']
    instance = os.environ['DB_INSTANCE']
    database = os.environ['DB_NAME']

    connector = Connector()
    conn = connector.connect(
        f"{project}:{region}:{instance}",
        "pg8000",
        user="postgres",
        password=password,
        db=database
    )
    return conn, connector


def create_schema(cursor):
    """Create extensions and jobs table."""
    cursor.execute("CREATE EXTENSION IF NOT EXISTS google_ml_integration")
    cursor.execute("CREATE EXTENSION IF NOT EXISTS vector")
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS jobs (
            id SERIAL PRIMARY KEY,
            title VARCHAR NOT NULL,
            company VARCHAR NOT NULL,
            role VARCHAR NOT NULL,
            tech_stack VARCHAR NOT NULL,
            salary_range VARCHAR NOT NULL,
            location VARCHAR NOT NULL,
            openings INTEGER NOT NULL,
            description TEXT NOT NULL,
            description_embedding vector(3072)
        )
    """)


def seed_jobs(cursor, conn):
    """Insert job listings."""
    cursor.execute("SELECT COUNT(*) FROM jobs")
    existing_count = cursor.fetchone()[0]

    if existing_count > 0:
        print(f"      {existing_count} jobs already exist, skipping seed")
        return 0

    cursor.executemany("""
        INSERT INTO jobs (title, company, role, tech_stack, salary_range, location, openings, description)
        VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
    """, JOBS)
    conn.commit()
    return len(JOBS)


def generate_embeddings(cursor, conn):
    """Generate embeddings using Cloud SQL's embedding() function."""
    cursor.execute("SELECT COUNT(*) FROM jobs WHERE description_embedding IS NULL")
    null_count = cursor.fetchone()[0]

    if null_count == 0:
        print("      All jobs already have embeddings")
        return 0

    cursor.execute(f"""
        UPDATE jobs
        SET description_embedding = embedding('{EMBEDDING_MODEL}', description)::vector
        WHERE description_embedding IS NULL
    """)
    rows_updated = cursor.rowcount
    conn.commit()
    return rows_updated


def main():
    conn, connector = get_connection()
    cursor = conn.cursor()

    try:
        create_schema(cursor)
        conn.commit()

        seeded = seed_jobs(cursor, conn)
        if seeded > 0:
            print(f"      ✓ Inserted {seeded} jobs")

        # Waiting for vertex role propagation
        time.sleep(60)
        embedded = generate_embeddings(cursor, conn)
        if embedded > 0:
            print(f"      ✓ Generated {embedded} embeddings")

    except Exception as e:
        print(f"ERROR: {e}", file=sys.stderr)
        sys.exit(1)
    finally:
        cursor.close()
        conn.close()
        connector.close()


if __name__ == "__main__":
    main()

עכשיו עוברים לשלב הבא.

4. יצירה ואתחול של מסד הנתונים

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

הגדרת פרויקט Python

uv הוא מנהל פרויקטים וחבילות Python מהיר שנכתב ב-Rust ( מסמכי uv ). בשיעור Codelab הזה נעשה בו שימוש כדי לשמור על מהירות ופשטות בתחזוקת פרויקט Python

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

uv init
uv add cloud-sql-python-connector --extra pg8000
uv add python-dotenv

שימו לב שאנחנו משתמשים כאן ב-cloud-sql-python-connector Python SDK כדי להפעיל חיבור מאובטח למופע של מסד הנתונים שלנו, שמאומת באמצעות Application Default Credentials.

הפעלת סקריפט ההגדרה

עכשיו אפשר להריץ את סקריפט ההגדרה ברקע ולבדוק את הפלט של המסוף שייכתב לקובץ logs/atabase_setup.log באמצעות הפקודה הבאה. אפשר להמשיך לקטע הבא בזמן שמחכים לסיום התהליך.

mkdir -p ~/build-agent-adk-toolbox-cloudsql/logs
bash scripts/setup_database.sh > logs/database_setup.log 2>&1 &

הורדת קובץ הבינארי של Toolbox

במדריך הזה נשתמש ב-MCP Toolbox. למרבה המזל, הוא מגיע עם קובץ בינארי מוכן מראש שאפשר להשתמש בו בסביבת Linux. עכשיו נוריד אותו ברקע, כי זה ייקח זמן. מריצים את הפקודה הבאה כדי להוריד את הקובץ הבינארי ולבדוק את יומן הפלט ב-logs/toolbox_dl.log . אפשר להמשיך לקטע הבא בזמן שמחכים לסיום התהליך.

cd ~/build-agent-adk-toolbox-cloudsql
curl -O https://storage.googleapis.com/mcp-toolbox-for-databases/v1.0.0/linux/amd64/toolbox > logs/toolbox_dl.log 2>&1 &

הסבר על סקריפט ההגדרה scripts/setup_database.sh

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

  1. הפקודה הראשונה שמריצים היא gcloud sql instances create עם הדגל הבא
  • db-custom-1-3840 היא הרמה הקטנה ביותר של Cloud SQL עם ליבה ייעודית (‎1 vCPU, ‏ ‎3.75 GB RAM) במהדורת ENTERPRISE. פרטים נוספים זמינים כאן. נדרשת ליבה ייעודית לשילוב של Vertex AI ML – שכבות של ליבת מעבד משותפת (db-f1-micro, db-g1-small) לא תומכות בה.
  • --root-password מגדיר את הסיסמה למשתמש ברירת המחדל postgres.
  • --enable-google-ml-integration מאפשרת את השילוב המובנה של Cloud SQL עם Vertex AI, שמאפשר לכם להפעיל מודלים של הטמעות ישירות מ-SQL באמצעות הפונקציה embedding().
  1. בודקים אם המופע כבר בסטטוס RUNNABLE
  2. נותנים לחשבון השירות של מכונת Cloud SQL הרשאה לקרוא ל-Vertex AI באמצעות הפקודה gcloud projects add-iam-policy-binding. הפעולה הזו נדרשת עבור הפונקציה המובנית embedding() שבה נשתמש כשנזרע את מסד הנתונים
  3. יצירת מסד הנתונים
  4. הרצת סקריפט ההזנה setup_jobs_db.py

הסבר על סקריפט הזרע scripts/setup_jobs_db.py

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

  1. אתחול החיבור למופע של מסד הנתונים
  2. התקנה של שני תוספים ל-PostgreSQL:
  • google_ml_integration — מספק את פונקציית ה-SQL‏ embedding(), שקוראת למודלים של הטמעה ב-Vertex AI ישירות מ-SQL. זהו תוסף ברמת מסד הנתונים שמאפשר להשתמש בפונקציות של למידת מכונה בתוך jobs_db. הדגל ברמת המופע (--enable-google-ml-integration) שאתם מגדירים במהלך יצירת המופע מאפשר למכונה הווירטואלית של Cloud SQL להגיע אל Vertex AI – התוסף מאפשר להשתמש בפונקציות ה-SQL במסד הנתונים הספציפי הזה.
  • vector (pgvector) – מוסיף את סוג הנתונים vector ואת אופרטורי המרחק לאחסון ולשאילתת הטמעות.
  1. צור את הטבלה, שימו לב שהעמודה description_embedding היא vector(3072) – עמודה מסוג pgvector שמאחסנת וקטורים תלת-ממדיים בגודל 3,072.
  2. הזנת נתונים ראשוניים של משרות
  3. יצירת נתוני ההטמעה מהשדה description ומילוי השדה description_embedding באמצעות השילוב המובנה של Vertex דרך הפונקציה embedding()
  • embedding('gemini-embedding-001', description) — קוראת למודל ההטמעה של Gemini ב-Vertex AI ישירות מ-SQL, ומעבירה את הטקסט של כל משרה description. זהו התוסף google_ml_integration שהתקנתם בסקריפט ה-seed.
  • ::vector — מבצע המרה של מערך המספרים הממשיים שמוחזר לסוג vector של pgvector, כדי שאפשר יהיה לאחסן אותו ולשאול עליו שאילתות באמצעות אופרטורים של מרחק.
  • הפונקציה UPDATE פועלת על כל 15 השורות, ומפיקה הטמעה אחת בת 3,072 ממדים לכל תיאור משרה.

הפעולה הזו תכין נתונים ראשוניים שהנציג שלנו יוכל לגשת אליהם

5. הגדרת MCP Toolbox for Databases

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

מה זה MCP ולמה כדאי להשתמש ב-Toolbox?

e7b9be2e1c98b4db.png

MCP (Model Context Protocol) הוא פרוטוקול פתוח שקובע תקן לאופן שבו סוכני AI מגלים כלים חיצוניים ופועלים איתם. הוא מגדיר מודל של לקוח-שרת: הסוכן מארח לקוח MCP, והכלים נחשפים על ידי שרתי MCP. כל לקוח שתואם ל-MCP יכול להשתמש בכל שרת שתואם ל-MCP – לא צריך קוד שילוב מותאם אישית לסוכן עבור כל כלי.

5bf26eeecad2277d.png

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

באמצעות ארגז הכלים, כותבים קובץ YAML. כל כלי ממופה להצהרת SQL עם פרמטרים. ערכת הכלים מטפלת בחיבורים, בשאילתות עם פרמטרים, באימות ובאפשרות הצפייה. הכלים מופרדים מהסוכן – אפשר לעדכן שאילתה על ידי עריכת tools.yaml והפעלה מחדש של Toolbox, בלי לגעת בקוד של הסוכן. אותם כלים פועלים ב-ADK, ב-LangGraph, ב-LlamaIndex או בכל framework שתואם ל-MCP.

כתיבת ההגדרה של כלי העזר

עכשיו צריך ליצור קובץ בשם tools.yaml ב-Cloud Shell Editor כדי להגדיר את כלי ההגדרה

cloudshell edit tools.yaml

הקובץ משתמש ב-YAML עם כמה מסמכים – כל בלוק שמופרד על ידי --- הוא משאב עצמאי. לכל משאב יש מאפיין kind שמצהיר מהו (sources לחיבורי מסד נתונים, tools לפעולות שאפשר להפעיל באמצעות סוכן) ומאפיין type שמציין את ה-Backend (cloud-sql-postgres למקור, postgres-sql לכלים מבוססי SQL). כלי מפנה למקור שלו באמצעות name, וכך ארגז הכלים יודע מול איזה מאגר חיבורים לבצע את הפעולה. משתני הסביבה משתמשים בתחביר ${VAR_NAME} ונפתרים בזמן האתחול.

עכשיו מעתיקים את הסקריפטים הבאים לקובץ tools.yaml

# tools.yaml

# --- Data Source ---
kind: source
name: jobs-db
type: cloud-sql-postgres
project: ${GOOGLE_CLOUD_PROJECT}
region: ${REGION}
instance: ${DB_INSTANCE}
database: ${DB_NAME}
user: postgres
password: ${DB_PASSWORD}

---

הסקריפט הזה מגדיר את המשאב הבא:

  • מקור (jobs-db) – מציין לארגז הכלים איך להתחבר למופע Cloud SQL PostgreSQL. הסוג cloud-sql-postgres משתמש באופן פנימי במחבר Cloud SQL, ומטפל באימות ובחיבורים מאובטחים באופן אוטומטי. ה-placeholders‏ ${GOOGLE_CLOUD_PROJECT} , ‏ ${REGION} ו-${DB_PASSWORD} נפתרים ממשתני סביבה בזמן ההפעלה.

בשלב הבא, מוסיפים את הסקריפט הבא מתחת לסמל --- בקובץ tools.yaml

# --- Tool 1: Search jobs by role and/or tech stack ---
kind: tool
name: search-jobs
type: postgres-sql
source: jobs-db
description: >-
  Search for job listings by role category and/or tech stack.
  Use this tool when the developer wants to browse listings
  by role (e.g., Backend, Frontend, Data) or find jobs
  using a specific technology. Both parameters accept an
  empty string to match all values.
statement: |
  SELECT title, company, role, tech_stack, salary_range, location, openings
  FROM jobs
  WHERE ($1 = '' OR LOWER(role) = LOWER($1))
  AND ($2 = '' OR LOWER(tech_stack) LIKE '%' || LOWER($2) || '%')
  ORDER BY title
  LIMIT 10
parameters:
  - name: role
    type: string
    description: "The role category to filter by (e.g., 'Backend', 'Frontend', 'Data/AI', 'DevOps'). Use empty string for all roles."
  - name: tech_stack
    type: string
    description: "A technology to search for in the tech stack (partial match, e.g., 'Python', 'Kubernetes'). Use empty string for all tech stacks."

---

# --- Tool 2: Get full details for a specific job ---
kind: tool
name: get-job-details
type: postgres-sql
source: jobs-db
description: >-
  Get full details for a specific job listing including its description,
  salary range, location, and number of openings. Use this tool when the
  developer asks about a particular job by title or company.
statement: |
  SELECT title, company, role, tech_stack, salary_range, location, openings, description
  FROM jobs
  WHERE LOWER(title) LIKE '%' || LOWER($1) || '%'
  OR LOWER(company) LIKE '%' || LOWER($1) || '%'
parameters:
  - name: search_term
    type: string
    description: "The job title or company name to look up (partial match supported)."

---

הסקריפט הזה מגדיר את המשאב הבא:

  • Tools 1 and 2 (search-jobs, get-job-details) – כלים לשאילתות SQL סטנדרטיות. כל אחד מהם ממפה שם של כלי (מה שהסוכן רואה) להצהרת SQL עם פרמטרים (מה שמסד הנתונים מבצע). בפרמטרים משתמשים בפלייסהולדרים של מיקום $1, $2. ערכת הכלים מריצה את הפקודות האלה כהצהרות מוכנות, וכך מונעת הזרקת SQL.

ממשיכים ומוסיפים את הסקריפט הבא מתחת לסמל --- בקובץ tools.yaml

# --- Embedding Model ---
kind: embeddingModel
name: gemini-embedding
type: gemini
model: gemini-embedding-001
project: ${GOOGLE_CLOUD_PROJECT}
location: ${GOOGLE_CLOUD_LOCATION}
dimension: 3072

---

הסקריפט הזה מגדיר את המשאב הבא:

  • מודל הטמעה (gemini-embedding) – מגדיר את כלי העזר להפעלת המודל gemini-embedding-001 של Gemini כדי ליצור הטמעות טקסט תלת-ממדיות. האימות ב-Toolbox מתבצע באמצעות Application Default Credentials ‏ (ADC) – לא נדרש מפתח API ב-Cloud Shell או ב-Cloud Run. הערות שdimension שהוגדר כאן חייב להיות זהה למה שהוגדר קודם כדי לאכלס את מסד הנתונים

ממשיכים ומוסיפים את הסקריפט הבא מתחת לסמל --- בקובץ tools.yaml

# --- Tool 3: Semantic search by description ---
kind: tool
name: search-jobs-by-description
type: postgres-sql
source: jobs-db
description: >-
  Find jobs that match a natural language description of what the developer
  is looking for. Use this tool when the developer describes their ideal job
  using interests, work style, career goals, or project type rather than a
  specific role or tech stack. Examples: "I want to work on AI chatbots,"
  "a remote job at a fintech startup," "something involving infrastructure
  and reliability."
statement: |
  SELECT title, company, role, tech_stack, salary_range, location, description
  FROM jobs
  WHERE description_embedding IS NOT NULL
  ORDER BY description_embedding <=> $1
  LIMIT 5
parameters:
  - name: search_query
    type: string
    description: "A natural language description of the kind of job the developer is looking for."
    embeddedBy: gemini-embedding

---

הסקריפט הזה מגדיר את המשאב הבא:

  • כלי 3 (search-jobs-by-description) – כלי לחיפוש וקטורי. הפרמטר search_query מכיל את הערך embeddedBy: gemini-embedding, שמורה ל-Toolbox ליירט את הטקסט הגולמי, לשלוח אותו למודל ההטמעה ולהשתמש בווקטור שמתקבל בהצהרת ה-SQL. האופרטור <=> הוא מרחק הקוסינוס של pgvector – ערכים קטנים יותר מציינים תיאורים דומים יותר.

בסוף, מוסיפים את הכלי האחרון מתחת לסמל --- בקובץ tools.yaml.

# --- Tool 4: Add a new job listing with automatic embedding ---
kind: tool
name: add-job
type: postgres-sql
source: jobs-db
description: >-
  Add a new job listing to the platform. Use this tool when a user asks
  to post a job that is not currently listed.
statement: |
  INSERT INTO jobs (title, company, role, tech_stack, salary_range, location, openings, description, description_embedding)
  VALUES ($1, $2, $3, $4, $5, $6, CAST($7 AS INTEGER), $8, $9)
  RETURNING title, company
parameters:
  - name: title
    type: string
    description: "The job title (e.g., 'Senior Backend Engineer')."
  - name: company
    type: string
    description: "The company name (e.g., 'Stripe', 'Spotify')."
  - name: role
    type: string
    description: "The role category (e.g., 'Backend', 'Frontend', 'Data/AI', 'DevOps')."
  - name: tech_stack
    type: string
    description: "Comma-separated list of technologies (e.g., 'Python, FastAPI, GCP')."
  - name: salary_range
    type: string
    description: "The salary range (e.g., '$150-200K/year')."
  - name: location
    type: string
    description: "Work location and arrangement (e.g., 'Remote')."
  - name: openings
    type: string
    description: "The number of open positions."
  - name: description
    type: string
    description: "A short description of the job (2-3 sentences)."
  - name: description_vector
    type: string
    description: "Auto-generated embedding vector for the job description."
    valueFromParam: description
    embeddedBy: gemini-embedding

הסקריפט הזה מגדיר את המשאב הבא:

  • כלי 4 (add-job) – הדגמה של הטמעה של וקטורים. לפרמטר description_vector יש שני שדות מיוחדים:
  • valueFromParam: description — הכלי מעתיק את הערך מהפרמטר description אל הפרמטר הזה. ה-LLM אף פעם לא רואה את הפרמטר הזה.
  • embeddedBy: gemini-embedding — Toolbox מטמיע את הטקסט שהועתק בווקטור לפני שהוא מעביר אותו ל-SQL.

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

בפורמט YAML של כמה מסמכים, כל משאב מופרד באמצעות ---. לכל מסמך יש שדות kind, name ו-type שמגדירים מהו המסמך. לסיכום, כבר הגדרנו את כל הדברים הבאים:

  • הגדרת מסד הנתונים של המקור
  • הגדרת כלים ( tool 1 ו-tool 2 ) לשליחת שאילתות למסד נתונים עם מסנן רגיל
  • הגדרת מודל להטמעה
  • הגדרת כלי לביצוע חיפוש וקטורי ( כלי 3 ) במסד הנתונים
  • הגדרת כלי להטמעת נתוני וקטור ( כלי 4 ) במסד נתונים

6. הפעלת השרת MCP Toolbox

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

אימות הנתונים הראשוניים

לפני שמתחילים להשתמש ב-Toolbox, צריך לוודא שהגדרת מסד הנתונים הושלמה. יוצרים סקריפט Python‏ scripts/verify_database.py באמצעות הפקודה הבאה

cloudshell edit scripts/verify_seed.py

לאחר מכן, מעתיקים את הקוד הבא לקובץ scripts/verify_seed.py

#!/usr/bin/env python3
"""Verify the database has 15 jobs with embeddings."""

import os
import sys
from pathlib import Path
from dotenv import load_dotenv
from google.cloud.sql.connector import Connector
import pg8000

# Load environment variables
env_path = Path(__file__).parent.parent / '.env'
load_dotenv(env_path)

# Verify required environment variables
required_vars = ['GOOGLE_CLOUD_PROJECT', 'REGION', 'DB_PASSWORD', 'DB_INSTANCE', 'DB_NAME']
missing_vars = [var for var in required_vars if not os.environ.get(var)]

if missing_vars:
    print(f"ERROR: Missing environment variables: {', '.join(missing_vars)}", file=sys.stderr)
    sys.exit(1)


def verify_database():
    """Check that 15 jobs exist with embeddings."""
    connector = Connector()

    try:
        project = os.environ['GOOGLE_CLOUD_PROJECT']
        region = os.environ['REGION']
        password = os.environ['DB_PASSWORD']
        instance = os.environ['DB_INSTANCE']
        database = os.environ['DB_NAME']

        conn = connector.connect(
            f"{project}:{region}:{instance}",
            "pg8000",
            user="postgres",
            password=password,
            db=database
        )
        cursor = conn.cursor()

        # Count jobs and embeddings
        cursor.execute("SELECT COUNT(*) FROM jobs")
        job_count = cursor.fetchone()[0]

        cursor.execute("SELECT COUNT(*) FROM jobs WHERE description_embedding IS NOT NULL")
        embedding_count = cursor.fetchone()[0]

        print(f"Jobs: {job_count}/15")
        print(f"Embeddings: {embedding_count}/15")

        cursor.close()
        conn.close()

        if job_count == 15 and embedding_count == 15:
            print("\n✓ Database ready!")
            return True
        else:
            print("\n✗ Database not ready")
            return False

    except Exception as e:
        print(f"\nERROR: {e}", file=sys.stderr)
        return False
    finally:
        connector.close()


if __name__ == "__main__":
    success = verify_database()
    sys.exit(0 if success else 1)

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

uv run scripts/verify_seed.py

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

Jobs: 15/15
Embeddings: 15/15

✓ Database ready!

הפעלת השרת של ארגז הכלים

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

cd ~/build-agent-adk-toolbox-cloudsql
if [ ! -f toolbox ]; then
  curl -O https://storage.googleapis.com/mcp-toolbox-for-databases/v1.0.0/linux/amd64/toolbox
fi
chmod +x toolbox

נצטרך לחשוף את המשתנים .env לתהליך המשני שמופעל על ידי ערכת הכלים MCP. מריצים את הפקודה הבאה כדי להפעיל את שרת ארגז הכלים ולרשום את הפלט של המסוף בקובץ logs/mcp_toolbox.log

set -a; source .env; set +a
./toolbox --config tools.yaml --enable-api > logs/mcp_toolbox.log 2>&1 &

אמור להופיע פלט בקובץ logs/mcp_toolbox.log שמאשר שהשרת מוכן, כמו שמוצג בהמשך:

... INFO "Initialized 1 sources: jobs-db"
... INFO "Initialized 0 authServices: "
... INFO "Using Vertex AI backend for Gemini embedding" 
... INFO "Initialized 1 embeddingModels: gemini-embedding" 
... INFO "Initialized 4 tools: add-job, search-jobs, get-job-details, search-jobs-by-description" 
...
... INFO "Server ready to serve!"

אימות הכלים

כדי לראות רשימה של כל הכלים הרשומים, שולחים שאילתה ל-Toolbox API:

curl -s http://localhost:5000/api/toolset | uv run -m json.tool

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

...
       "search-jobs-by-description": {
            "description": "Find jobs that match a natural language description of what the developer is looking for. Use this tool when the developer describes their ideal job using interests, work style, career goals, or project type rather than a specific role or tech stack. Examples: \"I want to work on AI chatbots,\" \"a remote job at a fintech startup,\" \"something involving infrastructure and reliability.\"",
            "parameters": [
                {
                    "name": "search_query",
                    "type": "string",
                    "required": true,
                    "description": "A natural language description of the kind of job the developer is looking for.",
                    "authSources": []
                }
            ],
            "authRequired": []
        }
...

בודקים את הכלי search-jobs ישירות:

curl -s -X POST http://localhost:5000/api/tool/search-jobs/invoke \
  -H "Content-Type: application/json" \
  -d '{"role": "Backend", "tech_stack": ""}' | jq '.result | fromjson'

התשובה צריכה לכלול את שתי משרות הנדסת ה-Backend מנתוני ה-Seed.

[
  {
    "title": "Backend Engineer (Payments)",
    "company": "Square",
    "role": "Backend",
    "tech_stack": "Java, Spring Boot, PostgreSQL, Kafka",
    "salary_range": "$160-220K/year",
    "location": "San Francisco, Hybrid",
    "openings": 3
  },
  {
    "title": "Senior Backend Engineer",
    "company": "Stripe",
    "role": "Backend",
    "tech_stack": "Go, PostgreSQL, gRPC, Kubernetes",
    "salary_range": "$180-250K/year",
    "location": "San Francisco, Hybrid",
    "openings": 3
  }
]

7. פיתוח סוכן ADK

עכשיו נשתמש ב-ADK ב-Python לפרויקט הזה. נוסיף את התלויות הנדרשות:

uv add google-adk==1.29.0 toolbox-adk==1.0.0
  • google-adk — הערכה לפיתוח סוכנים של Google, כולל Gemini SDK
  • toolbox-adk — שילוב ADK ב-MCP Toolbox for Databases.

יצירת מבנה ספריות של סוכנים

ערכת ה-ADK מצפה לפריסת תיקיות ספציפית: ספרייה שנקראת על שם הסוכן שלכם ומכילה את הקבצים __init__.py, agent.py ו-.env. כדי לעזור לכם בכך, יש פקודה מובנית ליצירת המבנה במהירות:

uv run adk create jobs_agent \
    --model gemini-2.5-flash \
    --project ${GOOGLE_CLOUD_PROJECT} \
    --region ${GOOGLE_CLOUD_LOCATION}

עכשיו הספריה אמורה להיראות כך:

build-agent-adk-toolbox-cloudsql/
├── jobs_agent/
│   ├── __init__.py
│   ├── agent.py
│   └── .env
├── logs
├── scripts
└── ...

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

הגדרת הסביבה של הסוכן

‫ADK קורא את GOOGLE_GENAI_USE_VERTEXAI,‏ GOOGLE_CLOUD_PROJECT ו-GOOGLE_CLOUD_LOCATION מסביבת המעטפת, שכבר הגדרתם בשלב הקודם. המשתנה היחיד שספציפי לנציג הוא TOOLBOX_URL – צריך לצרף אותו לקובץ .env של הנציג:

echo -e "\nTOOLBOX_URL=http://127.0.0.1:5000" >> jobs_agent/.env

עדכון מודול הסוכן

פתיחה של jobs_agent/agent.py ב-Cloud Shell Editor

cloudshell edit jobs_agent/agent.py

ומחליפים את התוכן בקוד הבא:

# jobs_agent/agent.py
import os

from google.adk.agents import LlmAgent
from toolbox_adk import ToolboxToolset

TOOLBOX_URL = os.environ.get("TOOLBOX_URL", "http://127.0.0.1:5000")

toolbox = ToolboxToolset(TOOLBOX_URL)

root_agent = LlmAgent(
    name="jobs_agent",
    model="gemini-2.5-flash",
    instruction="""You are a helpful assistant at "TechJobs," a tech job listing platform.

Your job:
- Help developers browse job listings by role or tech stack.
- Provide full details about specific positions, including salary range and number of openings.
- Recommend jobs based on natural language descriptions of what the developer is looking for.
- Add new job listings to the platform when asked.

When a developer asks about a specific job by title or company, use the get-job-details tool.
When a developer asks for a specific role category or tech stack, use the search-jobs tool.
When a developer describes what kind of job they want — by interest area, work style,
career goals, or project type — use the search-jobs-by-description tool for semantic search.
When in doubt between search-jobs and search-jobs-by-description, prefer
search-jobs-by-description — it searches job descriptions and finds more relevant matches.

If a position has no openings (openings is 0), let the developer know
and suggest similar alternatives from the search results.

Be conversational, knowledgeable, and concise.""",
    tools=[toolbox],
)

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

משתנה הסביבה TOOLBOX_URL מוגדר כברירת מחדל ל-http://127.0.0.1:5000 לפיתוח מקומי. כשפורסים ל-Cloud Run בהמשך, מחליפים את כתובת ה-URL הזו בכתובת ה-URL של שירות Toolbox ב-Cloud Run – אין צורך לשנות את הקוד.

בשלב הזה, ההוראה מתייחסת רק לשני הכלים הסטנדרטיים (search-jobs ו-get-job-details). בשלב הבא תרחיבו אותה כשתוסיפו את כלי החיפוש הסמנטי וכלי ההטמעה.

בדיקת הסוכן

מפעילים את ממשק המשתמש של ADK למפתחים:

cd ~/build-agent-adk-toolbox-cloudsql
uv run adk web --allow_origins "regex:https://.*\.cloudshell\.dev"

פותחים את כתובת ה-URL שמוצגת במסוף (בדרך כלל http://localhost:8000) באמצעות התכונה Web Preview של Cloud Shell או באמצעות Ctrl + click על כתובת ה-URL שמוצגת במסוף. בוחרים באפשרות jobs_agent מהתפריט הנפתח של הסוכן בפינה הימנית העליונה.

בדיקת שאילתות רגילות

כדי לאמת את הכלים של SQL סטנדרטי, אפשר לנסות את ההנחיות הבאות:

What backend engineering jobs do you have?
Any jobs using Kubernetes?
Tell me about the Cloud Architect position

93ac33e7f73aa0b9.png 240c53376042a916.png

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

I want a remote job where I can work on AI and machine learning
Find me something in fintech with good work-life balance
I'm interested in infrastructure and reliability engineering

הנציג ינסה לבחור את הכלי המתאים על סמך סוג השאילתה: מסננים מובנים עוברים דרך search-jobs, ותיאורים בשפה טבעית עוברים דרך search-jobs-by-description.

b0ea629f5c9b4c26.png

בדיקה של הטמעת וקטורים

מבקשים מהנציג להוסיף משרה חדשה:

Add a new job: 'Robotics Software Engineer' at Boston Dynamics, role Robotics, tech stack: Python, C++, ROS, Computer Vision, salary $160-230K/year, location Waltham MA, Hybrid, 2 openings. Description: Design and implement autonomous navigation and manipulation algorithms for next-generation robots. Work on perception pipelines using computer vision and lidar, develop motion planning software in C++ and Python, and test systems on real hardware in warehouse and logistics environments.

c601a7a9bc0a705b.png

עכשיו נסו לחפש אותו:

Find me jobs involving autonomous systems and working with physical hardware

ההטמעה נוצרה אוטומטית במהלך הפעולה INSERT – לא נדרש שלב נפרד.

5a3d8e6f523dc18b.png

עכשיו יש לכם אפליקציית Agentic RAG שעובדת באופן מלא ומבוססת על ADK,‏ MCP Toolbox ו-CloudSQL. מזל טוב! בשלב הבא נסביר איך פורסים את האפליקציות האלה ב-Cloud Run.

עכשיו, כדי להמשיך, צריך לעצור את ממשק המשתמש למפתחים על ידי סגירת התהליך. לשם כך, מקישים על Ctrl+C פעמיים.

8. פריסה ב-Cloud Run

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

הכנת ערכת הכלים לפריסה

יוצרים ספריית פריסה לשירות Toolbox:

cd ~/build-agent-adk-toolbox-cloudsql
mkdir -p deploy-toolbox
cp toolbox tools.yaml deploy-toolbox/

יוצרים את קובץ ה-Dockerfile עבור ערכת הכלים. פותחים את deploy-toolbox/Dockerfile ב-Cloud Shell Editor:

cloudshell edit deploy-toolbox/Dockerfile

מעתיקים אליו את הסקריפט הבא

# deploy-toolbox/Dockerfile
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY toolbox tools.yaml ./
RUN chmod +x toolbox
EXPOSE 8080
CMD ["./toolbox", "--config", "tools.yaml", "--enable-api", "--address", "0.0.0.0", "--port", "8080"]

קובץ ההפעלה של Toolbox ו-tools.yaml נארזים בתמונת Debian מינימלית. תעבורת הנתונים מנותבת ב-Cloud Run ליציאה 8080.

פריסת שירות ארגז הכלים

cd ~/build-agent-adk-toolbox-cloudsql
gcloud run deploy toolbox-service \
  --source deploy-toolbox/ \
  --region $REGION \
  --set-env-vars "DB_PASSWORD=$DB_PASSWORD,DB_INSTANCE=$DB_INSTANCE,DB_NAME=$DB_NAME,GOOGLE_CLOUD_PROJECT=$GOOGLE_CLOUD_PROJECT,REGION=$REGION,GOOGLE_CLOUD_LOCATION=$GOOGLE_CLOUD_LOCATION" \
  --allow-unauthenticated \
  --quiet > logs/deploy_toolbox.log 2>&1 &

הפקודה הזו שולחת את המקור ל-Cloud Build, יוצרת קובץ אימג' של קונטיינר, מעבירה אותו בדחיפה ל-Artifact Registry ופורסת אותו ב-Cloud Run. התהליך יימשך כמה דקות. אפשר לבדוק את יומן הפריסה בקובץ logs/deploy_toolbox.log.

הכנת הסוכן לפריסה

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

יוצרים Dockerfile בספריית הבסיס של הפרויקט. פותחים את Dockerfile ב-Cloud Shell Editor:

cloudshell edit Dockerfile

לאחר מכן, מעתיקים את התוכן הבא

# Dockerfile
FROM ghcr.io/astral-sh/uv:python3.12-trixie-slim
WORKDIR /app
COPY pyproject.toml ./
COPY uv.lock ./
RUN uv sync --no-dev
COPY jobs_agent/ jobs_agent/
EXPOSE 8080
CMD ["uv", "run", "adk", "web", "--host", "0.0.0.0", "--port", "8080"]

קובץ Dockerfile הזה משתמש ב-ghcr.io/astral-sh/uv כקובץ האימג' הבסיסי, שכולל את Python ואת uv שהותקנו מראש – אין צורך להתקין את uv בנפרד דרך pip.

יוצרים קובץ .dockerignore כדי להחריג קבצים מיותרים מקובץ אימג' של קונטיינר:

cloudshell edit .dockerignore

לאחר מכן מעתיקים לתוכו את הסקריפט הבא

# .dockerignore
.venv/
__pycache__/
*.pyc
.env
jobs_agent/.env
toolbox
tools.yaml
seed.sql
deploy-toolbox/

פריסת שירות הסוכן

ממתינים עד שהפריסה של Toolbox תסתיים. כדי לאמת את התהליך, בודקים שוב את תהליך הפריסה ב-logs/deploy_toolbox.log. לאחר מכן, מאחזרים את כתובת ה-URL של Cloud Run באמצעות הפקודה הבאה

TOOLBOX_URL=$(gcloud run services describe toolbox-service \
  --region=$REGION \
  --format='value(status.url)')
echo "Toolbox URL: $TOOLBOX_URL"

הפלט שיוצג יהיה דומה לזה:

Toolbox URL: https://toolbox-service-xxxxxx-xx.a.run.app

עכשיו נבדוק שה-Toolbox שפרסתם פועל:

curl -s "$TOOLBOX_URL/api/toolset" | python3 -m json.tool | head -5

אם הפלט נראה כמו בדוגמה הזו, הפריסה כבר הצליחה

{
    "serverVersion": "1.0.0+binary.linux.amd64.c5524d3",
    "tools": {
        "add-job": {
            "description": "Add a new job listing to the platform. Use this tool when a user asks to post a job that is not currently listed.",

בשלב הבא, פורסים את הסוכן ומעבירים את כתובת ה-URL של ערכת הכלים כמשתנה סביבה:

cd ~/build-agent-adk-toolbox-cloudsql
gcloud run deploy jobs-agent \
  --source . \
  --region $REGION \
  --set-env-vars "TOOLBOX_URL=$TOOLBOX_URL,GOOGLE_CLOUD_PROJECT=$GOOGLE_CLOUD_PROJECT,GOOGLE_CLOUD_LOCATION=$GOOGLE_CLOUD_LOCATION,GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
  --allow-unauthenticated \
  --quiet

קוד הסוכן קורא את TOOLBOX_URL מהסביבה (הגדרתם את זה קודם). באופן מקומי, הוא מצביע על http://127.0.0.1:5000. ב-Cloud Run הוא מצביע על כתובת ה-URL של שירות Toolbox. אין צורך לשנות את הקוד.

בדיקת הסוכן הפעיל

מאחזרים את כתובת ה-URL של הסוכן ב-Cloud Run:

AGENT_URL=$(gcloud run services describe jobs-agent \
  --region=$REGION \
  --format='value(status.url)')
echo "Agent URL: $AGENT_URL"

פותחים את כתובת ה-URL בדפדפן. ממשק המשתמש של ADK Dev נטען – אותו ממשק שבו השתמשתם באופן מקומי, ועכשיו הוא פועל ב-Cloud Run.

בוחרים באפשרות jobs_agent מהתפריט הנפתח ובודקים:

What backend engineering jobs do you have?
I want a remote job working on AI and machine learning

שתי השאילתות פועלות דרך השירותים שנפרסו: הסוכן ב-Cloud Run קורא ל-Toolbox ב-Cloud Run, ששולח שאילתה ל-Cloud SQL.

9. מזל טוב / ניקוי

בניתם ופרסתם עוזר אישי חכם ללוח משרות שמשתמש ב-MCP Toolbox for Databases כדי לגשר בין סוכן ADK לבין Cloud SQL PostgreSQL – עם שאילתות SQL סטנדרטיות וחיפוש סמנטי של וקטורים.

מה למדתם

  • איך MCP מתקנן את הגישה לכלי סוכני AI, ואיך MCP Toolbox for Databases מיישם את זה באופן ספציפי על פעולות במסד נתונים – החלפת קוד מסד נתונים מותאם אישית בהגדרת YAML הצהרתית
  • איך מגדירים את Cloud SQL PostgreSQL כמקור נתונים ב-Toolbox באמצעות cloud-sql-postgres סוג המקור
  • איך מגדירים כלי שאילתות SQL סטנדרטי עם הצהרות פרמטריות שמונעות הזרקת SQL
  • איך מפעילים חיפוש וקטורי באמצעות pgvector ו-gemini-embedding-001, עם הפרמטר embeddedBy להטמעה אוטומטית של שאילתות
  • איך valueFromParam מאפשרת הטמעה אוטומטית של וקטורים – מודל ה-LLM מספק תיאור טקסט, והכלי מעתיק, מטמיע ומאחסן את הווקטור לצד הטקסט בלי להציג הודעות
  • איך ToolboxToolset ב-ADK טוען כלים משרת Toolbox פעיל, כדי לשמור על קוד הסוכן מינימלי ולנתק לחלוטין את הלוגיקה של מסד הנתונים
  • איך פורסים את השרת MCP Toolbox ואת סוכן ה-ADK ב-Cloud Run כשירותים נפרדים

פינוי נפח

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

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

gcloud projects delete $GOOGLE_CLOUD_PROJECT

אפשרות 2: מחיקת משאבים ספציפיים

אם אתם רוצים לשמור את הפרויקט אבל להסיר רק את המשאבים שנוצרו ב-Codelab הזה:

gcloud run services delete jobs-agent --region=$REGION --quiet
gcloud run services delete toolbox-service --region=$REGION --quiet
gcloud sql instances delete jobs-instance --quiet
gcloud artifacts repositories delete cloud-run-source-deploy --location=$REGION --quiet 2>/dev/null