工具打造智能体:使用 ADK 从零开始创建助理

1. 简介

在本实验中,您将使用智能体开发套件 (ADK) 构建一个智能体!您将学习如何使用 ADK 和各种工具类型来构建软件 bug 助理代理。您将从一个基本智能体开始,逐步添加工具来增强其功能,包括函数工具内置工具第三方工具Model Context Protocol (MCP) 工具

工具使代理

学习内容

  • 如何设置 Python 项目以进行 ADK 开发。
  • 如何创建基本的 ADK 代理。
  • 如何实现和使用函数工具。
  • 如何集成 Google 搜索等内置工具。
  • 如何在 ADK 中利用 LangChain 等框架中的第三方工具。
  • 如何使用 MCP 工具与数据库 (Cloud SQL) 和 API 进行交互。

2. 概览

假设您是全球咖啡机公司 QuantumRoast 的项目经理。

Quantum Roast

您帮助团队成员应对各种工程路线图、突如其来的策略转变(我们现在开始做抹茶了!),以及客户发来的各种问题工单,从有 bug 的账单系统到 24 小时发出尖锐噪音的咖啡机,无所不包。

在平常的一天,您会打开大约 50 个浏览器标签页:内部工单系统、电子邮件、聊天、GitHub、Google 搜索、StackOverflow 等等。您喜欢自己的工作和队友,但有时会感到不堪重负。

不堪重负的项目经理

如果我们能打造一个助手,帮助您创建和分诊软件工单,并调试问题,会怎样?AI 智能体可实现这一点。

AI 智能体

智能体开发套件 (ADK)

智能体开发套件 (ADK) 是一个灵活的模块化框架,用于开发和部署 AI 智能体。尽管 ADK 针对 Gemini 和 Google 生态系统进行了优化,但它仍不限模型、与部署无关,并且可与其他框架兼容。ADK 的设计旨在让智能体开发更像软件开发,从而让开发者能够更轻松地创建、部署和编排从简单任务到复杂工作流的智能体架构。

ADK 是我们将用于构建 QuantumRoast 软件 bug 助理的框架。

代理图

工具 101

AI 智能体使用模型(而不仅仅是硬编码的逻辑)来推理解决问题。但 AI 智能体不仅能进行基于 LLM 的推理,还具备独特的强大功能,可以收集外部数据,然后代表用户采取行动。AI 智能体不仅会告诉您如何解决问题,还能帮助您实际解决问题。那要如何做呢?使用工具

工具是一种功能,可帮助 AI 智能体与世界互动。工具几乎可以是任何东西:内嵌函数、托管数据库、第三方 API,甚至是另一个智能体。智能体开发套件 (ADK) 等 AI 智能体框架内置了对工具的支持,支持多种工具类型,我们稍后会介绍。

但代理不仅知道何时调用特定工具,还知道如何调用该工具,这是怎么做到的?智能体的模型在这里发挥着几个关键作用。

工具的工作原理

首先是工具选择。我们会向智能体提供工具列表以及有关如何使用这些工具的一些说明。当用户提示智能体时,智能体的模型会帮助确定要调用哪些工具以及调用原因,以便帮助用户。

第二个关键步骤是函数调用。函数调用以下名称有点用词不当,因为模型实际上并没有调用工具,而是通过格式化请求正文准备调用工具,然后框架会使用该请求正文来调用工具。

最后,该模型有助于解读来自该工具的响应(例如数据库中的开放 bug 列表),并决定是否采取进一步行动,或者是否向用户回复该信息。

为了实际体验上述所有功能,现在让我们使用 ADK Python 构建 QuantumRoast bug 助理代理

QuantumRoast Bug Assistant

3. 准备工作

Google Cloud 项目设置

  1. 如果您还没有 Google 账号,则必须先创建一个 Google 账号
    • 请改用个人账号,而不是工作账号或学校账号。工作账号和学校账号可能存在限制,导致您无法启用本实验所需的 API。
  2. 登录 Google Cloud 控制台
  3. 在 Cloud 控制台中启用结算功能
    • 完成本实验的 Cloud 资源费用应不到 1 美元。
    • 您可以按照本实验结束时的步骤删除资源,以避免产生更多费用。
    • 新用户符合参与 $300 USD 免费试用计划的条件。
  4. 创建新项目或选择重复使用现有项目。

打开 Cloud Shell Editor

  1. 前往 Cloud Shell 编辑器
  2. 如果终端未显示在屏幕底部,请打开它:
    • 点击汉堡式菜单 汉堡式菜单图标
    • 点击终端
    • 点击 New Terminal在 Cloud Shell 编辑器中打开新终端
  3. 在终端中,使用以下命令设置项目(替换 YOUR_PROJECT_ID):
    • 格式:
      gcloud config set project YOUR_PROJECT_ID
      
    • 示例:
      gcloud config set project lab-project-id-example
      
    • 如果您不记得项目 ID,请执行以下操作:
      • 您可以使用以下命令列出所有项目 ID:
        gcloud projects list | awk '/PROJECT_ID/{print $2}'
        
      在 Cloud Shell 编辑器终端中设置项目 ID
  4. 如果系统提示您进行授权,请点击授权继续。点击以授权 Cloud Shell
  5. 您应会看到以下消息:
    Updated property [core/project].
    
    如果您看到 WARNING 并被问到 Do you want to continue (Y/N)?,则很可能是您输入的项目 ID 有误。按 N,按 Enter,然后尝试再次运行 gcloud config set project 命令。
  6. 在终端中,设置要在后续步骤中使用的 PROJECT_ID 环境变量。
    export PROJECT_ID=$(gcloud config get project)
    

启用 API

在终端中,运行以下命令以启用必要的 Google Cloud API:

gcloud services enable sqladmin.googleapis.com \
   compute.googleapis.com \
   cloudresourcemanager.googleapis.com \
   secretmanager.googleapis.com \
   servicenetworking.googleapis.com \
   aiplatform.googleapis.com \
   run.googleapis.com \
   artifactregistry.googleapis.com \
   cloudbuild.googleapis.com

创建 Cloud SQL for PostgreSQL 实例

QuantumRoast 有一个 bug 工单数据库,其中包含所有内部工单。接下来,我们创建一个 Cloud SQL for PostgreSQL 实例来设置它。

gcloud sql instances create software-assistant \
   --database-version=POSTGRES_16 \
   --tier=db-custom-1-3840 \
   --region=us-central1 \
   --edition=ENTERPRISE \
   --enable-google-ml-integration \
   --database-flags cloudsql.enable_google_ml_integration=on \
   --root-password=admin

等待实例创建完成(可能需要几分钟时间)。

创建实例后,您可以点击此处在 Cloud 控制台中查看该实例。

创建 Cloud SQL 数据库

创建 SQL 数据库 (tickets-db),并授予 Cloud SQL 服务账号对 Vertex AI 的访问权限(以便我们可以创建嵌入来执行相似性搜索)。

gcloud sql databases create tickets-db --instance=software-assistant

SERVICE_ACCOUNT_EMAIL=$(gcloud sql instances describe software-assistant --format="value(serviceAccountEmailAddress)")
echo $SERVICE_ACCOUNT_EMAIL

gcloud projects add-iam-policy-binding $PROJECT_ID --member="serviceAccount:$SERVICE_ACCOUNT_EMAIL" --role="roles/aiplatform.user"

设置 tickets

Cloud 控制台 (Cloud SQL) 中,为 software-assistant 实例打开 Cloud SQL Studio

使用 postgres 用户名和 admin 密码登录 tickets-db 数据库。

Cloud SQL Studio

打开新的 Editor 标签页。

Cloud SQL Studio 编辑器

然后,粘贴以下 SQL 代码以设置表并创建向量嵌入。按 Run 按钮执行命令。

CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE;
CREATE EXTENSION IF NOT EXISTS vector CASCADE;
GRANT EXECUTE ON FUNCTION embedding TO postgres;

CREATE TABLE tickets (
    ticket_id SERIAL PRIMARY KEY,             -- PostgreSQL's auto-incrementing integer type (SERIAL is equivalent to INT AUTO_INCREMENT)
    title VARCHAR(255) NOT NULL,              -- A concise summary or title of the bug/issue.
    description TEXT,                         -- A detailed description of the bug.
    assignee VARCHAR(100),                    -- The name or email of the person/team assigned to the ticket.
    priority VARCHAR(50),                     -- The priority level (e.g., 'P0 - Critical', 'P1 - High').
    status VARCHAR(50) DEFAULT 'Open',        -- The current status of the ticket (e.g., 'Open', 'In Progress', 'Resolved'). Default is 'Open'.
    creation_time TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, -- Timestamp when the ticket was first created. 'WITH TIME ZONE' is recommended for clarity and compatibility.
    updated_time TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP  -- Timestamp when the ticket was last updated. Will be managed by a trigger.
);

tickets 表已创建,点击 Clear 可清除旧查询。

现在,插入示例数据,然后再次点击 Run 按钮。

INSERT INTO tickets (title, description, assignee, priority, status) VALUES
('Login Page Freezes After Multiple Failed Attempts', 'Users are reporting that after 3 failed login attempts, the login page becomes unresponsive and requires a refresh. No specific error message is displayed.', 'samuel.green@example.com', 'P0 - Critical', 'Open');

INSERT INTO tickets (title, description, assignee, priority, status) VALUES
('Dashboard Sales Widget Intermittent Data Loading Failure', 'The "Sales Overview" widget on the main dashboard intermittently shows a loading spinner but no data. Primarily affects Chrome browser users.', 'maria.rodriguez@example.com', 'P1 - High', 'In Progress');

INSERT INTO tickets (title, description, assignee, priority, status) VALUES
('Broken Link in Footer - Privacy Policy', 'The "Privacy Policy" hyperlink located in the website footer leads to a 404 "Page Not Found" error.', 'maria.rodriguez@example.com', 'P3 - Low', 'Resolved');

INSERT INTO tickets (title, description, assignee, priority, status) VALUES
('UI Misalignment on Mobile Landscape View (iOS)', 'On specific iOS devices (e.g., iPhone 14 models), the top navigation bar shifts downwards when the device is viewed in landscape orientation, obscuring content.', 'maria.rodriguez@example.com', 'P2 - Medium', 'In Progress');

INSERT INTO tickets (title, description, assignee, priority, status) VALUES
('Critical XZ Utils Backdoor Detected in Core Dependency (CVE-2024-3094)', 'Urgent: A sophisticated supply chain compromise (CVE-2024-3094) has been identified in XZ Utils versions 5.6.0 and 5.6.1. This malicious code potentially allows unauthorized remote SSH access by modifying liblzma. Immediate investigation and action required for affected Linux/Unix systems and services relying on XZ Utils.', 'frank.white@example.com', 'P0 - Critical', 'Open');

INSERT INTO tickets (title, description, assignee, priority, status) VALUES
('Database Connection Timeouts During Peak Usage', 'The application is experiencing frequent database connection timeouts, particularly during peak hours (10 AM - 12 PM EDT), affecting all users and causing service interruptions.', 'frank.white@example.com', 'P1 - High', 'Open');

INSERT INTO tickets (title, description, assignee, priority, status) VALUES
('Export to PDF Truncates Long Text Fields in Reports', 'When generating PDF exports of reports containing extensive text fields, the text is abruptly cut off at the end of the page instead of wrapping or continuing to the next page.', 'samuel.green@example.com', 'P1 - High', 'Open');

INSERT INTO tickets (title, description, assignee, priority, status) VALUES
('Search Filter "Date Range" Not Applying Correctly', 'The "Date Range" filter on the search results page does not filter records accurately; results outside the specified date range are still displayed.', 'samuel.green@example.com', 'P2 - Medium', 'Resolved');

INSERT INTO tickets (title, description, assignee, priority, status) VALUES
('Typo in Error Message: "Unathorized Access"', 'The error message displayed when a user attempts an unauthorized action reads "Unathorized Access" instead of "Unauthorized Access."', 'maria.rodriguez@example.com', 'P3 - Low', 'Resolved');

INSERT INTO tickets (title, description, assignee, priority, status) VALUES
('Intermittent File Upload Failures for Large Files', 'Users are intermittently reporting that file uploads fail without a clear error message or explanation, especially for files exceeding 10MB in size.', 'frank.white@example.com', 'P1 - High', 'Open');

在 QuantumRoast,我们可能想知道 bug/工单上次更新的时间。

为此,我们可以创建一个触发器,以便在每次更新记录时更新 updated_time 字段。

点击 Clear,然后粘贴以下 SQL 以实现触发器。

Run 按钮执行。

CREATE OR REPLACE FUNCTION update_updated_time_tickets()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_time = NOW();  -- Set the updated_time to the current timestamp
    RETURN NEW;                -- Return the new row
END;
$$ language 'plpgsql';        

CREATE TRIGGER update_tickets_updated_time
BEFORE UPDATE ON tickets
FOR EACH ROW                  -- This means the trigger fires for each row affected by the UPDATE statement
EXECUTE PROCEDURE update_updated_time_tickets();

根据 description 字段创建向量嵌入。这样一来,我们的代理就可以对数据库执行相似度搜索。例如,“网站首页是否存在任何未解决的问题?”。

ALTER TABLE tickets ADD COLUMN embedding vector(768) GENERATED ALWAYS AS (embedding('text-embedding-005',description)) STORED;

现在,您可以查询数据库以验证其是否已准备就绪。

SELECT * FROM tickets;

您应该会看到返回的 10 行内容,如下所示:

验证 Cloud SQL 数据库

现在,您可以开始进入有趣的环节了,那就是编码!

4. Python 项目设置

在深入了解如何构建代理之前,我们必须确保已正确设置 Python 项目。我们将在 Cloud Shell 中完成所有操作!

首先,创建一个 quantum-roast 文件夹,然后通过 cd 命令进入该文件夹:

mkdir quantum-roast && cd quantum-roast

现在,我们已经为项目创建了一个文件夹,接下来可以初始化项目并创建所需的相关文件了。

我们将使用 uv(Python 的极速软件包和项目管理器),该工具已预安装在 Cloud Shell 中,用于管理我们的项目和依赖项。Uv 将帮助我们设置一些文件,并管理虚拟环境、依赖项等,这样我们就无需执行这些操作了!

使用 uv init 初始化新项目:

uv init --description "QuantumRoast Software Bug Assistant with ADK" --bare --python 3.10

运行该命令后,我们应该会获得项目的 pyproject.toml 文件。如需验证,请在 Cloud Shell 终端中运行 cat pyproject.toml

cat pyproject.toml

您应该会看到以下输出:

[project]
name = "quantum-roast"
version = "0.1.0"
description = "QuantumRoast Software Bug Assistant with ADK"
requires-python = ">=3.10"
dependencies = []

现在,我们使用 uv addgoogle-adk (ADK) 作为依赖项添加到项目中。

uv add google-adk==1.11.0

这会将 google-adk 添加到我们 pyproject.toml 中的 dependencies 列表中。

ADK 需要特定的项目结构才能实现最佳效果。

quantum-roast/
    software_bug_assistant/
        __init__.py
        agent.py
        .env

创建 software_bug_assistant 文件夹及其中的文件:

mkdir software_bug_assistant && touch software_bug_assistant/__init__.py \
software_bug_assistant/agent.py \
software_bug_assistant/tools.py \
software_bug_assistant/.env

使用 ls 验证文件的创建:

ls -a software_bug_assistant/

您应该会看到以下内容:

__init__.py	 .  ..	 .env	 agent.py    tools.py

现在,我们需要在 .env 文件中填充 ADK 正确调用 Gemini 模型所需的环境变量。我们将通过 Vertex API 访问 Gemini。

echo "GOOGLE_GENAI_USE_VERTEXAI=TRUE" >> software_bug_assistant/.env \
&& echo "GOOGLE_CLOUD_PROJECT=$PROJECT_ID" >> software_bug_assistant/.env \
&& echo "GOOGLE_CLOUD_LOCATION=us-central1" >> software_bug_assistant/.env

如需验证 .env 是否已正确填充,请运行以下命令:

cat software_bug_assistant/.env

您应该会看到以下内容,其中 your-project-id 是您的项目 ID:

GOOGLE_GENAI_USE_VERTEXAI=TRUE
GOOGLE_CLOUD_PROJECT=your-project-id
GOOGLE_CLOUD_LOCATION=us-central1

现在,我们已准备好开始创建 ADK 代理。

5. 基础 ADK 智能体

让我们先设置一个基本的 ADK 代理,然后在本次研讨会期间逐步添加工具,打造一个强大的 bug 助理!

在 Cloud Shell Editor 中打开 agent.py

cloudshell edit software_bug_assistant/agent.py

将以下代码粘贴到 agent.py 中,然后保存文件 Ctrl + s

from google.adk.agents import Agent

# --- Agent Definition (model, instructions, tools) ---
root_agent = Agent(
    model="gemini-2.5-flash",
    name="software_assistant",
    instruction="""
    You are a skilled expert in triaging and debugging software issues for a
    coffee machine company, QuantumRoast.
    """,
    tools=[],
)

通过启动 ADK 的开发界面 (adk web) 运行新创建的智能体。使用 uv run 执行此操作会自动创建一个安装了 ADK 的虚拟环境。

uv run adk web --port 8080 --reload_agents

在控制台中,您应该会看到 ADK Web 服务器成功启动。

INFO:     Started server process [1557]
INFO:     Waiting for application startup.

+-----------------------------------------------------------------------------+
| ADK Web Server started                                                      |
|                                                                             |
| For local testing, access at http://localhost:8080.                         |
+-----------------------------------------------------------------------------+

INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8080 (Press CTRL+C to quit)

打开 Cloud Shell 的网页预览,查看界面。

Cloud Shell 网页预览

您应该会看到 ADK Web 界面。

ADK 网页界面

不妨试试与 ADK 代理对话。

询问代理 What day is it today?

ADK Web 示例

从回答中可以看出,该代理无法回答这个基本问题!请注意,LLM 是基于过往数据训练的隔离系统。它们无法实时了解近期发生的事件,甚至无法知道当前日期…除非您为它们提供工具

现在,我们来实现 ADK 的第一种工具类型,即功能工具

6. 函数工具

第一种也是最简单的 ADK 工具类型是函数工具。顾名思义,它是一个由代理调用的 Python 函数!

函数工具

函数工具非常强大,因为您可以使用它为代理编写自定义代码,让代理将其作为工具来调用,例如执行计算、调用 API、查询数据库。这些函数可以是简单的函数,也可以是复杂的函数,完全取决于您。

在 QuantumRoast,我们希望定义一个基本函数来获取当前日期,以便稍后在本实验中处理“显示上周的 bug”或“今天是什么日期?”之类的查询。(我们所有人都会遇到这种情况)。

/software_bug_assistant 文件夹中的 tools.py 文件将用于整理我们在整个实验中构建的所有工具。

点击 + 图标,打开终端。

新终端

现在,在新终端中,设置 PROJECT_ID 并打开 tools.py

cd quantum-roast
export PROJECT_ID=$(gcloud config get project)
cloudshell edit software_bug_assistant/tools.py

现在,定义将用作函数工具的 get_current_date 函数。

from datetime import datetime

# ----- Example of a Function tool -----
def get_current_date() -> dict:
    """
    Get the current date in the format YYYY-MM-DD
    """
    return {"current_date": datetime.now().strftime("%Y-%m-%d")}

该函数现已定义!现在,是时候将其作为工具传递给客服人员了。

在 Cloud Shell Editor 中打开 agent.py

cloudshell edit software_bug_assistant/agent.py

我们希望从 tools.py 中导入 get_current_date 函数,并将该函数传递给代理的 tools 实参。

更新后的 agent.py 如下所示:

from google.adk.agents import Agent

from .tools import get_current_date

# --- Agent Definition (model, instructions, tools) ---
root_agent = Agent(
    model="gemini-2.5-flash",
    name="software_assistant",
    instruction="""
    You are a skilled expert in triaging and debugging software issues for a
    coffee machine company, QuantumRoast.
    """,
    tools=[get_current_date],
)

现在,如果您返回到运行 ADK Web 界面的“网页预览”标签页,并再次询问 What day is it today?...

ADK Web 函数工具

智能体可以通过调用 get_current_date 函数工具成功告知日期!🎉

接下来,我们来探索下一个 ADK 工具类型。

7. 内置工具

另一种类型的 ADK 工具是内置工具。这些工具可与 Google 的旗舰模型功能搭配使用,例如在模型本身内进行代码执行。我们可以将 Google 搜索内置工具附加到 bug 助理代理,让代理能够访问网络,从而根据相关背景信息做出回答。这样,智能体就可以收集有关 bug 或已知漏洞的最新信息。

内置工具

打开 tools.py 文件,以添加对 Google 搜索内置工具的支持。

cloudshell edit software_bug_assistant/tools.py

将以下内容添加到 tools.py 的底部:

# ----- Built-in Tool Imports -----
from google.adk.agents import Agent
from google.adk.tools import google_search
from google.adk.tools.agent_tool import AgentTool

# ----- Example of a Built-in Tool -----
search_agent = Agent(
    model="gemini-2.5-flash",
    name="search_agent",
    description="A specialist in Google Search.",
    instruction="""
    You're a specialist in Google Search.
    """,
    tools=[google_search],
)

search_tool = AgentTool(search_agent)

在这里,我们实际上是将 Google 搜索工具封装在具有自己系统指令的智能体中,从而有效地将智能体用作工具

现在,我们可以导入 search_tool 并将其传递给 agent.py 中的根代理:

cloudshell edit software_bug_assistant/agent.py

您可以将 agent.py 替换为以下代码,以包含 search_tool

from google.adk.agents import Agent

from .tools import get_current_date, search_tool

# --- Agent Definition (model, instructions, tools) ---
root_agent = Agent(
    model="gemini-2.5-flash",
    name="software_assistant",
    instruction="""
    You are a skilled expert in triaging and debugging software issues for a
    coffee machine company, QuantumRoast.
    """,
    tools=[get_current_date, search_tool],
)

保存该文件,然后返回到运行 ADK Web 界面的打开状态。

QuantumRoast 希望确保我们的网站和软件免受常见漏洞和披露 (CVE) 的侵害,这些都是公开的网络安全漏洞。我们可以使用代理的新 Google 搜索工具在网络上搜索最新发现的 CVE。

运行以下查询:Do a web search for 5 of the most recent CVEs?

我们的代理应调用 search_agent 来搜索网页。

ADK Web 内置工具示例

我们的代理现已成功解锁通过 ADK 的 内置工具进行 网络搜索的功能!🎉

接下来介绍另一种 ADK 工具类型。

8. 第三方工具

ADK 具有高度可扩展性,可让您无缝集成 CrewAILangChain 等其他第三方 AI 智能体框架中的工具。这种互操作性至关重要,因为它可以缩短开发时间,并能够重复使用现有工具。

第三方工具

为了将我们的 bug 代理接入 StackOverflow 强大的问答数据,我们可以从 LangChain 的广泛工具库中提取数据,具体来说,就是 StackExchange API 封装容器工具。ADK 支持 LangChain 等第三方工具,因此只需几行代码即可将此工具添加到我们的 ADK 代理中!

首先,我们必须为 LangChain 和 StackOverflow(langchain-communitystackapi)向项目添加新的依赖项:

uv add langchain-community==0.3.27 stackapi==0.3.1

打开 tools.py 文件,以添加对 LangChain StackExchange 工具的支持。

cloudshell edit software_bug_assistant/tools.py

将以下内容添加到 tools.py 的底部:

# ----- Example of a Third-Party Tool -----
from google.adk.tools.langchain_tool import LangchainTool
from langchain_community.tools import StackExchangeTool
from langchain_community.utilities import StackExchangeAPIWrapper

stack_exchange_tool = StackExchangeTool(api_wrapper=StackExchangeAPIWrapper())
langchain_tool = LangchainTool(stack_exchange_tool)

现在,我们可以导入 langchain_tool 并将其传递给 agent.py 中的根代理:

cloudshell edit software_bug_assistant/agent.py

您可以将 agent.py 替换为以下代码,以包含 langchain_tool

from google.adk.agents import Agent

from .tools import get_current_date, langchain_tool, search_tool

# --- Agent Definition (model, instructions, tools) ---
root_agent = Agent(
    model="gemini-2.5-flash",
    name="software_assistant",
    instruction="""
    You are a skilled expert in triaging and debugging software issues for a
    coffee machine company, QuantumRoast.
    """,
    tools=[get_current_date, search_tool, langchain_tool],
)

保存文件,然后返回到打开的 ADK Web 界面标签页。

您可以尝试向代理询问有关之前 CVE 的问题 "Are there similar issues on stack exchange?",也可以询问一些新问题,例如 "Our database queries with SQLAlchemy seem to be timing out, is there anything on StackExchange relevant to this?"

ADK Web 第三方工具示例

我们的代理现已成功利用 ADK 中的 LangChain 工具查询 StackOverflow。🥳

接下来该介绍哪种 ADK 工具类型了呢?MCP 工具!

9. MCP 工具(数据库)

MCP 是 Model Context Protocol 的缩写。它是 Anthropic 于 2024 年推出的开放协议。MCP 在 AI 代理与工具“后端”(API、数据库)之间提供了一个抽象层。

MCP 的运作方式

MCP 有一些独特的规范。与标准 HTTP 不同,MCP 在客户端和服务器之间提供有状态的双向连接。它有自己的方式来定义工具和特定于工具的错误消息。工具提供商随后可以在其 API 的基础上构建 MCP 服务器,从而为开发者和用户提供一个或多个预建工具。然后,代理框架可以在代理应用内初始化 MCP 客户端,以发现和调用这些工具。

在 QuantumRoast,我们有一个 Cloud SQL for PostgreSQL 数据库,用于存储内部软件 bug。我们希望创建 ADK 工具,以便我们的代理可以对数据库执行某些查询。

MCP 工具数据库

最简单的方法是使用 MCP Toolbox for Databases,这是一个面向数据库的开源 MCP 服务器!Toolbox 支持 15 多个数据库,其中包括 Cloud SQL!

工具箱提供:

  • 简化开发:只需不到 10 行代码即可将工具集成到代理中,在多个代理或框架之间重复使用工具,并更轻松地部署新版工具。
  • 更出色的性能:连接池、身份验证等最佳实践。
  • 增强的安全性:集成式身份验证,可更安全地访问您的数据
  • 端到端可观测性:开箱即用的指标和跟踪功能,内置对 OpenTelemetry 的支持。

ADK 支持 MCP Toolbox for Database 工具,可实现快速集成。

MCP Toolbox for Databases

将 MCP Toolbox for Databases 服务器部署到 Cloud Run

首先,我们将 MCP Toolbox for Databases Server 部署到 Cloud Run,并将其指向我们的 Cloud SQL 实例。

该工具箱需要一个 YAML 文件进行配置,您可以在其中概述数据库来源和要配置的工具。

为部署创建 tools.yaml 文件。

cloudshell edit tools.yaml

将以下内容粘贴到 tools.yaml 中:

sources:
  postgresql:
    kind: cloud-sql-postgres
    project: ${PROJECT_ID}
    region: us-central1
    instance: software-assistant
    database: tickets-db
    user: postgres
    password: admin

tools:
  search-tickets:
    kind: postgres-sql
    source: postgresql
    description: Search for similar tickets based on their descriptions.
    parameters:
      - name: query
        type: string
        description: The query to perform vector search with.
    statement: |
      SELECT ticket_id, title, description, assignee, priority, status, (embedding <=> embedding('text-embedding-005', $1)::vector) as distance
      FROM tickets
      ORDER BY distance ASC
      LIMIT 3;
  get-ticket-by-id:
    kind: postgres-sql
    source: postgresql
    description: Retrieve a ticket's details using its unique ID.
    parameters:
      - name: ticket_id
        type: string
        description: The unique ID of the ticket.
    statement: SELECT * FROM tickets WHERE ticket_id = $1;
  get-tickets-by-assignee:
    kind: postgres-sql
    source: postgresql
    description: Search for tickets based on assignee (email).
    parameters:
      - name: assignee
        type: string
        description: The email of the assignee.
    statement: SELECT * FROM tickets WHERE assignee ILIKE '%' || $1 || '%';
  update-ticket-priority:
    kind: postgres-sql
    source: postgresql
    description: Update the priority of a ticket based on its ID.
    parameters:
      - name: priority
        type: string
        description: The priority of the ticket. Can be one of 'P0 - Critical', 'P1 - High', 'P2 - Medium', or 'P3 - Low'.
      - name: ticket_id
        type: string
        description: The ID of the ticket.
    statement: UPDATE tickets SET priority = $1 WHERE ticket_id = $2;
  update-ticket-status:
    kind: postgres-sql
    source: postgresql
    description: Update the status of a ticket based on its ID.
    parameters:
      - name: status
        type: string
        description: The new status of the ticket (e.g., 'Open', 'In Progress', 'Closed', 'Resolved').
      - name: ticket_id
        type: string
        description: The ID of the ticket.
    statement: UPDATE tickets SET status = $1 WHERE ticket_id = $2;
  get-tickets-by-status:
    kind: postgres-sql
    source: postgresql
    description: Search for tickets based on their current status.
    parameters:
      - name: status
        type: string
        description: The status of the tickets to retrieve (e.g., 'Open', 'In Progress', 'Closed', 'Resolved').
    statement: SELECT * FROM tickets WHERE status ILIKE '%' || $1 || '%';
  get-tickets-by-priority:
    kind: postgres-sql
    source: postgresql
    description: Search for tickets based on their priority.
    parameters:
      - name: priority
        type: string
        description: The priority of the tickets to retrieve (e.g., 'P0 - Critical', 'P1 - High', 'P2 - Medium', 'P3 - Low').
    statement: SELECT * FROM tickets WHERE priority ILIKE '%' || $1 || '%';
  create-new-ticket:
    kind: postgres-sql
    source: postgresql
    description: Create a new software ticket.
    parameters:
      - name: title
        type: string
        description: The title of the new ticket.
      - name: description
        type: string
        description: A detailed description of the bug or issue.
      - name: assignee
        type: string
        description: (Optional) The email of the person to whom the ticket should be assigned.
      - name: priority
        type: string
        description: (Optional) The priority of the ticket. Can be 'P0 - Critical', 'P1 - High', 'P2 - Medium', or 'P3 - Low'. Default is 'P3 - Low'.
      - name: status
        type: string
        description: (Optional) The initial status of the ticket. Default is 'Open'.
    statement: INSERT INTO tickets (title, description, assignee, priority, status) VALUES ($1, $2, $3, COALESCE($4, 'P3 - Low'), COALESCE($5, 'Open')) RETURNING ticket_id;
  get-tickets-by-date-range:
    kind: postgres-sql
    source: postgresql
    description: Retrieve tickets created or updated within a specific date range.
    parameters:
      - name: start_date
        type: string
        description: The start date (inclusive) for the range (e.g., 'YYYY-MM-DD').
      - name: end_date
        type: string
        description: The end date (inclusive) for the range (e.g., 'YYYY-MM-DD').
      - name: date_field
        type: string
        description: The date field to filter by ('creation_time' or 'updated_time').
    statement: SELECT * FROM tickets WHERE CASE WHEN $3 = 'creation_time' THEN creation_time ELSE updated_time END BETWEEN $1::timestamp AND $2::timestamp;

toolsets:
  tickets_toolset:
    - search-tickets
    - get-ticket-by-id
    - get-tickets-by-assignee
    - get-tickets-by-status
    - get-tickets-by-priority
    - get-tickets-by-date-range
    - update-ticket-priority
    - update-ticket-status
    - create-new-ticket

该 YAML 文件定义了与 QuantumRoast 支持请求数据库相关的 9 个工具

现在,我们需要为 Toolbox Cloud Run 服务配置服务账号,向其授予访问 Cloud SQL 和 Secret Manager 的权限,并为 tools.yaml 文件创建 Secret Manager Secret。

我们将把 tools.yaml 文件存储在 Secret Manager 中,因为它包含敏感的 Cloud SQL 凭据。

gcloud iam service-accounts create toolbox-identity

gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member serviceAccount:toolbox-identity@$PROJECT_ID.iam.gserviceaccount.com \
    --role roles/secretmanager.secretAccessor

gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member serviceAccount:toolbox-identity@$PROJECT_ID.iam.gserviceaccount.com \
    --role roles/cloudsql.client

gcloud secrets create tools --data-file=tools.yaml

现在,将 MCP Toolbox for Databases 部署到 Cloud Run。我们将使用 MCP Toolbox 容器映像的最新发布版本

gcloud run deploy toolbox \
    --image us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:latest \
    --service-account toolbox-identity \
    --region us-central1 \
    --set-secrets "/app/tools.yaml=tools:latest" \
    --set-env-vars="PROJECT_ID=$PROJECT_ID" \
    --args="--tools-file=/app/tools.yaml","--address=0.0.0.0","--port=8080" \
    --allow-unauthenticated

等待部署完成…

通过查询 Cloud Run 日志,验证 Toolbox 是否正在运行:

gcloud run services logs read toolbox --region us-central1 --limit 10

您应该会看到:

2025-08-20 18:03:55 2025-08-20T18:03:55.465847801Z INFO "Initialized 1 sources."
2025-08-20 18:03:55 2025-08-20T18:03:55.466152914Z INFO "Initialized 0 authServices."
2025-08-20 18:03:55 2025-08-20T18:03:55.466374245Z INFO "Initialized 9 tools."
2025-08-20 18:03:55 2025-08-20T18:03:55.466477938Z INFO "Initialized 2 toolsets."
2025-08-20 18:03:55 2025-08-20T18:03:55.467492303Z INFO "Server ready to serve!"

将 Toolbox 服务的 Cloud Run 网址保存为环境变量,以便 ADK 代理知道在哪里找到它。

export MCP_TOOLBOX_URL=$(gcloud run services describe toolbox --region us-central1 --format "value(status.url)")
echo MCP_TOOLBOX_URL=$MCP_TOOLBOX_URL >> software_bug_assistant/.env

更新 QuantumRoast 代理

其次,我们必须将 MCP Toolbox for Databases SDK (toolbox-core) 的依赖项添加到我们的项目中:

uv add toolbox-core==0.5.0

打开 tools.py 文件,以添加对 MCP Toolbox 工具的支持。

cloudshell edit software_bug_assistant/tools.py

将以下内容添加到 tools.py 的底部:

# ----- Example MCP Toolbox for Databases tools -----
import os
from toolbox_core import ToolboxSyncClient

TOOLBOX_URL = os.getenv("MCP_TOOLBOX_URL", "http://127.0.0.1:5000")

# Initialize Toolbox client
toolbox = ToolboxSyncClient(TOOLBOX_URL)
# Load all the tools from toolset
toolbox_tools = toolbox.load_toolset("tickets_toolset")

现在,我们可以导入 toolbox_tools 并将其传递给 agent.py 中的根代理:

cloudshell edit software_bug_assistant/agent.py

您可以将 agent.py 替换为以下代码,以包含 toolbox_tools

from google.adk.agents import Agent

from .tools import get_current_date, langchain_tool, search_tool, toolbox_tools

# --- Agent Definition (model, instructions, tools) ---
root_agent = Agent(
    model="gemini-2.5-flash",
    name="software_assistant",
    instruction="""
    You are a skilled expert in triaging and debugging software issues for a
    coffee machine company, QuantumRoast.
    """,
    tools=[get_current_date, search_tool, langchain_tool, *toolbox_tools],
)

保存文件,然后返回到打开的 ADK Web 界面标签页。

现在,您可以询问有关存储在 Cloud SQL 内部工单数据库中的工单的问题了!

提出如下问题:

  • I am seeing an issue with database timeouts, has anyone else seen a similar issue?
  • How many bugs are assigned to samuel.green@example.com? Show a table.
  • Can you bump the priority of ticket with ID 6 to to P0 - Critical priority
  • Create a new ticket(让代理引导您完成 bug 创建流程)

MCP 数据库工具示例

我们的 ADK 代理现已成功通过 MCP Toolbox for Databases 工具查询数据库!🚀

10. 可选:MCP 工具 (API)

如果 MCP 工具没有自己的 SDK(例如 MCP Toolbox for Database),我们该如何将 ADK 代理连接到这些工具?

ADK 通过其 MCPToolset 类支持通用 MCP 工具。MCPToolset 类是 ADK 用于集成 MCP 服务器中的工具的主要机制。

MCP 工具 (API)

MCPToolset 可用于连接到本地或远程 MCP 服务器,而在 QuantumRoast,我们希望将代理连接到 GitHub 的远程 MCP 服务器,以便轻松调用 GitHub 的 API。这样一来,我们的代理就可以从公共软件代码库甚至我们自己的代码库中提取有关问题的信息。GitHub MCP 服务器公开了 GitHub 功能的不同部分,从问题和拉取请求到通知和代码安全性。

MCP 工具 GitHub

GitHub 个人访问令牌 (PAT)

如需向 GitHub MCP 服务器进行身份验证,您需要 GitHub 个人访问令牌

如需获取此卡,请按以下步骤操作:

  1. 前往 GitHub 开发者设置。
  2. 依次点击“个人访问令牌”和“令牌(经典版)”。
  3. 依次点击“生成新令牌” ->“生成新令牌(旧版)”。
  4. 为您的令牌指定一个描述性名称。
  5. 为令牌设置失效日期。
  6. 重要提示:出于安全考虑,请为您的令牌授予必要的最小范围。对于对代码库的只读访问权限,repo:statuspublic_reporead:user 范围通常就足够了。除非绝对必要,否则请避免授予完整的代码库或管理员权限。
  7. 点击 Generate token
  8. 复制生成的令牌。

在 Cloud Shell 终端中,运行以下命令以设置 GitHub PAT,以便代理能够使用。将 YOUR_GITHUB_PAT 替换为您生成的 PAT。

export GITHUB_PAT=YOUR_GITHUB_PAT

更新 QuantumRoast 代理

对于我们的 bug 助理,我们将仅公开一些只读 GitHub 工具,以便 QuantumRoast 员工查找与开源依赖项相关的问题,看看这是否有助于找出他们在内部工单系统中发现的 bug 的根本原因。我们将使用 ADK 的 MCPToolsettool_filter 来进行设置。tool-filter 仅公开我们需要的 GitHub 工具,这不仅隐藏了我们不希望用户访问的工具(例如:敏感的代码库操作),还可防止代理的模型在尝试选择合适的工具来完成任务时不堪重负。

打开 tools.py 文件,以添加对 GitHub 工具的支持。

cloudshell edit software_bug_assistant/tools.py

将以下内容添加到 tools.py 的底部:

# ----- Example MCP Tools with MCPToolset (GitHub) -----
from google.adk.tools.mcp_tool import MCPToolset, StreamableHTTPConnectionParams

mcp_tools = MCPToolset(
    connection_params=StreamableHTTPConnectionParams(
        url="https://api.githubcopilot.com/mcp/",
        headers={
            "Authorization": "Bearer " + os.getenv("GITHUB_PAT"),
        },
    ),
    # Read only tools
    tool_filter=[
        "search_repositories",
        "search_issues",
        "list_issues",
        "get_issue",
        "list_pull_requests",
        "get_pull_request",
    ],
)

请注意,我们还需要向 MCPToolset 定义提供 GitHub 个人访问令牌 (PAT),就像在代码中设置标准 API 客户端时提供身份验证令牌一样。此 PAT 的范围仅限于访问公开代码库数据,不包含与敏感用户或代码库操作相关的范围。

现在,我们可以导入 mcp_tools 并将其传递给 agent.py 中的根代理:

cloudshell edit software_bug_assistant/agent.py

您可以将 agent.py 替换为以下代码,以包含 mcp_tools

from google.adk.agents import Agent

from .tools import get_current_date, langchain_tool, mcp_tools, search_tool, toolbox_tools

# --- Agent Definition (model, instructions, tools) ---
root_agent = Agent(
    model="gemini-2.5-flash",
    name="software_assistant",
    instruction="""
    You are a skilled expert in triaging and debugging software issues for a
    coffee machine company, QuantumRoast.
    """,
    tools=[get_current_date, search_tool, langchain_tool, *toolbox_tools, mcp_tools],
)

保存文件,然后返回到打开的 ADK Web 界面标签页。

现在,我们有一组代理可以调用的 GitHub MCP 工具。QuantumRoast 的服务依赖于 XZ utils(一种数据压缩工具)。我们的内部 bug 工单系统正在跟踪去年发现的 CVE(安全漏洞),我们可以使用 StackOverflow 和 Google 搜索工具追溯到 XZ Utils GitHub 代码库。然后,我们可以使用 GitHub 的 MCP 工具之一 search_issues 来确定该 CVE 的修补时间和方式:

向代理提出以下问题:

  • Find the official XZ Utils GitHub repository
  • Search the repository for issues related to CVE-2024-3094

您应该会看到代理正在调用 GitHub 工具。

MCP 工具 GitHub 示例

QuantumRoast ADK 代理现已能够与 GitHub MCP 服务器工具互动!🤩

11. 恭喜

恭喜!您已使用智能体开发套件 (ADK) 成功构建 QuantumRoast bug 助理智能体,并集成了各种工具类型来增强其功能。您从基本智能体开始,逐步添加了函数工具、内置工具、第三方工具和 MCP 工具。

所学内容

  • 如何设置 Python 项目以进行 ADK 开发。
  • 如何创建基本的 ADK 代理。
  • 如何实现和使用函数工具。
  • 如何集成 Google 搜索等内置工具。
  • 如何在 ADK 中利用 LangChain 等框架中的第三方工具。
  • 如何使用 MCP 工具与数据库 (Cloud SQL) 和 API 进行交互。

QuantumRoast Final

清理

您可以删除 Cloud 项目,以避免产生额外费用。

虽然 Cloud Run 不会对未在使用中的服务计费,但您可能仍然需要支付将容器映像存储在 Artifact Registry 中而产生的相关费用。删除 Cloud 项目后,系统即会停止对该项目中使用的所有资源计费。

如果您愿意,可以删除项目:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

您可能还需要从 Cloud Shell 磁盘中删除不必要的资源。您可以:

  1. 删除 Codelab 项目目录:
    rm -rf ~/quantum-roast
    
  2. 警告!接下来要执行的操作无法撤消!如果您想删除 Cloud Shell 中的所有内容以释放空间,可以删除整个主目录。请务必将要保留的所有内容保存到其他位置。
    sudo rm -rf $HOME