Spring AI e Elasticsearch como seu banco de dados vetorial

Construindo uma aplicação de IA completa usando Spring AI e Elasticsearch.

O Elasticsearch conta com integrações nativas com as principais ferramentas e provedores de IA generativa do setor. Confira nossos webinars sobre como ir além do básico do RAG ou criar aplicativos prontos para produção com o banco de dados vetorial da Elastic.

Para criar as melhores soluções de busca para seu caso de uso, inicie um teste gratuito na nuvem ou experimente o Elastic em sua máquina local agora mesmo.

O Spring AI já está disponível para o público em geral, com sua primeira versão estável, a 1.0, pronta para download no Maven Central. Vamos usá-lo imediatamente para construir um aplicativo de IA completo, usando seu LLM favorito e nosso banco de dados vetorial favorito. Ou então, acesse diretamente o repositório com a aplicação final.

O que é Spring AI?

O Spring AI 1.0, uma solução abrangente para engenharia de IA em Java, já está disponível após um período significativo de desenvolvimento influenciado pelos rápidos avanços na área de IA. Esta versão inclui diversas novas funcionalidades essenciais para engenheiros de IA.

Java e Spring estão em uma posição privilegiada para aproveitar toda essa onda de IA. Inúmeras empresas estão executando seus sistemas com Spring Boot, o que torna extremamente fácil integrar IA ao que elas já fazem. Basicamente, você pode conectar sua lógica de negócios e seus dados diretamente a esses modelos de IA sem muita dificuldade.

O Spring AI oferece suporte a diversos modelos e tecnologias de IA, tais como:

  • Modelos de imagem: geram imagens a partir de instruções de texto.
  • Modelos de transcrição: pegam fontes de áudio e as convertem em texto.
  • Modelos de incorporação: convertem dados arbitrários em vetores, que são tipos de dados otimizados para busca de similaridade semântica.
  • Modelos de bate-papo: estes Deve ser familiar! Você sem dúvida já teve até mesmo uma breve conversa com um deles em algum lugar.

Os modelos de bate-papo parecem ser o foco da maior parte da atenção no campo da IA, e com razão, eles são incríveis! Você pode pedir a ajuda deles para corrigir um documento ou escrever um poema. (Só não peça para eles contarem uma piada... ainda.) Eles são incríveis, mas têm alguns problemas.

Soluções de IA da Spring para desafios de IA

Vamos analisar alguns desses problemas e suas soluções no Spring AI.

ProblemaSolução
ConsistênciaOs modelos de bate-papo são de mente aberta e propensos a distrações.Você pode fornecer a eles um comando do sistema para controlar sua forma e estrutura geral.
MemóriaOs modelos de IA não têm memória, portanto não conseguem correlacionar uma mensagem de um determinado usuário com outra.Você pode fornecer a eles um sistema de memória para armazenar as partes relevantes da conversa.
IsolamentoOs modelos de IA vivem em pequenos ambientes isolados, mas podem fazer coisas realmente incríveis se você lhes der acesso a ferramentas — funções que eles podem invocar quando julgarem necessário.O Spring AI oferece suporte à chamada de ferramentas, o que permite informar ao modelo de IA sobre as ferramentas em seu ambiente, que ele poderá então solicitar que você as invoque. Essa interação de múltiplas etapas é gerenciada de forma transparente para você.
Dados privadosOs modelos de IA são inteligentes, mas não são oniscientes! Eles não sabem o que está contido em seus bancos de dados proprietários - e acreditamos que você também não gostaria que soubessem!Você precisa influenciar as respostas inserindo texto nos prompts — basicamente, usando o poderoso operador de concatenação de strings para adicionar texto à requisição antes que o modelo analise a pergunta feita. Informações adicionais, se quiser. Como você decide o que deve ser enviado e o que não deve? Utilize um armazenamento vetorial para selecionar apenas os dados relevantes e enviá-los adiante. Isso é chamado de geração aumentada por recuperação, ou RAG.
AlucinaçãoOs modelos de bate-papo com IA gostam, bem, de conversar! E às vezes fazem isso com tanta confiança que chegam a inventar coisas.É necessário usar a avaliação — utilizando um modelo para validar a saída de outro — para confirmar resultados razoáveis.

E, claro, nenhuma aplicação de IA é uma ilha. Atualmente, os sistemas e serviços de IA modernos funcionam melhor quando integrados a outros sistemas e serviços. Protocolo de Contexto do Modelo (MCP) possibilita conectar seus aplicativos de IA com outros serviços baseados em MCP, independentemente da linguagem em que foram escritos. Você pode reunir tudo isso em fluxos de trabalho orientados a agentes que conduzem a um objetivo maior.

A melhor parte? Você pode fazer tudo isso enquanto aproveita os padrões e abstrações familiares que qualquer desenvolvedor Spring Boot já espera: dependências iniciais convenientes para praticamente tudo estão disponíveis no Spring Initializr.

O Spring AI oferece configurações automáticas convenientes do Spring Boot, proporcionando a abordagem de convenção sobre configuração que você já conhece e espera. E o Spring AI oferece suporte à observabilidade com o Actuator do Spring Boot e o projeto Micrometer. Ele também funciona bem com o GraalVM e threads virtuais, permitindo que você crie aplicativos de IA super rápidos, eficientes e escaláveis.

Por que o Elasticsearch?

O Elasticsearch é um mecanismo de busca de texto completo, você provavelmente já sabe disso. Então, por que estamos usando isso para este projeto? Bem, também é uma loja de vetores! E uma muito boa, por sinal, onde os dados ficam lado a lado com o texto completo. Outras vantagens notáveis:

  • Super fácil de configurar
  • Código aberto
  • Escalável horizontalmente
  • A maior parte dos dados de formato livre da sua organização provavelmente já reside em um cluster Elasticsearch.
  • Funcionalidade completa de mecanismo de busca
  • Totalmente integrado ao Spring AI!

Considerando tudo, o Elasticsearch atende a todos os requisitos para ser um excelente banco de dados de vetores, então vamos configurá-lo e começar a construir nossa aplicação!

Introdução ao Elasticsearch

Precisaremos tanto do Elasticsearch quanto do Kibana, o console de interface do usuário que você usará para interagir com os dados hospedados no banco de dados.

Você pode experimentar tudo em sua máquina local graças aos recursos das imagens Docker e à página inicial da Elastic.co. Acesse essa página, role a tela para baixo até encontrar o comando curl , execute-o e redirecione a saída diretamente para o seu shell:

Isso simplesmente baixará e configurará as imagens Docker para Elasticsearch e Kibana e, após alguns minutos, você as terá em execução em sua máquina local, com as credenciais de conexão já definidas.

Você também tem duas URLs diferentes que pode usar para interagir com sua instância do Elasticsearch. Faça como indicado e acesse http://localhost:5601 pelo seu navegador.

Observe também o nome de usuário elastic e a senha impressos no console: você precisará deles para fazer login (no exemplo de saída acima, eles são respectivamente elastic e w1GB15uQ).

Reunindo o aplicativo

Acesse a página do Spring Initializr e gere um novo projeto Spring AI com as seguintes dependências:

  • Elasticsearch Vector Store
  • Spring Boot Actuator
  • GraalVM
  • OpenAI
  • Web

Certifique-se de escolher a versão mais recente do Java (idealmente Java 24 - na data desta publicação - ou posterior) e a ferramenta de compilação de sua preferência. Neste exemplo, estamos usando o Apache Maven.

Clique em Generate e depois descompacte o projeto e importe-o para o seu IDE de escolha. (Estamos usando o IntelliJ IDEA.)

Em primeiro lugar, vamos especificar os detalhes de conexão para sua aplicação Spring Boot. Em application.properties, escreva o seguinte:

Também utilizaremos a capacidade de armazenamento vetorial do Spring AI para inicializar o que for necessário no lado do Elasticsearch em termos de estruturas de dados, portanto, especifique:

Nesta demonstração, usaremos o OpenAI , especificamente o Modelo de Incorporação e o Modelo de Chat (sinta-se à vontade para usar o serviço de sua preferência, desde que o Spring AI o suporte).

O modelo de incorporação é necessário para criar representações dos dados antes de armazená-los no Elasticsearch. Para que o OpenAI funcione, precisamos especificar o API key:

Você pode defini-la como uma variável de ambiente, como SPRING_AI_OPENAI_API_KEY para evitar armazenar a credencial no seu código-fonte.

Vamos enviar arquivos, então certifique-se de personalizar a quantidade de dados que pode ser enviada para o contêiner de servlet:

Estamos quase lá! Antes de começarmos a escrever o código, vamos ter uma prévia de como isso vai funcionar.

Em nossa máquina, baixamos o seguinte arquivo (uma lista de regras para um jogo de tabuleiro), renomeamos para test.pdf e o colocamos em ~/Downloads/test.pdf.

O arquivo será enviado para o endpoint /rag/ingest (substitua o caminho de acordo com sua configuração local):

Isso pode levar alguns segundos…

Nos bastidores, os dados são enviados para a OpenAI, que cria representações vetoriais (embeddings) dos dados; esses dados são então gravados no Elasticsearch, tanto os vetores quanto o texto original.

É nesses dados, juntamente com todos os elementos incorporados neles, que a mágica acontece. Podemos então consultar o Elasticsearch usando a interface VectorStore .

O fluxo completo se parece com isto:

  • O cliente HTTP carrega o PDF de sua escolha para a aplicação Spring.
  • O Spring AI se encarrega da extração de texto do nosso PDF e divide cada página em blocos de 800 caracteres.
  • A OpenAI gera a representação vetorial para cada segmento.
  • Tanto o texto fragmentado quanto o arquivo incorporado são armazenados no Elasticsearch.

Por fim, vamos fazer uma consulta:

E obteremos uma resposta relevante:

Que legal! Como tudo isso funciona?

  • O cliente HTTP envia a pergunta para a aplicação Spring.
  • O Spring AI obtém a representação vetorial da pergunta da OpenAI.
  • Com essa incorporação, ele busca documentos semelhantes nos blocos armazenados do Elasticsearch e recupera os documentos mais similares.
  • A Spring AI envia então a pergunta e o contexto obtido para a OpenAI para gerar uma resposta LLM.
  • Por fim, retorna a resposta gerada e uma referência ao contexto recuperado.

Vamos analisar o código Java para ver como ele realmente funciona.

Primeiramente, a classe Main : é uma classe principal padrão para qualquer aplicação Spring Boot.

Nada para ver ali. Continuando…

Em seguida, um controlador HTTP básico:

O controlador está simplesmente chamando um serviço que criamos para lidar com a ingestão de arquivos e sua gravação no armazenamento vetorial do Elasticsearch, e então facilitar consultas nesse mesmo armazenamento vetorial.

Vamos analisar o serviço:

Este código lida com toda a ingestão: dado um Resource do Spring Framework, que é um contêiner em torno de bytes, lemos os dados do PDF (presumido ser um arquivo .PDF - certifique-se de validar isso antes de aceitar entradas arbitrárias!) usando o PagePdfDocumentReader do Spring AI e, em seguida, tokenizamos usando o TokenTextSplitter do Spring AI, finalmente adicionando os List<Document>resultantes à implementação do VectorStore , ElasticsearchVectorStore.

Você pode confirmar isso usando o Kibana: depois de enviar um arquivo para o endpoint /rag/ingest , abra seu navegador em localhost:5601 e no menu lateral à esquerda navegue até Dev Tools. Ali você pode enviar consultas para interagir com os dados na instância do Elasticsearch.

Faça uma consulta como esta:

Agora vem a parte divertida: como recuperamos esses dados em resposta às consultas dos usuários?

Aqui está uma primeira tentativa de implementação da consulta, em um método chamado directRag.

O código é bastante simples, mas vamos dividi-lo em várias etapas:

  1. Use VectorStore para realizar uma pesquisa de similaridade.
  2. Dados todos os resultados, obtenha os Documentsubjacentes do Spring AI e extraia seu texto, concatenando-os em um único resultado.
  3. Envie os resultados de VectorStore para o modelo, juntamente com uma instrução para o modelo saber o que fazer com eles e a pergunta do usuário. Aguarde a resposta e retorne-a.

Isto é RAG - geração aumentada de recuperação. A ideia é usar dados de um repositório vetorial para orientar o processamento e a análise realizados pelo modelo. Agora que você já sabe como fazer, esperemos que nunca precise fazer! Não assim: os Advisors da Spring AI estão aqui para simplificar ainda mais esse processo.

O Advisors permite pré-processar e pós-processar uma solicitação para um determinado modelo, além de fornecer uma camada de abstração entre seu aplicativo e o armazenamento de vetores. Adicione a seguinte dependência à sua compilação:

Adicione outro método chamado advisedRag(String question) à classe:

Toda a lógica do padrão RAG está encapsulada em QuestionAnswerAdvisor. Todo o resto é exatamente como qualquer solicitação a um ChatModel seria! Legal!

E você pode obter o código completo no GitHub.

Conclusão

Nesta demonstração, usamos imagens Docker e fizemos tudo em nossa máquina local, mas o objetivo aqui é construir sistemas e serviços de IA prontos para produção. Existem várias coisas que você pode fazer para tornar isso realidade.

Em primeiro lugar, você pode adicionar o Spring Boot Actuator para monitorar o consumo de tokens. Os tokens são uma representação da complexidade (e, às vezes, do custo em dólares) de uma determinada solicitação ao modelo.

Você já tem o Spring Boot Actuator no classpath, então basta especificar as seguintes propriedades para exibir todas as métricas (capturadas pelo magnífico projeto Micrometer.io ):

Reinicie o aplicativo. Faça uma consulta e depois acesse: http://localhost:8080/actuator/metrics. Pesquise por “token” e você verá informações sobre os tokens que estão sendo usados pelo aplicativo. Fique de olho nisso. Você também pode usar a integração do Micrometer com o Elasticsearch para enviar essas métricas e fazer com que o Elasticsearch funcione como seu banco de dados de séries temporais preferido!

Você deve então considerar que, cada vez que fazemos uma solicitação a um armazenamento de dados como o Elasticsearch, ou ao OpenAI, ou a outros serviços de rede, estamos realizando operações de entrada/saída (E/S) e, frequentemente, essas operações de E/S bloqueiam os threads nos quais são executadas. O Java 21 e versões posteriores incluem threads virtuais não bloqueantes que melhoram drasticamente a escalabilidade. Ative-o com:

E, por fim, você vai querer hospedar seu aplicativo e seus dados em um local onde eles possam prosperar e ser escaláveis. Temos certeza de que você provavelmente já pensou em onde executar seu aplicativo, mas onde você hospedará seus dados? Podemos recomendar a Elastic Cloud? É seguro, privado, escalável e repleto de recursos. Nossa parte favorita? Se quiser, você pode adquirir a edição Serverless, onde o Elasticsearch é que controla o pager, e não você!

Conteúdo relacionado

Pronto para criar buscas de última geração?

Uma pesquisa suficientemente avançada não se consegue apenas com o esforço de uma só pessoa. O Elasticsearch é impulsionado por cientistas de dados, especialistas em operações de aprendizado de máquina, engenheiros e muitos outros que são tão apaixonados por buscas quanto você. Vamos nos conectar e trabalhar juntos para construir a experiência de busca mágica que lhe trará os resultados desejados.

Experimente você mesmo(a)