ツールとしてのデータベース: ADK、MCP ツールボックス、Cloud SQL を使用したエージェント RAG

1. はじめに

AI エージェントの有用性は、アクセスできるデータの質によって決まります。実際のデータのほとんどはデータベースに存在します。通常、エージェントをデータベースに接続するには、接続管理、クエリ ロジック、埋め込みパイプラインをエージェント コード内に記述する必要があります。データベース アクセスを必要とするエージェントはすべてこの作業を繰り返し、クエリを変更するたびにエージェントを再デプロイする必要があります。

この Codelab では、別のアプローチを示します。標準 SQL クエリ、ベクトル類似性検索、自動エンベディング生成など、データベース ツールを YAML ファイルで宣言すると、データベース向け MCP ツールボックスが MCP サーバーとしてすべてのデータベース オペレーションを処理します。エージェント コードは最小限に抑えられます。ツールを読み込み、呼び出すツールを Gemini に決定させます。

作成するアプリの概要

「TechJobs」用の Smart Job Board Assistant - Gemini を搭載した ADK エージェント。デベロッパーが標準フィルタ(ロール、技術スタック)を使用して技術職の求人情報を閲覧し、「AI チャットボットのリモートワークがしたい」などの自然言語の説明で仕事を見つけるのに役立ちます。エージェントは、MCP Toolbox for Databases を介して Cloud SQL PostgreSQL データベースから読み取り、書き込みを行います。MCP Toolbox for Databases は、ベクトル検索の自動エンベディング生成など、すべてのデータベース アクセスを処理します。最終的に、ツールボックスとエージェントの両方が Cloud Run で実行されます。

eb6de681c40990c1.jpeg

学習内容

  • MCP(Model Context Protocol)が AI エージェントのツールアクセスを標準化する方法と、データベース向け MCP ツールボックスがこれをデータベース オペレーションに適用する方法
  • ADK エージェントと Cloud SQL PostgreSQL の間のミドルウェアとしてデータベース向け MCP ツールボックスを設定する
  • tools.yaml でデータベース ツールを宣言的に定義する - エージェントにデータベース コードは不要
  • ToolboxToolset を使用して、実行中の Toolbox サーバーからツールを読み込む ADK エージェントを構築する
  • Cloud SQL の組み込みの embedding() 関数を使用してベクトル エンベディングを生成し、pgvector でセマンティック検索を有効にする
  • 書き込みオペレーションでベクターを自動的に取り込むために valueFromParam 機能を使用する
  • ツールボックス サーバーと ADK エージェントの両方を Cloud Run にデプロイする

前提条件

  • トライアルの請求先アカウントを含む Google Cloud アカウント
  • Python と SQL に関する基本的な知識
  • Cloud Database と ADK の経験があると役立ちます

2. 環境をセットアップする

このステップでは、Cloud Shell 環境を準備し、Google Cloud プロジェクトを構成して、リファレンス リポジトリのクローンを作成します。

Cloud Shell を開く

ブラウザで Cloud Shell を開きます。Cloud Shell には、この Codelab で必要なすべてのツールがプリインストールされた環境が用意されています。プロンプトが表示されたら、[Authorize] をクリックします。

[表示] -> [ターミナル] をクリックしてターミナルを開きます。インターフェースは次のようになります。

86307fac5da2f077.png

これがメイン インターフェースになります。上部に IDE、下部にターミナルが表示されます。

作業ディレクトリを設定する

作業ディレクトリを作成します。この Codelab で記述するコードはすべて、次の場所にあります。

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

その後、シード スクリプトやログなどを管理するためのディレクトリをいくつか準備します。

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

Google Cloud プロジェクトをセットアップする

ロケーション変数を含む .env ファイルを作成します。

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

ターミナルでのプロジェクト設定を簡素化するには、このプロジェクト設定スクリプトを作業ディレクトリにダウンロードします。

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

スクリプトを実行します。トライアルの請求先アカウントを確認し、新しいプロジェクトを作成(または既存のプロジェクトを検証)し、プロジェクト ID を現在のディレクトリの .env ファイルに保存し、gcloud でアクティブ プロジェクトを設定します。

bash setup_verify_trial_project.sh && source .env

このスクリプトによって行われる処理は次のとおりです。

  1. 有効なトライアルの請求先アカウントがあることを確認する
  2. .env に既存のプロジェクトがあるかどうかを確認します(ある場合)。
  3. 新しいプロジェクトを作成するか、既存のプロジェクトを再利用する
  4. トライアルの請求先アカウントをプロジェクトにリンクする
  5. プロジェクト ID を .env に保存する
  6. プロジェクトをアクティブな gcloud プロジェクトとして設定する

Cloud Shell ターミナルのプロンプトで、作業ディレクトリの横にある黄色のテキストを確認して、プロジェクトが正しく設定されていることを確認します。プロジェクト ID が表示されます。

dcba35ce1389f313.png

必要な API を有効にする

次に、操作するプロダクトに対していくつかの API を有効にする必要があります。

gcloud services enable \
  aiplatform.googleapis.com \
  sqladmin.googleapis.com \
  compute.googleapis.com \
  run.googleapis.com \
  cloudbuild.googleapis.com \
  artifactregistry.googleapis.com
  • Vertex AI APIaiplatform.googleapis.com) - エージェントは Gemini モデルを使用し、ツールボックスはベクトル検索にエンベディング API を使用します。
  • Cloud SQL Admin APIsqladmin.googleapis.com) - PostgreSQL インスタンスをプロビジョニングして管理します。
  • Compute Engine APIcompute.googleapis.com)- Cloud SQL インスタンスの作成に必要です。
  • Cloud Run、Cloud Build、Artifact Registry - この Codelab の後半のデプロイ ステップで使用されます。

3. データベースの初期化用のスクリプトの準備

このステップでは、Cloud SQL インスタンスの作成を開始し、インスタンスの準備が整うまで待機してから、データベースを作成し、求人情報でシードし、エンベディングを生成する自動設定スクリプトを実行します。

まず、データベースのパスワードを .env ファイルに追加して、再読み込みします。

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

インスタンスとデータベースの作成用の Bash スクリプトを作成する

次のコマンドを使用して scripts/setup_database.sh スクリプトを作成します。

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

次に、次のコードを scripts/setup_database.sh ファイルにコピーします。

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

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

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

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

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

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

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

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

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

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

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

echo "      ✓ Permissions granted"
echo ""

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

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

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

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

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

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

uv run "$SETUP_SCRIPT"

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

データ シード用の Python スクリプトを作成する

その後、次のコマンドを使用してシード スクリプト Python ファイル scripts/setup_jobs_db.py を作成します。

cloudshell edit scripts/setup_jobs_db.py

次に、次のコードを scripts/setup_jobs_db.py ファイルにコピーします。

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

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

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

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

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


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

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


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


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

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

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


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

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

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


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

    try:
        create_schema(cursor)
        conn.commit()

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

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

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


if __name__ == "__main__":
    main()

次のステップに進みましょう

4. データベースを作成して初期化する

これで、スクリプトを実行する準備が整いました。準備したスクリプトを実行するには Python が必要なので、まず Python を準備しましょう。

Python プロジェクトを設定する

uv は、Rust で記述された高速な Python パッケージおよびプロジェクト管理ツールです(uv のドキュメント)。この Codelab では、Python プロジェクトの保守を高速かつ簡単に行うために使用します。

Python プロジェクトを初期化し、必要な依存関係を追加します。

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

ここでは、cloud-sql-python-connector Python SDK を使用して、アプリケーションのデフォルト認証情報を使用して認証されたデータベース インスタンスとの安全な接続を初期化しています。

設定スクリプトを実行する

これで、次のコマンドを使用して、設定スクリプトをバックグラウンドで実行し、logs/atabase_setup.log ファイルに書き込まれるコンソール出力を確認できます。完了を待機している間に、次のセクションに進みます。

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

Toolbox バイナリをダウンロードする

このチュートリアルでは MCP Toolbox を使用します。幸いなことに、MCP Toolbox には Linux 環境ですぐに使用できるビルド済みバイナリが付属しています。ダウンロードには時間がかかるため、バックグラウンドでダウンロードします。次のコマンドを実行してバイナリをダウンロードし、logs/toolbox_dl.log の出力ログを調べます。完了を待機している間に、次のセクションに進みます。

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

設定スクリプト scripts/setup_database.sh について

次に、以前に構成した設定スクリプトについて説明します。次の処理を行います。

  1. 最初に実行するコマンドは、次のフラグを指定した gcloud sql instances create コマンドです。
  • db-custom-1-3840 は、ENTERPRISE エディションで最小の専用コア Cloud SQL 階層(1 vCPU、3.75 GB RAM)です。詳しくは、こちらをご覧ください。Vertex AI ML の統合には専用コアが必要です。共有コア階層(db-f1-microdb-g1-small)ではサポートされていません。
  • --root-password は、デフォルトの postgres ユーザーのパスワードを設定します。
  • --enable-google-ml-integration を使用すると、Cloud SQL と Vertex AI の組み込みインテグレーションが有効になり、embedding() 関数を使用して SQL からエンベディング モデルを直接呼び出すことができます。
  1. インスタンスがすでに RUNNABLE ステータスであるかどうかを確認する
  2. gcloud projects add-iam-policy-binding コマンドを使用して、Cloud SQL インスタンスのサービス アカウントに Vertex AI を呼び出す権限を付与します。これは、データベースのシード時に使用する組み込みの embedding() 関数に必要です。
  3. データベースを作成する
  4. シーディング スクリプト setup_jobs_db.py スクリプトの実行

シード スクリプト scripts/setup_jobs_db.py について

次に、シード スクリプトについて説明します。このスクリプトは次の処理を行います。

  1. データベース インスタンスへの接続を初期化する
  2. 次の 2 つの PostgreSQL 拡張機能をインストールします。
  • google_ml_integration - embedding() SQL 関数を提供します。この関数は、SQL から Vertex AI エンベディング モデルを直接呼び出します。これは、jobs_db 内で ML 関数を使用できるようにするデータベース レベルの拡張機能です。インスタンスの作成時に設定したインスタンスレベルのフラグ(--enable-google-ml-integration)により、Cloud SQL VM は Vertex AI にアクセスできます。この拡張機能により、この特定のデータベース内で SQL 関数を使用できるようになります。
  • vectorpgvector) - エンベディングの保存とクエリを行うための vector データ型と距離演算子を追加します。
  1. テーブルを作成します。description_embedding 列は vector(3072)(3, 072 次元のベクトルを格納する pgvector 列)であることに注意してください。
  2. 初期ジョブデータをシードする
  3. description フィールドからエンベディング データを生成し、embedding() 関数を介して組み込みの Vertex インテグレーションを使用して description_embedding を入力します。
  • embedding('gemini-embedding-001', description) - SQL から Vertex AI の Gemini エンベディング モデルを直接呼び出し、各ジョブの description テキストを渡します。これは、シード スクリプトにインストールした google_ml_integration 拡張機能です。
  • ::vector - 返された浮動小数点配列を pgvector の vector 型にキャストして、距離演算子で保存してクエリできるようにします。
  • UPDATE は 15 行すべてで実行され、職務説明ごとに 1 つの 3, 072 次元エンベディングが生成されます。

これにより、エージェントがアクセスする初期データが準備されます。

5. MCP Toolbox for Databases を構成する

このステップでは、MCP Toolbox for Databases を導入し、Cloud SQL インスタンスに接続するように構成して、2 つの標準 SQL クエリツールを定義します。

MCP とは何か、ツールボックスを使用する理由

e7b9be2e1c98b4db.png

MCP(Model Context Protocol)は、AI エージェントが外部ツールを検出して操作する方法を標準化するオープン プロトコルです。クライアント / サーバー モデルを定義します。エージェントは MCP クライアントをホストし、ツールは MCP サーバーによって公開されます。MCP 対応クライアントは MCP 対応サーバーを使用できます。エージェントは各ツールにカスタム統合コードを必要としません。

5bf26eeecad2277d.png

データベース向け MCP ツールボックスは、データベース アクセス専用に構築されたオープンソースの MCP サーバーです。これがないと、データベース接続を開き、接続プールを管理し、SQL インジェクションを防ぐためにパラメータ化されたクエリを作成し、エラーを処理し、そのすべてのコードをエージェント内に埋め込む Python 関数を作成する必要があります。データベース アクセスを必要とするすべてのエージェントがこの作業を繰り返します。クエリを変更すると、エージェントが再デプロイされます。

Toolbox では、YAML ファイルを記述します。各ツールは、パラメータ化された SQL ステートメントにマッピングされます。ツールボックスは、接続プーリング、パラメータ化されたクエリ、認証、オブザーバビリティを処理します。ツールがエージェントから切り離されているため、エージェント コードを変更することなく、tools.yaml を編集して Toolbox を再起動することでクエリを更新できます。同じツールは、ADK、LangGraph、LlamaIndex、または MCP 互換のフレームワークで動作します。

ツールの構成を記述する

次に、Cloud Shell コードエディタで tools.yaml というファイルを作成して、ツールの構成を設定する必要があります。

cloudshell edit tools.yaml

このファイルでは、マルチドキュメント YAML が使用されています。--- で区切られた各ブロックは、スタンドアロン リソースです。すべてのリソースには、リソースの内容(データベース接続の場合は sources、エージェント呼び出し可能なアクションの場合は tools)を宣言する kind と、バックエンド(ソースの場合は cloud-sql-postgres、SQL ベースのツールの場合は postgres-sql)を指定する type があります。ツールは name でソースを参照します。これにより、ツールボックスは実行する接続プールを認識します。環境変数は ${VAR_NAME} 構文を使用し、起動時に解決されます。

次のスクリプトを tools.yaml ファイルにコピーします。

# tools.yaml

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

---

このスクリプトでは、次のリソースを定義します。

  • Sourcejobs-db) - Cloud SQL PostgreSQL インスタンスへの接続方法を Toolbox に指示します。cloud-sql-postgres タイプは内部で Cloud SQL コネクタを使用し、認証と安全な接続を自動的に処理します。${GOOGLE_CLOUD_PROJECT}${REGION}${DB_PASSWORD} のプレースホルダは、起動時に環境変数から解決されます。

次に、tools.yaml--- 記号の下に次のスクリプトを追加します。

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

---

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

---

このスクリプトでは、次のリソースを定義します。

  • ツール 1 と 2search-jobsget-job-details) - 標準 SQL クエリツール。各マッピングは、ツール名(エージェントが認識する名前)をパラメータ化された SQL ステートメント(データベースが実行するステートメント)にマッピングします。パラメータは $1$2 位置プレースホルダを使用します。Toolbox はこれらをプリペアド ステートメントとして実行するため、SQL インジェクションを防ぐことができます。

次に、tools.yaml--- 記号の下に次のスクリプトを追加します。

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

---

このスクリプトでは、次のリソースを定義します。

  • エンベディング モデルgemini-embedding) - 3,072 次元のテキスト エンベディングを生成するために Gemini の gemini-embedding-001 モデルを呼び出すように Toolbox を構成します。Toolbox は、アプリケーションのデフォルト認証情報(ADC)を使用して認証を行います。Cloud Shell や Cloud Run で API キーは必要ありません。ここで構成する dimension は、データベースのシード用に以前に構成したものと同じである必要があります。

次に、tools.yaml--- 記号の下に次のスクリプトを追加します。

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

---

このスクリプトでは、次のリソースを定義します。

  • Tool 3search-jobs-by-description) - ベクトル検索ツール。search_query パラメータには embeddedBy: gemini-embedding があります。これは、Toolbox に未加工のテキストをインターセプトしてエンベディング モデルに送信し、結果のベクトルを SQL ステートメントで使用するように指示します。<=> 演算子は pgvector のコサイン距離です。値が小さいほど、説明が類似していることを意味します。

最後に、tools.yaml--- 記号の下に最後のツールを追加します。

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

このスクリプトでは、次のリソースを定義します。

  • ツール 4add-job) - ベクトル取り込みを示します。description_vector パラメータには、次の 2 つの特別なフィールドがあります。
  • valueFromParam: description - ツールボックスは、description パラメータの値をこのパラメータにコピーします。LLM はこのパラメータを認識しません。
  • embeddedBy: gemini-embedding - ツールボックスは、コピーしたテキストをベクトルに埋め込んでから SQL に渡します。

結果として、エージェントがエンベディングについて何も知らなくても、1 つのツール呼び出しで元の説明テキストとそのベクトル エンベディングの両方が保存されます。

マルチドキュメント YAML 形式では、各リソースが --- で区切られます。各ドキュメントには、その内容を定義する kindnametype フィールドがあります。要約すると、次のすべての項目をすでに構成しています。

  • ソース データベースを定義する
  • 標準フィルタを使用してデータベースをクエリするツール(ツール 1 と 2)を定義します。
  • エンベディング モデルを定義する
  • ベクトル検索を行うツール(ツール 3)をデータベースに定義する
  • ベクトルデータの取り込みを行うツール(ツール 4)をデータベースに定義します。

6. MCP ツールボックス サーバーの実行

前のステップで、MCP ツールボックスに必要な構成はすでに設定されています。これで、サーバーを実行する準備が整いました。

シードデータの検証

Toolbox を起動する前に、データベースの設定が完了していることを確認しましょう。次のコマンドを使用して Python スクリプト scripts/verify_database.py を作成します。

cloudshell edit scripts/verify_seed.py

次に、次のコードを scripts/verify_seed.py ファイルにコピーします。

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

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

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

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

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


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

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

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

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

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

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

        cursor.close()
        conn.close()

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

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


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

このスクリプトは、求人情報のデータ数とそのエンベディングを確認します。次のコマンドを使用してスクリプトを実行します。

uv run scripts/verify_seed.py

次のターミナル出力が表示された場合は、データが準備完了状態であることを示しています。

Jobs: 15/15
Embeddings: 15/15

✓ Database ready!

ツールボックス サーバーを起動する

前の設定ステップで、toolbox 実行可能ファイルをすでにダウンロードしています。このバイナリ ファイルが存在し、正常にダウンロードされたことを確認します。存在しない場合は、ダウンロードして完了するまで待ちます。

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

MCP ツールボックスによって実行される子プロセスに .env 変数を公開する必要があります。次のコマンドを実行して、ツールボックス サーバーを起動し、コンソール出力を logs/mcp_toolbox.log ファイルに記録します。

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

次の例に示すように、logs/mcp_toolbox.log ファイルにサーバーの準備が完了したことを確認する出力が表示されます。

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

ツールを確認する

Toolbox API をクエリして、登録されているすべてのツールを一覧表示します。

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

ツールとその説明とパラメータが表示されます。以下のようにします。

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

search-jobs ツールを直接テストします。

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

レスポンスには、シードデータから 2 つのバックエンド エンジニアリング ジョブが含まれているはずです。

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

7. ADK エージェントをビルドする

このプロジェクトでは Python で ADK を使用します。必要な依存関係を追加しましょう。

uv add google-adk==1.29.0 toolbox-adk==1.0.0
  • google-adk - Google の Agent Development Kit(Gemini SDK を含む)
  • toolbox-adk - データベース向け MCP ツールボックスの ADK 統合。

エージェントのディレクトリ構造を作成する

ADK では、特定フォルダ レイアウト(エージェントの名前のディレクトリに __init__.pyagent.py.env が含まれている)が想定されています。このために、構造をすばやく確立するコマンドが組み込まれています。

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

ディレクトリは次のようになります。

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

次に、ADK エージェントを実行中の Toolbox サーバーに統合し、標準クエリ、セマンティック検索、ベクトル取り込みの 4 つのツールすべてをテストする必要があります。エージェント コードは最小限です。すべてのデータベース ロジックは tools.yaml にあります。

エージェントの環境を構成する

ADK は、前の手順で設定したシェル環境から GOOGLE_GENAI_USE_VERTEXAIGOOGLE_CLOUD_PROJECTGOOGLE_CLOUD_LOCATION を読み取ります。エージェント固有の変数は TOOLBOX_URL のみです。エージェントの .env ファイルに追加します。

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

エージェント モジュールを更新する

Cloud Shell エディタで jobs_agent/agent.py を開く

cloudshell edit jobs_agent/agent.py

次のコードで内容を上書きします。

# jobs_agent/agent.py
import os

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

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

toolbox = ToolboxToolset(TOOLBOX_URL)

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

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

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

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

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

ここにはデータベース コードはありません。ToolboxToolset は起動時に Toolbox サーバーに接続し、利用可能なすべてのツールを読み込みます。エージェントはツールを名前で呼び出します。Toolbox は、これらの呼び出しを Cloud SQL に対する SQL クエリに変換します。

TOOLBOX_URL 環境変数は、ローカル開発ではデフォルトで http://127.0.0.1:5000 に設定されます。後で Cloud Run にデプロイするときに、Toolbox サービスの Cloud Run URL でこの値をオーバーライドします。コードの変更は必要ありません。

この手順では、2 つの標準ツール(search-jobsget-job-details)のみを参照しています。次のステップで、セマンティック検索ツールと取り込みツールを追加するときに、この手順を拡張します。

エージェントをテストする

ADK 開発 UI を起動します。

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

Cloud Shell の [ウェブ プレビュー] 機能を使用するか、ターミナルに表示された URL を Ctrl+クリックして、ターミナルに表示された URL(通常は http://localhost:8000)を開きます。左上のエージェント プルダウンから [jobs_agent] を選択します。

標準クエリをテストする

次のプロンプトを試して、標準 SQL ツールを検証します。

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

93ac33e7f73aa0b9.png 240c53376042a916.png

特定のロールや技術スタックにマッピングされない自然言語の説明を試してください。

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

エージェントは、クエリのタイプに基づいて適切なツールを選択しようとします。構造化フィルタは search-jobs を通過し、自然言語の説明は search-jobs-by-description を通過します。

b0ea629f5c9b4c26.png

ベクトル取り込みをテストする

エージェントに新しいジョブを追加するよう依頼します。

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

c601a7a9bc0a705b.png

検索してみましょう。

Find me jobs involving autonomous systems and working with physical hardware

エンベディングは INSERT 中に自動的に生成されたため、個別の手順は必要ありません。

5a3d8e6f523dc18b.png

これで、ADK、MCP ツールボックス、CloudSQL を利用した完全なエージェント RAG アプリケーションが完成しました。これで完了です。さらに、これらのアプリを Cloud Run にデプロイしてみましょう。

次に、Ctrl+C キーを 2 回押してプロセスを強制終了し、デベロッパー UI を停止してから続行します。

8. Cloud Run にデプロイする

エージェントとツールボックスはローカルで動作します。このステップでは、両方を Cloud Run サービスとしてデプロイし、インターネット経由でアクセスできるようにします。ツールボックス サービスは Cloud Run で MCP サーバーとして実行され、エージェント サービスがそれに接続します。

デプロイ用に Toolbox を準備する

Toolbox サービスのデプロイ ディレクトリを作成します。

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

Toolbox の Dockerfile を作成します。Cloud Shell エディタで deploy-toolbox/Dockerfile を開きます。

cloudshell edit deploy-toolbox/Dockerfile

次のスクリプトをコピーします。

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

Toolbox バイナリと tools.yaml は、最小限の Debian イメージにパッケージ化されます。Cloud Run はトラフィックをポート 8080 に転送します。

ツールボックス サービスをデプロイする

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

このコマンドは、ソースを Cloud Build に送信し、コンテナ イメージをビルドして Artifact Registry に push し、Cloud Run にデプロイします。数分かかります。デプロイ プロセスのログは logs/deploy_toolbox.log ファイルで確認できます。

デプロイ用にエージェントを準備する

ツールボックスのビルド中に、エージェントのデプロイ ファイルを設定します。

プロジェクトのルートに Dockerfile を作成します。Cloud Shell エディタで Dockerfile を開きます。

cloudshell edit Dockerfile

次に、次のコンテンツをコピーします。

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

この Dockerfile は、Python と uv の両方がプリインストールされている ghcr.io/astral-sh/uv をベースイメージとして使用します。pip を介して uv を個別にインストールする必要はありません。

.dockerignore ファイルを作成して、コンテナ イメージから不要なファイルを除外します。

cloudshell edit .dockerignore

次のスクリプトをコピーして貼り付けます。

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

エージェント サービスをデプロイする

Toolbox のデプロイが完了するまで待ちます。logs/deploy_toolbox.log でデプロイ プロセスを再度確認して、プロセスを検証します。次のコマンドを使用して、Cloud Run URL を取得します。

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

次のような出力が表示されます。

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

次に、デプロイされた Toolbox が動作していることを確認します。

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

この例のような出力が表示された場合、デプロイはすでに成功しています。

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

次に、ツールボックスの URL を環境変数として渡して、エージェントをデプロイします。

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

エージェント コードは、環境から TOOLBOX_URL を読み取ります(これは以前に設定しました)。ローカルでは http://127.0.0.1:5000 を指し、Cloud Run では Toolbox サービスの URL を指します。コードを変更する必要はありません。

デプロイしたエージェントをテストする

エージェントの Cloud Run URL を取得します。

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

ブラウザでその URL を開きます。ADK デベロッパー UI が読み込まれます。これは、ローカルで使用していたのと同じインターフェースで、Cloud Run で実行されています。

プルダウンから [jobs_agent] を選択してテストします。

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

どちらのクエリも、デプロイされたサービスを介して機能します。Cloud Run のエージェントが Cloud Run の Toolbox を呼び出し、Cloud SQL にクエリを実行します。

9. おめでとうございます / クリーンアップ

データベース向け MCP ツールボックスを使用して ADK エージェントと Cloud SQL PostgreSQL を接続するスマートな求人掲示板アシスタントを構築してデプロイしました。このアシスタントは、標準 SQL クエリとセマンティック ベクトル検索の両方を使用します。

学習した内容

  • MCP が AI エージェントのツールアクセスを標準化する方法と、MCP Toolbox for Databases がこれをデータベース オペレーションに適用する方法(カスタム データベース コードを宣言型 YAML 構成に置き換える)
  • cloud-sql-postgres ソースタイプを使用して Cloud SQL PostgreSQL を Toolbox データソースとして構成する方法
  • SQL インジェクションを防ぐパラメータ化されたステートメントを使用して標準 SQL クエリツールを定義する方法
  • pgvector と gemini-embedding-001 を使用してベクトル検索を有効にする方法(自動クエリ エンベディング用の embeddedBy パラメータを使用)
  • valueFromParam がベクトル自動取り込みを可能にする仕組み - LLM がテキストの説明を提供し、Toolbox がテキストとともにベクトルをコピー、埋め込み、保存する
  • ADK の ToolboxToolset が実行中の Toolbox サーバーからツールを読み込み、エージェント コードを最小限に抑え、データベース ロジックを完全に分離する方法
  • ツールボックス MCP サーバーと ADK エージェントの両方を個別のサービスとして Cloud Run にデプロイする方法

クリーンアップ

この Codelab で作成したリソースについて、Google Cloud アカウントに課金されないようにするには、個々のリソースを削除するか、プロジェクト全体を削除します。

最も簡単にクリーンアップするには、プロジェクトを削除します。これにより、プロジェクトに関連付けられているすべてのリソースが削除されます。

gcloud projects delete $GOOGLE_CLOUD_PROJECT

オプション 2: 個々のリソースを削除する

プロジェクトを保持し、この Codelab で作成したリソースのみを削除する場合は:

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