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

1. Wprowadzenie

Agenty AI są tak przydatne, jak dane, do których mają dostęp. Większość danych w świecie rzeczywistym znajduje się w bazach danych, a łączenie agentów z bazami danych zwykle oznacza pisanie kodu zarządzania połączeniami, logiki zapytań i potoków osadzania w kodzie agenta. 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

Konsjerż restauracyjny w sekcji „Odkrycia kulinarne” – agent ADK oparty na Gemini, który pomaga gościom przeglądać menu restauracji za pomocą standardowych filtrów (kategoria, rodzaj kuchni) i odkrywać dania na podstawie opisów w języku naturalnym, np. „Chcę coś pikantnego i wegetariańskiego”. 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óry 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.

53a7afb736a8fc8c.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żywanie 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ść języków Python i SQL;
  • Przydatne będzie wcześniejsze doświadczenie z bazą danych w chmurze i zestawem 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:

  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ć.
  • Interfejs 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 samouczka

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=restaurant-pwd" >> .env
echo "DB_INSTANCE=restaurant-instance" >> .env
echo "DB_NAME=restaurant_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_restaurant_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_restaurant_db.py za pomocą poniższego polecenia.

cloudshell edit scripts/setup_restaurant_db.py

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

# Menu items data
MENU_ITEMS = [
    ("Truffle Mushroom Risotto", "Italian", "Main Course",
     "Arborio rice, truffle oil, porcini mushrooms, parmesan, white wine",
     "$28", "Vegetarian, Gluten-Free", True,
     "A creamy, luxurious risotto made with arborio rice slow-cooked in white wine and mushroom broth, finished with shaved black truffle and aged parmesan. The porcini mushrooms add a deep, earthy flavor that pairs beautifully with the delicate truffle oil drizzled on top."),
    ("Spicy Tuna Tartare", "Japanese", "Appetizer",
     "Ahi tuna, sriracha, sesame oil, avocado, crispy wonton",
     "$22", "Gluten-Free, Dairy-Free", True,
     "Fresh ahi tuna diced and tossed with sriracha aioli, toasted sesame oil, and lime juice, served atop creamy avocado slices with crispy wonton chips. A perfect balance of heat, richness, and crunch inspired by modern Japanese fusion cuisine."),
    ("Lamb Kofta Kebab", "Middle Eastern", "Main Course",
     "Ground lamb, cumin, coriander, yogurt sauce, flatbread",
     "$24", "Halal", True,
     "Hand-formed spiced lamb kebabs grilled over charcoal, seasoned with cumin, coriander, and sumac. Served with warm flatbread, tangy yogurt-cucumber sauce, and a fresh herb salad. A classic Middle Eastern street food elevated with premium ingredients."),
    ("Pad Thai", "Thai", "Main Course",
     "Rice noodles, shrimp, tamarind, peanuts, bean sprouts, lime",
     "$19", "Gluten-Free, Dairy-Free", True,
     "Stir-fried rice noodles with tiger shrimp, scrambled egg, and a sweet-sour tamarind sauce, topped with crushed peanuts, fresh bean sprouts, and a squeeze of lime. This classic Thai street food dish balances sweet, sour, salty, and umami in every bite."),
    ("Margherita Pizza", "Italian", "Main Course",
     "San Marzano tomatoes, fresh mozzarella, basil, olive oil",
     "$18", "Vegetarian", True,
     "A Neapolitan-style pizza with a thin, charred crust topped with crushed San Marzano tomatoes, creamy buffalo mozzarella, fresh basil leaves, and a drizzle of extra virgin olive oil. Simple, classic, and made with imported Italian ingredients."),
    ("Miso Glazed Black Cod", "Japanese", "Main Course",
     "Black cod, white miso, mirin, sake, pickled ginger",
     "$36", "Gluten-Free, Dairy-Free", True,
     "Buttery black cod marinated for 72 hours in a sweet white miso glaze with mirin and sake, then broiled until caramelized. Served with pickled ginger and steamed bok choy. A signature dish inspired by Nobu's iconic preparation."),
    ("Caesar Salad", "American", "Appetizer",
     "Romaine lettuce, parmesan, croutons, anchovy dressing",
     "$14", "Contains Gluten", True,
     "Crisp romaine hearts tossed with a house-made anchovy-garlic dressing, shaved parmesan, and golden sourdough croutons. A timeless salad that serves as the perfect light starter or side dish with grilled proteins."),
    ("Chicken Tikka Masala", "Indian", "Main Course",
     "Chicken thigh, tomato cream sauce, garam masala, basmati rice",
     "$21", "Gluten-Free", True,
     "Tender chunks of tandoori-marinated chicken simmered in a rich, creamy tomato sauce spiced with garam masala, cumin, and fenugreek. Served over fragrant basmati rice with warm garlic naan on the side."),
    ("Chocolate Lava Cake", "French", "Dessert",
     "Dark chocolate, butter, eggs, vanilla, powdered sugar",
     "$15", "Vegetarian", True,
     "A warm, individual-sized chocolate cake with a molten dark chocolate center that flows when you break through the delicate outer shell. Made with 70% Belgian dark chocolate and served with a scoop of vanilla bean ice cream."),
    ("Pho Bo", "Vietnamese", "Main Course",
     "Rice noodles, beef brisket, star anise, cinnamon, bean sprouts, Thai basil",
     "$17", "Gluten-Free, Dairy-Free", True,
     "A deeply aromatic beef broth simmered for 12 hours with star anise, cinnamon, and charred ginger, ladled over rice noodles and thinly sliced beef brisket. Served with fresh Thai basil, bean sprouts, jalapeño, and lime for the table to customize."),
    ("Lobster Bisque", "French", "Appetizer",
     "Lobster, heavy cream, cognac, tarragon, cayenne",
     "$19", "Gluten-Free", True,
     "A velvety smooth soup made from roasted lobster shells, finished with heavy cream, a splash of cognac, and fresh tarragon. Each bowl is garnished with tender lobster meat and a pinch of cayenne for subtle warmth."),
    ("Falafel Plate", "Middle Eastern", "Main Course",
     "Chickpeas, herbs, tahini, pickled vegetables, hummus",
     "$16", "Vegan, Gluten-Free", True,
     "Crispy-on-the-outside, fluffy-on-the-inside chickpea fritters seasoned with fresh parsley, cilantro, and cumin. Served with creamy tahini sauce, house-made hummus, pickled turnips, and warm pita bread."),
    ("Crème Brûlée", "French", "Dessert",
     "Heavy cream, vanilla bean, egg yolks, caramelized sugar",
     "$13", "Vegetarian, Gluten-Free", True,
     "A classic French custard made with Madagascar vanilla bean and farm-fresh egg yolks, topped with a perfectly torched layer of caramelized sugar that cracks with a satisfying snap. Rich, creamy, and elegantly simple."),
    ("Korean BBQ Short Ribs", "Korean", "Main Course",
     "Beef short ribs, soy sauce, sesame, garlic, pear marinade",
     "$32", "Dairy-Free", False,
     "Premium beef short ribs marinated overnight in a sweet and savory blend of soy sauce, Asian pear, garlic, and toasted sesame. Grilled tableside over charcoal and served with lettuce wraps, pickled daikon, and gochujang dipping sauce."),
    ("Tiramisu", "Italian", "Dessert",
     "Mascarpone, espresso, ladyfingers, cocoa, Marsala wine",
     "$14", "Vegetarian, Contains Gluten", True,
     "Layers of espresso-soaked ladyfingers and whipped mascarpone cream flavored with Marsala wine, dusted with premium Dutch cocoa powder. Made fresh daily and chilled for 24 hours to develop rich, complex flavors."),
]


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 menu_items 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 menu_items (
            id SERIAL PRIMARY KEY,
            name VARCHAR NOT NULL,
            cuisine_type VARCHAR NOT NULL,
            category VARCHAR NOT NULL,
            ingredients VARCHAR NOT NULL,
            price VARCHAR NOT NULL,
            dietary_tags VARCHAR NOT NULL,
            available BOOLEAN NOT NULL DEFAULT TRUE,
            description TEXT NOT NULL,
            description_embedding vector(3072)
        )
    """)


def seed_menu_items(cursor, conn):
    """Insert menu items."""
    cursor.execute("SELECT COUNT(*) FROM menu_items")
    existing_count = cursor.fetchone()[0]

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

    cursor.executemany("""
        INSERT INTO menu_items (name, cuisine_type, category, ingredients, price, dietary_tags, available, description)
        VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
    """, MENU_ITEMS)
    conn.commit()
    return len(MENU_ITEMS)


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

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

    cursor.execute(f"""
        UPDATE menu_items
        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_menu_items(cursor, conn)
        if seeded > 0:
            print(f"      ✓ Inserted {seeded} menu items")

        # 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

Skrypty są teraz gotowe do wykonania. Do wykonania przygotowanego skryptu potrzebujemy Pythona, więc najpierw przygotujmy go.

Konfigurowanie projektu w Pythonie

uv to szybki menedżer pakietów i projektów Pythona napisany w języku Rust ( dokumentacja uv ). W tym ćwiczeniu 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ślnych danych logowania 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 urządzeniu 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.1.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 został wcześniej skonfigurowany. Wykonuje on 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 vCPU, 3,75 GB pamięci RAM) w wersji ENTERPRISE. Więcej informacji znajdziesz tutaj. Dedykowany rdzeń jest wymagany w przypadku integracji Vertex AI ML – warstwy ze współużytkowanym rdzeniem (db-f1-micro, db-g1-small) nie obsługują tej funkcji.
  • --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_restaurant_db.py script

Informacje o skrypcie początkowym scripts/setup_restaurant_db.py

Skrypt początkowy 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 restaurant_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 wektorów.
  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 pozycji menu
  3. Wygeneruj dane wektorowe 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.
  • Funkcja UPDATE jest uruchamiana we wszystkich 15 wierszach, generując 1 osadzenie o 3072 wymiarach dla każdego opisu stanowiska.

Przygotuje to wstępne dane, do których będzie miał dostęp nasz agent.

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 agenty 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ą sparametryzowane zapytania, 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 narzędziu Toolbox piszesz plik YAML. Każde narzędzie jest mapowane na sparametryzowaną instrukcję SQL. Toolbox 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 modyfikowania kodu 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 wielodokumentowego formatu YAML – każdy blok oddzielony znakiem --- jest samodzielnym zasobem. Każdy zasób ma pole kind, które deklaruje, czym jest (sources w przypadku połączeń z bazą danych, tools w przypadku działań wywoływanych przez agenta), oraz pole 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ą pola name, dzięki czemu Toolbox 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: restaurant-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 (restaurant-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ą rozwiązywane ze zmiennych środowiskowych podczas uruchamiania.

Następnie dodaj ten skrypt pod symbolem --- w pliku tools.yaml.

 
# --- Tool 1: Search menu items by category and/or cuisine type ---
kind: tool
name: search-menu
type: postgres-sql
source: restaurant-db
description: >-
  Search for menu items by category and/or cuisine type.
  Use this tool when the user wants to browse menu items
  by category (e.g., Main Course, Appetizer, Dessert) or find dishes
  from a specific cuisine. Both parameters accept an
  empty string to match all values.
statement: |
  SELECT name, cuisine_type, category, ingredients, price, dietary_tags, available
  FROM menu_items
  WHERE ($1 = '' OR LOWER(category) = LOWER($1))
  AND ($2 = '' OR LOWER(cuisine_type) LIKE '%' || LOWER($2) || '%')
  ORDER BY name
  LIMIT 10
parameters:
  - name: category
    type: string
    description: "The menu category to filter by (e.g., 'Main Course', 'Appetizer', 'Dessert'). Use empty string for all categories."
  - name: cuisine_type
    type: string
    description: "A cuisine type to search for (partial match, e.g., 'Italian', 'Japanese'). Use empty string for all cuisines."

---

# --- Tool 2: Get full details for a specific menu item ---
kind: tool
name: get-item-details
type: postgres-sql
source: restaurant-db
description: >-
  Get full details for a specific menu item including its description,
  price, dietary tags, and availability. Use this tool when the
  user asks about a particular dish by name or cuisine.
statement: |
  SELECT name, cuisine_type, category, ingredients, price, dietary_tags, available, description
  FROM menu_items
  WHERE LOWER(name) LIKE '%' || LOWER($1) || '%'
  OR LOWER(cuisine_type) LIKE '%' || LOWER($1) || '%'
parameters:
  - name: search_term
    type: string
    description: "The dish name or cuisine type to look up (partial match supported)."

---

Ten skrypt definiuje ten zasób:

  • Narzędzia 1 i 2 (search-menu, get-item-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ą symboli zastępczych pozycji $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 do wywoływania modelu gemini-embedding-001 Gemini w celu generowania 3072-wymiarowych wektorów dystrybucyjnych tekstu. Toolbox używa domyślnego uwierzytelniania aplikacji (ADC) do uwierzytelniania – 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-menu-by-description
type: postgres-sql
source: restaurant-db
description: >-
  Find menu items that match a natural language description of what the user
  is looking for. Use this tool when the user describes their ideal dish
  using flavors, textures, dietary preferences, or cravings rather than a
  specific category or cuisine. Examples: "I want something spicy and creamy,"
  "a light vegetarian appetizer," "something rich and chocolatey for dessert."
statement: |
  SELECT name, cuisine_type, category, ingredients, price, dietary_tags, description
  FROM menu_items
  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 dish the user is looking for."
    embeddedBy: gemini-embedding

---

Ten skrypt definiuje ten zasób:

  • Narzędzie 3 (search-menu-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 menu item with automatic embedding ---
kind: tool
name: add-menu-item
type: postgres-sql
source: restaurant-db
description: >-
  Add a new menu item to the restaurant. Use this tool when a user asks
  to add a dish that is not currently on the menu.
statement: |
  INSERT INTO menu_items (name, cuisine_type, category, ingredients, price, dietary_tags, available, description, description_embedding)
  VALUES ($1, $2, $3, $4, $5, $6, CAST($7 AS BOOLEAN), $8, $9)
  RETURNING name, cuisine_type
parameters:
  - name: name
    type: string
    description: "The dish name (e.g., 'Truffle Mushroom Risotto')."
  - name: cuisine_type
    type: string
    description: "The cuisine type (e.g., 'Italian', 'Japanese', 'Thai')."
  - name: category
    type: string
    description: "The menu category (e.g., 'Main Course', 'Appetizer', 'Dessert')."
  - name: ingredients
    type: string
    description: "Comma-separated list of key ingredients (e.g., 'salmon, miso, ginger')."
  - name: price
    type: string
    description: "The price (e.g., '$24')."
  - name: dietary_tags
    type: string
    description: "Dietary information (e.g., 'Vegetarian, Gluten-Free')."
  - name: available
    type: string
    description: "Whether the dish is currently available (true or false)."
  - name: description
    type: string
    description: "A short description of the dish (2-3 sentences)."
  - name: description_vector
    type: string
    description: "Auto-generated embedding vector for the dish description."
    valueFromParam: description
    embeddedBy: gemini-embedding

Ten skrypt definiuje ten zasób:

  • Narzędzie 4 (add-menu-item) – pokazuje wczytywanie wektorów. Parametr description_vector ma 2 pola specjalne:
  • valueFromParam: description – Toolbox kopiuje wartość z parametru description do tego parametru. Model 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 żadnej wiedzy 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 wysyłać zapytania do bazy 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 menu items 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 menu items 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 menu items and embeddings
        cursor.execute("SELECT COUNT(*) FROM menu_items")
        item_count = cursor.fetchone()[0]

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

        print(f"Menu Items: {item_count}/15")
        print(f"Embeddings: {embedding_count}/15")

        cursor.close()
        conn.close()

        if item_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 pozycji menu i ich osadzanie. Uruchom go 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.

Menu Items: 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.1.0/linux/amd64/toolbox
fi
chmod +x toolbox

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

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 gotowość serwera, jak pokazano poniżej:

... INFO "Initialized 1 sources: restaurant-db"
... INFO "Initialized 0 authServices: "
... INFO "Using Vertex AI backend for Gemini embedding" 
... INFO "Initialized 1 embeddingModels: gemini-embedding" 
... INFO "Initialized 4 tools: search-menu-by-description, add-menu-item, search-menu, get-item-details"
...
... 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-menu-by-description": {
            "description": "Find menu items that match a natural language description of what the user is looking for. Use this tool when the user describes their ideal dish using flavors, textures, dietary preferences, or cravings rather than a specific category or cuisine. Examples: \"I want something spicy and creamy,\" \"a light vegetarian appetizer,\" \"something rich and chocolatey for dessert.\"",
            "parameters": [
                {
                    "name": "search_query",
                    "type": "string",
                    "required": true,
                    "description": "A natural language description of the kind of dish the user is looking for.",
                    "authServices": []
                }
            ],
            "authRequired": []
        }
...

Przetestuj narzędzie search-menu bezpośrednio:

curl -s -X POST http://localhost:5000/api/tool/search-menu/invoke \\   -H "Content-Type: application/json" \\   -d '{"category": "Main Course", "cuisine_type": "Italian"}' | jq '.result | fromjson'

Odpowiedź powinna zawierać włoskie dania główne z danych początkowych.

[
  {
    "name": "Margherita Pizza",
    "cuisine_type": "Italian",
    "category": "Main Course",
    "ingredients": "San Marzano tomatoes, fresh mozzarella, basil, olive oil",
    "price": "$18",
    "dietary_tags": "Vegetarian",
    "available": true
  },
  {
    "name": "Truffle Mushroom Risotto",
    "cuisine_type": "Italian",
    "category": "Main Course",
    "ingredients": "Arborio rice, truffle oil, porcini mushrooms, parmesan, white wine",
    "price": "$28",
    "dietary_tags": "Vegetarian, Gluten-Free",
    "available": true
  }
]

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 Agent Development Kit od Google, w tym pakiet SDK Gemini.
  • toolbox-adk – integracja ADK z MCP Toolbox for Databases.

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 ułatwić to zadanie, ma wbudowane polecenie, które pozwala szybko ustalić strukturę:

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

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

build-agent-adk-toolbox-cloudsql/
├── restaurant_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: zapytania standardowe, 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 zmienne 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" >> restaurant_agent/.env

Aktualizowanie modułu agenta

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

cloudshell edit restaurant_agent/agent.py

i zastąp zawartość tym kodem:

# restaurant_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="restaurant_agent",
    model="gemini-3.5-flash",
    instruction="""You are a friendly and knowledgeable concierge at "Foodie Finds," a restaurant. Your job:
- Help diners browse the menu by category or cuisine type.
- Provide full details about specific dishes, including ingredients, price, and dietary information.
- Recommend dishes based on natural language descriptions of what the diner is craving.
- Add new menu items when asked.

When a diner asks about a specific dish by name or cuisine, use the get-item-details tool.
When a diner asks for a specific category or cuisine type, use the search-menu tool.
When a diner describes what kind of food they want — by flavor, texture, dietary needs, or cravings — use the search-menu-by-description tool for semantic search.

When in doubt between search-menu and search-menu-by-description, prefer search-menu-by-description — it searches dish descriptions and finds more relevant matches.
If a dish is not available (available is false), let the diner 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.

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

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 internecie Cloud Shell lub kliknij adres URL wyświetlany w terminalu, przytrzymując Ctrl. W menu agenta w lewym górnym rogu wybierz restaurant_agent.

Testowanie standardowych zapytań

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

What Italian dishes do you have?
Tell me about the Miso Glazed Black Cod

99807eceeb0212f6.png

834580f0aa4c5106.png

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

I want something spicy and creamy
Something rich and chocolatey for dessert
I'm in the mood for something light and healthy

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

2b9ed5d90c5990c6.png

Testowanie przetwarzania wektorów

Poproś agenta o dodanie nowego zadania:

Add a new dish: 'Seared Duck Breast' cuisine type French, category Main Course, ingredients: duck breast, cherry reduction, roasted root vegetables, thyme, price $34, dietary tags: Gluten-Free Dairy-Free, available true. Description: A perfectly seared duck breast with crispy skin, served with a tart cherry reduction sauce and a medley of roasted root vegetables. The duck is cooked sous vide for tender, pink meat,
then
finished in a cast iron skillet for maximum crispiness.

f7a96ca8c37e7c56.png

Spróbuj teraz wyszukać:

Find me something with rich, gamey flavors and fruit sauce

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

be78e692c056596e.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 dalej i wdróżmy te aplikacje 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 narzędzia działają lokalnie. Ten krok wdraża je 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 nim.

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 Toolbox i tools.yaml są spakowane w minimalnym obrazie Debiana. Cloud Run kieruje ruch na port 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, przenosi 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 restaurant_agent/ restaurant_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
restaurant_agent/.env
toolbox
tools.yaml
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ź, 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.1.0+binary.linux.amd64.da6f5f8",
    "tools": {
        "add-menu-item": {
            "description": "Add a new menu item to the restaurant. Use this tool when a user asks to add a dish that is not currently on the menu.",

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

cd ~/build-agent-adk-toolbox-cloudsql
gcloud run deploy restaurant-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 restaurant-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.

Wybierz z menu restaurant_agent i przetestuj:

What Italian dishes do you have?
I want something spicy and creamy

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

Masz już utworzonego i wdrożonego inteligentnego asystenta menu restauracji, który używa MCP Toolbox for Databases do łączenia agenta ADK z Cloud SQL PostgreSQL – zarówno za pomocą standardowej wersji 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 z parametrem embeddedBy do automatycznego osadzania zapytań
  • Jak valueFromParam umożliwia automatyczne wczytywanie wektorów – model LLM podaje opis tekstowy, a Toolbox cicho 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 laboratorium, 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 restaurant-agent --region=$REGION --quiet
gcloud run services delete toolbox-service --region=$REGION --quiet
gcloud sql instances delete restaurant-instance --quiet
gcloud artifacts repositories delete cloud-run-source-deploy --location=$REGION --quiet 2>/dev/null