将数据库作为工具:使用 ADK、MCP Toolbox 和 Cloud SQL 实现智能体 RAG

1. 简介

AI 智能体的实用性取决于其可访问的数据。大多数真实世界的数据都存储在数据库中,而将代理连接到数据库通常意味着需要在代理代码中编写连接管理、查询逻辑和嵌入流水线。需要数据库访问权限的每个代理都会重复这项工作,并且每次更改查询都需要重新部署代理。

此 Codelab 展示了另一种方法。您可以在 YAML 文件中声明数据库工具(标准 SQL 查询、向量相似性搜索,甚至自动嵌入生成),然后 MCP Toolbox for Databases 会作为 MCP 服务器处理所有数据库操作。代理代码保持最简:加载工具,让 Gemini 决定调用哪个工具。

构建内容

面向“TechJobs”的智能职位发布助理 - 一款由 Gemini 提供支持的 ADK 代理,可帮助开发者使用标准过滤条件(职位、技术堆栈)浏览技术职位列表,并通过自然语言描述(例如“我想找一份远程工作,开发 AI 聊天机器人”)发现职位。代理完全通过 MCP Toolbox for Databases 从 Cloud SQL PostgreSQL 数据库读取数据和向其中写入数据,该工具箱可处理所有数据库访问操作,包括为向量搜索自动生成嵌入。最终,工具箱和代理都将在 Cloud Run 上运行。

eb6de681c40990c1.jpeg

学习内容

  • MCP(Model Context Protocol)如何为 AI 智能体标准化工具访问,以及 MCP Toolbox for Databases 如何将此应用于数据库操作
  • 将 MCP Toolbox for Databases 设置为 ADK 代理和 Cloud SQL PostgreSQL 之间的中间件
  • tools.yaml 中以声明方式定义数据库工具 - 智能体中没有数据库代码
  • 构建一个 ADK 智能体,该智能体使用 ToolboxToolset 从正在运行的 Toolbox 服务器加载工具
  • 使用 Cloud SQL 的内置 embedding() 函数生成向量嵌入,并使用 pgvector 启用语义搜索
  • 使用 valueFromParam 功能在写入操作时自动提取向量
  • 将 Toolbox 服务器和 ADK 代理都部署到 Cloud Run

前提条件

  • 具有试用结算账号的 Google Cloud 账号
  • 基本熟悉 Python 和 SQL
  • 无需拥有 ADK、MCP Toolbox 或 pgvector 相关经验

2. 设置环境

此步骤将准备 Cloud Shell 环境、配置 Google Cloud 项目并克隆参考代码库。

打开 Cloud Shell

在浏览器中打开 Cloud Shell。Cloud Shell 提供了一个预配置的环境,其中包含本 Codelab 所需的所有工具。在系统提示时点击授权,以

然后,依次点击“查看” ->“终端”,打开终端。您的界面应与此类似

86307fac5da2f077.png

这将是我们的主要界面,顶部是 IDE,底部是终端

设置工作目录

创建工作目录。您在此 Codelab 中编写的所有代码都位于此处:

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

设置您的 Google Cloud 项目

创建包含位置变量的 .env 文件:

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

将项目设置脚本下载到您的工作目录中:

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

运行脚本。它会验证您的试用结算账号,创建新项目(或验证现有项目),将项目 ID 保存到当前目录中的 .env 文件,并在 gcloud 中设置有效项目。

bash setup_verify_trial_project.sh && source .env

该脚本将:

  1. 验证您是否拥有有效的试用结算账号
  2. 检查 .env 中是否存在现有项目(如果有)
  3. 创建新项目或重复使用现有项目
  4. 将试用结算账号与您的项目相关联
  5. 将项目 ID 保存到 .env
  6. 将项目设置为活跃 gcloud 项目

通过检查 Cloud Shell 终端提示中工作目录旁边的黄色文字,验证项目是否已正确设置。其中应显示您的项目 ID。

dcba35ce1389f313.png

如果您的 Cloud Shell 会话在本 Codelab 期间的任何时间重置,请返回到工作目录并重新运行 bash setup_verify_trial_project.sh && source .env 以恢复项目配置。确认终端提示中重新显示了黄色项目 ID 文本。

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 实例时需要此 API。
  • Cloud Run、Cloud Build、Artifact Registry - 在本 Codelab 后面的部署步骤中使用

3. 创建数据库实例

此步骤会在后台设置 Cloud SQL 实例创建过程,以便在您继续学习本教程时进行预配。

开始创建实例

将数据库密码添加到 .env 文件并重新加载该文件:

echo "DB_PASSWORD=techjobs-pwd-2025" >> .env
source .env

开始创建 Cloud SQL 实例。此命令会在后台运行,因此您可以继续工作:

gcloud sql instances create jobs-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 &
  • db-custom-1-3840ENTERPRISE 版中最小的专用核心 Cloud SQL 层(1 个 vCPU、3.75 GB RAM)。如需了解更多详情,请点击此处。Vertex AI 机器学习集成需要专用核心,共享核心层级(db-f1-microdb-g1-small)不支持此功能。
  • --root-password 为默认 postgres 用户设置密码。
  • --enable-google-ml-integration 可启用 Cloud SQL 与 Vertex AI 的内置集成,让您可以使用 embedding() 函数直接从 SQL 调用嵌入模型。
  • & 在后台运行命令。

此命令将在后台运行,接下来我们下载 MCP Toolbox 二进制文件。您可以在同一终端中执行此操作

下载 Toolbox 二进制文件

在本教程中,我们将使用 MCP Toolbox,幸运的是,它附带了一个预构建的二进制文件,可在 Linux 环境中使用。由于下载需要相当长的时间,我们让它在后台下载

cd ~/build-agent-adk-toolbox-cloudsql
curl -O https://storage.googleapis.com/genai-toolbox/v0.27.0/linux/amd64/toolbox &

让此进程在当前标签页中运行(我们已在后台运行此进程,但仍会显示输出)。我们来在 Cloud Shell 中打开一个新终端标签页(点击 + 图标),以便更专注于操作。

b01e3fbd89f17332.png

再次前往工作目录,然后使用之前的设置脚本激活项目。

cd ~/build-agent-adk-toolbox-cloudsql
bash setup_verify_trial_project.sh && source .env

此步骤会设置 Python 项目、安装依赖项,并搭建 ADK 代理目录

4. 初始化代理项目

设置 Python 项目

uv 是一个用 Rust 编写的快速 Python 软件包和项目管理器(请参阅 uv 文档)。此 Codelab 使用它是因为它速度快且简单。

初始化 Python 项目并添加所需的依赖项:

uv init
uv add google-adk==1.25.0 toolbox-adk==0.6.0
  • google-adk - Google 的智能体开发套件,包括 Gemini SDK
  • toolbox-adk - 针对 MCP Toolbox for Databases 的 ADK 集成。

创建代理目录结构

ADK 需要特定的文件夹布局:一个以代理命名的目录,其中包含 __init__.pyagent.py.env。为了帮助您完成此操作,它内置了可快速建立结构的命令:

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

您的目录现在应如下所示:

build-agent-adk-toolbox-cloudsql/
├── jobs_agent/
│   ├── __init__.py
│   ├── agent.py
│   └── .env
├── pyproject.toml
├── .env              (project setup — already exists)
└── .venv/

5. 为招聘信息数据库植入种子数据

此步骤会写入初始数据,等待 Cloud SQL 实例完成预配,并加载包含 15 个职位信息及其说明嵌入内容的 jobs

编写初始 SQL

我们将在 Cloud Shell 编辑器中创建一个名为 seed.sql 的文件,其中包含职位列表内容。这会创建支持 pgvectorjobs 表,并插入 15 个科技公司的职位列表。

首先,使用以下命令创建 seed.sql 文件:

cloudshell edit seed.sql

然后,将这些脚本复制到文件中

-- seed.sql
-- DISCLAIMER: These job listings are entirely fictional and created for tutorial
-- purposes only. Company names are used for illustrative context — the positions,
-- salaries, and descriptions do not reflect real openings.

CREATE EXTENSION IF NOT EXISTS google_ml_integration;
CREATE EXTENSION IF NOT EXISTS vector;

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)
);

INSERT INTO jobs (title, company, role, tech_stack, salary_range, location, openings, description) VALUES
('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.');

种子脚本会安装两个 PostgreSQL 扩展程序:

  • google_ml_integration - 提供 embedding() SQL 函数,该函数可直接从 SQL 调用 Vertex AI 嵌入模型。这是一个数据库级扩展程序,可让 jobs_db 内使用机器学习函数。您在创建实例期间设置的实例级标志 (--enable-google-ml-integration) 允许 Cloud SQL 虚拟机访问 Vertex AI;该扩展程序使 SQL 函数在此特定数据库中可用。
  • vector (pgvector) - 添加了 vector 数据类型和距离运算符,用于存储和查询嵌入内容。

description_embedding 列是 vector(3072),即存储 3072 维向量的 pgvector 列。目前为 NULL;您将在下一步中使用 embedding() 函数生成并填充嵌入内容。

完成数据库设置

您在上一步中开始创建的 Cloud SQL 实例可能仍在运行,尚未完成。验证实例是否已准备就绪:

gcloud sql instances describe jobs-instance --format="value(state)"

您应该会看到以下输出内容:

RUNNABLE

34f5b48006b4cb3a.png

接下来,向 Cloud SQL 实例的服务账号授予调用 Vertex AI 的权限。这是内置 embedding() 函数所必需的,您将在下一步中使用该函数:

SERVICE_ACCOUNT=$(gcloud sql instances describe jobs-instance --format="value(serviceAccountEmailAddress)")

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

之后,为职位发布信息创建专用数据库:

gcloud sql databases create jobs_db --instance=jobs-instance

您应该会看到确认数据库已创建的输出内容:

Creating Cloud SQL database...done.                                                                         
Created database [jobs_db].
instance: jobs-instance
name: jobs_db
project: workshop-xxxxxxx

连接并填充数据库

启动 Cloud SQL Auth 代理(cloud-sql-proxy 已预安装在 Cloud Shell 中)。这样一来,Cloud Shell 便可与 Cloud SQL 实例建立经过身份验证的安全连接:

d72e56478b517b5c.jpeg

cloud-sql-proxy ${GOOGLE_CLOUD_PROJECT}:${REGION}:jobs-instance --port 5432 &

如果代理启动,您应该会在终端中看到以下输出:

... Authorizing with Application Default Credentials
... [workshop-xxxxxx:your-location:jobs-instance] Listening on 127.0.0.1:5432
... The proxy has started successfully and is ready for new connections!

现在,当前终端会持续输出 Cloud SQL 代理的日志。我们来在 Cloud Shell 中打开一个新终端标签页(点击 + 图标),以便更专注于操作。

b01e3fbd89f17332.png

再次前往工作目录,然后使用之前的设置脚本激活项目。

cd ~/build-agent-adk-toolbox-cloudsql
bash setup_verify_trial_project.sh && source .env

然后,运行初始脚本

psql "host=127.0.0.1 port=5432 dbname=jobs_db user=postgres password=$DB_PASSWORD" -f seed.sql

您将看到如下所示的终端输出

CREATE EXTENSION
CREATE EXTENSION
CREATE TABLE
INSERT 0 15

验证数据

psql "host=127.0.0.1 port=5432 dbname=jobs_db user=postgres password=$DB_PASSWORD" \
  -c "SELECT title, company, role, openings FROM jobs ORDER BY role, title;"

您应该会看到 15 个职位,涵盖多个角色:

             title              |    company     |   role    | openings
---------------------------------+----------------+-----------+----------
 Senior Backend Engineer         | Stripe         | Backend   |        3
 Backend Engineer (Payments)     | Square         | Backend   |        3
 Cloud Architect                 | Google Cloud   | Cloud     |        1
 ...
(15 rows)

为职位描述生成嵌入

jobs 表中的 description_embedding 列目前为 NULL。Cloud SQL 的内置 google_ml_integration 扩展程序提供了一个 embedding() 函数,可直接从 SQL 调用 Vertex AI,无需 Python 脚本或外部 SDK。

在后台开始生成嵌入内容。此代码会调用 Vertex AI,使用 gemini-embedding-001 模型为 15 个职位描述中的每一个生成一个 3072 维向量:

psql "host=127.0.0.1 port=5432 dbname=jobs_db user=postgres password=$DB_PASSWORD" \
  -c "UPDATE jobs SET description_embedding = embedding('gemini-embedding-001', description)::vector;" &

脚本的作用如下:

  • embedding('gemini-embedding-001', description) - 直接从 SQL 调用 Vertex AI 的 Gemini 嵌入模型,并传递每个作业的 description 文本。这是您在初始脚本中安装的 google_ml_integration 扩展程序。
  • ::vector - 将返回的浮点数组转换为 pgvector 的 vector 类型,以便可以使用距离运算符存储和查询该数组。
  • UPDATE 跨所有 15 行运行,为每个职位说明生成一个 3072 维的嵌入。
  • & 会在后台运行命令,因此您可以在 Vertex AI 处理嵌入内容的同时继续工作。

与之前的后台进程执行类似,当前终端将输出该进程的日志。我们来在 Cloud Shell 中打开一个新终端标签页(点击 + 图标),以便更专注于操作。

b01e3fbd89f17332.png

再次前往工作目录,然后使用之前的设置脚本激活项目。

cd ~/build-agent-adk-toolbox-cloudsql
bash setup_verify_trial_project.sh && source .env

然后,我们可以继续执行下一个流程

6. 配置 MCP Toolbox for Databases

此步骤介绍了 MCP Toolbox for Databases,将其配置为连接到您的 Cloud SQL 实例,并定义了两个标准 SQL 查询工具。

什么是 MCP?为什么要使用 Toolbox?

e7b9be2e1c98b4db.png

MCP(Model Context Protocol)是一种开放协议,可标准化 AI 智能体发现和与外部工具互动的方式。它定义了客户端-服务器模型:智能体托管 MCP 客户端,而工具由 MCP 服务器公开。任何兼容 MCP 的客户端都可以使用任何兼容 MCP 的服务器,智能体无需为每个工具编写自定义集成代码。

d5baa77423f0f465.png

MCP Toolbox for Databases 是一款专门为数据库访问而构建的开源 MCP 服务器。如果没有它,您需要编写 Python 函数来打开数据库连接、管理连接池、构建参数化查询以防止 SQL 注入、处理错误,并将所有这些代码嵌入到代理中。需要访问数据库的每个代理都会重复这项工作。更改查询意味着重新部署代理。

使用 Toolbox 时,您需要编写一个 YAML 文件。每种工具都映射到一条参数化 SQL 语句。Toolbox 可处理连接池、参数化查询、身份验证和可观测性。工具与代理分离 - 通过编辑 tools.yaml 并重启 Toolbox 来更新查询,无需修改代理代码。这些工具适用于 ADK、LangGraph、LlamaIndex 或任何 MCP 兼容框架。

编写工具配置

现在,我们需要在 Cloud Shell 编辑器中创建一个名为 tools.yaml 的文件,以设置工具配置

cloudshell edit tools.yaml

该文件使用多文档 YAML,每个以 --- 分隔的块都是一个独立的资源。每个资源都有一个 kind,用于声明其类型(sources 表示数据库连接,tools 表示可供代理调用的操作),还有一个 type,用于指定后端(cloud-sql-postgres 表示来源,postgres-sql 表示基于 SQL 的工具)。工具通过 name 引用其来源,这样 Toolbox 就能知道要针对哪个连接池执行操作。环境变量使用 ${VAR_NAME} 语法,并在启动时解析。

现在,我们先将以下脚本复制到 tools.yaml 文件中

# tools.yaml

# --- Data Source ---
kind: sources
name: jobs-db
type: cloud-sql-postgres
project: ${GOOGLE_CLOUD_PROJECT}
region: ${REGION}
instance: jobs-instance
database: jobs_db
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: tools
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: tools
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-jobsget-job-details) - 标准 SQL 查询工具。每个映射都将工具名称(代理看到的内容)映射到参数化 SQL 语句(数据库执行的内容)。参数使用 $1$2 位置占位符。Toolbox 会将这些语句作为预处理语句执行,从而防止 SQL 注入。

我们继续操作,在 tools.yaml--- 符号下方附加以下脚本

# --- Embedding Model ---
kind: embeddingModels
name: gemini-embedding
type: gemini
model: gemini-embedding-001
dimension: 3072

---

此脚本定义了以下资源:

  • 嵌入模型 (gemini-embedding) - 将工具箱配置为调用 Gemini 的 gemini-embedding-001 模型来生成 3072 维的文本嵌入。该工具箱使用应用默认凭据 (ADC) 进行身份验证,因此在 Cloud Shell 或 Cloud Run 中无需使用 API 密钥。请注意,此处配置的 dimension 必须与之前配置的用于为数据库提供初始数据的 dimension 相同

我们继续操作,在 tools.yaml--- 符号下方附加以下脚本

# --- Tool 3: Semantic search by description ---
kind: tools
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: tools
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 - 工具箱会将复制的文本嵌入到向量中,然后再将其传递给 SQL。

结果:一个工具调用存储了原始说明文本及其向量嵌入,而代理对嵌入一无所知。

多文档 YAML 格式使用 --- 分隔每个资源。每个文档都有 kindnametype 字段来定义其内容。总而言之,我们已配置以下所有内容:

  • 定义源数据库
  • 定义工具(工具 1 和 2),以使用标准过滤条件查询数据库
  • 定义嵌入模型
  • 定义用于对数据库执行向量搜索的工具(工具 3
  • 定义用于将向量数据注入(工具 4)到数据库的工具

验证嵌入内容

在启动 Toolbox 之前,请确认后台嵌入生成已完成。检查所有作业现在是否都包含嵌入内容:

psql "host=127.0.0.1 port=5432 dbname=jobs_db user=postgres password=$DB_PASSWORD" \
  -c "SELECT title, (description_embedding IS NOT NULL) AS has_embedding FROM jobs ORDER BY title;"

每行都应在 has_embedding 列中显示 t(true)。如果不是,您可以选择等待所有行嵌入创建过程完成

           title            | has_embedding 
-----------------------------+---------------
 AI Engineer                 | t
 Backend Engineer (Payments) | t
 Cloud Architect             | t
 Data Engineer               | t
 DevOps Engineer             | t
 Frontend Engineer           | t
 Full Stack Engineer         | t

启动 Toolbox 服务器

在设置步骤中,我们已下载 toolbox 可执行文件。确保此二进制文件存在且已成功下载,否则,请下载该文件并等待下载完成

cd ~/build-agent-adk-toolbox-cloudsql
if [ ! -f toolbox ]; then
  curl -O https://storage.googleapis.com/genai-toolbox/v0.27.0/linux/amd64/toolbox
fi
chmod +x toolbox

导出所需的环境变量并启动 Toolbox。GOOGLE_CLOUD_LOCATIONGOOGLE_GENAI_USE_VERTEXAI 变量是必需的,因为配置中包含嵌入模型 - GOOGLE_GENAI_USE_VERTEXAI 会告知 Gemini SDK 通过 Vertex AI(而不是消费者 Gemini API)进行路由,而 GOOGLE_CLOUD_LOCATION 会告知它要使用的区域端点。

export GOOGLE_CLOUD_PROJECT=$GOOGLE_CLOUD_PROJECT
export GOOGLE_CLOUD_LOCATION=$GOOGLE_CLOUD_LOCATION
export GOOGLE_GENAI_USE_VERTEXAI=true
export DB_PASSWORD=$DB_PASSWORD
export REGION=$REGION
./toolbox --tools-file tools.yaml &

您应该会看到确认服务器已准备就绪的输出,如下所示:

... INFO "Initialized 0 authServices: " 
... 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!"

与上一步类似,此命令将生成另一个进程并输出结果。我们来在 Cloud Shell 中打开一个新终端标签页(点击 + 图标),以便更专注于操作。

b01e3fbd89f17332.png

再次前往工作目录,然后使用之前的设置脚本激活项目。

cd ~/build-agent-adk-toolbox-cloudsql
bash setup_verify_trial_project.sh && source .env

验证工具

查询 Toolbox API 以列出所有已注册的工具:

curl -s http://localhost:5000/api/toolset | python3 -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 代理

此步骤将 ADK 代理连接到正在运行的 Toolbox 服务器,并测试所有四种工具 - 标准查询、语义搜索和向量提取。代理代码非常简单:所有数据库逻辑都位于 tools.yaml 中。

配置代理的环境

ADK 从 shell 环境中读取 GOOGLE_GENAI_USE_VERTEXAIGOOGLE_CLOUD_PROJECTGOOGLE_CLOUD_LOCATION,您已在之前的步骤中设置了这些变量。唯一特定于代理的变量是 TOOLBOX_URL - 将其附加到代理的 .env 文件:

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

更新代理模块

在 Cloud Shell Editor 中打开 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 网址替换此网址,而无需更改任何代码。

该指令目前仅引用了两个标准工具(search-jobsget-job-details)。您将在下一步中添加语义搜索和提取工具,届时会扩展该指令。

测试代理

启动 ADK 开发者界面:

cd ~/build-agent-adk-toolbox-cloudsql
uv run adk web

使用 Cloud Shell 的网页预览功能打开终端中显示的网址(通常为 http://localhost:8000),或按住 Ctrl 键并点击终端中显示的网址。从左上角的智能体下拉菜单中选择 jobs_agent

测试标准查询

不妨试试以下提示,验证标准 SQL 工具:

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

93ac33e7f73aa0b9.png 240c53376042a916.png

尝试使用未映射到特定角色或技术堆栈的自然语言描述:

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

智能体将尝试根据查询类型选择合适的工具:结构化过滤条件通过 search-jobs,自然语言描述通过 search-jobs-by-description

b0ea629f5c9b4c26.png

测试向量提取

让代理添加新作业:

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

c601a7a9bc0a705b.png

现在,尝试搜索该内容:

Find me jobs involving autonomous systems and working with physical hardware

嵌入是在 INSERT 期间自动生成的,无需单独的步骤。

5a3d8e6f523dc18b.png

现在,您已经拥有一个功能完善的智能体 RAG 应用,该应用利用了 ADK、MCP Toolbox 和 CloudSQL。恭喜!接下来,我们将进一步把这些应用部署到 Cloud Run!

现在,让我们在继续操作之前,按两次 Ctrl+C 来终止进程,从而停止开发者界面。

8. 部署到 Cloud Run

代理和工具箱在本地运行。此步骤会将两者都部署为 Cloud Run 服务,以便通过互联网访问它们。Toolbox 服务在 Cloud Run 上作为 MCP 服务器运行,而代理服务会连接到该服务。

准备部署工具箱

为 Toolbox 服务创建部署目录:

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

为 Toolbox 创建 Dockerfile。在 Cloud Shell Editor 中打开 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", "--tools-file", "tools.yaml", "--address", "0.0.0.0", "--port", "8080"]

工具箱二进制文件和 tools.yaml 打包到最小的 Debian 映像中。Cloud Run 会将流量路由到端口 8080。

部署 Toolbox 服务

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

此命令会将源代码提交给 Cloud Build,构建容器映像,将其推送到 Artifact Registry,并将其部署到 Cloud Run。这需要几分钟时间,我们不妨在 Cloud Shell 中打开一个新的终端标签页(点击加号图标),以便更专注于此任务。

b01e3fbd89f17332.png

再次前往工作目录,然后使用之前的设置脚本激活项目。

cd ~/build-agent-adk-toolbox-cloudsql
bash setup_verify_trial_project.sh && source .env

准备智能体以进行部署

在构建 Toolbox 的同时,设置代理的部署文件。

在项目根目录中创建 Dockerfile。在 Cloud Shell Editor 中打开 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 使用 ghcr.io/astral-sh/uv 作为基础映像,其中预安装了 Python 和 uv,无需通过 pip 单独安装 uv

创建一个 .dockerignore 文件,以从容器映像中排除不必要的文件:

cloudshell edit .dockerignore

然后将以下脚本复制到其中

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

部署代理服务

等待工具箱部署完成。使用以下命令检索其 Cloud Run 网址

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

您将看到类似于以下内容的输出

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

接下来,我们验证已部署的工具箱是否正常运行:

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

如果输出内容与此示例类似,则表示部署已成功

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

接下来,我们来部署代理,并将 Toolbox 网址作为环境变量传递:

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 服务网址。无需更改任何代码。

测试已部署的代理

检索代理的 Cloud Run 网址:

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

在浏览器中打开该网址。ADK 开发者界面会加载,该界面与您一直在本地使用的界面相同,现在在 Cloud Run 上运行。

从下拉菜单中选择 jobs_agent 并进行测试:

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

这两个查询都通过已部署的服务运行:Cloud Run 上的代理调用 Cloud Run 上的 Toolbox,后者查询 Cloud SQL。

9. 恭喜 / 清理

您已构建并部署了一个智能招聘信息板助理,该助理使用 MCP Toolbox for Databases 将 ADK 代理和 Cloud SQL PostgreSQL 连接起来,同时支持标准 SQL 查询和语义向量搜索。

您学到的内容

  • MCP 如何为 AI 智能体标准化工具访问,以及 MCP Toolbox for Databases 如何将此功能专门应用于数据库操作 - 使用声明性 YAML 配置替换自定义数据库代码
  • 如何使用 cloud-sql-postgres 源类型将 Cloud SQL PostgreSQL 配置为工具箱数据源
  • 如何使用参数化语句定义可防止 SQL 注入的标准 SQL 查询工具
  • 如何使用 pgvector 和 gemini-embedding-001 启用向量搜索,并使用 embeddedBy 参数自动嵌入查询
  • valueFromParam 如何实现自动向量提取 - LLM 提供文本说明,而 Toolbox 会在后台复制、嵌入向量并将其与文本一起存储
  • ADK 的 ToolboxToolset 如何从正在运行的 Toolbox 服务器加载工具,从而最大限度地减少代理代码并使数据库逻辑完全解耦
  • 如何将 Toolbox MCP 服务器和 ADK 代理作为单独的服务部署到 Cloud Run

清理

为避免因本 Codelab 中创建的资源导致您的 Google Cloud 账号产生费用,您可以删除各个资源,也可以删除整个项目。

最简单的清理方法是删除项目。这会移除与项目关联的所有资源。

gcloud projects delete $GOOGLE_CLOUD_PROJECT

方法 2:删除单个资源

如果您想保留项目,但仅移除在本 Codelab 中创建的资源,请执行以下操作:

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