使用 ADK、AlloyDB 和 Gemini 在 Java 中构建强大的有状态端到端 AI 代理应用!

使用 ADK、AlloyDB 和 Gemini 在 Java 中构建强大的有状态端到端 AI 代理应用!

关于此 Codelab

subject上次更新时间:5月 28, 2025
account_circleAuthor: Abirami Sukumaran 编写

1. 概览

在各个行业中,内容相关搜索都是构成应用核心的关键功能。检索增强生成(RAG)已成为推动这项关键技术演变的重要驱动力,其检索机制由生成式 AI 赋能。生成式模型具有较大的上下文窗口和出色的输出质量,正在颠覆 AI 技术。RAG 提供了一种系统的方法,可将上下文注入 AI 应用和代理,将其置于结构化数据库或各种媒体中的信息之上。这些背景数据对于阐明真相和确保输出结果的准确性至关重要,但这些结果的准确性如何?您的业务是否在很大程度上取决于这些内容相关匹配的准确性和相关性?那么,这个项目一定会让您爱不释手!

现在,假设我们可以利用生成式模型的强大功能,构建能够依托此类关键情境信息做出自主决策且基于事实的互动式代理;我们今天要构建的就是这样一种代理。我们将使用 AlloyDB 中由高级 RAG 提供支持的 Agent Development Kit 构建一个端到端 AI 代理应用,以便用于专利分析应用。

专利分析代理可帮助用户查找与其搜索文本在情境上相关的专利,并在用户提出要求时,为所选专利提供清晰简洁的说明和必要的其他详细信息。准备好了解具体操作了吗?我们开始吧!

目标

目标很简单。允许用户根据文本说明搜索专利,然后从搜索结果中获取特定专利的详细说明。为此,系统使用了使用 Java ADK、AlloyDB、向量搜索(使用高级索引)、Gemini 构建的 AI 代理,以及在 Cloud Run 上无服务器部署的整个应用。

构建内容

在本实验中,您将执行以下操作:

  1. 创建 AlloyDB 实例并加载专利公开数据集数据
  2. 使用 ScaNN 和 Recall 评估功能在 AlloyDB 中实现高级向量搜索
  3. 使用 Java ADK 创建代理
  4. 在 Java 无服务器 Cloud Functions 中实现数据库服务器端逻辑
  5. 在 Cloud Run 中部署和测试代理

下图显示了数据流和实现过程中涉及的步骤。

c22563ace65a6930.png

High level diagram representing the flow of the Patent Search Agent with AlloyDB & ADK

要求

  • 一个浏览器,例如 ChromeFirefox
  • 启用了结算功能的 Google Cloud 项目。

2. 准备工作

创建项目

  1. Google Cloud Console 的项目选择器页面上,选择或创建一个 Google Cloud 项目
  2. 确保您的 Cloud 项目已启用结算功能。了解如何检查项目是否已启用结算功能
  3. 您将使用 Cloud Shell,它是在 Google Cloud 中运行的命令行环境。点击 Google Cloud 控制台顶部的“激活 Cloud Shell”。

“激活 Cloud Shell”按钮图片

  1. 连接到 Cloud Shell 后,您可以使用以下命令检查自己是否已通过身份验证,以及项目是否已设置为您的项目 ID:
gcloud auth list
  1. 在 Cloud Shell 中运行以下命令,以确认 gcloud 命令了解您的项目。
gcloud config list project
  1. 如果项目未设置,请使用以下命令进行设置:
gcloud config set project <YOUR_PROJECT_ID>
  1. 启用所需的 API。 您可以在 Cloud Shell 终端中使用 gcloud 命令:
gcloud services enable alloydb.googleapis.com compute.googleapis.com cloudresourcemanager.googleapis.com servicenetworking.googleapis.com run.googleapis.com cloudbuild.googleapis.com cloudfunctions.googleapis.com aiplatform.googleapis.com

您可以通过控制台搜索各个产品或使用此链接,以替代 gcloud 命令。

如需了解 gcloud 命令和用法,请参阅文档

3. 数据库设置

在本实验中,我们将使用 AlloyDB 作为专利数据的数据库。它使用集群来存储所有资源,例如数据库和日志。每个集群都有一个主实例,可提供对数据的访问点。表将存储实际数据。

我们来创建一个 AlloyDB 集群、实例和表,以便加载专利数据集。

创建集群和实例

  1. 在 Cloud 控制台中,前往 AlloyDB 页面。在 Cloud 控制台中查找大多数页面时,最简单的方法是使用控制台的搜索栏进行搜索。
  2. 从该页面中选择创建集群

f76ff480c8c889aa.png

  1. 您将看到如下所示的界面。使用以下值创建集群和实例(如果您要从代码库克隆应用代码,请确保这些值一致):
  • 集群 ID:“vector-cluster
  • 密码:“alloydb
  • PostgreSQL 15 / 推荐使用最新版本
  • 区域"us-central1"
  • 网络:“default

538dba58908162fb.png

  1. 选择默认网络后,您会看到如下所示的界面。

选择设置连接
7939bbb6802a91bf.png

  1. 然后,选择使用自动分配的 IP 范围并点击“继续”。查看信息后,选择“创建关联”。768ff5210e79676f.png
  2. 设置好网络后,您可以继续创建集群。点击创建集群以完成集群设置,如下所示:

e06623e55195e16e.png

请务必将实例 ID(您可以在配置集群 / 实例时找到)更改为

vector-instance。如果您无法更改它,请记得在接下来的所有引用中使用实例 ID

请注意,创建集群大约需要 10 分钟。成功完成后,您应该会看到一个屏幕,其中显示了您刚刚创建的集群的概览。

4. 数据注入

现在,我们需要添加一个包含商店数据的表格。前往 AlloyDB,选择主集群,然后选择 AlloyDB Studio:

847e35f1bf8a8bd8.png

您可能需要等待实例创建完成。完成后,使用您在创建集群时创建的凭据登录 AlloyDB。使用以下数据对 PostgreSQL 进行身份验证:

  • 用户名:“postgres
  • 数据库:“postgres
  • 密码:“alloydb

成功通过身份验证登录 AlloyDB Studio 后,您可以在编辑器中输入 SQL 命令。您可以使用最后一个窗口右侧的加号添加多个编辑器窗口。

91a86d9469d499c4.png

您将在编辑器窗口中输入 AlloyDB 命令,并根据需要使用“Run”“Format”和“Clear”选项。

启用扩展程序

如需构建此应用,我们将使用扩展程序 pgvectorgoogle_ml_integration。借助 pgvector 扩展程序,您可以存储和搜索向量嵌入。google_ml_integration 扩展程序提供了用于访问 Vertex AI 预测端点以在 SQL 中获取预测结果的函数。通过运行以下 DDL 启用这些扩展程序:

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

如果您想检查数据库上已启用的扩展程序,请运行以下 SQL 命令:

select extname, extversion from pg_extension;

创建表

您可以在 AlloyDB Studio 中使用以下 DDL 语句创建表:

CREATE TABLE patents_data ( id VARCHAR(25), type VARCHAR(25), number VARCHAR(20), country VARCHAR(2), date VARCHAR(20), abstract VARCHAR(300000), title VARCHAR(100000), kind VARCHAR(5), num_claims BIGINT, filename VARCHAR(100), withdrawn BIGINT, abstract_embeddings vector(768)) ;

abstract_embeddings 列将允许存储文本的向量值。

授予权限

运行以下语句以授予对“embedding”函数的执行权限:

GRANT EXECUTE ON FUNCTION embedding TO postgres;

向 AlloyDB 服务账号授予 Vertex AI User 角色

Google Cloud IAM 控制台中,向 AlloyDB 服务账号(格式为:service-<<PROJECT_NUMBER>>@gcp-sa-alloydb.iam.gserviceaccount.com)授予“Vertex AI 用户”角色的访问权限。PROJECT_NUMBER 将包含您的项目编号。

或者,您也可以从 Cloud Shell 终端运行以下命令:

PROJECT_ID=$(gcloud config get-value project)


gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"

将专利数据加载到数据库中

我们将使用 BigQuery 上的 Google 专利公共数据集作为数据集。我们将使用 AlloyDB Studio 运行查询。数据会导入到此 insert_scripts.sql 文件中,我们将运行此文件以加载专利数据。

  1. 在 Google Cloud 控制台中,打开 AlloyDB 页面。
  2. 选择新创建的集群,然后点击实例。
  3. 在 AlloyDB 导航菜单中,点击 AlloyDB Studio。使用凭据登录。
  4. 点击右侧的新标签页图标,打开新标签页。
  5. 将上述 insert_scripts.sql 脚本中的 insert 查询语句复制到编辑器中。您可以复制 10-50 个插入语句,以便快速演示此用例。
  6. 点击运行。查询结果会显示在结果表中。

5. 为专利数据创建嵌入

首先,我们通过运行以下示例查询来测试嵌入函数:

SELECT embedding('text-embedding-005', 'AlloyDB is a managed, cloud-hosted SQL database service.');

这应该会返回查询中示例文本的嵌入向量,该向量看起来像一个浮点数数组。如下所示:

25a1d7ef0e49e91e.png

更新 abstract_embeddings 向量字段

运行以下 DML 以使用相应的嵌入更新表中的专利摘要:

UPDATE patents_data set abstract_embeddings = embedding( 'text-embedding-005', abstract);

6. 执行向量搜索

现在,表格、数据和嵌入都已准备就绪,接下来我们将对用户搜索文本执行实时向量搜索。您可以通过运行以下查询来进行测试:

SELECT id || ' - ' || title as title FROM patents_data ORDER BY abstract_embeddings <=> embedding('text-embedding-005', 'Sentiment Analysis')::vector LIMIT 10;

在此查询中,

  1. 用户搜索的文字是:“Sentiment Analysis”。
  2. 我们将在 embedding() 方法中使用模型 text-embedding-005 将其转换为嵌入。
  3. “<=>”表示使用 COSINE SIMILARITY 距离方法。
  4. 我们将嵌入方法的结果转换为向量类型,以使其与存储在数据库中的向量兼容。
  5. LIMIT 10 表示我们要选择与搜索文本最相符的 10 个结果。

AlloyDB 将向量搜索 RAG 提升到了一个新的水平:

我们介绍了许多内容。其中两个侧重于开发者的功能是:

  1. 内嵌过滤
  2. 召回率评估器

内嵌过滤

以前,作为开发者,您必须执行向量搜索查询,并处理过滤和召回。AlloyDB 查询优化器会选择如何执行带有过滤条件的查询。内嵌过滤是一种新的查询优化技术,可让 AlloyDB 查询优化器同时评估元数据过滤条件和向量搜索,同时利用元数据列的向量索引和索引。这提高了召回率,让开发者能够充分利用 AlloyDB 的开箱即用功能。

内嵌过滤最适合选择性中等的情况。在搜索向量索引时,AlloyDB 只会计算与元数据过滤条件(查询中的函数过滤条件,通常在 WHERE 子句中处理)匹配的向量的距离。这极大地提高了这些查询的性能,并补充了后置过滤器或前置过滤器的优势。

  1. 安装或更新 pgvector 扩展程序
CREATE EXTENSION IF NOT EXISTS vector WITH VERSION '0.8.0.google-3';

如果 pgvector 扩展程序已安装,请将向量扩展程序升级到 0.8.0.google-3 或更高版本,以获取召回率评估器功能。

ALTER EXTENSION vector UPDATE TO '0.8.0.google-3';

只有当您的矢量扩展程序版本低于 0.8.0.google-3 时,才需要执行此步骤。

重要提示:如果行数少于 100 行,则无需创建 ScaNN 索引,因为它不适用于行数较少的情况。在这种情况下,请跳过以下步骤。

  1. 如需创建 ScaNN 索引,请安装 alloydb_scann 扩展程序。
CREATE EXTENSION IF NOT EXISTS alloydb_scann;
  1. 首先,在不启用索引和内嵌过滤器的情况下运行向量搜索查询:
SELECT id || ' - ' || title as title FROM patents_data 
WHERE num_claims >= 15
ORDER BY abstract_embeddings <=> embedding('text-embedding-005', 'Sentiment Analysis')::vector LIMIT 10;

结果应类似如下所示:

6989de0fc3f0f753.png

  1. 对其运行“解释分析”功能:(不使用索引也不使用内嵌过滤)

908dcf87c7f00ed4.png

执行时间为 2.4 毫秒

  1. 我们在 num_claims 字段上创建一个常规索引,以便按该字段进行过滤:
CREATE INDEX idx_patents_data_num_claims ON patents_data (num_claims);
  1. 让我们为专利搜索应用创建 ScaNN 索引。从 AlloyDB Studio 运行以下代码:
CREATE INDEX patent_index ON patents_data 
USING scann (abstract_embeddings cosine)
WITH (num_leaves=32);

重要提示 (num_leaves=32) 适用于包含 1,000 多行的整个数据集。如果行数少于 100 行,则无需创建索引,因为索引不适用于行数较少的情况。

  1. 在 ScaNN 索引上设置内嵌过滤功能:
SET scann.enable_inline_filtering = on
  1. 现在,我们来运行包含过滤条件和 Vector Search 的相同查询:
SELECT id || ' - ' || title as title FROM patents_data 
WHERE num_claims >= 15
ORDER BY abstract_embeddings <=> embedding('text-embedding-005', 'Sentiment Analysis')::vector LIMIT 10;

aa54cba2b2ada2cb.png

如您所见,相同的向量搜索的执行时间显著缩短。这得益于向 Vector Search 注入了内嵌过滤的 ScaNN 索引!

接下来,我们来评估启用了 ScaNN 的此向量搜索的召回率。

召回率评估器

在相似搜索中,召回率是指通过搜索检索到的相关实例的百分比,即真正正例的数量。这是衡量搜索质量时最常用的指标。召回率损失的一个来源是近似最近邻搜索 (aNN) 与 k(精确)最近邻搜索 (kNN) 之间的差异。AlloyDB 的 ScaNN 等向量索引会实现 aNN 算法,让您能够加快对大型数据集的向量搜索速度,但需要以略微降低召回率为代价。现在,AlloyDB 让您能够直接在数据库中衡量各个查询的这种权衡,并确保其长期稳定。您可以根据这些信息更新查询和索引参数,以取得更好的结果和性能。

您可以使用 evaluate_query_recall 函数获得采用给定配置时,对向量索引进行的向量查询的召回率。借助此函数,您可以对参数进行调优以实现所需的向量查询召回率结果。召回率是一种用于搜索质量的指标,定义为返回结果中与查询向量客观上最接近的结果所占的百分比。evaluate_query_recall 函数默认处于开启状态。

重要提示

如果您在执行以下步骤时遇到 HNSW 索引的权限被拒绝错误,请暂时跳过整个召回率评估部分。此时,这可能与访问权限限制有关,因为此 Codelab 文档编写时该库刚刚发布。

  1. 在 ScaNN 索引和 HNSW 索引上设置“启用索引扫描”标志:
SET scann.enable_indexscan = on
SET hnsw.enable_index_scan = on
  1. 在 AlloyDB Studio 中运行以下查询:
SELECT
  *
FROM
  evaluate_query_recall($$
  SELECT
    id || ' - ' || title AS title,
    abstract
  FROM
    patents_data
    where num_claims >= 15
  ORDER BY
    abstract_embeddings <=> embedding('text-embedding-005',
      'sentiment analysis')::vector
  LIMIT 25 $$,
    '{"scann.num_leaves_to_search":1, "scann.pre_reordering_num_neighbors":10}',
    ARRAY['scann']);

evaluate_query_recall 函数会将查询作为参数并返回其召回率。我将用于检查性能的查询用作函数输入查询。我已将 SCaNN 添加为索引方法。如需了解更多参数选项,请参阅文档

我们一直在使用的这个向量搜索查询的召回率为:

c98f38fbe6a0b6c5.png

我发现 Recall 为 70%。现在,我可以使用这些信息更改索引参数、方法和查询参数,并提高此向量搜索的召回率!

我已将结果集中的行数修改为 7(之前为 10),发现 RECALL 略有提高,即 86%。

c12f7b92b8481ceb.png

这意味着,我可以实时调整用户看到的匹配项数量,以便根据用户的搜索情境来提高匹配项的相关性。

好的!现在,是时候部署数据库逻辑并继续处理代理了!

7. 将数据库逻辑无服务器地移至 Web

准备好将此应用移植到 Web 平台了吗?请按以下步骤操作:

  1. 前往 Google Cloud 控制台中的 Cloud Run Functions 页面,创建新的 Cloud Run 函数,或使用以下链接:https://console.cloud.google.com/functions/add
  2. 将“环境”选择为“Cloud Run 函数”。提供函数名称“patent-search”,并选择“us-central1”作为区域。将“Authentication”(身份验证)设置为“Allow unauthenticated invocations”(允许未经身份验证的调用),然后点击“Next”(下一步)。选择 Java 17 作为运行时,并为源代码选择内嵌编辑器。
  3. 默认情况下,它会将入口点设置为“gcfv2.HelloHttpFunction”。将 Cloud Run 函数的 HelloHttpFunction.java 和 pom.xml 中的占位符代码分别替换为“PatentSearch.java”和“pom.xml”中的代码。PatentSearch.javapom.xml将类文件的名称更改为 PatentSearch.java。
  4. 请务必在 Java 文件中将 ************* 占位符和 AlloyDB 连接凭据更改为您的值。AlloyDB 凭据是我们在本 Codelab 开始时使用的凭据。如果您使用了其他值,请在 Java 文件中进行相应修改。
  5. 点击部署

重要步骤

部署完成后,为了允许 Cloud Function 访问 AlloyDB 数据库实例,我们将创建 VPC 连接器。

准备好部署后,您应该可以在 Google Cloud Run Functions 控制台中看到这些函数。搜索新创建的函数 (patent-search),点击该函数,然后点击“修改并部署新修订版本”(通过 Cloud Run Functions 控制台顶部的“修改”图标 [笔] 表示),然后更改以下内容:

  1. 前往“网络”标签页:

828cd861864d99ea.png

  1. 选择连接到 VPC 以接收出站流量,然后选择使用无服务器 VPC 访问通道连接器
  2. 在“网络”下拉菜单的“设置”下,点击“网络”下拉菜单,然后选择“添加新的 VPC 连接器”选项(如果您尚未配置默认连接器),并按照随即弹出的对话框中显示的说明操作:

6559ccfd10e597f2.png

  1. 为 VPC 连接器提供一个名称,并确保区域与您的实例相同。将“网络”值保留为默认值,并将“子网”设置为“自定义 IP 范围”,IP 范围为 10.8.0.0 或其他可用的类似 IP 地址。
  2. 展开“显示缩放设置”,并确保将配置设置为完全如下所示:

199b0ccd80215004.png

  1. 点击创建,此连接器现在应该会列在出站流量设置中。
  2. 选择新创建的连接器。
  3. 选择将所有流量都通过此 VPC 连接器路由。
  4. 依次点击下一步部署
  5. 更新后的 Cloud Functions 函数部署完毕后,您应该会看到生成的端点。复制该内容,然后在以下命令中进行替换:
PROJECT_ID=$(gcloud config get-value project)

curl -X POST <<YOUR_ENDPOINT>> \
  -H 'Content-Type: application/json' \
  -d '{"search":"Sentiment Analysis"}'

大功告成!只需这样,您就可以使用 AlloyDB 数据中的嵌入模型执行高级情境相似度向量搜索。

8. 我们来使用 Java ADK 构建代理

首先,我们在编辑器中开始使用 Java 项目。

  1. 前往 Cloud Shell 终端

https://shell.cloud.google.com/?fromcloudshell=true&show=ide%2Cterminal

  1. 在系统提示时授权
  2. 点击 Cloud Shell 控制台顶部的编辑器图标,切换到 Cloud Shell 编辑器

f913b886324e5196.png

  1. 在初始 Cloud Shell 编辑器控制台中,创建一个新文件夹并将其命名为“adk-agents”

在 Cloud Shell 的根目录中,点击“新建文件夹”,如下所示:

94c9804697614a94.png

将其命名为“adk-agents”:

37445dc1fe08f74c.png

  1. 在以下结构中创建以下文件夹结构以及具有相应文件名的空文件:
adk-agents/
 └—— pom.xml
 └—— src/
     └—— main/
         └—— java/
             └—— agents/
                 └—— App.java
  1. 在一个单独的标签页中打开 GitHub 代码库,然后复制 App.java 和 pom.xml 文件的源代码。
  2. 如果您使用右上角的“在新标签页中打开”图标在新标签页中打开了编辑器,则可以将终端打开在页面底部。您可以同时打开编辑器和终端,从而自由地进行操作。
  3. 克隆完成后,切换回 Cloud Shell 编辑器控制台
  4. 由于我们已创建 Cloud Run 函数,因此您无需代码库文件夹复制 Cloud Run 函数文件。

ADK Java SDK 使用入门

这个过程非常简单。您主要需要确保克隆步骤涵盖以下内容:

  1. 添加依赖项

在 pom.xml 中添加 google-adk 和 google-adk-dev(适用于 Web 界面)工件。

<!-- The ADK core dependency -->
        <dependency>
            <groupId>com.google.adk</groupId>
            <artifactId>google-adk</artifactId>
            <version>0.1.0</version>
        </dependency>
        <!-- The ADK dev web UI to debug your agent -->
        <dependency>
            <groupId>com.google.adk</groupId>
            <artifactId>google-adk-dev</artifactId>
            <version>0.1.0</version>
        </dependency>

请务必引用源代码库中的 pom.xml,因为应用需要其他依赖项和配置才能运行。

  1. 配置项目

确保在 pom.xml 中正确配置了 Java 版本(建议使用 17 或更高版本)和 Maven 编译器设置。您可以将项目配置为遵循以下结构:

adk-agents/
 └—— pom.xml
 └—— src/
     └—— main/
         └—— java/
             └—— agents/
                 └—— App.java
  1. 定义代理及其工具 (App.java)

这正是 ADK Java SDK 的强大之处。我们定义了代理、其功能(指令)以及它可以使用的工具。

您可以在此处找到主要代理类的几个代码段的简化版本。如需查看完整项目,请参阅此处的项目代码库。

// App.java (Simplified Snippets)
package agents;

import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.BaseAgent;
import com.google.adk.agents.InvocationContext;
import com.google.adk.tools.Annotations.Schema;
import com.google.adk.tools.FunctionTool;
// ... other imports

public class App {

    static FunctionTool searchTool = FunctionTool.create(App.class, "getPatents");
    static FunctionTool explainTool = FunctionTool.create(App.class, "explainPatent");

    public static BaseAgent ROOT_AGENT = initAgent();

    public static BaseAgent initAgent() {
        return LlmAgent.builder()
            .name("patent-search-agent")
            .description("Patent Search agent")
            .model("gemini-2.0-flash-001") // Specify your desired Gemini model
            .instruction(
                """
                You are a helpful patent search assistant capable of 2 things:
                // ... complete instructions ...
                """)
            .tools(searchTool, explainTool)
            .outputKey("patents") // Key to store tool output in session state
            .build();
    }

    // --- Tool: Get Patents ---
    public static Map<String, String> getPatents(
        @Schema(name="searchText",description = "The search text for which the user wants to find matching patents")
        String searchText) {
        try {
            String patentsJson = vectorSearch(searchText); // Calls our Cloud Run Function
            return Map.of("status", "success", "report", patentsJson);
        } catch (Exception e) {
            // Log error
            return Map.of("status", "error", "report", "Error fetching patents.");
        }
    }

    // --- Tool: Explain Patent (Leveraging InvocationContext) ---
    public static Map<String, String> explainPatent(
        @Schema(name="patentId",description = "The patent id for which the user wants to get more explanation for, from the database")
    String patentId,
    @Schema(name="ctx",description = "The list of patent abstracts from the database from which the user can pick the one to get more explanation for")
    InvocationContext ctx) { // Note the InvocationContext
        try {
            // Retrieve previous patent search results from session state
            String previousResults = (String) ctx.session().state().get("patents");
            if (previousResults != null && !previousResults.isEmpty()) {
// Logic to find the specific patent abstract from 'previousResults' by 'patentId'
                String[] patentEntries = previousResults.split("\n\n\n\n");
                for (String entry : patentEntries) {
                    if (entry.contains(patentId)) { // Simplified check
       // The agent will then use its instructions to summarize this 'report'
                        return Map.of("status", "success", "report", entry);
                    }
                }
            }
            return Map.of("status", "error", "report", "Patent ID not found in previous search.");
        } catch (Exception e) {
            // Log error
            return Map.of("status", "error", "report", "Error explaining patent.");
        }
    }

    public static void main(String[] args) throws Exception {
        InMemoryRunner runner = new InMemoryRunner(ROOT_AGENT);
        // ... (Session creation and main input loop - shown in your source)
    }
}

突出显示的关键 ADK Java 代码组件:

  1. LlmAgent.builder()::用于配置代理的 Fluent API。
  2. .instruction(...):为 LLM 提供核心提示和准则,包括何时使用哪种工具。
  3. FunctionTool.create(App.class, "methodName"):轻松将 Java 方法注册为代理可以调用的工具。方法名称字符串必须与实际的公共静态方法匹配。
  4. @Schema(description = ...):为工具参数添加注解,帮助 LLM 了解每个工具预期接受的输入。此说明对于准确选择工具和填充参数至关重要。
  5. InvocationContext ctx:自动传递给工具方法,以便访问会话状态 (ctx.session().state())、用户信息等。
  6. .outputKey("patents"):当工具返回数据时,ADK 可以自动将其存储在此键下的会话状态中。这样,explainPatent 就可以访问 getPatents 的结果了。
  7. VECTOR_SEARCH_ENDPOINT::此变量用于存储专利搜索用例中为用户提供内容相关问答的核心功能逻辑。
  8. 操作步骤:实现上一部分中的 Java Cloud Run 函数步骤后,您需要设置更新后的已部署端点值。
  9. searchTool:此 intent 会与用户互动,从专利数据库中为用户的搜索文本查找在情境上相关的专利匹配项。
  10. explainTool:此操作会要求用户提供要深入探究的特定专利。然后,它会总结专利摘要,并能够根据其掌握的专利详细信息回答用户的更多问题。

重要提示:请务必将 VECTOR_SEARCH_ENDPOINT 变量替换为您部署的 CRF 端点。

利用 InvocationContext 进行有状态互动

构建实用智能体的关键功能之一是管理对话的多轮状态。ADK 的 InvocationContext 可让您轻松实现此目的。

在 App.java 中:

  1. 定义 initAgent() 后,我们使用 .outputKey("patents")。这会告知 ADK,当某个工具(例如 getPatents)在其报告字段中返回数据时,应将这些数据存储在会话状态的“patents”键下。
  2. 在 explainPatent 工具方法中,我们会注入 InvocationContext ctx:
public static Map<String, String> explainPatent(
    @Schema(description = "...") String patentId, InvocationContext ctx) {
    String previousResults = (String) ctx.session().state().get("patents");
    // ... use previousResults ...
}

这样,explainPatent 工具就可以访问 getPatents 工具在上一个对话回合中提取的专利列表,从而使对话具有状态和连贯性。

9. 本地 CLI 测试

定义环境变量

您需要导出两个环境变量:

  1. 您可以从 AI Studio 获取 Gemini 密钥:

为此,请前往 https://aistudio.google.com/apikey,获取您要实现此应用的有效 Google Cloud 项目的 API 密钥,并将该密钥保存到某个位置:

ae2db169e6a94e4a.png

  1. 获取密钥后,打开 Cloud Shell 终端,然后运行以下命令,进入我们刚刚创建的新目录 adk-agents:
cd adk-agents
  1. 用于指定我们这次不使用 Vertex AI 的变量。
export GOOGLE_GENAI_USE_VERTEXAI=FALSE
export GOOGLE_API_KEY=AIzaSyDF...
  1. 在 CLI 上运行您的首个客服

如需启动此第一个代理,请在终端中使用以下 Maven 命令:

mvn compile exec:java -DmainClass="agents.App"

您会在终端中看到客服人员的互动回复。

10. 部署到 Cloud Run

将 ADK Java 代理部署到 Cloud Run 与部署任何其他 Java 应用类似:

  1. Dockerfile:创建 Dockerfile 以打包 Java 应用。
  2. 构建和推送 Docker 映像:使用 Google Cloud Build 和 Artifact Registry。
  3. 您只需一条命令即可执行上述步骤并部署到 Cloud Run:
gcloud run deploy --source . --set-env-vars GOOGLE_API_KEY=<<Your_Gemini_Key>>

同样,您需要部署 Java Cloud Run 函数 (gcfv2.PatentSearch)。或者,您也可以直接从 Cloud Run Functions 控制台为数据库逻辑创建和部署 Java Cloud Run Functions。

11. 使用 Web 界面进行测试

ADK 附带一个方便的 Web 界面,可用于本地测试和调试您的代理。当您在本地运行 App.java(例如,如果已配置,则使用 mvn exec:java -Dexec.mainClass="agents.App" 命令运行,或者只运行 main 方法)时,ADK 通常会启动本地 Web 服务器。

借助 ADK 网页界面,您可以:

  1. 向客服人员发送消息。
  2. 查看事件(用户消息、工具调用、工具响应、LLM 响应)。
  3. 检查会话状态。
  4. 查看日志和轨迹。

在开发过程中,这对于了解您的代理如何处理请求和使用其工具非常有用。这假定 pom.xml 中的 mainClass 已设置为 com.google.adk.web.AdkWebServer,并且您的代理已注册到该类,或者您正在运行会公开此类的本地测试运行程序。

使用 InMemoryRunner 和 Scanner 运行 App.java 以获取控制台输入时,您是在测试核心代理逻辑。Web 界面是一个单独的组件,可提供更直观的调试体验,通常在 ADK 通过 HTTP 提供代理时使用。

您可以从根目录使用以下 Maven 命令启动 SpringBoot 本地服务器:

mvn compile exec:java -Dexec.args="--adk.agents.source-dir=src/main/java/ --logging.level.com.google.adk.dev=TRACE --logging.level.com.google.adk.demo.agents=TRACE"

该界面通常可通过上述命令输出的网址访问。如果是已部署到 Cloud Run,您应该可以通过 Cloud Run 部署链接访问它。

您应该能够在交互式界面中看到结果。

请观看以下视频,了解我们已部署的专利代理:

演示:使用 AlloyDB 内嵌搜索和召回评估功能实现质量控制的专利代理!

ca7b0fc4fe571dd6.png

12. 清理

为避免系统因本博文中使用的资源向您的 Google Cloud 账号收取费用,请按照以下步骤操作:

  1. 在 Google Cloud 控制台中,前往 https://console.cloud.google.com/cloud-resource-manager?utm_campaign=CDR_0x1d2a42f5_default_b419133749&utm_medium=external&utm_source=blog
  2. https://console.cloud.google.com/cloud-resource-manager?utm_campaign=CDR_0x1d2a42f5_default_b419133749&utm_medium=external&utm_source=blog 页面。
  3. 在项目列表中,选择要删除的项目,然后点击删除
  4. 在对话框中输入项目 ID,然后点击关停以删除项目。

13. 恭喜

恭喜!您已成功使用 ADK、https://cloud.google.com/alloydb/docs?utm_campaign=CDR_0x1d2a42f5_default_b419133749&utm_medium=external&utm_source=blog、Vertex AI 和 Vector Search 的功能,在 Java 中构建了专利分析代理,我们还在使内容相关性搜索变得如此具有变革性、高效且真正以实用为导向方面取得了长足进步。

立即开始使用!

ADK 文档:[官方 ADK Java 文档的链接]

专利分析代理源代码:[指向您(现已公开)GitHub 代码库的链接]

Java 示例代理:[指向 adk-samples 代码库的链接]

加入 ADK 社区:https://www.reddit.com/r/agentdevelopmentkit/

祝您构建代理顺利!