Baza danych jako narzędzie: RAG z agentem z ADK, MCP Toolbox i Cloud SQL

1. Wprowadzenie

Agenty AI są przydatne tylko w zakresie danych, do których mają dostęp. Większość danych w świecie rzeczywistym znajduje się w bazach danych, a połączenie agentów z bazami danych zwykle wymaga napisania w kodzie agenta kodu do zarządzania połączeniami, logiki zapytań i potoków osadzania. Każdy agent, który potrzebuje dostępu do bazy danych, powtarza tę pracę, a każda zmiana zapytania wymaga ponownego wdrożenia agenta.

W tym laboratorium znajdziesz inne podejście. Narzędzia bazy danych deklarujesz w pliku YAML – standardowa wersja SQL, wyszukiwanie podobieństwa wektorowego, a nawet automatyczne generowanie wektorów dystrybucyjnych. MCP Toolbox for Databases obsługuje wszystkie operacje na bazie danych jako serwer MCP. Kod agenta pozostaje minimalny: wczytaj narzędzia i pozwól Gemini zdecydować, które z nich wywołać.

Co utworzysz

Inteligentny asystent tablicy ogłoszeń dla „TechJobs” – agent ADK oparty na Gemini, który pomaga programistom przeglądać oferty pracy w branży technicznej za pomocą standardowych filtrów (stanowisko, stos technologiczny) i odkrywać oferty pracy na podstawie opisów w języku naturalnym, np. „Chcę pracować zdalnie nad chatbotami AI”. Agent odczytuje dane z bazy danych Cloud SQL PostgreSQL i zapisuje w niej dane w całości za pomocą MCP Toolbox for Databases, które obsługuje cały dostęp do bazy danych, w tym automatyczne generowanie wektorów dystrybucyjnych na potrzeby wyszukiwania wektorowego. Na koniec zarówno Toolbox, jak i agent będą działać w Cloud Run.

eb6de681c40990c1.jpeg

Czego się nauczysz

  • Jak MCP (Model Context Protocol) standaryzuje dostęp do narzędzi dla agentów AI i jak MCP Toolbox for Databases stosuje to do operacji na bazie danych
  • Konfigurowanie MCP Toolbox for Databases jako oprogramowania pośredniczącego między agentem ADK a Cloud SQL PostgreSQL
  • Zdefiniuj narzędzia bazy danych deklaratywnie w tools.yaml – w agencie nie ma kodu bazy danych.
  • Tworzenie agenta ADK, który wczytuje narzędzia z działającego serwera zestawu narzędzi za pomocą ToolboxToolset
  • Generowanie wektorów dystrybucyjnych za pomocą wbudowanej funkcji embedding() Cloud SQL i włączanie wyszukiwania semantycznego za pomocą funkcji pgvector
  • Użyj funkcji valueFromParam do automatycznego wczytywania wektorów podczas operacji zapisu.
  • Wdrażanie serwera Toolbox i agenta pakietu ADK w Cloud Run

Wymagania wstępne

  • Konto Google Cloud z próbnym kontem rozliczeniowym
  • podstawowa znajomość Pythona i SQL,
  • Przydatne będzie wcześniejsze doświadczenie z bazą danych w chmurze i pakietem ADK.

2. Konfigurowanie środowiska

Ten krok przygotowuje środowisko Cloud Shell, konfiguruje projekt Google Cloud i klonuje repozytorium referencyjne.

Otwieranie Cloud Shell

Otwórz Cloud Shell w przeglądarce. Cloud Shell zapewnia wstępnie skonfigurowane środowisko ze wszystkimi narzędziami potrzebnymi do tego ćwiczenia. Gdy pojawi się prośba o autoryzację, kliknij Autoryzuj.

Następnie kliknij „Widok” –> „Terminal”, aby otworzyć terminal.Interfejs powinien wyglądać podobnie do tego:

86307fac5da2f077.png

Będzie to nasz główny interfejs: IDE u góry, terminal u dołu.

Konfigurowanie katalogu roboczego

Utwórz katalog roboczy. Cały kod, który napiszesz w tym ćwiczeniu, będzie się znajdować tutaj:

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

Następnie przygotuj kilka katalogów do zarządzania takimi elementami jak skrypty początkowe i logi.

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

Konfigurowanie projektu Google Cloud

Utwórz plik .env ze zmiennymi lokalizacji:

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

Aby uprościć konfigurację projektu w terminalu, pobierz ten skrypt konfiguracji projektu do katalogu roboczego:

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

Uruchom skrypt. Weryfikuje ono Twoje próbne konto rozliczeniowe, tworzy nowy projekt (lub weryfikuje istniejący), zapisuje identyfikator projektu w pliku .env w bieżącym katalogu i ustawia aktywny projekt w gcloud.

bash setup_verify_trial_project.sh && source .env

Skrypt wykona te działania:

  1. Sprawdź, czy masz aktywne konto rozliczeniowe w wersji próbnej
  2. Sprawdź, czy w .env istnieje projekt (jeśli tak)
  3. Utwórz nowy projekt lub użyj istniejącego.
  4. Połącz próbne konto rozliczeniowe z projektem
  5. Zapisz identyfikator projektu w .env.
  6. Ustaw projekt jako aktywny projekt gcloud.

Sprawdź, czy projekt jest prawidłowo ustawiony, sprawdzając żółty tekst obok katalogu roboczego w wierszu poleceń terminala Cloud Shell. Powinien wyświetlać identyfikator projektu.

dcba35ce1389f313.png

Aktywowanie wymaganego interfejsu API

Następnie musimy włączyć kilka interfejsów API dla usługi, z którą będziemy wchodzić w interakcje:

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) – Twój agent korzysta z modeli Gemini, a Toolbox używa interfejsu Embedding API do wyszukiwania wektorowego.
  • Cloud SQL Admin API (sqladmin.googleapis.com) – możesz udostępniać instancję PostgreSQL i nią zarządzać.
  • Compute Engine API (compute.googleapis.com) – wymagany do tworzenia instancji Cloud SQL.
  • Cloud Run, Cloud Build, Artifact Registry – używane w kroku wdrażania w dalszej części tego laboratorium

3. Przygotowywanie skryptów do inicjowania bazy danych

Ten krok rozpoczyna tworzenie instancji Cloud SQL i uruchamia automatyczny skrypt konfiguracji, który czeka na gotowość instancji, a następnie tworzy bazę danych, wypełnia ją ofertami pracy i generuje osadzanie – wszystko w ramach jednej operacji.

Najpierw dodaj hasło do bazy danych do pliku .env i wczytaj go ponownie:

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

Tworzenie skryptu Bash do utworzenia instancji i bazy danych

Następnie utwórz skrypt scripts/setup_database.sh za pomocą tego polecenia:

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

Następnie skopiuj ten kod do pliku 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 ""

Tworzenie skryptu w Pythonie do wypełniania danych

Następnie utwórz plik Pythona ze skryptem początkowym scripts/setup_jobs_db.py za pomocą poniższego polecenia.

cloudshell edit scripts/setup_jobs_db.py

Następnie skopiuj ten kod do pliku 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()

Przejdźmy teraz do następnego kroku.

4. Tworzenie i inicjowanie bazy danych

Teraz nasze skrypty są gotowe do wykonania. Do wykonania przygotowanego skryptu będziemy potrzebować Pythona, więc najpierw przygotujmy go.

Konfigurowanie projektu w Pythonie

uv to szybki menedżer pakietów i projektów Pythona napisany w Rust ( dokumentacja uv). W tym samouczku używamy go ze względu na szybkość i prostotę utrzymywania projektu w Pythonie.

Zainicjuj projekt w Pythonie i dodaj wymagane zależności:

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

Pamiętaj, że do zainicjowania bezpiecznego połączenia z instancją bazy danych, która jest uwierzytelniana za pomocą domyślnego uwierzytelniania aplikacji, używamy tutaj cloud-sql-python-connectorpakietu SDK Pythona.

Uruchom skrypt konfiguracji

Teraz możemy uruchomić skrypt konfiguracji w tle i sprawdzić dane wyjściowe konsoli, które zostaną zapisane w pliku logs/atabase_setup.log za pomocą tego polecenia. Nie musisz czekać na zakończenie tego procesu. Możesz przejść do następnej sekcji.

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

Pobierz plik binarny Toolbox

W tym samouczku użyjemy zestawu narzędzi MCP. Na szczęście zawiera on gotowy plik binarny, który można wykorzystać w środowisku Linux. Pobierzmy go teraz w tle, ponieważ zajmie to trochę czasu. Aby pobrać plik binarny i sprawdzić dziennik danych wyjściowych na platformie logs/toolbox_dl.log, uruchom to polecenie: Nie musisz czekać na zakończenie tego procesu. Możesz przejść do następnej sekcji.

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 &

Informacje o skrypcie konfiguracji scripts/setup_database.sh

Teraz spróbujmy zrozumieć skrypt konfiguracji, który wcześniej skonfigurowaliśmy. Wykonuje te czynności:

  1. Pierwsze polecenie, które tam wykonujemy, to polecenie gcloud sql instances create z tą flagą:
  • db-custom-1-3840 to najmniejsza warstwa Cloud SQL z dedykowanym rdzeniem (1 procesor wirtualny, 3,75 GB pamięci RAM) w wersji ENTERPRISE. Więcej informacji znajdziesz tutaj. Integracja z Vertex AI ML wymaga dedykowanego rdzenia. Nie jest obsługiwana w przypadku warstw ze współużytkowanym rdzeniem (db-f1-micro, db-g1-small).
  • --root-password ustawia hasło domyślnego użytkownika postgres.
  • --enable-google-ml-integration włącza wbudowaną integrację Cloud SQL z Vertex AI, która umożliwia wywoływanie modeli osadzania bezpośrednio z SQL za pomocą funkcji embedding().
  1. Sprawdź, czy instancja ma już stan RUNNABLE.
  2. Przyznaj kontu usługi instancji Cloud SQL uprawnienia do wywoływania Vertex AI za pomocą polecenia gcloud projects add-iam-policy-binding. Jest to wymagane w przypadku wbudowanej funkcji embedding(), której użyjemy podczas wypełniania bazy danych.
  3. Tworzenie bazy danych
  4. Uruchamianie skryptu początkowego setup_jobs_db.py script

Informacje o skrypcie początkowym scripts/setup_jobs_db.py

Skrypt inicjujący wykonuje te czynności:

  1. Inicjowanie połączenia z instancją bazy danych
  2. Instaluje 2 rozszerzenia PostgreSQL:
  • google_ml_integration – udostępnia funkcję SQL embedding(), która wywołuje modele wektorów dystrybucyjnych Vertex AI bezpośrednio z SQL. Jest to rozszerzenie na poziomie bazy danych, które udostępnia funkcje ML w jobs_db. Flaga na poziomie instancji (--enable-google-ml-integration) ustawiona podczas tworzenia instancji umożliwia maszynie wirtualnej Cloud SQL dostęp do Vertex AI – rozszerzenie udostępnia funkcje SQL w tej konkretnej bazie danych.
  • vector (pgvector) – dodaje typ danych vector i operatory odległości do przechowywania i wysyłania zapytań dotyczących osadzania.
  1. Utwórz tabelę. Zwróć uwagę, że kolumna description_embedding to vector(3072) – kolumna pgvector, która przechowuje wektory 3072-wymiarowe.
  2. Wypełnianie początkowych danych zadań
  3. Wygeneruj dane osadzania z pola description i wypełnij pole description_embedding za pomocą wbudowanej integracji Vertex za pomocą funkcji embedding().
  • embedding('gemini-embedding-001', description) – wywołuje model wektorów dystrybucyjnych Gemini w Vertex AI bezpośrednio z SQL, przekazując tekst description każdego zadania. Jest to rozszerzenie google_ml_integration zainstalowane w skrypcie początkowym.
  • ::vector – rzutuje zwróconą tablicę liczb zmiennoprzecinkowych na typ vector pgvector, aby można było ją przechowywać i wykonywać na niej zapytania za pomocą operatorów odległości.
  • Model UPDATE działa na wszystkich 15 wierszach, generując 1 osadzenie o 3072 wymiarach na opis stanowiska.

Przygotuje to wstępne dane, do których dostęp będzie miał nasz pracownik obsługi klienta.

5. Konfigurowanie zestawu narzędzi MCP dla baz danych

W tym kroku przedstawiamy MCP Toolbox for Databases, konfigurujemy go tak, aby łączył się z instancją Cloud SQL, i definiujemy 2 standardowe narzędzia do wykonywania zapytań SQL.

Czym jest MCP i dlaczego warto korzystać z Toolbox?

e7b9be2e1c98b4db.png

MCP (Model Context Protocol) to otwarty protokół, który standaryzuje sposób, w jaki agenci AI wykrywają narzędzia zewnętrzne i wchodzą z nimi w interakcje. Definiuje model klient-serwer: agent hostuje klienta MCP, a narzędzia są udostępniane przez serwery MCP. Każdy klient zgodny z MCP może używać dowolnego serwera zgodnego z MCP – agent nie potrzebuje niestandardowego kodu integracji dla każdego narzędzia.

5bf26eeecad2277d.png

MCP Toolbox for Databases to serwer MCP typu open source stworzony specjalnie z myślą o dostępie do baz danych. Bez niego musisz pisać funkcje Pythona, które otwierają połączenia z bazą danych, zarządzają pulami połączeń, tworzą zapytania parametryzowane, aby zapobiegać wstrzyknięciu kodu SQL, obsługują błędy i osadzają cały ten kod w agencie. Każdy agent, który potrzebuje dostępu do bazy danych, powtarza tę pracę. Zmiana zapytania oznacza ponowne wdrożenie agenta.

W Toolboxie piszesz plik YAML. Każde narzędzie jest mapowane na sparametryzowaną instrukcję SQL. Zestaw narzędzi obsługuje pulę połączeń, zapytania parametryzowane, uwierzytelnianie i obserwację. Narzędzia są odłączone od agenta – możesz zaktualizować zapytanie, edytując tools.yaml i ponownie uruchamiając Toolbox, bez ingerowania w kod agenta. Te same narzędzia działają w przypadku ADK, LangGraph, LlamaIndex i każdej platformy zgodnej z MCP.

Zapisz konfigurację narzędzi

Teraz musimy utworzyć plik o nazwie tools.yaml w edytorze Cloud Shell, aby skonfigurować narzędzia.

cloudshell edit tools.yaml

Plik używa formatu YAML z wieloma dokumentami – każdy blok oddzielony znakiem --- jest samodzielnym zasobem. Każde źródło ma kind, które określa, czym jest (sources w przypadku połączeń z bazą danych, tools w przypadku działań wywoływanych przez agenta), oraz type, które określa backend (cloud-sql-postgres w przypadku źródła, postgres-sql w przypadku narzędzi opartych na SQL). Narzędzie odwołuje się do swojego źródła za pomocą znaku name, dzięki czemu Zestaw narzędzi wie, z której puli połączeń ma korzystać. Zmienne środowiskowe używają składni ${VAR_NAME} i są rozwiązywane podczas uruchamiania.

Teraz skopiuj te skrypty do pliku 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}

---

Ten skrypt definiuje ten zasób:

  • Źródło (jobs-db) – informuje Toolbox, jak połączyć się z instancją Cloud SQL PostgreSQL. Typ cloud-sql-postgres wewnętrznie korzysta z oprogramowania sprzęgającego Cloud SQL, które automatycznie obsługuje uwierzytelnianie i bezpieczne połączenia. Obiekty zastępcze ${GOOGLE_CLOUD_PROJECT} , ${REGION}${DB_PASSWORD} są tłumaczone ze zmiennych środowiskowych podczas uruchamiania.

Następnie dodaj ten skrypt pod symbolem --- w pliku 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)."

---

Ten skrypt definiuje ten zasób:

  • Narzędzia 1 i 2 (search-jobs, get-job-details) – standardowe narzędzia do zapytań SQL. Każda z nich mapuje nazwę narzędzia (widoczną dla agenta) na sparametryzowaną instrukcję SQL (wykonywaną przez bazę danych). Parametry używają obiektów zastępczych $1$2. Narzędzia wykonują je jako przygotowane instrukcje, co zapobiega wstrzyknięciu kodu SQL.

Kontynuujmy. Dodaj ten skrypt pod symbolem --- w pliku 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

---

Ten skrypt definiuje ten zasób:

  • Model wektorów dystrybucyjnych (gemini-embedding) – konfiguruje Toolbox tak, aby wywoływał model gemini-embedding-001 Gemini do generowania 3072-wymiarowych wektorów dystrybucyjnych tekstu. Toolbox używa do uwierzytelniania domyślnego uwierzytelniania aplikacji (ADC) – w Cloud Shell ani Cloud Run nie jest potrzebny klucz interfejsu API. Zwróć uwagę, że dimension skonfigurowane tutaj musi być takie samo jak wcześniej skonfigurowane do wypełnienia bazy danych.

Kontynuujmy. Dodaj ten skrypt pod symbolem --- w pliku 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

---

Ten skrypt definiuje ten zasób:

  • Narzędzie 3 (search-jobs-by-description) – narzędzie do wyszukiwania wektorowego. Parametr search_query ma wartość embeddedBy: gemini-embedding, która informuje Zestaw narzędzi, że ma przechwycić tekst w formie surowej, wysłać go do modelu osadzania i użyć powstałego wektora w instrukcji SQL. Operator <=> to odległość kosinusowa pgvector – mniejsze wartości oznaczają bardziej podobne opisy.

Na koniec dodaj ostatnie narzędzie pod symbolem --- w pliku 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

Ten skrypt definiuje ten zasób:

  • Narzędzie 4 (add-job) – pokazuje wczytywanie wektorów. Parametr description_vector ma 2 pola specjalne:
  • valueFromParam: description – Toolbox kopiuje wartość z parametru description do tego parametru. LLM nigdy nie widzi tego parametru.
  • embeddedBy: gemini-embedding – Toolbox osadza skopiowany tekst w wektorze przed przekazaniem go do SQL.

W rezultacie jedno wywołanie narzędzia przechowuje zarówno tekst opisu w formie surowej, jak i jego wektor dystrybucyjny, a agent nie ma żadnych informacji o wektorach dystrybucyjnych.

Format YAML z wieloma dokumentami rozdziela poszczególne zasoby znakiem ---. Każdy dokument ma pola kind, nametype, które określają, czym jest. Podsumowując, skonfigurowaliśmy już wszystkie te elementy:

  • Określ źródłową bazę danych
  • Zdefiniuj narzędzia ( narzędzie 1 i 2), aby wykonywać zapytania w bazie danych za pomocą standardowego filtra.
  • Definiowanie modelu wektora dystrybucyjnego
  • Zdefiniuj narzędzie do wyszukiwania wektorowego ( narzędzie 3) w bazie danych.
  • Zdefiniuj narzędzie do pozyskiwania danych wektorowych ( narzędzie 4) do bazy danych.

6. Uruchamianie serwera MCP Toolbox

W poprzednim kroku skonfigurowaliśmy już niezbędne ustawienia dla narzędzi MCP. Teraz możemy uruchomić serwer.

Sprawdzanie danych początkowych

Zanim uruchomisz Toolbox, sprawdź, czy konfiguracja bazy danych została zakończona. Utwórz skrypt w Pythonie scripts/verify_database.py za pomocą tego polecenia:

cloudshell edit scripts/verify_seed.py

Następnie skopiuj ten kod do pliku 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)

Ten skrypt sprawdzi liczbę danych dotyczących ofert pracy i ich osadzanie. Uruchom skrypt za pomocą tego polecenia:

uv run scripts/verify_seed.py

Jeśli zobaczysz w terminalu te dane wyjściowe, oznacza to, że dane są gotowe:

Jobs: 15/15
Embeddings: 15/15

✓ Database ready!

Uruchamianie serwera Zestawu narzędzi

W poprzednim kroku konfiguracji pobraliśmy już plik wykonywalny toolbox. Sprawdź, czy ten plik binarny istnieje i czy został pobrany. Jeśli nie, pobierz go i poczekaj na zakończenie.

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

Musimy udostępnić nasze zmienne .env procesowi podrzędnemu, który jest uruchamiany przez narzędzia MCP. Uruchom to polecenie, aby uruchomić serwer zestawu narzędzi i zapisać dane wyjściowe konsoli w pliku logs/mcp_toolbox.log.

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

W pliku logs/mcp_toolbox.log powinny pojawić się dane wyjściowe potwierdzające, że serwer jest gotowy, jak pokazano poniżej:

... 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!"

Weryfikowanie narzędzi

Wyślij zapytanie do interfejsu Toolbox API, aby wyświetlić listę wszystkich zarejestrowanych narzędzi:

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

Powinny się wyświetlić narzędzia wraz z opisami i parametrami. Jak pokazano poniżej

...
       "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": []
        }
...

Przetestuj narzędzie search-jobs bezpośrednio:

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'

Odpowiedź powinna zawierać 2 oferty pracy na stanowisko inżyniera backendu z danych początkowych.

[
  {
    "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. Tworzenie agenta ADK

Teraz użyjemy w tym projekcie ADK w Pythonie. Dodajmy wymagane zależności:

uv add google-adk==1.29.0 toolbox-adk==1.0.0
  • google-adk – pakiet Google Agent Development Kit, w tym pakiet Gemini SDK.
  • toolbox-adk – integracja ADK z zestawem narzędzi MCP dla baz danych.

Tworzenie struktury katalogów agenta

ADK oczekuje określonego układu folderów: katalogu o nazwie agenta zawierającego pliki __init__.py, agent.py.env. Aby Ci w tym pomóc, ma wbudowane polecenie, które pozwala szybko utworzyć strukturę:

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

Twój katalog powinien teraz wyglądać tak:

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

Następnie musimy zintegrować agenta ADK z działającym serwerem Toolbox i przetestować wszystkie 4 narzędzia: standardowe zapytania, wyszukiwanie semantyczne i przetwarzanie wektorowe. Kod agenta jest minimalny: cała logika bazy danych znajduje się w tools.yaml.

Konfigurowanie środowiska agenta

Pakiet ADK odczytuje wartości GOOGLE_GENAI_USE_VERTEXAI, GOOGLE_CLOUD_PROJECTGOOGLE_CLOUD_LOCATION ze środowiska powłoki, które zostały już ustawione w poprzednim kroku. Jedyną zmienną specyficzną dla agenta jest TOOLBOX_URL – dodaj ją do pliku .env agenta:

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

Aktualizowanie modułu agenta

Otwórz plik jobs_agent/agent.py w edytorze Cloud Shell.

cloudshell edit jobs_agent/agent.py

i zastąp zawartość tym kodem:

# 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],
)

Zwróć uwagę, że nie ma tu kodu bazy danych – ToolboxToolset łączy się z serwerem Toolbox przy uruchamianiu i wczytuje wszystkie dostępne narzędzia. Agent wywołuje narzędzia po nazwie, a Toolbox tłumaczy te wywołania na zapytania SQL dotyczące Cloud SQL.

W przypadku lokalnego środowiska programistycznego zmienna środowiskowa TOOLBOX_URL ma domyślnie wartość http://127.0.0.1:5000. Gdy później wdrożysz usługę w Cloud Run, zastąpisz ten adres URL adresem URL Cloud Run usługi Toolbox – nie musisz wprowadzać żadnych zmian w kodzie.

Instrukcja odnosi się obecnie tylko do 2 standardowych narzędzi (search-jobsget-job-details). Rozszerzysz ją w następnym kroku, gdy dodasz narzędzia do wyszukiwania semantycznego i przetwarzania danych.

Testowanie agenta

Uruchom interfejs programisty ADK:

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

Otwórz adres URL wyświetlany w terminalu (zwykle http://localhost:8000) za pomocą funkcji Podgląd w przeglądarce w Cloud Shell lub kliknij adres URL wyświetlany w terminalu, przytrzymując Ctrl. W menu agentów w lewym górnym rogu kliknij jobs_agent.

Testowanie standardowych zapytań

Aby sprawdzić narzędzia standardowej wersji SQL-a, wypróbuj te prompty:

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

93ac33e7f73aa0b9.png 240c53376042a916.png

Wypróbuj opisy w języku naturalnym, które nie są powiązane z określoną rolą ani zestawem technologii:

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

W zależności od typu zapytania agent spróbuje wybrać odpowiednie narzędzie: zapytania z filtrami strukturalnymi są przetwarzane przez search-jobs, a opisy w języku naturalnym przez search-jobs-by-description.

b0ea629f5c9b4c26.png

Testowanie przetwarzania wektorów

Poproś agenta o dodanie nowego zadania:

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

Teraz spróbuj wyszukać:

Find me jobs involving autonomous systems and working with physical hardware

Osadzenie zostało wygenerowane automatycznie podczas operacji INSERT – nie jest potrzebny żaden dodatkowy krok.

5a3d8e6f523dc18b.png

Masz już w pełni działającą aplikację Agentic RAG, która korzysta z ADK, MCP Toolbox i Cloud SQL. Gratulacje! Przejdźmy do wdrażania tych aplikacji w Cloud Run.

Teraz zatrzymajmy interfejs programisty, kończąc proces przez dwukrotne naciśnięcie Ctrl+C.

8. Wdrożenie w Cloud Run

Agent i Toolbox działają lokalnie. W tym kroku obie usługi są wdrażane jako usługi Cloud Run, dzięki czemu są dostępne w internecie. Usługa Toolbox działa jako serwer MCP w Cloud Run, a usługa agenta łączy się z nią.

Przygotowywanie zestawu narzędzi do wdrożenia

Utwórz katalog wdrożenia usługi Toolbox:

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

Utwórz plik Dockerfile dla zestawu narzędzi. Otwórz plik deploy-toolbox/Dockerfile w edytorze Cloud Shell:

cloudshell edit deploy-toolbox/Dockerfile

i skopiuj do niego ten skrypt:

# 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"]

Plik binarny Narzędzia i tools.yaml są spakowane w minimalnym obrazie Debiana. Cloud Run kieruje ruch do portu 8080.

Wdrażanie usługi Zestaw narzędzi

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 &

To polecenie przesyła źródło do Cloud Build, tworzy obraz kontenera, wypycha go do Artifact Registry i wdraża w Cloud Run. Zajmie to kilka minut. Możemy sprawdzić dziennik procesu wdrażania w pliku logs/deploy_toolbox.log.

Przygotowywanie agenta do wdrożenia

Podczas tworzenia pakietu narzędzi skonfiguruj pliki wdrożenia agenta.

Utwórz plik Dockerfile w katalogu głównym projektu. Otwórz plik Dockerfile w edytorze Cloud Shell:

cloudshell edit Dockerfile

Następnie skopiuj tę treść:

# 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"]

Ten plik Dockerfile używa obrazu bazowego ghcr.io/astral-sh/uv, który zawiera wstępnie zainstalowane Pythona i uv. Nie musisz więc instalować uv osobno za pomocą pip.

Utwórz plik .dockerignore, aby wykluczyć niepotrzebne pliki z obrazu kontenera:

cloudshell edit .dockerignore

Następnie skopiuj do niego ten skrypt:

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

Wdróż usługę agenta

Poczekaj na zakończenie wdrażania Toolboxa. Aby sprawdzić proces wdrażania, ponownie otwórz stronę logs/deploy_toolbox.log. Następnie pobierz adres URL Cloud Run za pomocą tego polecenia:

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

Zobaczysz dane wyjściowe podobne do tych:

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

Następnie sprawdźmy, czy wdrożony zestaw narzędzi działa:

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

Jeśli wynik jest podobny do tego przykładu, wdrożenie zostało już zakończone.

{
    "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.",

Następnie wdróżmy agenta, przekazując adres URL Toolbox jako zmienną środowiskową:

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

Kod agenta odczytuje wartość TOOLBOX_URL ze środowiska (zostało to wcześniej skonfigurowane). Lokalnie wskazuje http://127.0.0.1:5000, a w Cloud Run wskazuje adres URL usługi Toolbox. Nie wymaga to żadnych zmian w kodzie.

Testowanie wdrożonego agenta

Pobierz adres URL Cloud Run agenta:

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

Otwórz adres URL w przeglądarce. Załaduje się interfejs programistyczny ADK – ten sam interfejs, którego używasz lokalnie, ale teraz działający w Cloud Run.

W menu wybierz jobs_agent i przetestuj:

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

Oba zapytania działają za pomocą wdrożonych usług: agent w Cloud Run wywołuje Toolbox w Cloud Run, który wysyła zapytanie do Cloud SQL.

9. Gratulacje / Czyszczenie

Utworzono i wdrożono inteligentnego asystenta tablicy ofert pracy, który korzysta z MCP Toolbox for Databases do łączenia agenta ADK z Cloud SQL PostgreSQL – zarówno w przypadku standardowych zapytań SQL, jak i semantycznego wyszukiwania wektorowego.

Czego się nauczysz

  • Jak MCP standaryzuje dostęp do narzędzi dla agentów AI i jak MCP Toolbox for Databases stosuje to w przypadku operacji na bazach danych – zastępowanie niestandardowego kodu bazy danych deklaratywną konfiguracją YAML.
  • Jak skonfigurować Cloud SQL PostgreSQL jako źródło danych w Toolbox przy użyciu typu źródła cloud-sql-postgres
  • Jak zdefiniować standardowe narzędzia do zapytań SQL z instrukcjami sparametryzowanymi, które zapobiegają wstrzykiwaniu kodu SQL
  • Jak włączyć wyszukiwanie wektorowe za pomocą pgvector i gemini-embedding-001, używając parametru embeddedBy do automatycznego osadzania zapytań
  • Jak valueFromParam umożliwia automatyczne wczytywanie wektorów – model LLM podaje opis tekstowy, a Toolbox w tle kopiuje, osadza i przechowuje wektor obok tekstu.
  • Jak ToolboxToolset ADK wczytuje narzędzia z działającego serwera Toolbox, dzięki czemu kod agenta jest minimalny, a logika bazy danych jest w pełni odseparowana
  • Jak wdrożyć serwer MCP Toolbox i agenta ADK w Cloud Run jako oddzielne usługi

Czyszczenie danych

Aby uniknąć obciążenia konta Google Cloud opłatami za zasoby utworzone w tym ćwiczeniu, możesz usunąć poszczególne zasoby lub cały projekt.

Najprostszym sposobem na zwolnienie miejsca jest usunięcie projektu. Spowoduje to usunięcie wszystkich zasobów powiązanych z projektem.

gcloud projects delete $GOOGLE_CLOUD_PROJECT

Opcja 2. Usuwanie poszczególnych zasobów

Jeśli chcesz zachować projekt, ale usunąć tylko zasoby utworzone w tym laboratorium:

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