1. 소개
AI 에이전트의 유용성은 액세스할 수 있는 데이터에 따라 달라집니다. 대부분의 실제 데이터는 데이터베이스에 저장되어 있으며, 에이전트를 데이터베이스에 연결하려면 일반적으로 연결 관리, 쿼리 로직을 작성하고 에이전트 코드 내에 파이프라인을 삽입해야 합니다. 데이터베이스 액세스가 필요한 모든 에이전트가 이 작업을 반복하며, 쿼리 변경이 있을 때마다 에이전트를 다시 배포해야 합니다.
이 Codelab에서는 다른 접근 방식을 보여줍니다. YAML 파일에 데이터베이스 도구(표준 SQL 쿼리, 벡터 유사성 검색, 자동 임베딩 생성 등)를 선언하면 데이터베이스용 MCP 도구 상자가 모든 데이터베이스 작업을 MCP 서버로 처리합니다. 에이전트 코드는 최소한으로 유지됩니다. 도구를 로드하고 Gemini가 호출할 도구를 결정하도록 합니다.
빌드할 항목
'TechJobs'를 위한 스마트 채용 게시판 어시스턴트: 개발자가 표준 필터 (역할, 기술 스택)를 사용하여 기술 직업 목록을 탐색하고 'AI 챗봇을 개발하는 원격 직업을 원해'와 같은 자연어 설명을 통해 직업을 찾을 수 있도록 지원하는 Gemini 기반 ADK 에이전트입니다. 에이전트는 데이터베이스용 MCP Toolbox를 통해서만 Cloud SQL PostgreSQL 데이터베이스에서 읽고 씁니다. 이 도구는 벡터 검색을 위한 자동 임베딩 생성을 비롯한 모든 데이터베이스 액세스를 처리합니다. 결국 Toolbox와 에이전트가 모두 Cloud Run에서 실행됩니다.
학습할 내용
- MCP (모델 컨텍스트 프로토콜)가 AI 에이전트의 도구 액세스를 표준화하는 방법과 데이터베이스용 MCP 도구 상자가 이를 데이터베이스 작업에 적용하는 방법
- 데이터베이스용 MCP Toolbox를 ADK 에이전트와 Cloud SQL PostgreSQL 간의 미들웨어로 설정
tools.yaml에서 선언적으로 데이터베이스 도구 정의 - 에이전트에 데이터베이스 코드가 없음ToolboxToolset를 사용하여 실행 중인 도구 상자 서버에서 도구를 로드하는 ADK 에이전트 빌드- Cloud SQL의 기본 제공
embedding()함수를 사용하여 벡터 임베딩을 생성하고pgvector로 시맨틱 검색 사용 설정 - 쓰기 작업 시 자동 벡터 수집을 위해
valueFromParam기능 사용 - Toolbox 서버와 ADK 에이전트를 모두 Cloud Run에 배포
기본 요건
- 평가판 결제 계정이 있는 Google Cloud 계정
- Python 및 SQL에 대한 기본 지식
- Cloud Database 및 ADK 사용 경험이 있으면 도움이 됩니다.
2. 환경 설정
이 단계에서는 Cloud Shell 환경을 준비하고, Google Cloud 프로젝트를 구성하고, 참조 저장소를 클론합니다.
Cloud Shell 열기
브라우저에서 Cloud Shell을 엽니다. Cloud Shell은 이 Codelab에 필요한 모든 도구가 사전 구성된 환경을 제공합니다. 메시지가 표시되면 승인을 클릭합니다.
그런 다음 '보기' -> '터미널'을 클릭하여 터미널을 엽니다. 인터페이스는 다음과 유사해야 합니다.

이것이 기본 인터페이스가 됩니다. 상단에 IDE, 하단에 터미널이 있습니다.
작업 디렉터리 설정하기
작업 디렉터리를 만듭니다. 이 Codelab에서 작성하는 모든 코드는 여기에 있습니다.
mkdir -p ~/build-agent-adk-toolbox-cloudsql
cloudshell workspace ~/build-agent-adk-toolbox-cloudsql && cd ~/build-agent-adk-toolbox-cloudsql
그런 다음 시드 스크립트와 로그와 같은 항목을 관리할 여러 디렉터리를 준비합니다.
mkdir -p ~/build-agent-adk-toolbox-cloudsql/scripts
mkdir -p ~/build-agent-adk-toolbox-cloudsql/logs
Google Cloud 프로젝트 설정
위치 변수를 사용하여 .env 파일을 만듭니다.
# For Vertex AI / Gemini API calls
echo "GOOGLE_CLOUD_LOCATION=global" > .env
# For Cloud SQL, Cloud Run, Artifact Registry
echo "REGION=us-central1" >> .env
터미널에서 프로젝트 설정을 간소화하려면 이 프로젝트 설정 스크립트를 작업 디렉터리에 다운로드하세요.
curl -sL https://raw.githubusercontent.com/alphinside/cloud-trial-project-setup/main/setup_verify_trial_project.sh -o setup_verify_trial_project.sh
스크립트를 실행합니다. 무료 체험 결제 계정을 확인하고, 새 프로젝트를 만들거나 기존 프로젝트를 검증하고, 현재 디렉터리의 .env 파일에 프로젝트 ID를 저장하고, gcloud에서 활성 프로젝트를 설정합니다.
bash setup_verify_trial_project.sh && source .env
스크립트는 다음을 수행합니다.
- 활성 상태의 무료 체험 결제 계정이 있는지 확인
.env에 기존 프로젝트가 있는지 확인합니다 (있는 경우).- 새 프로젝트를 만들거나 기존 프로젝트를 재사용합니다.
- 무료 체험 결제 계정을 프로젝트에 연결
- 프로젝트 ID를
.env에 저장합니다. - 프로젝트를 활성
gcloud프로젝트로 설정
Cloud Shell 터미널 프롬프트의 작업 디렉터리 옆에 있는 노란색 텍스트를 확인하여 프로젝트가 올바르게 설정되었는지 확인합니다. 프로젝트 ID가 표시되어야 합니다.

필수 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) - 에이전트가 Gemini 모델을 사용하고, 도구 상자가 벡터 검색을 위해 임베딩 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을 준비합니다.
Python 프로젝트 설정
uv은 Rust로 작성된 빠른 Python 패키지 및 프로젝트 관리자입니다 ( uv 문서). 이 Codelab에서는 Python 프로젝트를 유지관리할 때 속도와 단순성을 위해 이를 사용합니다.
Python 프로젝트를 초기화하고 필요한 종속 항목을 추가합니다.
uv init
uv add cloud-sql-python-connector --extra pg8000
uv add python-dotenv
여기서는 cloud-sql-python-connector Python SDK를 사용하여 애플리케이션 기본 사용자 인증 정보를 사용하여 인증된 데이터베이스 인스턴스와의 보안 연결을 초기화합니다.
설정 스크립트 실행
이제 다음 명령어를 사용하여 백그라운드에서 설정 스크립트를 실행하고 logs/atabase_setup.log 파일에 작성될 콘솔 출력을 검사할 수 있습니다. 이 작업이 완료되는 동안 다음 섹션을 계속 진행할 수 있습니다.
mkdir -p ~/build-agent-adk-toolbox-cloudsql/logs
bash scripts/setup_database.sh > logs/database_setup.log 2>&1 &
도구 상자 바이너리 다운로드
이 튜토리얼에서는 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 이해하기
이제 이전에 구성한 설정 스크립트를 이해해 보겠습니다. 다음 프로세스를 실행합니다.
- 여기에서 실행하는 첫 번째 명령어는 다음 플래그가 지정된
gcloud sql instances create명령어입니다.
db-custom-1-3840는ENTERPRISE버전에서 가장 작은 전용 코어 Cloud SQL 등급 (vCPU 1개, RAM 3.75GB)입니다. 자세한 내용은 여기를 참고하세요. Vertex AI ML 통합에는 전용 코어가 필요합니다. 공유 코어 등급 (db-f1-micro,db-g1-small)은 지원하지 않습니다.--root-password는 기본postgres사용자의 비밀번호를 설정합니다.--enable-google-ml-integration를 사용하면 Cloud SQL과 Vertex AI 간의 기본 제공 통합이 사용 설정되어embedding()함수를 사용하여 SQL에서 직접 임베딩 모델을 호출할 수 있습니다.
- 인스턴스가 이미
RUNNABLE상태인지 확인 gcloud projects add-iam-policy-binding명령어를 사용하여 Cloud SQL 인스턴스의 서비스 계정에 Vertex AI 호출 권한을 부여합니다. 이는 데이터베이스를 시드할 때 사용할 기본 제공embedding()함수에 필요합니다.- 데이터베이스 만들기
- 시드 스크립트
setup_jobs_db.py스크립트 실행
시드 스크립트 scripts/setup_jobs_db.py 이해하기
이제 시드 스크립트로 이동합니다. 이 스크립트는 다음 작업을 실행합니다.
- 데이터베이스 인스턴스에 대한 연결 초기화
- 다음 두 PostgreSQL 확장 프로그램을 설치합니다.
google_ml_integration- SQL에서 직접 Vertex AI 임베딩 모델을 호출하는embedding()SQL 함수를 제공합니다. 이는jobs_db내에서 ML 함수를 사용할 수 있도록 하는 데이터베이스 수준 확장 프로그램입니다. 인스턴스 생성 중에 설정한 인스턴스 수준 플래그 (--enable-google-ml-integration)를 사용하면 Cloud SQL VM이 Vertex AI에 도달할 수 있습니다. 확장 프로그램을 사용하면 이 특정 데이터베이스 내에서 SQL 함수를 사용할 수 있습니다.vector(pgvector) - 삽입을 저장하고 쿼리하기 위한vector데이터 유형과 거리 연산자를 추가합니다.
description_embedding열이vector(3072)(3072차원 벡터를 저장하는pgvector열)임을 참고하여 테이블을 만듭니다.- 초기 작업 데이터 시드
description필드에서 임베딩 데이터를 생성하고embedding()함수를 통해 기본 제공 Vertex 통합을 사용하여description_embedding를 채웁니다.
embedding('gemini-embedding-001', description)- 각 작업의description텍스트를 전달하여 SQL에서 Vertex AI의 Gemini 임베딩 모델을 직접 호출합니다. 이는 시드 스크립트에 설치한google_ml_integration확장 프로그램입니다.::vector- 반환된 float 배열을 pgvector의vector유형으로 변환하여 거리 연산자로 저장하고 쿼리할 수 있습니다.UPDATE은 15개 행 전체에서 실행되어 직무 설명당 3072차원 임베딩을 하나 생성합니다.
이렇게 하면 상담사가 액세스할 초기 데이터가 준비됩니다.
5. 데이터베이스용 MCP 도구 상자 구성
이 단계에서는 데이터베이스용 MCP 도구 상자를 소개하고, Cloud SQL 인스턴스에 연결하도록 구성하며, 두 개의 표준 SQL 쿼리 도구를 정의합니다.
MCP란 무엇이며 Toolbox를 사용해야 하는 이유는 무엇인가요?

MCP (모델 컨텍스트 프로토콜)은 AI 에이전트가 외부 도구를 검색하고 상호작용하는 방식을 표준화하는 개방형 프로토콜입니다. 클라이언트-서버 모델을 정의합니다. 에이전트는 MCP 클라이언트를 호스팅하고 도구는 MCP 서버에 의해 노출됩니다. 모든 MCP 호환 클라이언트는 모든 MCP 호환 서버를 사용할 수 있습니다. 에이전트에는 각 도구에 대한 맞춤 통합 코드가 필요하지 않습니다.

MCP Toolbox for Databases는 데이터베이스 액세스를 위해 특별히 빌드된 오픈소스 MCP 서버입니다. 이 기능을 사용하지 않으면 데이터베이스 연결을 열고, 연결 풀을 관리하고, SQL 삽입을 방지하기 위해 매개변수화된 쿼리를 구성하고, 오류를 처리하고, 이 모든 코드를 에이전트 내에 삽입하는 Python 함수를 작성해야 합니다. 데이터베이스 액세스가 필요한 모든 에이전트가 이 작업을 반복합니다. 쿼리를 변경하면 에이전트를 다시 배포해야 합니다.
Toolbox를 사용하면 YAML 파일을 작성합니다. 각 도구는 파라미터화된 SQL 문에 매핑됩니다. 툴박스는 연결 풀링, 매개변수화된 쿼리, 인증, 관측 가능성을 처리합니다. 도구가 에이전트에서 분리됩니다. 에이전트 코드를 건드리지 않고 tools.yaml를 수정하고 Toolbox를 다시 시작하여 질문을 업데이트합니다. 동일한 도구가 ADK, LangGraph, LlamaIndex 또는 MCP 호환 프레임워크에서 작동합니다.
도구 구성 작성
이제 Cloud Shell 편집기에서 tools.yaml이라는 파일을 만들어 도구 구성을 설정해야 합니다.
cloudshell edit tools.yaml
이 파일은 다중 문서 YAML을 사용합니다. ---로 구분된 각 블록은 독립형 리소스입니다. 모든 리소스에는 리소스가 무엇인지 선언하는 kind (데이터베이스 연결의 경우 sources, 에이전트 호출 가능 작업의 경우 tools)와 백엔드를 지정하는 type (소스의 경우 cloud-sql-postgres, SQL 기반 도구의 경우 postgres-sql)가 있습니다. 도구는 name로 소스를 참조합니다. 이를 통해 도구 상자는 실행할 연결 풀을 알 수 있습니다. 환경 변수는 ${VAR_NAME} 구문을 사용하며 시작 시 확인됩니다.
이제 다음 스크립트를 먼저 tools.yaml 파일에 복사합니다.
# tools.yaml
# --- Data Source ---
kind: source
name: jobs-db
type: cloud-sql-postgres
project: ${GOOGLE_CLOUD_PROJECT}
region: ${REGION}
instance: ${DB_INSTANCE}
database: ${DB_NAME}
user: postgres
password: ${DB_PASSWORD}
---
이 스크립트는 다음 리소스를 정의합니다.
- 소스 (
jobs-db) - Toolbox가 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): 3072차원 텍스트 임베딩을 생성하기 위해 Gemini의gemini-embedding-001모델을 호출하도록 Toolbox를 구성합니다. 툴박스는 애플리케이션 기본 사용자 인증 정보 (ADC)를 사용하여 인증하므로 Cloud Shell 또는 Cloud Run에 API 키가 필요하지 않습니다. 여기에서 구성된dimension는 데이터베이스를 시드하기 위해 이전에 구성한 것과 동일해야 합니다.
계속해서 tools.yaml의 --- 기호 아래에 다음 스크립트를 추가합니다.
# --- Tool 3: Semantic search by description ---
kind: tool
name: search-jobs-by-description
type: postgres-sql
source: jobs-db
description: >-
Find jobs that match a natural language description of what the developer
is looking for. Use this tool when the developer describes their ideal job
using interests, work style, career goals, or project type rather than a
specific role or tech stack. Examples: "I want to work on AI chatbots,"
"a remote job at a fintech startup," "something involving infrastructure
and reliability."
statement: |
SELECT title, company, role, tech_stack, salary_range, location, description
FROM jobs
WHERE description_embedding IS NOT NULL
ORDER BY description_embedding <=> $1
LIMIT 5
parameters:
- name: search_query
type: string
description: "A natural language description of the kind of job the developer is looking for."
embeddedBy: gemini-embedding
---
이 스크립트는 다음 리소스를 정의합니다.
- 도구 3 (
search-jobs-by-description) - 벡터 검색 도구search_query매개변수에는embeddedBy: gemini-embedding이 있습니다. 이는 Toolbox가 원시 텍스트를 가로채 임베딩 모델로 전송하고 결과 벡터를 SQL 문에서 사용하도록 지시합니다.<=>연산자는 pgvector의 코사인 거리입니다. 값이 작을수록 설명이 더 유사합니다.
마지막으로 tools.yaml의 --- 기호 아래에 마지막 도구를 추가합니다.
# --- Tool 4: Add a new job listing with automatic embedding ---
kind: tool
name: add-job
type: postgres-sql
source: jobs-db
description: >-
Add a new job listing to the platform. Use this tool when a user asks
to post a job that is not currently listed.
statement: |
INSERT INTO jobs (title, company, role, tech_stack, salary_range, location, openings, description, description_embedding)
VALUES ($1, $2, $3, $4, $5, $6, CAST($7 AS INTEGER), $8, $9)
RETURNING title, company
parameters:
- name: title
type: string
description: "The job title (e.g., 'Senior Backend Engineer')."
- name: company
type: string
description: "The company name (e.g., 'Stripe', 'Spotify')."
- name: role
type: string
description: "The role category (e.g., 'Backend', 'Frontend', 'Data/AI', 'DevOps')."
- name: tech_stack
type: string
description: "Comma-separated list of technologies (e.g., 'Python, FastAPI, GCP')."
- name: salary_range
type: string
description: "The salary range (e.g., '$150-200K/year')."
- name: location
type: string
description: "Work location and arrangement (e.g., 'Remote')."
- name: openings
type: string
description: "The number of open positions."
- name: description
type: string
description: "A short description of the job (2-3 sentences)."
- name: description_vector
type: string
description: "Auto-generated embedding vector for the job description."
valueFromParam: description
embeddedBy: gemini-embedding
이 스크립트는 다음 리소스를 정의합니다.
- 도구 4 (
add-job) - 벡터 수집을 보여줍니다.description_vector매개변수에는 다음과 같은 두 개의 특수 필드가 있습니다. valueFromParam: description- 도구 상자에서description매개변수의 값을 여기에 복사합니다. LLM은 이 매개변수를 볼 수 없습니다.embeddedBy: gemini-embedding: Toolbox는 복사된 텍스트를 SQL에 전달하기 전에 벡터에 삽입합니다.
결과적으로 에이전트는 임베딩에 대해 아무것도 알지 못하지만 하나의 도구 호출이 원시 설명 텍스트와 벡터 임베딩을 모두 저장합니다.
다중 문서 YAML 형식은 각 리소스를 ---로 구분합니다. 각 문서에는 무엇인지 정의하는 kind, name, type 필드가 있습니다. 요약하자면 다음 사항을 모두 구성했습니다.
- 소스 데이터베이스 정의
- 표준 필터로 데이터베이스를 쿼리하는 도구 ( 도구 1 및 2) 정의
- 임베딩 모델 정의
- 데이터베이스에 벡터 검색을 실행하는 도구 ( 도구 3) 정의
- 벡터 데이터 수집 ( 도구 4)을 데이터베이스에 실행하는 도구 정의
6. MCP Toolbox 서버 실행
이전 단계에서 MCP 도구 상자에 필요한 구성을 이미 설정했습니다. 이제 서버를 실행할 준비가 되었습니다.
시드된 데이터 확인
Toolbox를 시작하기 전에 데이터베이스 설정이 완료되었는지 확인해 보겠습니다. 다음 명령어를 사용하여 Python 스크립트 scripts/verify_database.py를 만듭니다.
cloudshell edit scripts/verify_seed.py
그런 다음 다음 코드를 scripts/verify_seed.py 파일에 복사합니다.
#!/usr/bin/env python3
"""Verify the database has 15 jobs with embeddings."""
import os
import sys
from pathlib import Path
from dotenv import load_dotenv
from google.cloud.sql.connector import Connector
import pg8000
# Load environment variables
env_path = Path(__file__).parent.parent / '.env'
load_dotenv(env_path)
# Verify required environment variables
required_vars = ['GOOGLE_CLOUD_PROJECT', 'REGION', 'DB_PASSWORD', 'DB_INSTANCE', 'DB_NAME']
missing_vars = [var for var in required_vars if not os.environ.get(var)]
if missing_vars:
print(f"ERROR: Missing environment variables: {', '.join(missing_vars)}", file=sys.stderr)
sys.exit(1)
def verify_database():
"""Check that 15 jobs exist with embeddings."""
connector = Connector()
try:
project = os.environ['GOOGLE_CLOUD_PROJECT']
region = os.environ['REGION']
password = os.environ['DB_PASSWORD']
instance = os.environ['DB_INSTANCE']
database = os.environ['DB_NAME']
conn = connector.connect(
f"{project}:{region}:{instance}",
"pg8000",
user="postgres",
password=password,
db=database
)
cursor = conn.cursor()
# Count jobs and embeddings
cursor.execute("SELECT COUNT(*) FROM jobs")
job_count = cursor.fetchone()[0]
cursor.execute("SELECT COUNT(*) FROM jobs WHERE description_embedding IS NOT NULL")
embedding_count = cursor.fetchone()[0]
print(f"Jobs: {job_count}/15")
print(f"Embeddings: {embedding_count}/15")
cursor.close()
conn.close()
if job_count == 15 and embedding_count == 15:
print("\n✓ Database ready!")
return True
else:
print("\n✗ Database not ready")
return False
except Exception as e:
print(f"\nERROR: {e}", file=sys.stderr)
return False
finally:
connector.close()
if __name__ == "__main__":
success = verify_database()
sys.exit(0 if success else 1)
이 스크립트는 채용 공고 데이터의 수와 임베딩을 확인합니다. 다음 명령어를 사용하여 스크립트를 실행합니다.
uv run scripts/verify_seed.py
다음 터미널 출력이 표시되면 데이터가 준비된 것입니다.
Jobs: 15/15 Embeddings: 15/15 ✓ Database ready!
도구 상자 서버 시작
앞서 설정 단계에서 toolbox 실행 파일을 이미 다운로드했습니다. 이 바이너리 파일이 존재하고 다운로드되었는지 확인합니다. 그렇지 않은 경우 다운로드하고 완료될 때까지 기다립니다.
cd ~/build-agent-adk-toolbox-cloudsql
if [ ! -f toolbox ]; then
curl -O https://storage.googleapis.com/mcp-toolbox-for-databases/v1.0.0/linux/amd64/toolbox
fi
chmod +x toolbox
MCP 툴박스에서 실행되는 하위 프로세스에 .env 변수를 노출해야 합니다. 다음 명령어를 실행하여 툴박스 서버를 시작하고 콘솔 출력을 logs/mcp_toolbox.log 파일에 로깅합니다.
set -a; source .env; set +a
./toolbox --config tools.yaml --enable-api > logs/mcp_toolbox.log 2>&1 &
logs/mcp_toolbox.log 파일에 서버가 준비되었음을 확인하는 출력이 다음과 같이 표시됩니다.
... INFO "Initialized 1 sources: jobs-db" ... INFO "Initialized 0 authServices: " ... INFO "Using Vertex AI backend for Gemini embedding" ... INFO "Initialized 1 embeddingModels: gemini-embedding" ... INFO "Initialized 4 tools: add-job, search-jobs, get-job-details, search-jobs-by-description" ... ... INFO "Server ready to serve!"
도구 확인
도구 상자 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'
응답에는 시드 데이터의 두 백엔드 엔지니어링 작업이 포함되어야 합니다.
[
{
"title": "Backend Engineer (Payments)",
"company": "Square",
"role": "Backend",
"tech_stack": "Java, Spring Boot, PostgreSQL, Kafka",
"salary_range": "$160-220K/year",
"location": "San Francisco, Hybrid",
"openings": 3
},
{
"title": "Senior Backend Engineer",
"company": "Stripe",
"role": "Backend",
"tech_stack": "Go, PostgreSQL, gRPC, Kubernetes",
"salary_range": "$180-250K/year",
"location": "San Francisco, Hybrid",
"openings": 3
}
]
7. ADK 에이전트 빌드
이제 이 프로젝트에서 Python의 ADK를 활용할 것이므로 필요한 종속 항목을 추가해 보겠습니다.
uv add google-adk==1.29.0 toolbox-adk==1.0.0
google-adk: Gemini SDK를 포함한 Google의 에이전트 개발 키트toolbox-adk- 데이터베이스용 MCP 도구 상자를 위한 ADK 통합
에이전트 디렉터리 구조 만들기
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 └── ...
다음으로 실행 중인 Toolbox 서버에 ADK 에이전트를 통합하고 표준 질문, 시맨틱 검색, 벡터 수집 등 네 가지 도구를 모두 테스트해야 합니다. 에이전트 코드는 최소한입니다. 모든 데이터베이스 로직은 tools.yaml에 있습니다.
에이전트 환경 구성
ADK는 이전 단계에서 이미 설정한 셸 환경에서 GOOGLE_GENAI_USE_VERTEXAI, GOOGLE_CLOUD_PROJECT, GOOGLE_CLOUD_LOCATION을 읽습니다. 에이전트별 변수는 TOOLBOX_URL뿐입니다. 이를 에이전트의 .env 파일에 추가합니다.
echo -e "\nTOOLBOX_URL=http://127.0.0.1:5000" >> jobs_agent/.env
에이전트 모듈 업데이트
Cloud Shell 편집기에서 jobs_agent/agent.py 열기
cloudshell edit jobs_agent/agent.py
다음 코드로 콘텐츠를 덮어씁니다.
# jobs_agent/agent.py
import os
from google.adk.agents import LlmAgent
from toolbox_adk import ToolboxToolset
TOOLBOX_URL = os.environ.get("TOOLBOX_URL", "http://127.0.0.1:5000")
toolbox = ToolboxToolset(TOOLBOX_URL)
root_agent = LlmAgent(
name="jobs_agent",
model="gemini-2.5-flash",
instruction="""You are a helpful assistant at "TechJobs," a tech job listing platform.
Your job:
- Help developers browse job listings by role or tech stack.
- Provide full details about specific positions, including salary range and number of openings.
- Recommend jobs based on natural language descriptions of what the developer is looking for.
- Add new job listings to the platform when asked.
When a developer asks about a specific job by title or company, use the get-job-details tool.
When a developer asks for a specific role category or tech stack, use the search-jobs tool.
When a developer describes what kind of job they want — by interest area, work style,
career goals, or project type — use the search-jobs-by-description tool for semantic search.
When in doubt between search-jobs and search-jobs-by-description, prefer
search-jobs-by-description — it searches job descriptions and finds more relevant matches.
If a position has no openings (openings is 0), let the developer know
and suggest similar alternatives from the search results.
Be conversational, knowledgeable, and concise.""",
tools=[toolbox],
)
여기에는 데이터베이스 코드가 없습니다. ToolboxToolset는 시작 시 Toolbox 서버에 연결하고 사용 가능한 모든 도구를 로드합니다. 에이전트는 이름으로 도구를 호출합니다. 도구 상자는 이러한 호출을 Cloud SQL에 대한 SQL 쿼리로 변환합니다.
TOOLBOX_URL 환경 변수는 로컬 개발의 경우 기본적으로 http://127.0.0.1:5000로 설정됩니다. 나중에 Cloud Run에 배포할 때 Toolbox 서비스의 Cloud Run URL로 이를 재정의하면 됩니다. 코드 변경은 필요하지 않습니다.
현재 안내에서는 두 가지 표준 도구 (search-jobs 및 get-job-details)만 참조합니다. 다음 단계에서 시맨틱 검색 및 수집 도구를 추가할 때 이를 확장합니다.
에이전트 테스트
ADK 개발 UI를 시작합니다.
cd ~/build-agent-adk-toolbox-cloudsql
uv run adk web --allow_origins "regex:https://.*\.cloudshell\.dev"
Cloud Shell의 웹 미리보기 기능을 사용하거나 터미널에 표시된 URL을 ctrl + 클릭하여 터미널에 표시된 URL (일반적으로 http://localhost:8000)을 엽니다. 왼쪽 상단의 에이전트 드롭다운에서 jobs_agent를 선택합니다.
표준 쿼리 테스트
다음 프롬프트를 사용하여 표준 SQL 도구를 확인해 보세요.
What backend engineering jobs do you have?
Any jobs using Kubernetes?
Tell me about the Cloud Architect position

시맨틱 검색 테스트하기
특정 역할이나 기술 스택에 매핑되지 않는 자연어 설명을 사용해 보세요.
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를 통과합니다.

벡터 수집 테스트
에이전트에게 새 작업을 추가해 달라고 요청합니다.
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.

이제 검색해 보세요.
Find me jobs involving autonomous systems and working with physical hardware
삽입 중에 삽입이 자동으로 생성되었으므로 별도의 단계가 필요하지 않습니다.

이제 ADK, MCP Toolbox, CloudSQL을 활용하는 완전한 에이전트형 RAG 애플리케이션이 있습니다. 수고하셨습니다. 한 단계 더 나아가 이러한 앱을 Cloud Run에 배포해 보겠습니다.
이제 계속하기 전에 Ctrl+C를 두 번 눌러 프로세스를 종료하여 개발 UI를 중지합니다.
8. Cloud Run에 배포
에이전트와 Toolbox는 로컬로 작동합니다. 이 단계에서는 인터넷을 통해 액세스할 수 있도록 두 항목을 모두 Cloud Run 서비스로 배포합니다. 도구 상자 서비스는 Cloud Run에서 MCP 서버로 실행되고 에이전트 서비스는 여기에 연결됩니다.
배포를 위해 Toolbox 준비하기
도구 상자 서비스의 배포 디렉터리를 만듭니다.
cd ~/build-agent-adk-toolbox-cloudsql
mkdir -p deploy-toolbox
cp toolbox tools.yaml deploy-toolbox/
Toolbox의 Dockerfile을 만듭니다. Cloud Shell 편집기에서 deploy-toolbox/Dockerfile를 엽니다.
cloudshell edit deploy-toolbox/Dockerfile
다음 스크립트를 복사합니다.
# deploy-toolbox/Dockerfile
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY toolbox tools.yaml ./
RUN chmod +x toolbox
EXPOSE 8080
CMD ["./toolbox", "--config", "tools.yaml", "--enable-api", "--address", "0.0.0.0", "--port", "8080"]
툴박스 바이너리와 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에 배포합니다. 몇 분 정도 걸립니다. logs/deploy_toolbox.log 파일에서 배포 프로세스 로그를 검사할 수 있습니다.
배포를 위해 에이전트 준비
도구 상자가 빌드되는 동안 에이전트의 배포 파일을 설정합니다.
프로젝트 루트에 Dockerfile를 만듭니다. Cloud Shell 편집기에서 Dockerfile를 엽니다.
cloudshell edit Dockerfile
그런 다음 다음 콘텐츠를 복사합니다.
# Dockerfile
FROM ghcr.io/astral-sh/uv:python3.12-trixie-slim
WORKDIR /app
COPY pyproject.toml ./
COPY uv.lock ./
RUN uv sync --no-dev
COPY jobs_agent/ jobs_agent/
EXPOSE 8080
CMD ["uv", "run", "adk", "web", "--host", "0.0.0.0", "--port", "8080"]
이 Dockerfile은 Python과 uv이 모두 사전 설치된 ghcr.io/astral-sh/uv를 기본 이미지로 사용하므로 pip를 통해 uv을 별도로 설치할 필요가 없습니다.
.dockerignore 파일을 만들어 컨테이너 이미지에서 불필요한 파일을 제외합니다.
cloudshell edit .dockerignore
그런 다음 다음 스크립트를 복사하여 붙여넣습니다.
# .dockerignore
.venv/
__pycache__/
*.pyc
.env
jobs_agent/.env
toolbox
tools.yaml
seed.sql
deploy-toolbox/
에이전트 서비스 배포
툴박스 배포가 완료될 때까지 기다립니다. logs/deploy_toolbox.log에서 배포 프로세스를 다시 확인하여 프로세스를 검증합니다. 그런 다음 다음 명령어를 사용하여 Cloud Run URL을 가져옵니다.
TOOLBOX_URL=$(gcloud run services describe toolbox-service \
--region=$REGION \
--format='value(status.url)')
echo "Toolbox URL: $TOOLBOX_URL"
다음과 비슷한 출력이 표시됩니다.
Toolbox URL: https://toolbox-service-xxxxxx-xx.a.run.app
그런 다음 배포된 Toolbox가 작동하는지 확인합니다.
curl -s "$TOOLBOX_URL/api/toolset" | python3 -m json.tool | head -5
출력이 이 예와 같이 표시되면 배포가 이미 성공한 것입니다.
{
"serverVersion": "1.0.0+binary.linux.amd64.c5524d3",
"tools": {
"add-job": {
"description": "Add a new job listing to the platform. Use this tool when a user asks to post a job that is not currently listed.",
이제 도구 상자 URL을 환경 변수로 전달하여 에이전트를 배포합니다.
cd ~/build-agent-adk-toolbox-cloudsql
gcloud run deploy jobs-agent \
--source . \
--region $REGION \
--set-env-vars "TOOLBOX_URL=$TOOLBOX_URL,GOOGLE_CLOUD_PROJECT=$GOOGLE_CLOUD_PROJECT,GOOGLE_CLOUD_LOCATION=$GOOGLE_CLOUD_LOCATION,GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
--allow-unauthenticated \
--quiet
에이전트 코드는 환경에서 TOOLBOX_URL을 읽습니다 (이전에 설정함). 로컬에서는 http://127.0.0.1:5000를 가리키고 Cloud Run에서는 Toolbox 서비스 URL을 가리킵니다. 코드를 변경하지 않아도 됩니다.
배포된 에이전트 테스트
에이전트의 Cloud Run URL을 가져옵니다.
AGENT_URL=$(gcloud run services describe jobs-agent \
--region=$REGION \
--format='value(status.url)')
echo "Agent URL: $AGENT_URL"
브라우저에서 URL을 엽니다. ADK 개발 UI가 로드됩니다. 로컬에서 사용하던 것과 동일한 인터페이스가 이제 Cloud Run에서 실행됩니다.
드롭다운에서 jobs_agent를 선택하고 테스트합니다.
What backend engineering jobs do you have?
I want a remote job working on AI and machine learning
두 쿼리 모두 배포된 서비스를 통해 작동합니다. Cloud Run의 에이전트가 Cloud Run의 Toolbox를 호출하고, Toolbox가 Cloud SQL을 쿼리합니다.
9. 축하합니다 / 정리
데이터베이스용 MCP Toolbox를 사용하여 ADK 에이전트와 Cloud SQL PostgreSQL을 연결하는 스마트 채용 게시판 어시스턴트를 빌드하고 배포했습니다. 이 어시스턴트는 표준 SQL 쿼리와 시맨틱 벡터 검색을 모두 사용합니다.
학습한 내용
- MCP가 AI 에이전트의 도구 액세스를 표준화하는 방법과 데이터베이스용 MCP 도구 상자가 이를 데이터베이스 작업에 적용하여 선언적 YAML 구성으로 맞춤 데이터베이스 코드를 대체하는 방법
cloud-sql-postgres소스 유형을 사용하여 Cloud SQL PostgreSQL을 도구 상자 데이터 소스로 구성하는 방법- SQL 삽입을 방지하는 매개변수화된 문을 사용하여 표준 SQL 쿼리 도구를 정의하는 방법
- pgvector 및
gemini-embedding-001를 사용하여 벡터 검색을 사용 설정하는 방법(자동 쿼리 임베딩을 위한embeddedBy매개변수 포함) valueFromParam를 통해 자동 벡터 수집이 가능합니다. LLM이 텍스트 설명을 제공하면 Toolbox가 텍스트와 함께 벡터를 자동으로 복사, 삽입, 저장합니다.- ADK의
ToolboxToolset가 실행 중인 도구 상자 서버에서 도구를 로드하여 에이전트 코드를 최소화하고 데이터베이스 로직을 완전히 분리하는 방법 - Toolbox MCP 서버와 ADK 에이전트를 별도의 서비스로 Cloud Run에 배포하는 방법
정리
이 Codelab에서 생성된 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 개별 리소스를 삭제하거나 전체 프로젝트를 삭제하면 됩니다.
옵션 1: 프로젝트 삭제 (권장)
삭제하는 가장 쉬운 방법은 프로젝트를 삭제하는 것입니다. 이렇게 하면 프로젝트와 연결된 모든 리소스가 삭제됩니다.
gcloud projects delete $GOOGLE_CLOUD_PROJECT
옵션 2: 개별 리소스 삭제
프로젝트는 유지하고 이 Codelab에서 만든 리소스만 삭제하려면 다음 단계를 따르세요.
gcloud run services delete jobs-agent --region=$REGION --quiet
gcloud run services delete toolbox-service --region=$REGION --quiet
gcloud sql instances delete jobs-instance --quiet
gcloud artifacts repositories delete cloud-run-source-deploy --location=$REGION --quiet 2>/dev/null
