Cơ sở dữ liệu như một công cụ: RAG dựa trên tác nhân bằng ADK, MCP Toolbox và Cloud SQL

1. Giới thiệu

Các tác nhân AI chỉ hữu ích khi có thể truy cập vào dữ liệu. Hầu hết dữ liệu thực tế đều nằm trong cơ sở dữ liệu và việc kết nối các tác nhân với cơ sở dữ liệu thường có nghĩa là bạn phải viết quy trình quản lý kết nối, logic truy vấn và nhúng các quy trình vào trong mã tác nhân của mình. Mọi tác nhân cần truy cập vào cơ sở dữ liệu đều lặp lại công việc này và mọi thay đổi về truy vấn đều yêu cầu triển khai lại tác nhân.

Lớp học lập trình này trình bày một phương pháp khác. Bạn khai báo các công cụ cơ sở dữ liệu trong một tệp YAML (các truy vấn SQL chuẩn, tìm kiếm mức độ tương đồng của vectơ, thậm chí là tạo vectơ nhúng tự động) và Bộ công cụ MCP dành cho cơ sở dữ liệu sẽ xử lý tất cả các thao tác cơ sở dữ liệu dưới dạng một máy chủ MCP. Mã tác nhân của bạn vẫn ở mức tối thiểu: tải các công cụ, để Gemini quyết định gọi công cụ nào.

Sản phẩm bạn sẽ tạo ra

Trợ lý thông minh cho bảng tin việc làm cho "TechJobs" – một tác nhân ADK dựa trên Gemini, giúp nhà phát triển duyệt xem danh sách tin tuyển dụng trong ngành công nghệ bằng các bộ lọc tiêu chuẩn (vai trò, bộ phần mềm cơ sở) và khám phá việc làm thông qua nội dung mô tả bằng ngôn ngữ tự nhiên như "Tôi muốn làm việc từ xa trong lĩnh vực chatbot AI". Tác nhân này đọc và ghi vào cơ sở dữ liệu Cloud SQL PostgreSQL hoàn toàn thông qua Bộ công cụ MCP cho Cơ sở dữ liệu. Bộ công cụ này xử lý mọi hoạt động truy cập vào cơ sở dữ liệu, bao gồm cả việc tự động tạo vectơ nhúng để tìm kiếm vectơ. Đến cuối cùng, cả Toolbox và tác nhân đều chạy trên Cloud Run.

eb6de681c40990c1.jpeg

Kiến thức bạn sẽ học được

  • Cách MCP (Giao thức ngữ cảnh mô hình) chuẩn hoá quyền truy cập vào công cụ cho các tác nhân AI và cách MCP Toolbox for Databases áp dụng điều này cho các hoạt động cơ sở dữ liệu
  • Thiết lập Bộ công cụ MCP cho cơ sở dữ liệu làm phần mềm trung gian giữa một tác nhân ADK và Cloud SQL PostgreSQL
  • Xác định các công cụ cơ sở dữ liệu một cách khai báo trong tools.yaml – không có mã cơ sở dữ liệu trong tác nhân của bạn
  • Tạo một tác nhân ADK tải các công cụ từ một máy chủ Bộ công cụ đang chạy bằng cách sử dụng ToolboxToolset
  • Tạo các vectơ nhúng bằng hàm embedding() tích hợp của Cloud SQL và bật tính năng tìm kiếm ngữ nghĩa bằng pgvector
  • Sử dụng tính năng valueFromParam để tự động nhập vectơ trong các thao tác ghi
  • Triển khai cả máy chủ Toolbox và tác nhân ADK lên Cloud Run

Điều kiện tiên quyết

  • Tài khoản Google Cloud có tài khoản thanh toán dùng thử
  • Hiểu biết cơ bản về Python và SQL
  • Kinh nghiệm sử dụng Cơ sở dữ liệu đám mây và ADK sẽ rất hữu ích

2. Thiết lập môi trường

Bước này chuẩn bị môi trường Cloud Shell, định cấu hình dự án trên đám mây của bạn và sao chép kho lưu trữ tham chiếu.

Mở Cloud Shell

Mở Cloud Shell trong trình duyệt. Cloud Shell cung cấp một môi trường được định cấu hình sẵn với tất cả các công cụ bạn cần cho lớp học lập trình này. Nhấp vào Uỷ quyền khi được nhắc

Sau đó, nhấp vào "View" (Xem) -> "Terminal" (Thiết bị đầu cuối) để mở thiết bị đầu cuối.Giao diện của bạn sẽ trông tương tự như thế này

86307fac5da2f077.png

Đây sẽ là giao diện chính của chúng ta, IDE ở trên cùng, thiết bị đầu cuối ở dưới cùng

Thiết lập thư mục làm việc

Tạo thư mục làm việc. Tất cả mã bạn viết trong lớp học lập trình này đều nằm ở đây:

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

Sau đó, hãy chuẩn bị một số thư mục để quản lý những thứ như tập lệnh gieo hạt và nhật ký

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

Thiết lập dự án trên đám mây của bạn

Tạo tệp .env bằng các biến vị trí:

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

Để đơn giản hoá việc thiết lập dự án trong thiết bị đầu cuối, hãy tải tập lệnh thiết lập dự án này xuống thư mục làm việc của bạn:

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

Chạy tập lệnh. Lệnh này xác minh tài khoản thanh toán dùng thử của bạn, tạo một dự án mới (hoặc xác thực một dự án hiện có), lưu mã dự án vào một tệp .env trong thư mục hiện tại và đặt dự án đang hoạt động trong gcloud.

bash setup_verify_trial_project.sh && source .env

Tập lệnh sẽ:

  1. Xác minh rằng bạn có một tài khoản thanh toán dùng thử đang hoạt động
  2. Kiểm tra xem có dự án nào trong .env hay không (nếu có)
  3. Tạo dự án mới hoặc sử dụng lại dự án hiện có
  4. Liên kết tài khoản thanh toán dùng thử với dự án của bạn
  5. Lưu mã dự án vào .env
  6. Đặt dự án làm dự án gcloud đang hoạt động

Xác minh rằng dự án được thiết lập đúng cách bằng cách kiểm tra văn bản màu vàng bên cạnh thư mục đang hoạt động trong dấu nhắc của thiết bị đầu cuối Cloud Shell. Mã này sẽ hiển thị mã dự án của bạn.

dcba35ce1389f313.png

Kích hoạt API bắt buộc

Tiếp theo, chúng ta cần bật một số API cho sản phẩm mà chúng ta sẽ tương tác:

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) – tác nhân của bạn sử dụng các mô hình Gemini và Toolbox sử dụng API nhúng để tìm kiếm vectơ.
  • Cloud SQL Admin API (sqladmin.googleapis.com) – bạn cung cấp và quản lý một phiên bản PostgreSQL.
  • Compute Engine API (compute.googleapis.com) – bắt buộc để tạo phiên bản Cloud SQL.
  • Cloud Run, Cloud Build, Artifact Registry – được dùng trong bước triển khai sau này trong lớp học lập trình này

3. Chuẩn bị tập lệnh để khởi chạy cơ sở dữ liệu

Bước này bắt đầu quá trình tạo phiên bản Cloud SQL và chạy một tập lệnh thiết lập tự động. Tập lệnh này sẽ đợi phiên bản sẵn sàng, sau đó tạo cơ sở dữ liệu, gieo dữ liệu vào cơ sở dữ liệu bằng danh sách việc làm và tạo các mục nhúng – tất cả trong một thao tác.

Trước tiên, hãy thêm mật khẩu cơ sở dữ liệu vào tệp .env rồi tải lại tệp đó:

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

Tạo tập lệnh Bash để tạo phiên bản và cơ sở dữ liệu

Sau đó, hãy tạo tập lệnh scripts/setup_database.sh bằng lệnh sau

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

Sau đó, sao chép mã sau vào tệp 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 ""

Tạo tập lệnh Python để gieo dữ liệu

Sau đó, hãy tạo tệp python tập lệnh gieo hạt scripts/setup_jobs_db.py bằng lệnh bên dưới

cloudshell edit scripts/setup_jobs_db.py

Sau đó, sao chép mã sau vào tệp 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()

Bây giờ, hãy chuyển sang bước tiếp theo

4. Tạo và khởi động cơ sở dữ liệu

Giờ đây, các tập lệnh của chúng ta đã sẵn sàng để thực thi. Chúng ta sẽ cần Python để thực thi tập lệnh đã chuẩn bị, vì vậy, trước tiên, hãy chuẩn bị tập lệnh đó

Thiết lập dự án Python

uv là một trình quản lý dự án và gói Python nhanh được viết bằng Rust ( tài liệu uv ). Lớp học lập trình này sử dụng uv để duy trì dự án Python một cách nhanh chóng và đơn giản

Khởi chạy một dự án Python và thêm các phần phụ thuộc bắt buộc:

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

Xin lưu ý rằng ở đây, chúng tôi đang sử dụng cloud-sql-python-connector Python SDK để khởi động một kết nối bảo mật với phiên bản cơ sở dữ liệu của chúng tôi. Phiên bản này được xác thực bằng Thông tin xác thực mặc định của ứng dụng.

Thực thi tập lệnh thiết lập

Giờ đây, chúng ta có thể chạy tập lệnh thiết lập ở chế độ nền và kiểm tra đầu ra của bảng điều khiển sẽ được ghi vào tệp logs/atabase_setup.log bằng lệnh sau. Bạn có thể tiếp tục chuyển sang phần tiếp theo trong khi chờ quá trình này hoàn tất

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

Tải tệp nhị phân của Hộp công cụ xuống

Chúng ta sẽ sử dụng MCP Toolbox trong hướng dẫn này. May mắn là công cụ này đi kèm với một tệp nhị phân được tạo sẵn và sẵn sàng sử dụng trong môi trường Linux. Bây giờ, hãy tải tệp này xuống ở chế độ nền vì quá trình này sẽ mất khá nhiều thời gian. Chạy lệnh sau để tải tệp nhị phân xuống và kiểm tra nhật ký đầu ra trên logs/toolbox_dl.log . Bạn có thể tiếp tục chuyển sang phần tiếp theo trong khi chờ quá trình này hoàn tất

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 &

Tìm hiểu về tập lệnh thiết lập scripts/setup_database.sh

Bây giờ, hãy thử tìm hiểu tập lệnh thiết lập mà chúng ta đã định cấu hình trước đó. Quy trình này thực hiện những việc sau

  1. Lệnh đầu tiên mà chúng ta thực thi ở đó là lệnh gcloud sql instances create có cờ sau
  • db-custom-1-3840 là cấp Cloud SQL nhỏ nhất có lõi chuyên dụng (1 vCPU, RAM 3,75 GB) trong phiên bản ENTERPRISE. Bạn có thể đọc thêm thông tin chi tiết tại đây. Bạn cần có một lõi chuyên dụng để tích hợp Vertex AI ML – các cấp lõi dùng chung (db-f1-micro, db-g1-small) không hỗ trợ tính năng này.
  • --root-password đặt mật khẩu cho người dùng postgres mặc định.
  • --enable-google-ml-integration cho phép tích hợp sẵn Cloud SQL với Vertex AI, giúp bạn gọi các mô hình nhúng trực tiếp từ SQL bằng hàm embedding().
  1. Xác minh xem phiên bản đã ở trạng thái RUNNABLE hay chưa
  2. Cấp cho tài khoản dịch vụ của phiên bản Cloud SQL quyền gọi Vertex AI bằng lệnh gcloud projects add-iam-policy-binding. Đây là yêu cầu bắt buộc đối với hàm embedding() tích hợp mà chúng ta sẽ dùng khi gieo dữ liệu vào cơ sở dữ liệu
  3. Tạo cơ sở dữ liệu
  4. Thực thi tập lệnh gieo hạt setup_jobs_db.py

Tìm hiểu về tập lệnh ban đầu scripts/setup_jobs_db.py

Bây giờ, hãy chuyển sang tập lệnh gieo hạt. Tập lệnh này sẽ thực hiện những việc sau:

  1. Khởi tạo kết nối đến phiên bản cơ sở dữ liệu
  2. Cài đặt 2 tiện ích PostgreSQL:
  • google_ml_integration – cung cấp hàm SQL embedding(), gọi các mô hình nhúng Vertex AI trực tiếp từ SQL. Đây là một tiện ích ở cấp cơ sở dữ liệu giúp các hàm ML có sẵn trong jobs_db. Cờ cấp phiên bản (--enable-google-ml-integration) mà bạn đặt trong quá trình tạo phiên bản cho phép VM Cloud SQL truy cập vào Vertex AI – tiện ích này cung cấp các hàm SQL trong cơ sở dữ liệu cụ thể này.
  • vector (pgvector) – thêm kiểu dữ liệu vector và các toán tử khoảng cách để lưu trữ và truy vấn các vectơ nhúng.
  1. Tạo bảng, lưu ý rằng cột description_embeddingvector(3072) — một cột pgvector lưu trữ các vectơ 3072 chiều.
  2. Gieo dữ liệu ban đầu cho các công việc
  3. Tạo dữ liệu nhúng từ trường description và điền vào description_embedding bằng cách sử dụng tính năng tích hợp đỉnh được tích hợp sẵn thông qua hàm embedding()
  • embedding('gemini-embedding-001', description) – gọi mô hình nhúng Gemini của Vertex AI trực tiếp từ SQL, truyền văn bản description của từng công việc. Đây là tiện ích google_ml_integration mà bạn đã cài đặt trong tập lệnh ban đầu.
  • ::vector – truyền mảng số thực được trả về sang kiểu vector của pgvector để có thể lưu trữ và truy vấn bằng các toán tử khoảng cách.
  • UPDATE chạy trên cả 15 hàng, tạo ra một mục nhúng 3072 chiều cho mỗi nội dung mô tả công việc.

Thao tác này sẽ chuẩn bị dữ liệu ban đầu mà tác nhân của chúng tôi sẽ truy cập

5. Định cấu hình Bộ công cụ MCP cho cơ sở dữ liệu

Bước này giới thiệu Bộ công cụ MCP dành cho cơ sở dữ liệu, định cấu hình bộ công cụ này để kết nối với phiên bản Cloud SQL của bạn và xác định 2 công cụ truy vấn SQL tiêu chuẩn.

MCP là gì và tại sao nên sử dụng Toolbox?

e7b9be2e1c98b4db.png

MCP (Giao thức ngữ cảnh mô hình) là một giao thức mở giúp chuẩn hoá cách các tác nhân AI khám phá và tương tác với các công cụ bên ngoài. Nền tảng này xác định một mô hình ứng dụng-máy chủ: tác nhân lưu trữ một ứng dụng MCP và các công cụ được máy chủ MCP hiển thị. Mọi ứng dụng tương thích với MCP đều có thể sử dụng mọi máy chủ tương thích với MCP – tác nhân không cần mã tích hợp tuỳ chỉnh cho từng công cụ.

5bf26eeecad2277d.png

Bộ công cụ MCP cho cơ sở dữ liệu là một máy chủ MCP nguồn mở được xây dựng dành riêng cho quyền truy cập vào cơ sở dữ liệu. Nếu không có thư viện này, bạn sẽ viết các hàm Python mở kết nối cơ sở dữ liệu, quản lý nhóm kết nối, tạo các truy vấn được tham số hoá để ngăn chặn việc chèn mã SQL, xử lý lỗi và nhúng tất cả mã đó vào trong tác nhân của bạn. Mọi tác nhân cần truy cập vào cơ sở dữ liệu đều lặp lại công việc này. Việc thay đổi một truy vấn có nghĩa là bạn phải triển khai lại tác nhân.

Với Toolbox, bạn sẽ viết một tệp YAML. Mỗi công cụ sẽ ánh xạ đến một câu lệnh SQL được tham số hoá. Hộp công cụ xử lý việc gộp kết nối, truy vấn được tham số hoá, xác thực và khả năng quan sát. Các công cụ được tách biệt khỏi tác nhân – cập nhật một truy vấn bằng cách chỉnh sửa tools.yaml và khởi động lại Toolbox mà không cần chỉnh sửa mã tác nhân. Các công cụ này hoạt động trên ADK, LangGraph, LlamaIndex hoặc bất kỳ khung tương thích với MCP nào.

Viết cấu hình công cụ

Bây giờ, chúng ta cần tạo một tệp có tên là tools.yaml trong Cloud Shell Editor để thiết lập cấu hình công cụ

cloudshell edit tools.yaml

Tệp này sử dụng YAML nhiều tài liệu – mỗi khối được phân tách bằng --- là một tài nguyên độc lập. Mỗi tài nguyên đều có một kind khai báo tài nguyên đó là gì (sources cho các mối kết nối cơ sở dữ liệu, tools cho các thao tác có thể gọi của tác nhân) và một type chỉ định phần phụ trợ (cloud-sql-postgres cho nguồn, postgres-sql cho các công cụ dựa trên SQL). Một công cụ tham chiếu nguồn của nó bằng name, đây là cách Toolbox biết nên thực thi nhóm kết nối nào. Các biến môi trường sử dụng cú pháp ${VAR_NAME} và được phân giải khi khởi động.

Bây giờ, trước tiên hãy sao chép các tập lệnh sau vào tệp 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}

---

Tập lệnh này xác định tài nguyên sau:

  • Nguồn (jobs-db) – cho biết cách Toolbox kết nối với thực thể Cloud SQL PostgreSQL của bạn. Loại cloud-sql-postgres sử dụng trình kết nối Cloud SQL nội bộ, tự động xử lý việc xác thực và các kết nối an toàn. Các phần giữ chỗ ${GOOGLE_CLOUD_PROJECT} , ${REGION}${DB_PASSWORD} được phân giải từ các biến môi trường khi khởi động.

Tiếp theo, hãy thêm tập lệnh sau vào dưới biểu tượng --- trong 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)."

---

Tập lệnh này xác định tài nguyên sau:

  • Công cụ 1 và 2 (search-jobs, get-job-details) – công cụ truy vấn SQL chuẩn. Mỗi mục liên kết một tên công cụ (những gì mà tác nhân nhìn thấy) với một câu lệnh SQL được tham số hoá (những gì mà cơ sở dữ liệu thực thi). Các tham số sử dụng phần giữ chỗ vị trí $1, $2. Hộp công cụ thực thi các câu lệnh này dưới dạng câu lệnh đã chuẩn bị, giúp ngăn chặn việc chèn SQL.

Hãy tiếp tục, thêm tập lệnh sau vào biểu tượng --- trong 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

---

Tập lệnh này xác định tài nguyên sau:

  • Mô hình nhúng (gemini-embedding) – định cấu hình Toolbox để gọi mô hình gemini-embedding-001 của Gemini nhằm tạo các vectơ nhúng văn bản 3072 chiều. Toolbox sử dụng Thông tin xác thực mặc định của ứng dụng (ADC) để xác thực – không cần khoá API trong Cloud Shell hoặc Cloud Run. Lưu ý rằng dimension được định cấu hình ở đây phải giống với dimension mà chúng ta đã định cấu hình trước đó để gieo dữ liệu vào cơ sở dữ liệu

Hãy tiếp tục, thêm tập lệnh sau vào biểu tượng --- trong 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

---

Tập lệnh này xác định tài nguyên sau:

  • Công cụ 3 (search-jobs-by-description) – một công cụ tìm kiếm vectơ. Tham số search_queryembeddedBy: gemini-embedding, cho biết Toolbox sẽ chặn văn bản thô, gửi văn bản đó đến mô hình nhúng và sử dụng vectơ kết quả trong câu lệnh SQL. Toán tử <=> là khoảng cách cosine của pgvector – giá trị càng nhỏ thì nội dung mô tả càng giống nhau.

Cuối cùng, hãy thêm công cụ cuối cùng vào biểu tượng --- trong 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

Tập lệnh này xác định tài nguyên sau:

  • Công cụ 4 (add-job) – minh hoạ việc sử dụng vectơ. Tham số description_vector có 2 trường đặc biệt:
  • valueFromParam: description – Hộp công cụ sao chép giá trị từ tham số description vào tham số này. LLM không bao giờ thấy tham số này.
  • embeddedBy: gemini-embedding – Hộp công cụ nhúng văn bản đã sao chép vào một vectơ trước khi truyền văn bản đó đến SQL.

Kết quả: một lệnh gọi công cụ lưu trữ cả văn bản mô tả thô và vectơ nhúng của văn bản đó, mà không cần tác nhân biết bất kỳ thông tin nào về các vectơ nhúng.

Định dạng YAML nhiều tài liệu phân tách từng tài nguyên bằng ---. Mỗi tài liệu có các trường kind, nametype xác định nội dung của tài liệu. Tóm lại, chúng ta đã định cấu hình tất cả những điều sau:

  • Xác định cơ sở dữ liệu nguồn
  • Xác định các công cụ ( công cụ 1 và 2) để truy vấn cơ sở dữ liệu bằng bộ lọc tiêu chuẩn
  • Xác định mô hình nhúng
  • Xác định công cụ để thực hiện tìm kiếm vectơ ( công cụ 3 ) vào cơ sở dữ liệu
  • Xác định công cụ để thực hiện quá trình nhập dữ liệu vectơ ( công cụ 4) vào cơ sở dữ liệu

6. Chạy máy chủ MCP Toolbox

Ở bước trước, chúng ta đã thiết lập cấu hình cần thiết cho MCP Toolbox. Giờ đây, chúng ta đã sẵn sàng chạy máy chủ

Xác minh dữ liệu ban đầu

Trước khi bắt đầu Toolbox, hãy xác nhận rằng bạn đã hoàn tất quá trình thiết lập cơ sở dữ liệu. Tạo tập lệnh python scripts/verify_database.py bằng lệnh sau

cloudshell edit scripts/verify_seed.py

Sau đó, sao chép mã sau vào tệp 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)

Tập lệnh này sẽ kiểm tra số lượng dữ liệu bài đăng tuyển dụng và thông tin nhúng của dữ liệu đó. Chạy tập lệnh bằng lệnh sau

uv run scripts/verify_seed.py

Nếu bạn thấy đầu ra sau đây trên cửa sổ dòng lệnh, tức là dữ liệu đã sẵn sàng

Jobs: 15/15
Embeddings: 15/15

✓ Database ready!

Khởi động máy chủ Hộp công cụ

Trong bước thiết lập trước đó, chúng ta đã tải tệp thực thi toolbox xuống. Đảm bảo rằng tệp nhị phân này tồn tại và đã tải xuống thành công. Nếu chưa, hãy tải tệp xuống và đợi cho đến khi hoàn tất

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

Chúng ta cần hiển thị các biến .env cho quy trình con do hộp công cụ MCP chạy. Chạy lệnh sau để khởi động máy chủ hộp công cụ và ghi nhật ký đầu ra của bảng điều khiển vào tệp logs/mcp_toolbox.log

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

Bạn sẽ thấy kết quả trong tệp logs/mcp_toolbox.log xác nhận rằng máy chủ đã sẵn sàng như minh hoạ bên dưới:

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

Xác minh các công cụ

Truy vấn Toolbox API để liệt kê tất cả các công cụ đã đăng ký:

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

Bạn sẽ thấy các công cụ cùng với nội dung mô tả và thông số của chúng. Như minh hoạ bên dưới

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

Kiểm thử trực tiếp công cụ 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'

Phản hồi phải chứa 2 công việc kỹ thuật phụ trợ trong dữ liệu ban đầu của bạn.

[
  {
    "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. Tạo tác nhân ADK

Giờ đây, chúng ta sẽ sử dụng ADK trong Python cho dự án này. Hãy thêm các phần phụ thuộc bắt buộc:

uv add google-adk==1.29.0 toolbox-adk==1.0.0
  • google-adk – Bộ công cụ phát triển tác nhân của Google, bao gồm cả Gemini SDK
  • toolbox-adk – Tích hợp ADK cho Bộ công cụ MCP dành cho cơ sở dữ liệu.

Tạo cấu trúc thư mục của tác nhân

ADK yêu cầu một bố cục thư mục cụ thể: một thư mục được đặt tên theo tác nhân của bạn, chứa __init__.py, agent.py.env. Để giúp giải quyết vấn đề này, công cụ này có sẵn lệnh để nhanh chóng thiết lập cấu trúc:

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

Thư mục của bạn hiện sẽ có dạng như sau:

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

Tiếp theo, chúng ta sẽ cần tích hợp tác nhân ADK vào máy chủ Toolbox đang chạy và kiểm thử cả 4 công cụ: truy vấn tiêu chuẩn, tìm kiếm ngữ nghĩa và truyền vectơ. Mã tác nhân là tối thiểu: mọi logic cơ sở dữ liệu đều nằm trong tools.yaml.

Định cấu hình môi trường của tác nhân

ADK đọc GOOGLE_GENAI_USE_VERTEXAI, GOOGLE_CLOUD_PROJECTGOOGLE_CLOUD_LOCATION từ môi trường shell mà bạn đã thiết lập ở bước trước. Biến duy nhất dành riêng cho nhân viên hỗ trợ là TOOLBOX_URL – hãy thêm biến này vào tệp .env của nhân viên hỗ trợ:

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

Cập nhật mô-đun tác nhân

Mở jobs_agent/agent.py trong Trình chỉnh sửa Cloud Shell

cloudshell edit jobs_agent/agent.py

và ghi đè nội dung bằng mã sau:

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

Xin lưu ý rằng không có mã cơ sở dữ liệu nào ở đây – ToolboxToolset kết nối với máy chủ Toolbox khi khởi động và tải tất cả các công cụ có sẵn. Tác nhân gọi các công cụ theo tên; Bộ công cụ sẽ dịch những lệnh gọi đó thành các truy vấn SQL đối với Cloud SQL.

Biến môi trường TOOLBOX_URL mặc định là http://127.0.0.1:5000 để phát triển cục bộ. Khi triển khai lên Cloud Run sau này, bạn sẽ ghi đè URL này bằng URL Cloud Run của dịch vụ Toolbox mà không cần thay đổi mã.

Hướng dẫn này hiện chỉ đề cập đến 2 công cụ tiêu chuẩn (search-jobsget-job-details). Bạn sẽ mở rộng hướng dẫn này ở bước tiếp theo khi thêm các công cụ tìm kiếm và nhập dữ liệu ngữ nghĩa.

Kiểm thử tác nhân

Khởi động giao diện người dùng dành cho nhà phát triển ADK:

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

Mở URL xuất hiện trong thiết bị đầu cuối (thường là http://localhost:8000) bằng tính năng Xem trước trên web của Cloud Shell hoặc nhấn ctrl + nhấp vào URL xuất hiện trong thiết bị đầu cuối. Chọn jobs_agent trong trình đơn thả xuống của tác nhân ở góc trên bên trái.

Kiểm thử các truy vấn tiêu chuẩn

Hãy thử những câu lệnh sau để xác minh các công cụ SQL chuẩn:

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

93ac33e7f73aa0b9.png 240c53376042a916.png

Hãy thử nội dung mô tả bằng ngôn ngữ tự nhiên không liên kết với một vai trò hoặc bộ phần mềm cơ sở cụ thể:

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

Dựa trên loại truy vấn, tác nhân sẽ cố gắng chọn công cụ phù hợp: bộ lọc có cấu trúc sẽ đi qua search-jobs, nội dung mô tả bằng ngôn ngữ tự nhiên sẽ đi qua search-jobs-by-description.

b0ea629f5c9b4c26.png

Kiểm thử việc truyền dẫn vectơ

Yêu cầu trợ lý ảo thêm một công việc mới:

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

Bây giờ, hãy thử tìm kiếm:

Find me jobs involving autonomous systems and working with physical hardware

Hoạt động nhúng được tạo tự động trong quá trình INSERT – không cần thực hiện bước riêng biệt.

5a3d8e6f523dc18b.png

Giờ đây, bạn đã có một ứng dụng Agentic RAG hoạt động đầy đủ, sử dụng ADK, Bộ công cụ MCP và CloudSQL. Xin chúc mừng! Hãy tiến thêm một bước nữa để triển khai các ứng dụng này lên Cloud Run!

Bây giờ, hãy dừng giao diện người dùng dành cho nhà phát triển bằng cách kết thúc quy trình này bằng cách nhấn tổ hợp phím Ctrl+C hai lần trước khi tiếp tục.

8. Triển khai lên Cloud Run

Tác nhân và Hộp công cụ hoạt động cục bộ. Bước này triển khai cả hai dưới dạng các dịch vụ Cloud Run để có thể truy cập qua Internet. Dịch vụ Toolbox chạy dưới dạng một máy chủ MCP trên Cloud Run và dịch vụ tác nhân kết nối với dịch vụ này.

Chuẩn bị Hộp công cụ để triển khai

Tạo một thư mục triển khai cho dịch vụ Toolbox:

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

Tạo Dockerfile cho Toolbox. Mở deploy-toolbox/Dockerfile trong Trình chỉnh sửa Cloud Shell:

cloudshell edit deploy-toolbox/Dockerfile

Sao chép tập lệnh sau vào đó

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

Tệp nhị phân Toolbox và tools.yaml được đóng gói thành một hình ảnh Debian tối thiểu. Cloud Run định tuyến lưu lượng truy cập đến cổng 8080.

Triển khai dịch vụ Hộp công cụ

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 &

Lệnh này gửi nguồn đến Cloud Build, tạo một hình ảnh vùng chứa, đẩy hình ảnh đó vào Artifact Registry và triển khai hình ảnh đó lên Cloud Run. Quá trình này sẽ mất vài phút – chúng ta có thể kiểm tra nhật ký quy trình triển khai trên tệp logs/deploy_toolbox.log

Chuẩn bị tác nhân để triển khai

Trong khi Toolbox đang tạo, hãy thiết lập các tệp triển khai của tác nhân.

Tạo một Dockerfile trong thư mục gốc của dự án. Mở Dockerfile trong Trình chỉnh sửa Cloud Shell:

cloudshell edit Dockerfile

Sau đó, hãy sao chép nội dung sau

# 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 này sử dụng ghcr.io/astral-sh/uv làm hình ảnh cơ sở, bao gồm cả Python và uv được cài đặt sẵn – không cần cài đặt uv riêng biệt thông qua pip.

Tạo tệp .dockerignore để loại trừ các tệp không cần thiết khỏi hình ảnh vùng chứa:

cloudshell edit .dockerignore

Sau đó, sao chép tập lệnh sau vào đó

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

Triển khai dịch vụ tác nhân

Chờ quá trình triển khai Toolbox hoàn tất. Kiểm tra lại quy trình triển khai trên logs/deploy_toolbox.log để xác minh quy trình. Sau đó, hãy truy xuất URL Cloud Run của ứng dụng bằng lệnh sau

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

Bạn sẽ thấy kết quả tương tự như sau

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

Sau đó, hãy xác minh rằng Toolbox đã triển khai đang hoạt động:

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

Nếu kết quả đầu ra hiển thị như ví dụ này, thì quá trình triển khai đã thành công

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

Tiếp theo, hãy triển khai tác nhân bằng cách truyền URL của Hộp công cụ dưới dạng một biến môi trường:

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

Mã tác nhân đọc TOOLBOX_URL từ môi trường (bạn đã thiết lập mã này trước đó). Theo cách cục bộ, nó trỏ đến http://127.0.0.1:5000; trên Cloud Run, nó trỏ đến URL dịch vụ Toolbox. Bạn không cần thay đổi mã.

Kiểm thử nhân viên hỗ trợ đã triển khai

Truy xuất URL Cloud Run của nhân viên hỗ trợ:

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

Mở URL trong trình duyệt. Giao diện người dùng ADK dev sẽ tải – đây là giao diện mà bạn đã sử dụng cục bộ, hiện đang chạy trên Cloud Run.

Chọn jobs_agent trong trình đơn thả xuống rồi kiểm thử:

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

Cả hai truy vấn đều hoạt động thông qua các dịch vụ đã triển khai: tác nhân trên Cloud Run gọi Toolbox trên Cloud Run, truy vấn Cloud SQL.

9. Chúc mừng / Dọn dẹp

Bạn đã tạo và triển khai một trợ lý bảng tin tuyển dụng thông minh sử dụng Bộ công cụ MCP cho cơ sở dữ liệu để kết nối một tác nhân ADK và Cloud SQL PostgreSQL – bằng cả truy vấn SQL chuẩn và tìm kiếm vectơ ngữ nghĩa.

Kiến thức bạn học được

  • Cách MCP chuẩn hoá quyền truy cập vào công cụ cho các tác nhân AI và cách MCP Toolbox for Databases áp dụng điều này cụ thể cho các thao tác cơ sở dữ liệu – thay thế mã cơ sở dữ liệu tuỳ chỉnh bằng cấu hình YAML khai báo
  • Cách định cấu hình Cloud SQL PostgreSQL làm nguồn dữ liệu Toolbox bằng loại nguồn cloud-sql-postgres
  • Cách xác định các công cụ truy vấn SQL chuẩn bằng các câu lệnh có tham số giúp ngăn chặn việc chèn SQL
  • Cách bật tính năng tìm kiếm vectơ bằng pgvector và gemini-embedding-001, với tham số embeddedBy để nhúng truy vấn tự động
  • Cách valueFromParam cho phép tự động nhập vectơ – LLM cung cấp nội dung mô tả bằng văn bản và Toolbox sẽ sao chép, nhúng và lưu trữ vectơ cùng với văn bản một cách âm thầm
  • Cách ToolboxToolset của ADK tải các công cụ từ một máy chủ Bộ công cụ đang chạy, giúp mã đại lý ở mức tối thiểu và logic cơ sở dữ liệu hoàn toàn tách biệt
  • Cách triển khai cả máy chủ MCP Toolbox và tác nhân ADK lên Cloud Run dưới dạng các dịch vụ riêng biệt

Dọn dẹp

Để tránh bị tính phí vào tài khoản Google Cloud cho các tài nguyên được tạo trong lớp học lập trình này, bạn có thể xoá từng tài nguyên hoặc xoá toàn bộ dự án.

Cách dọn dẹp dễ nhất là xoá dự án. Thao tác này sẽ xoá tất cả tài nguyên liên kết với dự án.

gcloud projects delete $GOOGLE_CLOUD_PROJECT

Cách 2: Xoá từng tài nguyên

Nếu bạn muốn giữ lại dự án nhưng chỉ xoá các tài nguyên được tạo trong lớp học lập trình này, hãy làm như sau:

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