将数据代理与应用集成

本教程介绍了如何使用 Google Cloud 控制台在 Spanner 中设置和使用数据代理,以及如何将其与您的应用集成。您将学习如何构建智能体上下文文件、创建使用该上下文的数据智能体、使用 MCP Toolbox 调用 QueryData API 来针对自然语言问题生成 SQL 查询,并最终将其与您的应用集成。

如需了解详情,请参阅数据代理概览

目标

  • 创建并填充表格。
  • 使用 Gemini CLI 和 MCP 工具箱构建智能体上下文。
  • 创建数据代理并上传上下文。
  • 在 Studio 中检查智能体并生成 SQL 查询。
  • 使用 MCP Toolbox 中的 Gemini Data Analytics QueryData 工具将智能体与应用集成。

费用

在本文档中,您将使用 Google Cloud的以下收费组件:

您可使用价格计算器根据您的预计使用量来估算费用。

新 Google Cloud 用户可能有资格申请免费试用

完成本文档中描述的任务后,请删除您创建的资源,以免继续产生费用。如需了解详情,请参阅清理

准备工作

在创建代理之前,请完成以下前提条件。

启用必需服务

为项目启用以下服务:

准备 Spanner 实例

所需的角色和权限

创建 flightsairports 架构及表

在本部分中,您将为本教程创建 flightsairports 数据库。

  1. 在 Google Cloud 控制台中,前往 Spanner 页面。

    前往 Spanner

  2. 从列表中选择一个实例,然后选择一个数据库。

  3. 在导航菜单中,点击 Spanner Studio

  4. 点击新的 SQL 编辑器标签页新标签页以打开新标签页。

  5. 创建 airports 表和架构:

    CREATE TABLE IF NOT EXISTS airports (
      id INT64 PRIMARY KEY,
      iata STRING(MAX),
      name STRING(MAX),
      city STRING(MAX),
      country STRING(MAX)
    );
    
  6. 创建 flights 表和架构:

    CREATE TABLE flights (
      id INT64 NOT NULL,
      airline STRING(10),
      flight_number INT64,
      departure_airport STRING(5),
      arrival_airport STRING(5),
      departure_time TIMESTAMP,
      arrival_time TIMESTAMP,
      departure_gate STRING(10),
      arrival_gate STRING(10)
    ) PRIMARY KEY (id);
    
    

填充 flightsairport

在本部分中,您将使用提供的 SQL 脚本填充 flightsairports 表。

  1. 填充 airports 表。

  2. 填充 flights 表。

  3. 运行以下查询,验证表是否已填充:

    SELECT * FROM flights LIMIT 10;
    SELECT * FROM airports LIMIT 10;
    

创建数据代理

在本部分中,您将创建一个名为 flights-assistant 的数据代理。此代理不包含上传到其中的任何代理上下文。

  1. “探索器”窗格中,点击数据代理旁边的查看操作
  2. 点击创建代理
  3. 为代理命名中,输入 flights-assistant
  4. 点击创建

在 Studio 中检查代理

在此部分中,您将向 flights-assistant 代理提出自然语言问题,然后由该代理生成 SQL 查询。由于代理没有任何上下文,即使在提出包含上下文的问题(例如 nighttime traffic)后,代理也会生成次优查询。

  1. 探索器窗格中,点击数据代理旁边的查看操作
  2. 点击检查代理
  3. 在查询编辑器中,点击使用代理生成 SQL:flights-assistant
  4. 输入以下自然语言问题以生成 SQL 查询,然后点击生成

    Find flights from SFO to JFK.
    

    查看 SQL 查询。请注意,对于这个明确的问题,代理会生成正确的 SQL。

      SELECT
        *
      FROM
        flights
      WHERE
        departure_airport = 'SFO'
        AND arrival_airport = 'JFK';
    
  5. 使用代理生成 SQL:flights-assistant 窗口中,点击修改

  6. 输入以下自然语言问题以生成 SQL 查询,然后点击更新

    Tell me flights that can help me beat nighttime traffic if traveling from New York
    

    数据库无法理解“nighttime 流量”一词。这可能会导致它无法生成 SQL 查询,或者生成忽略该字词的查询,如下面的查询所示。

    -- The database schema does not contain information about traffic.
    -- Returning all flights departing from New York airports.
    SELECT
      f.airline,
      f.flight_number,
      a.name AS departure_airport_name,
      f.departure_time,
      b.name AS arrival_airport_name,
      f.arrival_time
    FROM
      flights AS f
    JOIN
      airports AS a
      ON f.departure_airport = a.iata
    JOIN
      airports AS b
      ON f.arrival_airport = b.iata
    WHERE
      a.city = 'New York'
    ORDER BY
      f.departure_time;
    

为代理生成上下文

在本部分中,您将创建一个有助于提高代理查询功能的上下文文件。为了解决上一部分中智能体无法识别 nighttime traffic 一词的问题,请在智能体上下文中将该词定义为 5:00 PM7:00 PM 之间发生的流量。

如需生成代理上下文,请执行以下步骤:

  1. 在本地目录中,安装 Gemini CLI。如需了解详情,请参阅 Gemini CLI 快速入门
  2. 安装 Google Cloud CLI设置应用默认凭据 (ADC)
  3. 安装 MCP Toolbox Gemini CLI 扩展程序,该扩展程序可连接到数据库。

    gemini extensions install https://github.com/gemini-cli-extensions/mcp-toolbox
  4. 在安装 MCP 工具箱的同一目录中创建一个 tools.yaml 配置文件,以配置数据库连接:

    sources:
      flight-sql-source:
        kind: spanner
        project: PROJECT_ID
        instance: INSTANCE_ID
        database: DATABASE_ID
    
    tools:
      # (Optional) Fetches database schemas for context generation in the bulk generation (/generate_bulk_templates) phase.
      list_flight_schemas_tool:
        kind: spanner-list-tables
        source: flight-spanner-source
        description: Use this tool to list all tables and their schemas in the flight database.
      # (Optional) Executes generated SQL for validation in the bulk generation (/generate_bulk_templates) phase.
      execute_sql_tool:
        kind: spanner-execute-sql
        source: flight-spanner-source
        description: Use this tool to execute SQL against the flight database.
    

    替换以下内容:

    • PROJECT_ID:您的 Google Cloud 项目 ID。
    • INSTANCE_ID:Spanner 实例的 ID。
    • DATABASE_ID:要连接的数据库名称。
  5. 按照官方安装指南安装 uv Python 软件包安装程序,并通过运行以下命令验证安装是否成功:

    uv --version
  6. 安装 DB Context Enrichment MCP 服务器,其中包含用于生成上下文的工作流。

    gemini extensions install https://github.com/GoogleCloudPlatform/db-context-enrichment
  7. 将 Gemini API 密钥导出为环境变量。如需详细了解如何查找 API 密钥,请参阅使用 Gemini API 密钥

    export GEMINI_API_KEY="YOUR_API_KEY"

    YOUR_API_KEY 替换为您的 Gemini API 密钥。

  8. 在创建 tools.yaml 文件的同一目录中,启动 Gemini:

    gemini
  9. 完成 Gemini CLI 身份验证设置

  10. 验证 MCP 工具箱和数据库扩充扩展程序是否已连接并可供使用。

    /mcp list
  11. 运行 /generate_targeted_templates 命令,然后按照工作流程操作:

    /generate_targeted_templates
  12. 在终端中提供要添加到查询模板中的自然语言查询。

    Tell me flights that can help me beat nighttime traffic if traveling from New York
  13. 提供要添加到查询模板中的相应 SQL 查询。此查询模板将字词 nighttime 定义为出现在 5:00 PM7:00 PM 之间。

    SELECT
      f.airline,
      f.flight_number,
      a.name AS airport_name,
      f.departure_time
    FROM
      flights f
    JOIN
      airports a
      ON f.departure_airport = a.iata
    WHERE
      a.city = 'New York'
      AND (
        EXTRACT(HOUR FROM f.departure_time) < 17
        OR EXTRACT(HOUR FROM f.departure_time) >= 19
      )
    ORDER BY
      f.departure_time;
    
  14. Enter 键。Gemini 会将您的输入内容转换为特定格式,从而提升代理在处理各种用户查询时的性能。如需了解详情,请参阅智能体上下文

    (可选)运行 /generate_bulk_templates 工作流,让 Gemini CLI 通过扫描数据库架构并建议相关上下文来生成更多上下文。确保将 list_flight_schemas_toolexecute_sql_tool 都添加到您在第 4 步中创建的 tools.yaml 配置中。

  15. 查看生成的查询模板。您可以将查询模板另存为新的代理上下文文件,也可以将其附加到现有代理上下文文件。

  16. 选择用于创建新代理上下文文件的选项。Gemini 在同一目录中创建了一个名为 INSTANCE_ID_DATABASE_ID_context_set_TIMESTAMP.json 的文件,其中包含以下内容:

    {
      "templates": [
        {
          "nl_query": "Tell me flights that can help me beat nighttime traffic if traveling from New York",
          "sql": "SELECT f.airline, f.flight_number, a.name AS airport_name, f.departure_time FROM flights f JOIN airports a ON f.departure_airport = a.iata WHERE a.city = 'New York' AND (EXTRACT(HOUR FROM f.departure_time) < 17 OR EXTRACT(HOUR FROM f.departure_time) >= 19) ORDER BY f.departure_time;",
          "intent": "Tell me flights that can help me beat nighttime traffic if traveling from New York",
          "manifest": "Tell me flights that can help me beat nighttime traffic if traveling from a given city",
          "parameterized": {
            "parameterized_sql": "SELECT f.airline, f.flight_number, a.name AS airport_name, f.departure_time FROM flights f JOIN airports a ON f.departure_airport = a.iata WHERE a.city = ? AND (EXTRACT(HOUR FROM f.departure_time) < 17 OR EXTRACT(HOUR FROM f.departure_time) >= 19) ORDER BY f.departure_time;",
            "parameterized_intent": "Tell me flights that can help me beat nighttime traffic if traveling from ?"
          }
        }
      ]
    }
    

向代理上传上下文

在本部分中,您将代理上下文文件上传到数据代理,以便数据代理能够更好地根据您的数据库生成 SQL。

如需上传上下文,请执行以下步骤:

  1. 在 Google Cloud 控制台中,前往 Spanner 页面。

    前往 Spanner

  2. 从列表中选择一个实例,然后选择一个数据库。

  3. 在导航菜单中,点击 Spanner Studio

  4. “探索器”窗格中,点击数据代理旁边的查看操作

  5. 点击修改代理

  6. 可选:修改代理说明

  7. 点击上传代理上下文文件部分中的浏览,然后选择之前生成的代理上下文文件。

  8. 点击保存

使用代理上下文生成 SQL 查询

在本部分中,您将使用上传的代理上下文文件提出自然语言问题。这样,您便可以验证代理是否正确理解并应用了 nighttime traffic 等术语和其他相关短语的定义。

如需生成 SQL 查询,请执行以下步骤:

  1. 探索器窗格中,点击数据代理旁边的查看操作
  2. 点击检查代理
  3. 在查询编辑器中,点击使用代理生成 SQL:flights-assistant
  4. 输入以下自然语言问题以生成 SQL 查询,然后点击生成

    Tell me flights that can help me beat nighttime traffic if traveling from New York

    生成的 SQL 查询类似于以下内容:

    SELECT
      f.airline,
      f.flight_number,
      a.name AS airport_name,
      f.departure_time
    FROM
      flights f
    JOIN
      airports a ON f.departure_airport = a.iata
    WHERE
      a.city = 'New York'
      AND (
        EXTRACT(HOUR FROM f.departure_time) < 17
        OR EXTRACT(HOUR FROM f.departure_time) >= 19
      )
    ORDER BY
      f.departure_time;
    

    这与您添加到数据代理上下文中的问题相同。请注意,智能体现在可以准确解读“nighttime traffic”一词。

    虽然上下文源自一个特定问题,但智能体可使用它来增强 SQL 生成功能,以应对各种类似问题。

  5. 使用代理生成 SQL:flights-assistant 窗口中,点击修改

  6. 输入以下类似问题以生成 SQL 查询,然后点击更新

    What are the flights that can help me avoid evening traffic if departing from Boston

    由于问题将字词 nighttime traffic 替换为类似字词 evening traffic,因此代理会应用相同的解释,从而针对此问题提供一致的回答。

    生成的 SQL 查询类似于以下内容:

    -- What are the flights that can help me avoid evening traffic if departing from Boston
    SELECT
      f.airline,
      f.flight_number,
      a.name AS airport_name,
      f.departure_time
    FROM
      flights f
    JOIN
      airports a
    ON
      f.departure_airport = a.iata
    WHERE
      a.city = 'Boston'
      AND (
        EXTRACT(HOUR FROM f.departure_time) < 17
        OR EXTRACT(HOUR FROM f.departure_time) >= 19
      )
    ORDER BY
      f.departure_time;
    

将代理与您的应用集成

在本部分中,您将为航班搜索应用创建数据代理。此数据代理可为之前创建的 flightsairports 表提供对话式界面。本文还介绍了如何使用智能体开发套件 (ADK)、Gemini Data Analytics QueryData MCP 工具和智能体上下文来创建此智能体并将其集成到应用中,从而提高回答质量。

  1. 下载 MCP Toolbox 0.24.0 版或更高版本。MCP Toolbox 将数据代理公开为供应用连接的工具。MCP Toolbox 与您之前安装的 MCP Toolbox Gemini CLI 扩展程序不同,后者用于生成上下文。

  2. 在终端中,设置您正在使用的项目。

    gcloud config set project [PROJECT_ID]
  3. 设置应用默认凭证 (ADC)

    gcloud auth application-default login
  4. 查找代理上下文 ID。如需详细了解如何查找上下文集 ID,请参阅查找代理上下文 ID

  5. 使用 MCP 工具箱创建 tools.yaml 配置以连接到数据代理。如需了解详情,请参阅 Gemini 数据分析来源和 Gemini 数据分析 QueryData 工具。

    sources:
      gda-api-source:
        kind: cloud-gemini-data-analytics
        projectId: "PROJECT_ID"
    
    tools:
      cloud_gda_query_tool:
        kind: cloud-gemini-data-analytics-query
        source: gda-api-source
        description: Use this tool to send natural language queries to the Gemini Data Analytics API and receive SQL, natural language answers, and explanations.
        location: "REGION_ID"
        context:
          datasourceReferences:
            spannerReference:
              databaseReference:
                engine: "GOOGLE_SQL"
                projectId: "PROJECT_ID"
                region: "REGION_ID"
                instanceId: "INSTANCE_ID"
                databaseId: "DATABASE_ID"
              agentContextReference:
                contextSetId: "DATA_AGENT_CONTEXT_SET_ID"
        generationOptions:
          generateQueryResult: true
          generateNaturalLanguageAnswer: true
          generateExplanation: true
          generateDisambiguationQuestion: true
    

    替换以下内容:

    • PROJECT_ID:您的 Google Cloud 项目 ID。
    • REGION_ID:Spanner 实例的区域(例如 us-central1)。
    • INSTANCE_ID:Spanner 实例的 ID。
    • DATABASE_ID:要连接的数据库名称。
    • DATA_AGENT_CONTEXT_SET_ID:数据智能体上下文集 ID。
  6. 使用 tools.yaml 文件运行 MCP Toolbox 服务器。

    ./toolbox --tools-file "tools.yaml"
  7. 使用 MCP Toolbox 的 Python SDK 创建一个调用 Gemini Data Analytics QueryData 工具的 ADK 应用。如需详细了解如何使用 MCP Toolbox 的 Python SDK,请参阅 Toolbox 快速入门;如需详细了解如何使用 Python ADK,请参阅 ADK 快速入门

    1. 创建一个用于存储应用的目录,例如 flight-assistant-app
    2. 将目录更改为 flight-assistant-app 目录。

      mkdir flight-assistant-app
      cd flight-assistant-app
    3. flight-assistant-app 目录下运行以下命令,以创建虚拟环境并安装所需组件。

      python3 -m venv .venv
      source .venv/bin/activate
      pip install toolbox-core
      pip install google-genai
      pip install google-adk
    4. 设置 ADK 代理。

      1. 创建 ADK 代理。

        adk create my_agent
      2. 选择 gemini-2.5-flash 模型。

      3. 选择 Google AI,然后输入 Gemini API 密钥。如需详细了解如何查找 API 密钥,请参阅使用 Gemini API 密钥

    5. agent.py 文件的内容替换为以下飞行数据助理示例应用代码。

      from typing import cast
      
      from google.adk.agents.llm_agent import Agent
      from google.adk.agents.llm_agent import ToolUnion
      
      from toolbox_core import ToolboxSyncClient
      
      TOOLBOX_URL = "http://127.0.0.1:5000"
      
      INSTRUCTION = """
      # ROLE
      You are a friendly and factual flight data assistant. Your goal is to help users find the best flights for their needs by providing accurate information with a helpful, professional tone.
      - use the Query Data Tool to answer the user's question, if the tool fails to generate a valid query, ask the user to clarify their question.
      
      # OPERATIONAL CONSTRAINTS
      - TOOL LIMITATION: You only have access to the Query Data Tool. Do not claim to have capabilities beyond what this tool provides.
      - TRANSPARENCY POLICY: Maintain a seamless user experience. Never mention that you are using a tool, querying a database, or generating SQL. Frame all responses as your own direct assistance.
      - SCOPE MANAGEMENT: If a user asks for something beyond your capabilities, politely state that you cannot perform that specific task. Guide the user towards what you can help with.
      
      # COMMUNICATION STYLE
      - Be concise and scannable when listing answers.
      - Maintain a helpful, professional persona.
      
      =====
      
      # QUERY DATA TOOL
      
      Inputs:
      1. query: A natural language formulation of a database query.
      
      Outputs: (all optional)
      1. disambiguation_question: Clarification questions or comments where the tool needs the users' input.
      2. generated_query: The generated query for the user query.
      3. intent_explanation: An explanation for why the tool produced `generated_query`.
      4. query_result: The result of executing `generated_query`.
      5. natural_language_answer: The natural language answer that summarizes the `query` and `query_result`.
      
      Usage guidance:
      1. If `disambiguation_question` is produced, then solicit the needed inputs from the user and try the tool with a new `query` that has the needed clarification.
      2. If `natural_language_answer` is produced, use `intent_explanation` and `generated_query` to see if you need to clarify any assumptions for the user.
      3. If the tool output indicates failure or empty results, explain that clearly using the provided reasoning.
      """
      
      client = ToolboxSyncClient(TOOLBOX_URL)
      
      mcp_tool = client.load_tool("cloud_gda_query_tool")
      
      root_agent = Agent(
          model="gemini-2.5-flash",
          name="root_agent",
          instruction=INSTRUCTION,
          tools=cast(list[ToolUnion], [mcp_tool]),
      )
      
  8. flight-assistant-app 目录下运行以下命令,以启动应用并访问 http://127.0.0.1:8000 处的 ADK Web 服务器。

    adk web --port 8000
  9. 输入任意文本(例如 hello),即可开始与智能体互动。

    ADK 代理会回答一般问题,并调用所需的 MCP 工具。

  10. 输入以下与航班相关的问题。

    How many flights depart from the west side?
    

    系统会调用 MCP 工具来回答此问题。不过,由于“the west”一词含义模糊,未指定任何机场,因此 MCP 工具会返回一个消除歧义的问题,智能体可以使用该问题来构建回答。

    I cannot determine how many flights depart from the 'west side' as the database does not contain information about which airports are considered to be on the 'west side'. However, I can help you with questions like:
    
    1. How many flights depart from a specific airport?
    
    2. What are the departure airports for all flights?
    
    3. How many flights depart from each airport? Would you like to rephrase your question based on these options?
    
  11. 输入与为代理生成的查询模板中的问题类似的问题。

    Help me find flights from San Francisco that avoid the evening rush hour.
    

    根据之前添加的代理上下文,MCP 工具了解到 evening traffic 发生在下午 5 点到 7 点之间。MCP 工具会返回关联数据,供代理用于构建回答。

    Here are the flights departing from San Francisco that avoid the evening rush hour (defined as 5 PM to 7 PM):
    
    * UA 1532 departing at 05:50:00
    * UA 1158 departing at 05:57:00
    * CY 922 departing at 06:38:00
    * OO 5441 departing at 07:08:00
    * UA 616 departing at 07:14:00
    * AA 24 departing at 07:14:00
    * B6 434 departing at 08:00:00
    * AA 242 departing at 08:18:00
    * UA 1739 departing at 08:22:00
    * OO 6336 departing at 08:32:00
    * US 1784 departing at 08:47:00
    * DL 1631 departing at 09:00:00
    * DL 1106 departing at 09:06:00
    * OO 5427 departing at 09:06:00
    * CY 352 departing at 09:25:00
    

迭代代理性能

借助 ADK 网页界面,您可以检查 Gemini Data Analytics QueryData MCP 工具的请求和响应。您可以使用此响应来观察工具响应,例如生成的 SQL 查询、结果集、意图说明、消除歧义问题和自然语言回答,以帮助您确认代理回答的正确性。

例如,对于您之前输入的文本 How many flights depart from the west side?,请点击代理气泡。在左侧导航栏的事件标签页中,展开 functionResponse 以查看以下响应。

"{"disambiguationQuestion": ["[NOT_ENOUGH_INFO] The database schema does not
contain information about which airports are on the 'west side'. Therefore, I
cannot determine how many flights depart from the west side.Possible alternative
questions: 1. How many flights depart from a specific airport? 2. What are the
departure airports for all flights? 3. How many flights depart from each
airport?"]}"

提高回答的准确性

您可以添加更多上下文信息,不断提高 Gemini Data Analytics QueryData 工具回答的准确性。使用 Gemini CLI 生成上下文,然后将更新后的代理上下文上传到现有的 flights-assistant 代理。如需了解详情,请参阅使用 Gemini CLI 构建上下文。上传新上下文后,控制台会立即提取这些上下文,以便您在无需停机的情况下提高代理的准确性。

多个代理

在开发环境中,您可以通过在 tools.yaml 文件中为工具分配不同的名称,对多个代理上下文执行 A/B 测试。例如,您可以通过定义两个具有不同名称(例如 cloud_gda_query_tool_v1cloud_gda_query_tool_v2)的 cloud-gemini-data-analytics-query 工具来创建唯一的 tools.yaml 配置。通过此设置,您可以实现应用逻辑,通过选择相应的工具名称以编程方式选择所需的代理上下文版本。

以下示例 tools.yaml 展示了如何为数据库来源设置多个代理:

sources:
  gda-api-source:
    kind: cloud-gemini-data-analytics
    projectId: "<var>PROJECT_ID</var>"
tools:
  cloud_gda_query_tool_v1:
    kind: cloud-gemini-data-analytics-query
    source: gda-api-source
    context:
      datasourceReferences:
        <var>DB_SOURCE</var>:
          databaseReference: ...
          agentContextReference:
            contextSetId: "V1_YOUR_DATA_AGENT_CONTEXT_SET_ID"
    generationOptions: ...
  cloud_gda_query_tool_v2:
    kind: cloud-gemini-data-analytics-query
    source: gda-api-source
    context:
      datasourceReferences:
        <var>DB_SOURCE</var>:
          databaseReference: ...
          agentContextReference:
            contextSetId: "V2_YOUR_DATA_AGENT_CONTEXT_SET_ID"
    generationOptions: ...

替换以下内容:

  • PROJECT_ID:您的 Google Cloud 项目 ID。
  • V1_YOUR_DATA_AGENT_CONTEXT_SET_ID:版本 1 的数据代理上下文集 ID。
  • V2_YOUR_DATA_AGENT_CONTEXT_SET_ID:版本 2 的数据代理上下文集 ID。

清理

以下部分介绍了如何删除这些资源和对象。

删除代理

在删除实例之前,请先删除您创建的代理。

  1. 在 Google Cloud 控制台中,前往 Spanner 页面。

    前往 Spanner

  2. 从列表中选择一个实例,然后选择一个数据库。

  3. 在导航菜单中,点击 Spanner Studio

  4. 探索器窗格中,点击数据代理旁边的查看操作

  5. 删除代理窗口中,在确认框中输入 flight-assistant

  6. 点击确认

删除实例

删除您在准备工作部分中创建的实例时,您创建的所有对象也会一并删除。

  1. 在 Google Cloud 控制台中,前往 Spanner 页面。

    前往 Spanner

  2. 点击要删除的实例的名称,例如 Test Instance

  3. 点击删除实例

  4. 输入实例名称,然后点击删除,以确认您要删除该实例。

后续步骤