Crie apps de agente de IA poderosos, com estado e E2E em Java com o ADK, o AlloyDB e o Gemini.

1. Visão geral

Em diferentes setores, a pesquisa contextual é uma funcionalidade essencial que forma o coração e o centro dos aplicativos. A Geração Aumentada de Recuperação tem sido um fator importante dessa evolução tecnológica crucial há algum tempo com seus mecanismos de recuperação baseados em IA generativa. Os modelos generativos, com suas grandes janelas de contexto e qualidade de saída impressionante, estão transformando a IA. A RAG oferece uma maneira sistemática de injetar contexto em aplicativos e agentes de IA, embasando-os em bancos de dados estruturados ou informações de várias mídias. Esses dados contextuais são cruciais para a clareza da verdade e a precisão da saída, mas qual é a acurácia desses resultados? Sua empresa depende muito da precisão e da relevância dessas correspondências contextuais? Então este projeto vai te agradar!

Agora imagine se pudéssemos aproveitar o poder dos modelos generativos e criar agentes interativos capazes de tomar decisões autônomas com base em informações críticas para o contexto e fundamentadas na verdade. É isso que vamos criar hoje. Vamos criar um app de agente de IA completo usando o Agent Development Kit com tecnologia de RAG avançado no AlloyDB para um aplicativo de análise de patentes.

O agente de análise de patentes ajuda o usuário a encontrar patentes contextualmente relevantes para o texto de pesquisa e, quando solicitado, fornece uma explicação clara e concisa e mais detalhes, se necessário, para uma patente selecionada. Quer saber como isso é feito? Vamos lá.

Objetivo

O objetivo é simples. Permitir que um usuário pesquise patentes com base em uma descrição textual e receba uma explicação detalhada de uma patente específica nos resultados da pesquisa. Tudo isso usando um agente de IA criado com Java ADK, AlloyDB, pesquisa vetorial (com índices avançados), Gemini e todo o aplicativo implantado sem servidor no Cloud Run.

O que você vai criar

Neste laboratório, você vai:

  1. Criar uma instância do AlloyDB e carregar dados do conjunto de dados públicos de patentes
  2. Implementar a pesquisa vetorial avançada no AlloyDB usando os recursos de avaliação de recall e ScaNN
  3. Criar um agente usando o ADK Java
  4. Implementar a lógica do lado do servidor do banco de dados em funções sem servidor do Cloud Functions em Java
  5. Implantar e testar o agente no Cloud Run

O diagrama a seguir representa o fluxo de dados e as etapas envolvidas na implementação.

c22563ace65a6930.png

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

Requisitos

  • Use um navegador, como o Chrome ou o Firefox.
  • Ter um projeto do Google Cloud com o faturamento ativado.

2. Antes de começar

Criar um projeto

  1. No console do Google Cloud, na página de seletor de projetos, selecione ou crie um projeto do Google Cloud.
  2. Verifique se o faturamento está ativado para seu projeto do Cloud. Saiba como verificar se o faturamento está ativado em um projeto .
  3. Você vai usar o Cloud Shell, um ambiente de linha de comando executado no Google Cloud. Clique em "Ativar o Cloud Shell" na parte de cima do console do Google Cloud.

Imagem do botão "Ativar o Cloud Shell"

  1. Depois de se conectar ao Cloud Shell, verifique se sua conta já está autenticada e se o projeto está configurado com seu ID do projeto usando o seguinte comando:
gcloud auth list
  1. Execute o comando a seguir no Cloud Shell para confirmar se o comando gcloud sabe sobre seu projeto.
gcloud config list project
  1. Se o projeto não estiver definido, use este comando:
gcloud config set project <YOUR_PROJECT_ID>
  1. Ative as APIs necessárias. Use um comando gcloud no terminal do Cloud Shell:
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

A alternativa ao comando gcloud é pesquisar cada produto no console ou usar este link.

Consulte a documentação para ver o uso e os comandos gcloud.

3. Configuração do banco de dados

Neste laboratório, vamos usar o AlloyDB como banco de dados para os dados de patentes. Ele usa clusters para armazenar todos os recursos, como bancos de dados e registros. Cada cluster tem uma instância principal que fornece um ponto de acesso aos dados. As tabelas vão conter os dados reais.

Vamos criar um cluster, uma instância e uma tabela do AlloyDB em que o conjunto de dados de patentes será carregado.

Criar um cluster e uma instância

  1. Navegue até a página do AlloyDB no console do Cloud. Uma maneira fácil de encontrar a maioria das páginas no console do Cloud é pesquisar usando a barra de pesquisa do console.
  2. Selecione CRIAR CLUSTER nessa página:

f76ff480c8c889aa.png

  1. Você vai ver uma tela como a abaixo. Crie um cluster e uma instância com os seguintes valores. Verifique se os valores correspondem caso você esteja clonando o código do aplicativo do repositório:
  • ID do cluster: "vector-cluster"
  • password: "alloydb"
  • PostgreSQL 15 / mais recente recomendado
  • Região: "us-central1"
  • Rede: "default"

538dba58908162fb.png

  1. Ao selecionar a rede padrão, uma tela como a abaixo vai aparecer.

Selecione CONFIGURAR CONEXÃO.
7939bbb6802a91bf.png

  1. Em seguida, selecione Usar um intervalo de IP alocado automaticamente e clique em "Continuar". Depois de revisar as informações, selecione "CRIAR CONEXÃO". 768ff5210e79676f.png
  2. Depois que a rede for configurada, você poderá continuar criando o cluster. Clique em CRIAR CLUSTER para concluir a configuração do cluster, conforme mostrado abaixo:

e06623e55195e16e.png

Mude o ID da instância (que pode ser encontrado no momento da configuração do cluster / instância) para

vector-instance. Se não for possível mudar, use o ID da instância em todas as referências futuras.

A criação do cluster leva cerca de 10 minutos. Se tudo der certo, uma tela vai mostrar a visão geral do cluster que você acabou de criar.

4. Ingestão de dados

Agora é hora de adicionar uma tabela com os dados da loja. Navegue até o AlloyDB, selecione o cluster principal e o AlloyDB Studio:

847e35f1bf8a8bd8.png

Talvez seja necessário aguardar a conclusão da criação da instância. Depois disso, faça login no AlloyDB usando as credenciais criadas ao criar o cluster. Use os seguintes dados para autenticar no PostgreSQL:

  • Nome de usuário : "postgres"
  • Banco de dados : "postgres"
  • Senha : "alloydb"

Depois de se autenticar no AlloyDB Studio, os comandos SQL são inseridos no editor. É possível adicionar várias janelas do Editor usando o sinal de mais à direita da última janela.

91a86d9469d499c4.png

Você vai inserir comandos para o AlloyDB nas janelas do editor, usando as opções "Executar", "Formatar" e "Limpar" conforme necessário.

Ativar extensões

Para criar esse app, vamos usar as extensões pgvector e google_ml_integration. A extensão pgvector permite armazenar e pesquisar embeddings de vetor. A extensão google_ml_integration oferece funções que você usa para acessar endpoints de previsão da Vertex AI e receber previsões em SQL. Ative essas extensões executando os seguintes DDLs:

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

Para verificar as extensões ativadas no seu banco de dados, execute este comando SQL:

select extname, extversion from pg_extension;

Criar uma tabela

É possível criar uma tabela usando a instrução DDL abaixo no AlloyDB Studio:

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

A coluna "abstract_embeddings" permite armazenar os valores de vetor do texto.

Conceder permissão

Execute a instrução abaixo para conceder a execução na função "embedding":

GRANT EXECUTE ON FUNCTION embedding TO postgres;

Conceder o papel de usuário da Vertex AI à conta de serviço do AlloyDB

No console do Google Cloud IAM, conceda à conta de serviço do AlloyDB (que tem esta aparência: service-<<PROJECT_NUMBER>>@gcp-sa-alloydb.iam.gserviceaccount.com) acesso à função "Usuário da Vertex AI". PROJECT_NUMBER vai ter o número do seu projeto.

Como alternativa, execute o comando abaixo no terminal do 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"

Carregar dados de patentes no banco de dados

Os conjuntos de dados públicos do Google Patentes no BigQuery serão usados como nosso conjunto de dados. Vamos usar o AlloyDB Studio para executar nossas consultas. Os dados são extraídos para o arquivo insert scripts sql neste repo, e vamos executar isso para carregar os dados de patentes.

  1. No console do Google Cloud, abra a página AlloyDB.
  2. Selecione o cluster recém-criado e clique na instância.
  3. No menu de navegação do AlloyDB, clique em AlloyDB Studio. Faça login com suas credenciais.
  4. Abra uma nova guia clicando no ícone Nova guia à direita.
  5. Copie e execute as instruções de consulta insert dos arquivos insert_scripts1.sql,* insert_script2.sql*,* insert_scripts3.sql*,* insert_scripts4.sql* um por um. Execute as instruções de inserção de cópia de 10 a 50 para uma demonstração rápida desse caso de uso.

Para executar, clique em Executar. Os resultados da consulta aparecem na tabela Resultados.

5. Criar embeddings para dados de patentes

Primeiro, vamos testar a função de incorporação executando a seguinte consulta de exemplo:

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

Isso vai retornar o vetor de embeddings, que parece uma matriz de números de ponto flutuante, para o texto de exemplo na consulta. Ela tem esta aparência:

25a1d7ef0e49e91e.png

Atualizar o campo de vetor abstract_embeddings

A DML abaixo deve ser usada para atualizar os resumos de patentes na tabela com as incorporações correspondentes caso seja necessário gerá-las para os resumos. No entanto, no nosso caso, as instruções de inserção já contêm esses encodings para cada resumo. Portanto, não é necessário chamar o método embeddings().

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

6. Fazer pesquisa vetorial

Agora que a tabela, os dados e os embeddings estão prontos, vamos realizar a pesquisa vetorial em tempo real para o texto de pesquisa do usuário. Para testar isso, execute a consulta abaixo:

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

Nesta consulta,

  1. O texto pesquisado pelo usuário é: "Análise de sentimentos".
  2. Estamos convertendo em embeddings no método embedding() usando o modelo: text-embedding-005.
  3. "<=>" representa o uso do método de distância de similaridade de cosseno.
  4. Estamos convertendo o resultado do método de embedding para o tipo de vetor para torná-lo compatível com os vetores armazenados no banco de dados.
  5. "LIMIT 10" representa que estamos selecionando as 10 correspondências mais próximas do texto de pesquisa.

O AlloyDB leva a RAG da pesquisa vetorial a outro nível:

Há várias coisas novas. Dois deles são:

  1. Filtragem inline
  2. Avaliador de recall

Filtragem inline

Antes, como desenvolvedor, você precisava realizar a consulta de pesquisa de vetor e lidar com a filtragem e o recall. O otimizador de consultas do AlloyDB escolhe como executar uma consulta com filtros. A filtragem inline é uma nova técnica de otimização de consultas que permite que o otimizador de consultas do AlloyDB avalie as condições de filtragem de metadados e a pesquisa vetorial simultaneamente, aproveitando os índices vetoriais e os índices nas colunas de metadados. Isso aumentou o desempenho de recall, permitindo que os desenvolvedores aproveitem o que o AlloyDB tem a oferecer sem precisar fazer nada.

A filtragem inline é ideal para casos com seletividade média. À medida que o AlloyDB pesquisa no índice de vetores, ele só calcula distâncias para vetores que correspondem às condições de filtragem de metadados (seus filtros funcionais em uma consulta geralmente processada na cláusula WHERE). Isso melhora muito o desempenho dessas consultas, complementando as vantagens do pós-filtro ou pré-filtro.

  1. Instalar ou atualizar a extensão pgvector
CREATE EXTENSION IF NOT EXISTS vector WITH VERSION '0.8.0.google-3';

Se a extensão pgvector já estiver instalada, faça upgrade para a versão 0.8.0.google-3 ou mais recente para ter recursos de avaliador de recall.

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

Essa etapa só precisa ser executada se a extensão de vetor for <0.8.0.google-3.

Observação importante:se a contagem de linhas for menor que 100, não será necessário criar o índice do ScaNN, já que ele não se aplica a menos linhas. Nesse caso, pule as etapas a seguir.

  1. Para criar índices do ScaNN, instale a extensão alloydb_scann.
CREATE EXTENSION IF NOT EXISTS alloydb_scann;
  1. Primeiro, execute a consulta do Vector Search sem o índice e sem o filtro inline ativado:
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;

O resultado será semelhante a este:

6989de0fc3f0f753.png

  1. Execute o recurso Explicar análise nele (sem índice nem filtragem inline):

908dcf87c7f00ed4.png

O tempo de execução é de 2,4 ms

  1. Vamos criar um índice regular no campo "num_claims" para poder filtrar por ele:
CREATE INDEX idx_patents_data_num_claims ON patents_data (num_claims);
  1. Vamos criar o índice do ScaNN para nosso aplicativo de pesquisa de patentes. Execute o seguinte no AlloyDB Studio:
CREATE INDEX patent_index ON patents_data 
USING scann (abstract_embeddings cosine)
WITH (num_leaves=32);

Observação importante : (num_leaves=32) se aplica ao nosso conjunto de dados total com mais de 1.000 linhas. Se a contagem de linhas for menor que 100, não será necessário criar um índice, já que ele não se aplica a menos linhas.

  1. Defina a filtragem inline ativada no índice ScaNN:
SET scann.enable_inline_filtering = on
  1. Agora, vamos executar a mesma consulta com filtro e 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

Como você pode ver, o tempo de execução é reduzido significativamente para a mesma pesquisa de vetor. O índice do ScaNN com filtragem inline na Pesquisa Vetorial tornou isso possível.

Em seguida, vamos avaliar o recall para essa Pesquisa Vetorial ativada pelo ScaNN.

Avaliador de recall

O recall na pesquisa de similaridade é a porcentagem de instâncias relevantes que foram recuperadas de uma pesquisa, ou seja, o número de verdadeiros positivos. Essa é a métrica mais comum usada para medir a qualidade da pesquisa. Uma fonte de perda de recall vem da diferença entre a pesquisa de vizinho mais próximo aproximado (aNN) e a pesquisa de vizinho k (exato) mais próximo (kNN). Os índices de vetores, como o ScaNN do AlloyDB, implementam algoritmos aNN, permitindo acelerar a pesquisa de vetores em grandes conjuntos de dados em troca de uma pequena compensação no recall. Agora, o AlloyDB permite medir essa troca diretamente no banco de dados para consultas individuais e garantir que ela seja estável ao longo do tempo. É possível atualizar os parâmetros de consulta e índice em resposta a essas informações para alcançar melhores resultados e desempenho.

É possível encontrar o recall de uma consulta de vetor em um índice vetorial para uma determinada configuração usando a função evaluate_query_recall. Com essa função, é possível ajustar os parâmetros para alcançar os resultados de recall da consulta de vetor desejados. O recall é a métrica usada para a qualidade da pesquisa e é definida como a porcentagem dos resultados retornados que estão objetivamente mais próximos dos vetores de consulta. A função evaluate_query_recall fica ativada por padrão.

Observação importante:

Se você estiver enfrentando um erro de permissão negada no índice HNSW nas etapas a seguir, pule toda esta seção de avaliação de recall por enquanto. Isso pode estar relacionado a restrições de acesso, já que ele foi lançado recentemente quando este codelab foi documentado.

  1. Defina a flag "Enable Index Scan" no índice ScaNN e HNSW:
SET scann.enable_indexscan = on
SET hnsw.enable_index_scan = on
  1. Execute a seguinte consulta no 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']);

A função "evaluate_query_recall" usa a consulta como um parâmetro e retorna a acurácia dela. Estou usando a mesma consulta que usei para verificar a performance como consulta de entrada da função. Adicionei o SCaNN como método de indexação. Para mais opções de parâmetros, consulte a documentação.

O recall para esta consulta de pesquisa vetorial que estamos usando:

c98f38fbe6a0b6c5.png

Vejo que o RECALL é de 70%. Agora posso usar essas informações para mudar os parâmetros, métodos e parâmetros de consulta do índice e melhorar a capacidade de recall para essa pesquisa vetorial.

Modifiquei o número de linhas no conjunto de resultados para 7 (antes eram 10) e notei uma melhoria leve no RECALL, ou seja, 86%.

c12f7b92b8481ceb.png

Isso significa que, em tempo real, posso variar o número de correspondências que meus usuários veem para melhorar a relevância de acordo com o contexto de pesquisa deles.

Tudo bem! Agora é hora de implantar a lógica do banco de dados e passar para o agente.

7. Levar a lógica do banco de dados para o servidor da Web sem servidor

Tudo pronto para levar esse app para a Web? Siga as etapas abaixo:

  1. Acesse as funções do Cloud Run no console do Google Cloud para criar uma função do Cloud Run clicando em "Escrever uma função" ou use o link: https://console.cloud.google.com/run/create?deploymentType=function.
  2. Escolha a opção "Usar um editor in-line para criar uma função" e comece a configuração. Forneça o nome do serviço "patent-search", escolha a região "us-central1" e o tempo de execução "Java 17". Defina a autenticação como Permitir invocações não autenticadas.
  3. Na seção "Contêineres, volumes, rede, segurança", siga as etapas abaixo sem perder nenhum detalhe:

Acesse a guia "Rede":

828cd861864d99ea.png

Selecione Conectar a uma VPC para tráfego de saída e Usar conectores de acesso VPC sem servidor.

No menu suspenso "Rede", clique nele e selecione a opção Adicionar novo conector VPC (se você ainda não tiver configurado o padrão) e siga as instruções na caixa de diálogo que aparece:

6559ccfd10e597f2.png

Forneça um nome para o conector da VPC e verifique se a região é a mesma da sua instância. Deixe o valor da rede como padrão e defina a sub-rede como "Intervalo de IP personalizado" com o intervalo de IP 10.8.0.0 ou algo semelhante que esteja disponível.

Expanda MOSTRAR CONFIGURAÇÕES DE ESCALONAMENTO e verifique se a configuração está exatamente assim:

199b0ccd80215004.png

Clique em CRIAR. O conector vai aparecer nas configurações de saída.

Selecione o conector recém-criado.

Opte por todo o tráfego ser roteado por esse conector de VPC.

Clique em PRÓXIMA e em IMPLANTAR.

  1. Por padrão, ele define o ponto de entrada como "gcfv2.HelloHttpFunction". Substitua o código de marcador de posição em "HelloHttpFunction.java" e "pom.xml" da sua função do Cloud Run pelo código de " PatentSearch.java" e " pom.xml", respectivamente. Mude o nome do arquivo de classe para PatentSearch.java.
  2. Não se esqueça de mudar o marcador ************* e as credenciais de conexão do AlloyDB pelos seus valores no arquivo Java. As credenciais do AlloyDB são as mesmas que usamos no início deste codelab. Se você usou valores diferentes, modifique-os no arquivo Java.
  3. Clique em Implantar.
  4. Depois que a função do Cloud atualizada for implantada, o endpoint gerado vai aparecer. Copie e substitua no comando a seguir:
PROJECT_ID=$(gcloud config get-value project)

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

Pronto! É simples assim realizar uma pesquisa vetorial avançada de similaridade contextual usando o modelo de embeddings em dados do AlloyDB.

8. Vamos criar o agente com o ADK Java

Primeiro, vamos começar com o projeto Java no editor.

  1. Acesse o terminal do Cloud Shell.

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

  1. Autorizar quando solicitado
  2. Clique no ícone do editor na parte de cima do console do Cloud Shell para alternar para o editor do Cloud Shell.

f913b886324e5196.png

  1. No console do editor do Cloud Shell, crie uma pasta chamada "adk-agents".

Clique em "Criar nova pasta" no diretório raiz do Cloud Shell, conforme mostrado abaixo:

94c9804697614a94.png

Nomeie como "adk-agents":

37445dc1fe08f74c.png

  1. Crie a seguinte estrutura de pastas e os arquivos vazios com os nomes correspondentes na estrutura abaixo:
adk-agents/
 └—— pom.xml
 └—— src/ 
     └—— main/
         └—— java/
             └—— agents/
                 └—— App.java
  1. Abra o repositório do GitHub em uma guia separada e copie o código-fonte dos arquivos App.java e pom.xml.
  2. Se você tiver aberto o editor em uma nova guia usando o ícone "Abrir em nova guia" no canto superior direito, o terminal vai aparecer na parte de baixo da página. Você pode abrir o editor e o terminal em paralelo para operar livremente.
  3. Depois de clonado, volte para o console do editor do Cloud Shell.
  4. Como já criamos a função do Cloud Run, não é necessário copiar os arquivos da função do Cloud Run da pasta do repositório.

Como começar a usar o SDK do ADK para Java

É bem simples. Principalmente, você precisa garantir que as seguintes etapas sejam cobertas na etapa de clonagem:

  1. Adicionar dependências:

Inclua os artefatos google-adk e google-adk-dev (para a interface da Web) no pom.xml.

<!-- 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>

Faça referência ao pom.xml do repositório de origem, já que há outras dependências e configurações necessárias para a execução do aplicativo.

  1. Configurar seu projeto:

Verifique se a versão do Java (17 ou mais recente recomendado) e as configurações do compilador Maven estão configuradas corretamente no pom.xml. Você pode configurar seu projeto para seguir a estrutura abaixo:

adk-agents/
 └—— pom.xml
 └—— src/ 
     └—— main/
         └—— java/
             └—— agents/
                 └—— App.java
  1. Definir o agente e as ferramentas dele (App.java):

É aqui que a mágica do SDK do ADK para Java brilha. Definimos nosso agente, as capacidades dele (instruções) e as ferramentas que ele pode usar.

Confira aqui uma versão simplificada de alguns snippets de código da classe principal do agente. Para o projeto completo, consulte o repositório aqui.

// 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)
    }
}

Principais componentes de código Java do ADK em destaque:

  1. LlmAgent.builder():: API fluente para configurar seu agente.
  2. .instruction(...): fornece o comando principal e as diretrizes para o LLM, incluindo quando usar cada ferramenta.
  3. FunctionTool.create(App.class, "methodName"): registra facilmente seus métodos Java como ferramentas que o agente pode invocar. A string do nome do método precisa corresponder a um método estático público real.
  4. @Schema(description = ...): anota os parâmetros da ferramenta, ajudando o LLM a entender quais entradas cada ferramenta espera. Essa descrição é essencial para a seleção precisa de ferramentas e o preenchimento de parâmetros.
  5. InvocationContext ctx:transmitido automaticamente para métodos de ferramentas, acesso ao estado da sessão (ctx.session().state()), informações do usuário e muito mais.
  6. .outputKey("patents"): quando uma ferramenta retorna dados, o ADK pode armazená-los automaticamente no estado da sessão com essa chave. É assim que o explainPatent pode acessar os resultados do getPatents.
  7. VECTOR_SEARCH_ENDPOINT::é uma variável que contém a lógica funcional principal para as perguntas e respostas contextuais do usuário no caso de uso de pesquisa de patentes.
  8. Item de ação: defina um valor de endpoint implantado atualizado depois de implementar a etapa da função do Cloud Run em Java da seção anterior.
  9. searchTool: interage com o usuário para encontrar correspondências de patentes contextualmente relevantes no banco de dados de patentes para o texto de pesquisa do usuário.
  10. explainTool: pede ao usuário uma patente específica para analisar em detalhes. Em seguida, ele resume o resumo da patente e pode responder a mais perguntas do usuário com base nos detalhes da patente que tem.

Observação importante: substitua a variável VECTOR_SEARCH_ENDPOINT pelo endpoint de CRF implantado.

Como aproveitar o InvocationContext para interações com estado

Um dos recursos essenciais para criar agentes úteis é gerenciar o estado em várias rodadas de uma conversa. O InvocationContext do ADK facilita isso.

No nosso App.java:

  1. Quando initAgent() é definido, usamos .outputKey("patents"). Isso informa ao ADK que, quando uma ferramenta (como getPatents) retorna dados no campo de relatório, eles precisam ser armazenados no estado da sessão com a chave "patents".
  2. No método explainPatent, injetamos InvocationContext ctx:
public static Map<String, String> explainPatent(
    @Schema(description = "...") String patentId, InvocationContext ctx) {
    String previousResults = (String) ctx.session().state().get("patents");
    // ... use previousResults ...
}

Isso permite que a ferramenta explainPatent acesse a lista de patentes buscada pela ferramenta getPatents em uma rodada anterior, tornando a conversa com estado e coerente.

9. Teste local da CLI

Definir as variáveis de ambiente

Você precisa exportar duas variáveis de ambiente:

  1. Uma chave do Gemini que você pode acessar no AI Studio:

Para fazer isso, acesse https://aistudio.google.com/apikey e receba a chave de API do seu projeto ativo do Google Cloud em que você está implementando esse aplicativo. Salve a chave em algum lugar:

ae2db169e6a94e4a.png

  1. Depois de conseguir a chave, abra o terminal do Cloud Shell e acesse o novo diretório que acabamos de criar, adk-agents, executando o seguinte comando:
cd adk-agents
  1. Uma variável para especificar que não estamos usando a Vertex AI desta vez.
export GOOGLE_GENAI_USE_VERTEXAI=FALSE
export GOOGLE_API_KEY=AIzaSyDF...
  1. Executar seu primeiro agente na CLI

Para iniciar o primeiro agente, use o seguinte comando do Maven no terminal:

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

Você verá a resposta interativa do agente no terminal.

10. Como implantar no Cloud Run

A implantação do agente Java do ADK no Cloud Run é semelhante à implantação de qualquer outro aplicativo Java:

  1. Dockerfile: crie um Dockerfile para empacotar seu aplicativo Java.
  2. Criar e enviar imagem do Docker: use o Google Cloud Build e o Artifact Registry.
  3. Você pode executar a etapa acima e implantar no Cloud Run com apenas um comando:
gcloud run deploy --source . --set-env-vars GOOGLE_API_KEY=<<Your_Gemini_Key>>

Da mesma forma, você implantaria sua função do Cloud Run em Java (gcfv2.PatentSearch). Também é possível criar e implantar a função do Cloud Run em Java para a lógica do banco de dados diretamente no console de funções do Cloud Run.

11. Como testar com a interface da Web

O ADK vem com uma interface da Web prática para testes e depuração locais do seu agente. Quando você executa o App.java localmente (por exemplo, mvn exec:java -Dexec.mainClass="agents.App" se configurado ou apenas executando o método principal), o ADK normalmente inicia um servidor da Web local.

Com a interface da Web do ADK, você pode:

  1. Envie mensagens para seu agente.
  2. Confira os eventos (mensagem do usuário, chamada de ferramenta, resposta da ferramenta, resposta do LLM).
  3. Inspecione o estado da sessão.
  4. Veja registros e traces.

Isso é muito útil durante o desenvolvimento para entender como o agente processa solicitações e usa as ferramentas. Isso pressupõe que sua mainClass em pom.xml esteja definida como com.google.adk.web.AdkWebServer e que seu agente esteja registrado nela ou que você esteja executando um executor de testes local que exponha isso.

Ao executar o App.java com o InMemoryRunner e o Scanner para entrada do console, você está testando a lógica principal do agente. A interface da Web é um componente separado para uma experiência de depuração mais visual, geralmente usada quando o ADK veicula seu agente por HTTP.

Use o seguinte comando do Maven no diretório raiz para iniciar o servidor local do 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"

A interface geralmente pode ser acessada no URL gerado pelo comando acima. Se for Implantado no Cloud Run, você poderá acessar o link implantado do Cloud Run.

Você poderá conferir o resultado em uma interface interativa.

Confira o vídeo abaixo para conhecer nosso agente de patentes implantado:

Demonstração de um agente de patentes com controle de qualidade usando a pesquisa inline do AlloyDB e a avaliação de recall.

ca7b0fc4fe571dd6.png

12. Limpar

Para evitar cobranças na sua conta do Google Cloud pelos recursos usados nesta postagem, siga estas etapas:

  1. No console do Google Cloud, acesse 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. Na lista de projetos, selecione o projeto que você quer excluir e clique em Excluir.
  4. Na caixa de diálogo, digite o ID do projeto e clique em Encerrar para excluí-lo.

13. Parabéns

Parabéns! Você criou com sucesso seu agente de análise de patentes em Java combinando os recursos do ADK, https://cloud.google.com/alloydb/docs?utm_campaign=CDR_0x1d2a42f5_default_b419133749&utm_medium=external&utm_source=blog, Vertex AI e pesquisa vetorial. Além disso, avançamos muito para tornar as pesquisas de similaridade contextual transformadoras, eficientes e realmente orientadas a significado.

Comece hoje mesmo!

Documentação do ADK: [Link para a documentação oficial do ADK em Java]

Código-fonte do agente de análise de patentes: [link para seu repositório do GitHub (agora público)]

Agentes de amostra em Java: [link para o repositório adk-samples]

Participe da comunidade do ADK: https://www.reddit.com/r/agentdevelopmentkit/

Boa criação de agentes!