ฐานข้อมูลในฐานะเครื่องมือ: RAG แบบเอเจนต์ที่มี ADK, MCP Toolbox และ Cloud SQL

1. บทนำ

เอเจนต์ AI จะมีประโยชน์เพียงใดก็ขึ้นอยู่กับข้อมูลที่เข้าถึงได้ ข้อมูลในโลกแห่งความเป็นจริงส่วนใหญ่อยู่ในฐานข้อมูล และการเชื่อมต่อเอเจนต์กับฐานข้อมูลมักหมายถึงการเขียนการจัดการการเชื่อมต่อ ตรรกะการค้นหา และไปป์ไลน์การฝังภายในโค้ดเอเจนต์ ตัวแทนทุกรายที่ต้องการเข้าถึงฐานข้อมูลจะต้องทำซ้ำงานนี้ และการเปลี่ยนแปลงการค้นหาทุกครั้งจะต้องมีการติดตั้งใช้งานตัวแทนอีกครั้ง

Codelab นี้แสดงแนวทางที่แตกต่างออกไป คุณประกาศเครื่องมือฐานข้อมูลในไฟล์ YAML ซึ่งได้แก่ การค้นหา SQL มาตรฐาน การค้นหาความคล้ายคลึงของเวกเตอร์ หรือแม้แต่การสร้างการฝังอัตโนมัติ และ MCP Toolbox สำหรับฐานข้อมูลจะจัดการการดำเนินการฐานข้อมูลทั้งหมดในฐานะเซิร์ฟเวอร์ MCP โค้ดของเอเจนต์จะยังคงมีขนาดเล็ก: โหลดเครื่องมือ แล้วปล่อยให้ Gemini ตัดสินใจว่าจะเรียกใช้เครื่องมือใด

สิ่งที่คุณจะสร้าง

ผู้ช่วยกระดานหางานอัจฉริยะสำหรับ "TechJobs" ซึ่งเป็น Agent ADK ที่ทำงานด้วยระบบ Gemini ที่ช่วยให้นักพัฒนาซอฟต์แวร์เรียกดูประกาศรับสมัครงานด้านเทคโนโลยีโดยใช้ตัวกรองมาตรฐาน (บทบาท ชุดซอฟต์แวร์โครงสร้างพื้นฐาน) และค้นพบงานผ่านคำอธิบายภาษาธรรมชาติ เช่น "ฉันต้องการงานระยะไกลที่ทำงานเกี่ยวกับแชทบ็อต AI" Agent จะอ่านและเขียนไปยังฐานข้อมูล Cloud SQL PostgreSQL ผ่าน MCP Toolbox สำหรับฐานข้อมูลทั้งหมด ซึ่งจะจัดการการเข้าถึงฐานข้อมูลทั้งหมด รวมถึงการสร้างการฝังอัตโนมัติสำหรับการค้นหาเวกเตอร์ เมื่อสิ้นสุดแล้ว ทั้ง Toolbox และ Agent จะทำงานใน Cloud Run

eb6de681c40990c1.jpeg

สิ่งที่คุณจะได้เรียนรู้

  • วิธีที่ MCP (Model Context Protocol) สร้างมาตรฐานการเข้าถึงเครื่องมือสำหรับ Agent AI และวิธีที่ MCP Toolbox สำหรับฐานข้อมูลใช้มาตรฐานนี้กับการดำเนินการฐานข้อมูล
  • ตั้งค่า MCP Toolbox สำหรับฐานข้อมูลเป็นมิดเดิลแวร์ระหว่าง Agent ADK กับ Cloud SQL PostgreSQL
  • กำหนดเครื่องมือฐานข้อมูลแบบประกาศใน tools.yaml โดยไม่ต้องมีโค้ดฐานข้อมูลใน Agent
  • สร้าง ADK Agent ที่โหลดเครื่องมือจากเซิร์ฟเวอร์ Toolbox ที่ทำงานอยู่โดยใช้ ToolboxToolset
  • สร้าง Vector Embeddings โดยใช้ฟังก์ชัน embedding() ในตัวของ Cloud SQL และเปิดใช้การค้นหาเชิงความหมายด้วย pgvector
  • ใช้ฟีเจอร์ valueFromParam เพื่อการนำเข้าเวกเตอร์อัตโนมัติในการดำเนินการเขียน
  • ติดตั้งใช้งานทั้งเซิร์ฟเวอร์ Toolbox และ Agent ADK ใน Cloud Run

ข้อกำหนดเบื้องต้น

  • บัญชี Google Cloud ที่มีบัญชีสำหรับการเรียกเก็บเงินช่วงทดลองใช้
  • มีความรู้พื้นฐานเกี่ยวกับ Python และ SQL
  • ประสบการณ์การใช้งาน Cloud Database และ ADK มาก่อนจะเป็นประโยชน์

2. ตั้งค่าสภาพแวดล้อม

ขั้นตอนนี้จะเตรียมสภาพแวดล้อม Cloud Shell กำหนดค่าโปรเจ็กต์ Google Cloud และโคลนที่เก็บข้อมูลอ้างอิง

เปิด Cloud Shell

เปิด Cloud Shell ในเบราว์เซอร์ Cloud Shell มีสภาพแวดล้อมที่กำหนดค่าไว้ล่วงหน้าพร้อมเครื่องมือทั้งหมดที่คุณต้องการสำหรับ Codelab นี้ คลิกให้สิทธิ์เมื่อมีข้อความแจ้งให้ทำ

จากนั้นคลิก "ดู" -> "เทอร์มินัล" เพื่อเปิดเทอร์มินัล อินเทอร์เฟซของคุณควรมีลักษณะคล้ายกับภาพนี้

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

สร้างไฟล์ .env ที่มีตัวแปรสถานที่ตั้ง

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

หากต้องการลดความซับซ้อนในการตั้งค่าโปรเจ็กต์ในเทอร์มินัล ให้ดาวน์โหลดสคริปต์การตั้งค่าโปรเจ็กต์นี้ลงในไดเรกทอรีการทำงาน

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

เรียกใช้สคริปต์ โดยจะยืนยันบัญชีสำหรับการเรียกเก็บเงินของช่วงทดลองใช้ สร้างโปรเจ็กต์ใหม่ (หรือตรวจสอบโปรเจ็กต์ที่มีอยู่) บันทึกรหัสโปรเจ็กต์ลงในไฟล์ .env ในไดเรกทอรีปัจจุบัน และตั้งค่าโปรเจ็กต์ที่ใช้งานอยู่ใน gcloud

bash setup_verify_trial_project.sh && source .env

สคริปต์จะทำสิ่งต่อไปนี้

  1. ตรวจสอบว่าคุณมีบัญชีสำหรับการเรียกเก็บเงินสำหรับการทดลองใช้ที่ใช้งานอยู่
  2. ตรวจสอบโปรเจ็กต์ที่มีอยู่ใน .env (หากมี)
  3. สร้างโปรเจ็กต์ใหม่หรือใช้โปรเจ็กต์ที่มีอยู่
  4. ลิงก์บัญชีสำหรับการเรียกเก็บเงินของช่วงทดลองใช้กับโปรเจ็กต์
  5. บันทึกรหัสโปรเจ็กต์ไปยัง .env
  6. ตั้งค่าโปรเจ็กต์เป็นโปรเจ็กต์ gcloud ที่ใช้งานอยู่

ยืนยันว่าตั้งค่าโปรเจ็กต์ถูกต้องแล้วโดยตรวจสอบข้อความสีเหลืองข้างไดเรกทอรีการทำงานในพรอมต์เทอร์มินัลของ Cloud Shell โดยควรแสดงรหัสโปรเจ็กต์ของคุณ

dcba35ce1389f313.png

เปิดใช้งาน API ที่จำเป็น

จากนั้นเราต้องเปิดใช้ API หลายรายการสำหรับผลิตภัณฑ์ที่เราจะโต้ตอบด้วย

gcloud services enable \
  aiplatform.googleapis.com \
  sqladmin.googleapis.com \
  compute.googleapis.com \
  run.googleapis.com \
  cloudbuild.googleapis.com \
  artifactregistry.googleapis.com
  • Vertex AI API (aiplatform.googleapis.com) - Agent ของคุณใช้โมเดล Gemini และกล่องเครื่องมือใช้ Embedding API สำหรับการค้นหาเวกเตอร์
  • Cloud SQL Admin API (sqladmin.googleapis.com) - คุณจัดสรรและจัดการอินสแตนซ์ PostgreSQL
  • Compute Engine API (compute.googleapis.com) — จำเป็นสำหรับการสร้างอินสแตนซ์ Cloud SQL
  • Cloud Run, Cloud Build, Artifact Registry - ใช้ในขั้นตอนการทำให้ใช้งานได้ในภายหลังใน Codelab นี้

3. การเตรียมสคริปต์สำหรับการเริ่มต้นฐานข้อมูล

ขั้นตอนนี้จะเริ่มสร้างอินสแตนซ์ Cloud SQL และเรียกใช้สคริปต์การตั้งค่าอัตโนมัติที่รอให้อินสแตนซ์พร้อม จากนั้นจะสร้างฐานข้อมูล ป้อนข้อมูลประกาศรับสมัครงาน และสร้างการฝัง ทั้งหมดนี้จะดำเนินการในครั้งเดียว

ก่อนอื่น ให้เพิ่มรหัสผ่านของฐานข้อมูลลงในไฟล์ .env แล้วโหลดซ้ำโดยทำดังนี้

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

การสร้างสคริปต์ Bash สำหรับการสร้างอินสแตนซ์และฐานข้อมูล

จากนั้นสร้างสคริปต์ scripts/setup_database.sh ด้วยคำสั่งต่อไปนี้

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

จากนั้นคัดลอกโค้ดต่อไปนี้ลงในไฟล์ scripts/setup_database.sh

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

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

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

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

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

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

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

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

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

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

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

echo "      ✓ Permissions granted"
echo ""

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

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

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

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

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

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

uv run "$SETUP_SCRIPT"

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

การสร้างสคริปต์ Python สำหรับการเริ่มต้นข้อมูล

หลังจากนั้น ให้สร้างไฟล์ Python ของสคริปต์การเริ่มต้น scripts/setup_jobs_db.py โดยใช้คำสั่งด้านล่าง

cloudshell edit scripts/setup_jobs_db.py

จากนั้นคัดลอกโค้ดต่อไปนี้ลงในไฟล์ scripts/setup_jobs_db.py

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

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

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

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

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


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

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


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


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

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

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


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

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

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


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

    try:
        create_schema(cursor)
        conn.commit()

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

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

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


if __name__ == "__main__":
    main()

ตอนนี้เรามาดูขั้นตอนถัดไปกัน

4. สร้างและเริ่มต้นฐานข้อมูล

ตอนนี้สคริปต์ของเราพร้อมที่จะดำเนินการแล้ว เราจะต้องใช้ Python เพื่อเรียกใช้สคริปต์ที่เตรียมไว้ ดังนั้นมาเตรียมสคริปต์นั้นก่อน

ตั้งค่าโปรเจ็กต์ Python

uv เป็นตัวจัดการแพ็กเกจและโปรเจ็กต์ Python ที่รวดเร็วซึ่งเขียนด้วย Rust ( เอกสารประกอบของ uv ) Codelab นี้ใช้เพื่อความเร็วและความเรียบง่ายในการดูแลโปรเจ็กต์ Python

เริ่มต้นโปรเจ็กต์ Python และเพิ่มการอ้างอิงที่จำเป็น

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

โปรดทราบว่าเราใช้ cloud-sql-python-connector Python SDK ที่นี่เพื่อเริ่มต้นการเชื่อมต่อที่ปลอดภัยกับอินสแตนซ์ฐานข้อมูลของเรา ซึ่งได้รับการตรวจสอบสิทธิ์โดยใช้ข้อมูลรับรองเริ่มต้นของแอปพลิเคชัน

เรียกใช้สคริปต์การตั้งค่า

ตอนนี้เราสามารถเรียกใช้สคริปต์การตั้งค่าในเบื้องหลังและตรวจสอบเอาต์พุตของคอนโซลที่จะเขียนลงในไฟล์ logs/atabase_setup.log โดยใช้คำสั่งต่อไปนี้ คุณไปที่ส่วนถัดไปได้ขณะรอให้การดำเนินการนี้เสร็จสิ้น

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

ดาวน์โหลดไบนารีของกล่องเครื่องมือ

ในบทแนะนำนี้ เราจะใช้ MCP Toolbox ซึ่งมาพร้อมกับไบนารีที่สร้างไว้ล่วงหน้าซึ่งพร้อมใช้งานในสภาพแวดล้อม Linux ตอนนี้เรามาดาวน์โหลดในเบื้องหลังกันเลยเพราะอาจใช้เวลานาน เรียกใช้คำสั่งต่อไปนี้เพื่อดาวน์โหลดไบนารีและตรวจสอบบันทึกเอาต์พุตใน logs/toolbox_dl.log คุณไปที่ส่วนถัดไปได้ขณะรอให้การดำเนินการนี้เสร็จสิ้น

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

ทำความเข้าใจสคริปต์การตั้งค่า scripts/setup_database.sh

ตอนนี้มาลองทำความเข้าใจสคริปต์การตั้งค่าที่เรากำหนดค่าไว้ก่อนหน้านี้กัน โดยจะดำเนินการตามกระบวนการต่อไปนี้

  1. คำสั่งแรกที่เราเรียกใช้คือคำสั่ง gcloud sql instances create ที่มีแฟล็กต่อไปนี้
  • db-custom-1-3840 คือระดับ Cloud SQL แบบคอร์เฉพาะที่เล็กที่สุด (1 vCPU, RAM 3.75 GB) ในรุ่น ENTERPRISE อ่านรายละเอียดเพิ่มเติมได้ที่นี่ ต้องใช้คอร์แบบเฉพาะสำหรับการผสานรวม Vertex AI ML โดยที่ระดับคอร์ที่ใช้แกนประมวลผลร่วมกัน (db-f1-micro, db-g1-small) ไม่รองรับ
  • --root-password ตั้งค่ารหัสผ่านสำหรับผู้ใช้ postgres เริ่มต้น
  • --enable-google-ml-integration ช่วยให้การผสานรวมในตัวของ Cloud SQL กับ Vertex AI ทำงานได้ ซึ่งช่วยให้คุณเรียกใช้โมเดลการฝังจาก SQL ได้โดยตรงโดยใช้ฟังก์ชัน embedding()
  1. ตรวจสอบว่าอินสแตนซ์อยู่ในสถานะ RUNNABLE แล้วหรือไม่
  2. ให้สิทธิ์บัญชีบริการของอินสแตนซ์ Cloud SQL ในการเรียกใช้ Vertex AI โดยใช้คำสั่ง gcloud projects add-iam-policy-binding ซึ่งจำเป็นสำหรับฟังก์ชัน embedding() ในตัวที่เราจะใช้เมื่อเริ่มต้นฐานข้อมูล
  3. การสร้างฐานข้อมูล
  4. การเรียกใช้สคริปต์การเริ่มต้น setup_jobs_db.py

ทำความเข้าใจสคริปต์เริ่มต้น scripts/setup_jobs_db.py

ตอนนี้มาดูสคริปต์การเริ่มต้นกัน สคริปต์นี้จะทำสิ่งต่อไปนี้

  1. เริ่มต้นการเชื่อมต่อกับอินสแตนซ์ฐานข้อมูล
  2. ติดตั้งส่วนขยาย PostgreSQL 2 รายการ
  • google_ml_integration — มีฟังก์ชัน SQL ของ embedding() ซึ่งเรียกใช้โมเดลการฝังของ Vertex AI จาก SQL โดยตรง ส่วนขยายนี้เป็นส่วนขยายระดับฐานข้อมูลที่ทำให้ฟังก์ชัน ML พร้อมใช้งานภายใน jobs_db แฟล็กระดับอินสแตนซ์ (--enable-google-ml-integration) ที่คุณตั้งค่าในระหว่างการสร้างอินสแตนซ์จะอนุญาตให้ VM ของ Cloud SQL เข้าถึง Vertex AI ได้ ส่วนการขยายจะทำให้ฟังก์ชัน SQL พร้อมใช้งานภายในฐานข้อมูลที่เฉพาะเจาะจงนี้
  • vector (pgvector) — เพิ่มประเภทข้อมูล vector และตัวดำเนินการระยะทางสำหรับการจัดเก็บและค้นหาการฝัง
  1. สร้างตาราง โปรดทราบว่าคอลัมน์ description_embedding คือ vector(3072) ซึ่งเป็นคอลัมน์ pgvector ที่จัดเก็บเวกเตอร์ 3072 มิติ
  2. เริ่มต้นข้อมูลงานเบื้องต้น
  3. สร้างข้อมูลการฝังจากฟิลด์ description และกรอก description_embedding โดยใช้การผสานรวม Vertex ในตัวผ่านฟังก์ชัน embedding()
  • embedding('gemini-embedding-001', description) — เรียกใช้โมเดลการฝัง Gemini ของ Vertex AI โดยตรงจาก SQL โดยส่งdescriptionข้อความของแต่ละงาน นี่คือส่วนขยาย google_ml_integration ที่คุณติดตั้งในสคริปต์เริ่มต้น
  • ::vector — แปลงอาร์เรย์ของค่าทศนิยมที่ส่งกลับเป็นประเภท vector ของ pgvector เพื่อให้จัดเก็บและค้นหาด้วยโอเปอเรเตอร์ระยะทางได้
  • UPDATE จะทำงานในทั้ง 15 แถว โดยสร้างการฝังมิติ 3072 จำนวน 1 รายการต่อคำอธิบายงาน

ซึ่งจะเตรียมข้อมูลเริ่มต้นที่ตัวแทนของเราจะเข้าถึงได้

5. กำหนดค่า MCP Toolbox สำหรับฐานข้อมูล

ขั้นตอนนี้จะแนะนำ MCP Toolbox สำหรับฐานข้อมูล กำหนดค่าให้เชื่อมต่อกับอินสแตนซ์ Cloud SQL และกำหนดเครื่องมือคำค้นหา SQL มาตรฐาน 2 รายการ

MCP คืออะไรและเหตุใดจึงควรใช้กล่องเครื่องมือ

e7b9be2e1c98b4db.png

MCP (Model Context Protocol) เป็นโปรโตคอลแบบเปิดที่กำหนดมาตรฐานวิธีที่ Agent AI ค้นพบและโต้ตอบกับเครื่องมือภายนอก โดยจะกำหนดรูปแบบไคลเอ็นต์-เซิร์ฟเวอร์ ซึ่ง Agent จะโฮสต์ไคลเอ็นต์ MCP และเซิร์ฟเวอร์ MCP จะแสดงเครื่องมือ ไคลเอ็นต์ที่เข้ากันได้กับ MCP สามารถใช้เซิร์ฟเวอร์ที่เข้ากันได้กับ MCP ได้ โดยเอเจนต์ไม่จำเป็นต้องใช้โค้ดการผสานรวมที่กำหนดเองสำหรับแต่ละเครื่องมือ

5bf26eeecad2277d.png

MCP Toolbox สำหรับฐานข้อมูลเป็นเซิร์ฟเวอร์ MCP โอเพนซอร์สที่สร้างขึ้นเพื่อการเข้าถึงฐานข้อมูลโดยเฉพาะ หากไม่มีฟีเจอร์นี้ คุณจะต้องเขียนฟังก์ชัน Python ที่เปิดการเชื่อมต่อฐานข้อมูล จัดการกลุ่มการเชื่อมต่อ สร้างการค้นหาที่มีพารามิเตอร์เพื่อป้องกันการแทรก SQL จัดการข้อผิดพลาด และฝังโค้ดทั้งหมดนั้นไว้ใน Agent ตัวแทนทุกคนที่ต้องการเข้าถึงฐานข้อมูลจะต้องทำซ้ำ การเปลี่ยนคำค้นหาหมายถึงการติดตั้งใช้งาน Agent อีกครั้ง

โดยคุณจะเขียนไฟล์ YAML ด้วย Toolbox เครื่องมือแต่ละอย่างจะแมปกับคำสั่ง SQL ที่มีพารามิเตอร์ Toolbox จัดการการจัดกลุ่มการเชื่อมต่อ คำค้นที่มีพารามิเตอร์ การตรวจสอบสิทธิ์ และความสามารถในการสังเกต เครื่องมือจะแยกออกจากเอเจนต์ ซึ่งคุณสามารถอัปเดตคําค้นหาได้โดยการแก้ไข tools.yaml และรีสตาร์ทกล่องเครื่องมือโดยไม่ต้องแตะต้องโค้ดเอเจนต์ เครื่องมือเดียวกันนี้ใช้ได้กับ ADK, LangGraph, LlamaIndex หรือเฟรมเวิร์กที่เข้ากันได้กับ MCP

เขียนการกำหนดค่าเครื่องมือ

ตอนนี้เราต้องสร้างไฟล์ชื่อ tools.yaml ใน Cloud Shell Editor เพื่อตั้งค่าเครื่องมือ

cloudshell edit tools.yaml

ไฟล์ใช้ YAML แบบหลายเอกสาร โดยแต่ละบล็อกที่คั่นด้วย --- จะเป็นทรัพยากรแบบสแตนด์อโลน ทรัพยากรทุกรายการมี kind ที่ประกาศว่าทรัพยากรนั้นคืออะไร (sources สำหรับการเชื่อมต่อฐานข้อมูล tools สำหรับการดำเนินการที่เรียกใช้ได้ของเอเจนต์) และ type ที่ระบุแบ็กเอนด์ (cloud-sql-postgres สำหรับแหล่งที่มา postgres-sql สำหรับเครื่องมือที่ใช้ SQL) เครื่องมือจะอ้างอิงแหล่งที่มาด้วย name ซึ่งเป็นวิธีที่กล่องเครื่องมือทราบว่าต้องใช้ Connection Pool ใด ตัวแปรสภาพแวดล้อมใช้ไวยากรณ์ ${VAR_NAME} และจะได้รับการแก้ไขเมื่อเริ่มต้นระบบ

ตอนนี้มาคัดลอกสคริปต์ต่อไปนี้ลงในไฟล์ tools.yaml ก่อน

# tools.yaml

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

---

สคริปต์นี้จะกำหนดทรัพยากรต่อไปนี้

  • แหล่งที่มา (jobs-db) — บอกกล่องเครื่องมือวิธีกำหนดค่าการเชื่อมต่อกับอินสแตนซ์ Cloud SQL PostgreSQL cloud-sql-postgres ประเภทนี้ใช้เครื่องมือเชื่อมต่อ Cloud SQL ภายใน โดยจะจัดการการตรวจสอบสิทธิ์และการเชื่อมต่อที่ปลอดภัยโดยอัตโนมัติ ระบบจะแก้ไขตัวยึดตำแหน่ง ${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 และ 2 (search-jobs, get-job-details) - เครื่องมือค้นหา SQL มาตรฐาน แต่ละรายการจะแมปชื่อเครื่องมือ (สิ่งที่เอเจนต์เห็น) กับคำสั่ง SQL ที่กำหนดพารามิเตอร์ (สิ่งที่ฐานข้อมูลดำเนินการ) พารามิเตอร์ใช้ตัวยึดตำแหน่งเชิงตำแหน่ง $1, $2 กล่องเครื่องมือจะดำเนินการเหล่านี้เป็นคำสั่งที่เตรียมไว้ ซึ่งจะป้องกันการแทรก SQL

มาต่อกันเลย เพิ่มสคริปต์ต่อไปนี้ใต้สัญลักษณ์ --- ใน tools.yaml

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

---

สคริปต์นี้จะกำหนดทรัพยากรต่อไปนี้

  • โมเดลการฝัง (gemini-embedding) - กำหนดค่ากล่องเครื่องมือให้เรียกใช้โมเดล gemini-embedding-001 ของ Gemini เพื่อสร้างการฝังข้อความแบบ 3072 มิติ กล่องเครื่องมือใช้ข้อมูลรับรองเริ่มต้นของแอปพลิเคชัน (ADC) เพื่อตรวจสอบสิทธิ์ โดยไม่จำเป็นต้องใช้คีย์ API ใน Cloud Shell หรือ Cloud Run โปรดทราบว่า dimension ที่กำหนดค่าไว้ที่นี่ต้องเหมือนกับที่เรากำหนดค่าไว้ก่อนหน้านี้เพื่อเริ่มต้นฐานข้อมูล

มาต่อกันเลย เพิ่มสคริปต์ต่อไปนี้ใต้สัญลักษณ์ --- ใน tools.yaml

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

---

สคริปต์นี้จะกำหนดทรัพยากรต่อไปนี้

  • เครื่องมือ 3 (search-jobs-by-description) - เครื่องมือค้นหาเวกเตอร์ พารามิเตอร์ search_query มี embeddedBy: gemini-embedding ซึ่งจะบอกกล่องเครื่องมือให้สกัดกั้นข้อความดิบ ส่งไปยังโมเดลการฝัง และใช้เวกเตอร์ที่ได้ในคำสั่ง SQL โอเปอเรเตอร์ <=> คือระยะทางโคไซน์ของ pgvector ซึ่งค่าที่น้อยกว่าหมายถึงคำอธิบายที่คล้ายกันมากขึ้น

สุดท้าย ให้เพิ่มเครื่องมือสุดท้ายภายใต้สัญลักษณ์ --- ใน tools.yaml

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

สคริปต์นี้จะกำหนดทรัพยากรต่อไปนี้

  • เครื่องมือ 4 (add-job) - แสดงการนำเข้าเวกเตอร์ พารามิเตอร์ description_vector มีฟิลด์พิเศษ 2 รายการ ดังนี้
  • valueFromParam: description — กล่องเครื่องมือจะคัดลอกค่าจากพารามิเตอร์ description ไปยังพารามิเตอร์นี้ LLM จะไม่เห็นพารามิเตอร์นี้
  • embeddedBy: gemini-embedding — กล่องเครื่องมือจะฝังข้อความที่คัดลอกลงในเวกเตอร์ก่อนส่งไปยัง SQL

ผลลัพธ์คือการเรียกใช้เครื่องมือเดียวจะจัดเก็บทั้งข้อความคำอธิบายดิบและเวกเตอร์ฝังโดยที่เอเจนต์ไม่จำเป็นต้องรู้เรื่องการฝัง

รูปแบบ YAML แบบหลายเอกสารจะแยกแต่ละทรัพยากรด้วย --- เอกสารแต่ละฉบับมีฟิลด์ kind, name และ type ที่กำหนดว่าเอกสารนั้นคืออะไร โดยสรุปคือเราได้กำหนดค่าสิ่งต่อไปนี้ทั้งหมดแล้ว

  • กำหนดฐานข้อมูลต้นทาง
  • กำหนดเครื่องมือ ( tool 1 และ 2 ) เพื่อค้นหาฐานข้อมูลด้วยตัวกรองมาตรฐาน
  • กำหนดโมเดลการฝัง
  • กำหนดเครื่องมือเพื่อทำการค้นหาเวกเตอร์ ( tool 3 ) ไปยังฐานข้อมูล
  • กำหนดเครื่องมือเพื่อทำการนำเข้าข้อมูลเวกเตอร์ ( เครื่องมือ 4) ไปยังฐานข้อมูล

6. การเรียกใช้เซิร์ฟเวอร์ MCP Toolbox

ในขั้นตอนก่อนหน้า เราได้ตั้งค่าที่จำเป็นสำหรับกล่องเครื่องมือ MCP แล้ว ตอนนี้เราพร้อมที่จะเรียกใช้เซิร์ฟเวอร์แล้ว

ยืนยันข้อมูลเริ่มต้น

ก่อนเริ่มใช้กล่องเครื่องมือ ให้ตรวจสอบว่าการตั้งค่าฐานข้อมูลเสร็จสมบูรณ์แล้ว สร้างสคริปต์ Python scripts/verify_database.py โดยใช้คำสั่งต่อไปนี้

cloudshell edit scripts/verify_seed.py

จากนั้นคัดลอกโค้ดต่อไปนี้ลงในไฟล์ scripts/verify_seed.py

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

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

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

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

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


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

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

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

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

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

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

        cursor.close()
        conn.close()

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

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


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

สคริปต์นี้จะตรวจสอบจำนวนข้อมูลประกาศรับสมัครงานและการฝัง เรียกใช้สคริปต์โดยใช้คำสั่งต่อไปนี้

uv run scripts/verify_seed.py

หากเห็นเอาต์พุตเทอร์มินัลต่อไปนี้ แสดงว่าข้อมูลพร้อมแล้ว

Jobs: 15/15
Embeddings: 15/15

✓ Database ready!

เริ่มเซิร์ฟเวอร์กล่องเครื่องมือ

ในขั้นตอนการตั้งค่าก่อนหน้านี้ เราได้ดาวน์โหลดไฟล์toolboxที่เรียกใช้งานได้แล้ว ตรวจสอบว่ามีไฟล์ไบนารีนี้และดาวน์โหลดสำเร็จ หากไม่มี ให้ดาวน์โหลดและรอจนกว่าจะเสร็จสิ้น

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

เราจะต้องเปิดเผยตัวแปร .env ให้กับกระบวนการย่อยซึ่งเรียกใช้โดยกล่องเครื่องมือ MCP เรียกใช้คำสั่งต่อไปนี้เพื่อเริ่มเซิร์ฟเวอร์กล่องเครื่องมือและบันทึกเอาต์พุตของคอนโซลลงในไฟล์ logs/mcp_toolbox.log

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

คุณควรเห็นเอาต์พุตในไฟล์ logs/mcp_toolbox.log ที่ยืนยันว่าเซิร์ฟเวอร์พร้อมแล้วดังที่แสดงด้านล่าง

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

ยืนยันเครื่องมือ

ส่งคำค้นหาไปยัง Toolbox API เพื่อแสดงรายการเครื่องมือที่ลงทะเบียนทั้งหมด

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

คุณควรเห็นเครื่องมือพร้อมคำอธิบายและพารามิเตอร์ ดังที่แสดงด้านล่าง

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

ทดสอบsearch-jobsเครื่องมือโดยตรง

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

คำตอบควรมีงานวิศวกรแบ็กเอนด์ 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 Agent

ตอนนี้เราจะใช้ ADK ใน Python สำหรับโปรเจ็กต์นี้ มาเพิ่มการอ้างอิงที่จำเป็นกัน

uv add google-adk==1.29.0 toolbox-adk==1.0.0
  • google-adk - ชุดพัฒนา Agent ของ Google ซึ่งรวมถึง Gemini SDK
  • toolbox-adk — การผสานรวม ADK สำหรับ MCP Toolbox สำหรับฐานข้อมูล

สร้างโครงสร้างไดเรกทอรีของเอเจนต์

ADK คาดหวังเลย์เอาต์โฟลเดอร์ที่เฉพาะเจาะจง ได้แก่ ไดเรกทอรีที่ตั้งชื่อตามเอเจนต์ของคุณซึ่งมี __init__.py, agent.py และ .env โดยมีคำสั่งในตัวเพื่อสร้างโครงสร้างได้อย่างรวดเร็ว ดังนี้

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

ตอนนี้ไดเรกทอรีควรมีลักษณะดังนี้

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

จากนั้นเราจะต้องผสานรวมเอเจนต์ ADK กับเซิร์ฟเวอร์ Toolbox ที่ทำงานอยู่ และทดสอบเครื่องมือทั้ง 4 รายการ ได้แก่ การค้นหามาตรฐาน การค้นหาเชิงความหมาย และการนำเข้าเวกเตอร์ โค้ดของเอเจนต์มีขนาดเล็ก โดยตรรกะของฐานข้อมูลทั้งหมดจะอยู่ใน tools.yaml

กำหนดค่าสภาพแวดล้อมของ Agent

ADK อ่าน GOOGLE_GENAI_USE_VERTEXAI, GOOGLE_CLOUD_PROJECT และ GOOGLE_CLOUD_LOCATION จากสภาพแวดล้อมของ Shell ที่คุณตั้งค่าไว้แล้วในขั้นตอนก่อนหน้า ตัวแปรเฉพาะตัวแทนมีเพียง TOOLBOX_URL เท่านั้น ให้ต่อท้ายตัวแปรนี้ในไฟล์ .env ของตัวแทน

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

อัปเดตโมดูลตัวแทน

เปิด jobs_agent/agent.py ใน Cloud Shell Editor

cloudshell edit jobs_agent/agent.py

และเขียนทับเนื้อหาด้วยโค้ดต่อไปนี้

# jobs_agent/agent.py
import os

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

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

toolbox = ToolboxToolset(TOOLBOX_URL)

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

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

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

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

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

โปรดทราบว่าไม่มีโค้ดฐานข้อมูลที่นี่ — ToolboxToolset จะเชื่อมต่อกับเซิร์ฟเวอร์ Toolbox เมื่อเริ่มต้นระบบและโหลดเครื่องมือทั้งหมดที่พร้อมใช้งาน Agent จะเรียกใช้เครื่องมือตามชื่อ ส่วนกล่องเครื่องมือจะแปลการเรียกเหล่านั้นเป็นการค้นหา SQL กับ Cloud SQL

ตัวแปรสภาพแวดล้อม TOOLBOX_URL จะมีค่าเริ่มต้นเป็น http://127.0.0.1:5000 สำหรับการพัฒนาในเครื่อง เมื่อติดตั้งใช้งานใน Cloud Run ในภายหลัง คุณจะลบล้างการตั้งค่านี้ด้วย URL ของ Cloud Run ของบริการ Toolbox โดยไม่ต้องเปลี่ยนแปลงโค้ด

ปัจจุบันคำสั่งอ้างอิงเฉพาะเครื่องมือมาตรฐาน 2 รายการ (search-jobs และ get-job-details) คุณจะขยายคำสั่งในขั้นตอนถัดไปเมื่อเพิ่มเครื่องมือการค้นหาเชิงความหมายและการนำเข้า

ทดสอบ Agent

เปิด UI สำหรับนักพัฒนาแอป ADK โดยทำดังนี้

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

เปิด URL ที่แสดงในเทอร์มินัล (โดยปกติคือ http://localhost:8000) โดยใช้ฟีเจอร์การแสดงตัวอย่างเว็บของ Cloud Shell หรือ Ctrl + คลิก URL ที่แสดงในเทอร์มินัล เลือก jobs_agent จากเมนูแบบเลื่อนลงของเอเจนต์ที่มุมซ้ายบน

ทดสอบคำค้นหามาตรฐาน

ลองใช้พรอมต์ต่อไปนี้เพื่อยืนยันเครื่องมือ SQL มาตรฐาน

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

93ac33e7f73aa0b9.png 240c53376042a916.png

ลองใช้คำอธิบายภาษาธรรมชาติที่ไม่ได้เชื่อมโยงกับบทบาทหรือชุดซอฟต์แวร์โครงสร้างพื้นฐานเฉพาะ

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

เอเจนต์จะพยายามเลือกเครื่องมือที่เหมาะสมตามประเภทคำค้นหา โดยตัวกรองที่มีโครงสร้างจะผ่าน search-jobs ส่วนคำอธิบายที่เป็นภาษาธรรมชาติจะผ่าน search-jobs-by-description

b0ea629f5c9b4c26.png

ทดสอบการส่งผ่านข้อมูลเวกเตอร์

ขอให้ตัวแทนเพิ่มงานใหม่โดยทำดังนี้

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

c601a7a9bc0a705b.png

ตอนนี้ลองค้นหาโดยใช้คำต่อไปนี้

Find me jobs involving autonomous systems and working with physical hardware

ระบบสร้างการฝังโดยอัตโนมัติระหว่างการ INSERT จึงไม่จำเป็นต้องมีขั้นตอนแยกต่างหาก

5a3d8e6f523dc18b.png

ตอนนี้คุณมีแอปพลิเคชัน Agentic RAG ที่ทำงานได้อย่างเต็มรูปแบบซึ่งใช้ ADK, MCP Toolbox และ CloudSQL แล้ว ยินดีด้วย มาดูขั้นตอนเพิ่มเติมในการทำให้แอปเหล่านี้ใช้งานได้กับ Cloud Run กัน

ตอนนี้มาหยุด UI สำหรับนักพัฒนาซอฟต์แวร์โดยการสิ้นสุดกระบวนการด้วยการกด Ctrl+C 2 ครั้งก่อนดำเนินการต่อ

8. ทำให้ใช้งานได้กับ Cloud Run

เอเจนต์และกล่องเครื่องมือทำงานในเครื่อง ขั้นตอนนี้จะติดตั้งใช้งานทั้ง 2 อย่างเป็นบริการ Cloud Run เพื่อให้เข้าถึงได้ผ่านอินเทอร์เน็ต บริการ Toolbox ทำงานเป็นเซิร์ฟเวอร์ MCP ใน Cloud Run และบริการ Agent จะเชื่อมต่อกับเซิร์ฟเวอร์ดังกล่าว

เตรียมกล่องเครื่องมือสำหรับการติดตั้งใช้งาน

สร้างไดเรกทอรีการติดตั้งใช้งานสำหรับบริการกล่องเครื่องมือ

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

สร้าง Dockerfile สำหรับกล่องเครื่องมือ เปิด deploy-toolbox/Dockerfile ใน Cloud Shell Editor โดยทำดังนี้

cloudshell edit deploy-toolbox/Dockerfile

แล้วคัดลอกสคริปต์ต่อไปนี้ลงในสเปรดชีต

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

ไบนารีของกล่องเครื่องมือและ tools.yaml จะรวมอยู่ในอิมเมจ Debian ขนาดเล็ก Cloud Run จะกำหนดเส้นทางการรับส่งข้อมูลไปยังพอร์ต 8080

ติดตั้งใช้งานบริการกล่องเครื่องมือ

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

คำสั่งนี้จะส่งซอร์สไปยัง Cloud Build สร้างอิมเมจคอนเทนเนอร์ พุชไปยัง Artifact Registry และทำให้ใช้งานได้ใน Cloud Run กระบวนการนี้จะใช้เวลา 2-3 นาที เราสามารถตรวจสอบบันทึกกระบวนการติดตั้งใช้งานในlogs/deploy_toolbox.logไฟล์

เตรียมตัวแทนสำหรับการติดตั้งใช้งาน

ขณะที่กล่องเครื่องมือสร้าง ให้ตั้งค่าไฟล์การติดตั้งใช้งานของ Agent

สร้าง Dockerfile ในรูทของโปรเจ็กต์ เปิด Dockerfile ใน Cloud Shell Editor โดยทำดังนี้

cloudshell edit Dockerfile

จากนั้นคัดลอกเนื้อหาต่อไปนี้

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

Dockerfile นี้ใช้ ghcr.io/astral-sh/uv เป็นอิมเมจพื้นฐาน ซึ่งมีทั้ง Python และ uv ที่ติดตั้งไว้ล่วงหน้าแล้ว จึงไม่จำเป็นต้องติดตั้ง uv แยกต่างหากผ่าน pip

สร้างไฟล์ .dockerignore เพื่อยกเว้นไฟล์ที่ไม่จำเป็นจากอิมเมจคอนเทนเนอร์

cloudshell edit .dockerignore

จากนั้นคัดลอกสคริปต์ต่อไปนี้ลงในไฟล์

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

ติดตั้งใช้งานบริการของ Agent

รอให้การติดตั้งใช้งานกล่องเครื่องมือเสร็จสมบูรณ์ ตรวจสอบกระบวนการติดตั้งใช้งานอีกครั้งใน logs/deploy_toolbox.log เพื่อยืนยันกระบวนการ จากนั้นดึงข้อมูล URL ของ Cloud Run โดยใช้คำสั่งต่อไปนี้

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

คุณจะเห็นเอาต์พุตที่คล้ายกันดังนี้

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

จากนั้นมาตรวจสอบว่ากล่องเครื่องมือที่ติดตั้งใช้งานทำงานได้หรือไม่

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

หากเอาต์พุตแสดงเหมือนตัวอย่างนี้ แสดงว่าการติดตั้งใช้งานสำเร็จแล้ว

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

จากนั้นมาติดตั้งใช้งานเอเจนต์โดยส่งผ่าน URL ของกล่องเครื่องมือเป็นตัวแปรสภาพแวดล้อมกัน

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

รหัสตัวแทนจะอ่าน TOOLBOX_URL จากสภาพแวดล้อม (คุณตั้งค่านี้ไว้ก่อนหน้านี้) ในเครื่องจะชี้ไปยัง http://127.0.0.1:5000 แต่ใน Cloud Run จะชี้ไปยัง URL ของบริการ Toolbox โดยไม่ต้องเปลี่ยนแปลงโค้ดใดๆ

ทดสอบ Agent ที่ติดตั้งใช้งาน

เรียกข้อมูล URL ของ Cloud Run ของ Agent โดยทำดังนี้

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

เปิด URL ในเบราว์เซอร์ UI สำหรับนักพัฒนาซอฟต์แวร์ ADK จะโหลดขึ้นมา ซึ่งเป็นอินเทอร์เฟซเดียวกันกับที่คุณใช้ในเครื่อง แต่ตอนนี้ทำงานบน Cloud Run

เลือก jobs_agent จากเมนูแบบเลื่อนลง แล้วทดสอบ

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

ทั้ง 2 คำค้นหาจะทำงานผ่านบริการที่ติดตั้งใช้งานแล้ว โดย Agent ใน Cloud Run จะเรียกใช้ Toolbox ใน Cloud Run ซึ่งจะค้นหา Cloud SQL

9. ยินดีด้วย / ล้างข้อมูล

คุณได้สร้างและติดตั้งใช้งานผู้ช่วยค้นหางานอัจฉริยะที่ใช้ MCP Toolbox สำหรับฐานข้อมูลเพื่อเชื่อมต่อ Agent ADK กับ Cloud SQL PostgreSQL ทั้งการค้นหา SQL มาตรฐานและการค้นหาแบบเวกเตอร์เชิงความหมาย

สิ่งที่คุณได้เรียนรู้

  • วิธีที่ MCP สร้างมาตรฐานการเข้าถึงเครื่องมือสำหรับ AI Agent และวิธีที่ MCP Toolbox สำหรับฐานข้อมูลใช้การดำเนินการนี้กับการดำเนินการฐานข้อมูลโดยเฉพาะ ซึ่งแทนที่โค้ดฐานข้อมูลที่กำหนดเองด้วยการกำหนดค่า YAML แบบประกาศ
  • วิธีกำหนดค่า Cloud SQL PostgreSQL เป็นแหล่งข้อมูล Toolbox โดยใช้cloud-sql-postgresประเภทแหล่งที่มา
  • วิธีกำหนดเครื่องมือค้นหา SQL มาตรฐานด้วยคำสั่งที่มีพารามิเตอร์ซึ่งป้องกันการแทรก SQL
  • วิธีเปิดใช้การค้นหาเวกเตอร์โดยใช้ pgvector และ gemini-embedding-001 โดยมีพารามิเตอร์ embeddedBy สำหรับการฝังคำค้นหาอัตโนมัติ
  • วิธีที่ valueFromParam ช่วยให้การส่งผ่านข้อมูลเวกเตอร์อัตโนมัติเป็นไปได้ - LLM จะให้คำอธิบายข้อความ และกล่องเครื่องมือจะคัดลอก ฝัง และจัดเก็บเวกเตอร์ควบคู่ไปกับข้อความโดยอัตโนมัติ
  • วิธีที่ ToolboxToolset ของ ADK โหลดเครื่องมือจากเซิร์ฟเวอร์ Toolbox ที่ทำงานอยู่ ทำให้โค้ดของเอเจนต์มีขนาดเล็กที่สุดและตรรกะของฐานข้อมูลแยกออกจากกันอย่างสมบูรณ์
  • วิธีกำหนดค่าทั้งเซิร์ฟเวอร์ MCP ของ Toolbox และ Agent ADK ให้ใช้งานได้ใน Cloud Run เป็นบริการแยกต่างหาก

ล้างข้อมูล

หากต้องการหลีกเลี่ยงการเรียกเก็บเงินจากบัญชี Google Cloud สำหรับทรัพยากรที่สร้างขึ้นใน Codelab นี้ คุณสามารถลบทรัพยากรแต่ละรายการหรือลบทั้งโปรเจ็กต์ก็ได้

วิธีที่ง่ายที่สุดในการล้างข้อมูลคือการลบโปรเจ็กต์ การดำเนินการนี้จะนำทรัพยากรทั้งหมดที่เชื่อมโยงกับโปรเจ็กต์ออก

gcloud projects delete $GOOGLE_CLOUD_PROJECT

ตัวเลือกที่ 2: ลบทรัพยากรแต่ละรายการ

หากต้องการเก็บโปรเจ็กต์ไว้แต่ต้องการนำเฉพาะทรัพยากรที่สร้างขึ้นในโค้ดแล็บนี้ออก ให้ทำดังนี้

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