Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Learn why MongoDB was selected as a leader in the 2024 Gartner® Magic Quadrant™
Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Idiomaschevron-right
Javachevron-right

Geração aumentada de recuperação com MongoDB e Spring AI: Trazendo AI para seus aplicativos Java

Tim Kelly6 min read • Published Sep 23, 2024 • Updated Sep 23, 2024
SpringIAJava
APLICATIVO COMPLETO
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
AI isso, AI isso. Bem, o que a AI pode realmente fazer pormim? Neste tutorial, vamos discutir como podemos aproveitar nossos próprios dados para aproveitar ao máximo a AI generativa.
E é aqui que entra ageração aumentada de recuperação (RAG). Ela usa a AI onde deve estar — recuperando as informações certas e gerando respostas inteligentes e sensíveis ao contexto. Neste tutorial, vamos construir um aplicativo RAG usando Spring Boot, MongoDB Atlase OpenAI. O código completo está disponível em Github.

O que é geração aumentada de recuperação?

RAG permite que você use dados que não estavam disponíveis para treinar um modelo de AI para preencher seu prompt e, em seguida, usar esses dados para complementar a resposta do modelo de linguagem grande (LLM).
Os LLMs são um tipo de inteligência artificial (AI) que pode gerar e compreender dados. Eles são formados em massivos conjuntos de dados e podem ser usados para responder às suas perguntas de forma informativa.
Embora os LLMs sejam muito poderosos, eles têm algumas limitações. Uma limitação é que nem sempre são precisos ou atualizados. Isso ocorre porque os LLMs são formados em dados que desde então se tornaram desatualizados, incompletos ou não têm conhecimento proprietário sobre um caso de uso ou domínio específico.
Se você tiver dados que precisam permanecer internos por motivos de segurança de dados, ou mesmo apenas perguntas sobre dados mais atualizados, o RAG pode ajudá-lo.
O RAG consiste em três componentes principais:
  1. Seu LLM pré-treinado: é isso que gerará a resposta - OpenAI, em nosso caso.
  2. Pesquisa vetorial (pesquisa semântica): é assim que recuperamos documentos relevantes de nosso banco de banco de dados MongoDB .
  3. Incorporações vetoriais: uma representação numérica de nossos dados captura o significado semântica de nossos dados.
Um grande modelo de linguagem que está sendo útil em uma aplicação de IA generativa, aproveitando a geração aumentada de recuperação.

Pré-requisitos

Antes de iniciar este tutorial, verifique se você tem o seguinte instalado e configurado:
  1. Java 21 ou superior.
  2. Maven ou Gradle (para gerenciar dependências): Usamos o Maven para este tutorial.
  3. MongoDB Atlas: você precisará de um cluster do MongoDB Atlas.
    • É necessário um cluster mínimo de10+ para usar o armazenamento de vetores do Spring AI MongoDB , pois ele cria o índice de pesquisa em nosso banco de dados de dados programaticamente.
  4. Chave de API OpenAI: inscreva-se no OpenAI e obtenha uma chave de API.
    • Outros modelos estão disponíveis, mas este tutorial usa OpenAI.

Preparando seu projeto

Inicialização do Spring

Para inicializar o projeto:
  1. Configure os metadados do projeto :
    • Grupo: com.mongodb
    • Artefato: RagApp
    • Dependencies:
      • Spring Web
      • Banco de dados vetorial do MongoDB Atlas
      • Open AI
  2. Baixe o projeto e abra-o no IDE de sua preferência. Um grande modelo de linguagem que está sendo útil em uma aplicação de IA generativa, aproveitando a geração aumentada de recuperação.

Configuração

Antes de fazer qualquer coisa, Go ao nosso arquivopom.xmle verificar se a versão da Spring AI é <spring-ai.version>1.0.0-SNAPSHOT</spring-ai.version>. Talvez precisemos alterá-lo para isso, dependendo da versão do Spring que estamos usando.
A configuração deste projeto envolve a configuração de dois componentes principais:
  • O EmbeddingModel usando OpenAI para gerar incorporações para documentos.
  • Um MongoDBAtlasVectorStore para armazenar e gerenciar vetores de documento para pesquisas de similaridade.
Precisamos configurar nosso projeto para se conectar ao OpenAI e ao MongoDB Atlas adicionando várias propriedades ao arquivoapplication.properties, junto com as credenciais necessárias.
1spring.application.name=RagApp
2
3spring.ai.openai.api-key=<Your-API-Key>
4spring.ai.openai.chat.options.model=gpt-4o
5
6spring.ai.vectorstore.mongodb.initialize-schema=true
7
8spring.data.mongodb.uri=<Your-Connection-URI>
9spring.data.mongodb.database=rag
Você verá que aqui temos initialize.schema definido como True. Isso cria automaticamente o índice em nossa coleção, usando o Spring AI. Se você estiver executando um cluster gratuito, isto não estará disponível. Uma solução alternativa para isso é criá-lo manualmente, o que você pode aprender a fazer na documentação do MongoDB.
Crie um pacote de configuração e adicione um Config.java para trabalhar. Veja como a configuração é feita na classeConfig:
1import org.springframework.ai.embedding.EmbeddingModel;
2import org.springframework.ai.openai.OpenAiEmbeddingModel;
3import org.springframework.ai.openai.api.OpenAiApi;
4import org.springframework.ai.vectorstore.MongoDBAtlasVectorStore;
5import org.springframework.ai.vectorstore.VectorStore;
6import org.springframework.beans.factory.annotation.Value;
7import org.springframework.context.annotation.Bean;
8import org.springframework.context.annotation.Configuration;
9import org.springframework.data.mongodb.core.MongoTemplate;
10
11@Configuration
12public class Config {
13
14 @Value("${spring.ai.openai.api-key}")
15 private String openAiKey;
16
17 @Bean
18 public EmbeddingModel embeddingModel() {
19 return new OpenAiEmbeddingModel(new OpenAiApi(openAiKey));
20 }
21
22 @Bean
23 public VectorStore mongodbVectorStore(MongoTemplate mongoTemplate, EmbeddingModel embeddingModel) {
24 return new MongoDBAtlasVectorStore(mongoTemplate, embeddingModel,
25 MongoDBAtlasVectorStore.MongoDBVectorStoreConfig.builder().build(), true);
26 }
27
28}
Essa classe inicializa a conexão com a API OpenAI e configura o armazenamento de vetores baseado no MongoDB para armazenar incorporações de documento .

Incorporando os dados

Para este tutorial, estamos usando o conjunto de dadosMongoDB/devcenter-argumentos, disponível em Abraçar a Face. Este conjunto de dados consiste em artigos do MongoDB Developer Center. Em nossos recursos, crie um diretório chamado Docs e adicione nosso arquivo para ler.
Para incorporar e armazenar dados no armazenamento de vetores, usaremos um serviço que lê documentos de um arquivo JSON, os converte em incorporações e os armazena no armazenamento de vetores do MongoDB Atlas . Isso é feito usando o DocsLoaderService.java que criaremos em um pacoteservice :
1package com.mongodb.RagApp.service;
2
3import com.fasterxml.jackson.databind.ObjectMapper;
4import org.springframework.ai.document.Document;
5import org.springframework.ai.vectorstore.VectorStore;
6import org.springframework.beans.factory.annotation.Autowired;
7import org.springframework.core.io.ClassPathResource;
8import org.springframework.stereotype.Service;
9
10import java.io.BufferedReader;
11import java.io.InputStream;
12import java.io.InputStreamReader;
13import java.util.ArrayList;
14import java.util.List;
15import java.util.Map;
16
17@Service
18public class DocsLoaderService {
19
20 private static final int MAX_TOKENS_PER_CHUNK = 2000;
21 private final VectorStore vectorStore;
22 private final ObjectMapper objectMapper;
23
24 @Autowired
25 public DocsLoaderService(VectorStore vectorStore, ObjectMapper objectMapper) {
26 this.vectorStore = vectorStore;
27 this.objectMapper = objectMapper;
28 }
29
30 public String loadDocs() {
31 try (InputStream inputStream = new ClassPathResource("docs/devcenter-content-snapshot.2024-05-20.json").getInputStream();
32 BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
33
34 List<Document> documents = new ArrayList<>();
35 String line;
36
37 while ((line = reader.readLine()) != null) {
38 Map<String, Object> jsonDoc = objectMapper.readValue(line, Map.class);
39 String content = (String) jsonDoc.get("body");
40
41 // Split the content into smaller chunks if it exceeds the token limit
42 List<String> chunks = splitIntoChunks(content, MAX_TOKENS_PER_CHUNK);
43
44 // Create a Document for each chunk and add it to the list
45 for (String chunk : chunks) {
46 Document document = createDocument(jsonDoc, chunk);
47 documents.add(document);
48 }
49 // Add documents in batches to avoid memory overload
50 if (documents.size() >= 100) {
51 vectorStore.add(documents);
52 documents.clear();
53 }
54 }
55 if (!documents.isEmpty()) {
56 vectorStore.add(documents);
57 }
58
59 return "All documents added successfully!";
60 } catch (Exception e) {
61 return "An error occurred while adding documents: " + e.getMessage();
62 }
63 }
64
65 private Document createDocument(Map<String, Object> jsonMap, String content) {
66 Map<String, Object> metadata = (Map<String, Object>) jsonMap.get("metadata");
67
68 metadata.putIfAbsent("sourceName", jsonMap.get("sourceName"));
69 metadata.putIfAbsent("url", jsonMap.get("url"));
70 metadata.putIfAbsent("action", jsonMap.get("action"));
71 metadata.putIfAbsent("format", jsonMap.get("format"));
72 metadata.putIfAbsent("updated", jsonMap.get("updated"));
73
74 return new Document(content, metadata);
75 }
76
77 private List<String> splitIntoChunks(String content, int maxTokens) {
78 List<String> chunks = new ArrayList<>();
79 String[] words = content.split("\\s+");
80 StringBuilder chunk = new StringBuilder();
81 int tokenCount = 0;
82
83 for (String word : words) {
84 // Estimate token count for the word (approximated by character length for simplicity)
85 int wordTokens = word.length() / 4; // Rough estimate: 1 token = ~4 characters
86 if (tokenCount + wordTokens > maxTokens) {
87 chunks.add(chunk.toString());
88 chunk.setLength(0); // Clear the buffer
89 tokenCount = 0;
90 }
91 chunk.append(word).append(" ");
92 tokenCount += wordTokens;
93 }
94 if (chunk.length() > 0) {
95 chunks.add(chunk.toString());
96 }
97 return chunks;
98 }
99}
Esse serviço lê um arquivo JSON, processa cada documento e o armazena no MongoDB, junto com um vetor incorporado de nosso conteúdo.
Agora, essa é uma abordagem muito simplista de chunking (divisão de documentos grandes em partes menores que permanecem dentro do limite de token e os processam separadamente) implementada. Isso ocorre porque o Go tem um limite de token, então alguns de nossos documentos são grandes demais para serem incorporados de uma só vez. Isso é bom para testes, mas se você estiver mudando para a produção, faça sua pesquisa e decida sua melhor maneira de lidar com esses documentos grandes.
Chame esse método como quiser, mas criei um DocsLoaderControllersimples no meu pacotecontrollerpara teste.
1import com.mongodb.RagApp.service.DocsLoaderService;
2import org.springframework.web.bind.annotation.GetMapping;
3import org.springframework.web.bind.annotation.RequestMapping;
4import org.springframework.web.bind.annotation.RestController;
5
6@RestController
7@RequestMapping("/api/docs")
8public class DocsLoaderController {
9
10 private DocsLoaderService docsLoaderService;
11
12 public DocsLoaderController(DocsLoaderService docsLoaderService) {
13 this.docsLoaderService = docsLoaderService;
14 }
15
16 @GetMapping("/load")
17 public String loadDocuments() {
18 return docsLoaderService.loadDocs();
19 }
20
21}

Recuperando e aumentando a mencionada geração

Depois que os dados forem incorporados e armazenados, podemos recuperá-los por meio de uma API que usa uma pesquisa vetorial para retornar resultados relevantes. A classeRagController é responsável por isso:
1import org.springframework.ai.chat.client.ChatClient;
2import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;
3import org.springframework.ai.vectorstore.SearchRequest;
4import org.springframework.ai.vectorstore.VectorStore;
5import org.springframework.web.bind.annotation.CrossOrigin;
6import org.springframework.web.bind.annotation.GetMapping;
7import org.springframework.web.bind.annotation.RequestParam;
8import org.springframework.web.bind.annotation.RestController;
9
10@RestController
11public class RagController {
12
13 private final ChatClient chatClient;
14
15 public RagController(ChatClient.Builder builder, VectorStore vectorStore) {
16 this.chatClient = builder
17 .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()))
18 .build();
19 }
20
21 @GetMapping("/question")
22 public String question(@RequestParam(value = "message", defaultValue = "How to analyze time-series data with Python and MongoDB?") String message) {
23 return chatClient.prompt()
24 .user(message)
25 .call()
26 .content();
27 }
28}
Está passando um pouco aqui. Vejamos o ChatClient. Ele oferece uma API para comunicação com nosso modelo de AI .
O modelo de AI processa dois tipos de mensagens: 1. Mensagens do usuário, que são entradas diretas do usuário. 2. Mensagens do sistema, que são geradas pelo sistema para orientar a conversa.
Para a mensagem do sistema, estamos usando o padrão do QuestionsAnswerAdvisor:
1private static final String DEFAULT_USER_TEXT_ADVISE = """
2 Context information is below.
3 ---------------------
4 {question_answer_context}
5 ---------------------
6 Given the context and provided history information and not prior knowledge,
7 reply to the user comment. If the answer is not in the context, inform
8 the user that you can't answer the question.
9 """;
Mas podemos editar esta mensagem e adaptá-la às nossas necessidades. Também há opções de prompt que podem ser especificadas, como a configuração de temperatura que controla a aleatoriedade ou a Criatividade da saída gerada. Você pode descobrir mais na documentação da Spring.
O endpoint/question permite que os usuários façam perguntas e recupera respostas do armazenamento de vetores pesquisando nos documentos incorporados semanticamente e enviando-os para o LLM com nosso contexto.

Testando a implementação

Para testar nossa implementação:
  1. Inicie o aplicação Spring Boot .
  2. Navegue até http://localhost:8080/api/docs/load para carregar documentos no armazenamento de vetores.
  3. Use http://localhost:8080/question?message=Your question here para testar a funcionalidade de pergunta-resposta.
Por exemplo, tente perguntar:
http://localhost:8080/question?message=How to analyze time-series data with Python and MongoDB?Explain the steps
Devemos receber uma resposta relevante do aplicativo RAG, formada a partir dos dados do documento incorporado e do LLM.

Conclusão

Neste projeto, integramos um geração aumentada de recuperação (RAG) usando MongoDB, incorporações OpenAI e Spring Boot. O sistema pode incorporar grandes quantidades de dados de documento e responder a perguntas, aproveitando pesquisas de similaridade vetorial de um armazenamento vetorial do MongoDB Atlas .
Em seguida, saiba mais sobre o que você pode fazer com Java e MongoDB. Você pode aproveitar o Armazenamento de mídia contínua: Integrando o Armazenamento de Blobs do Azure e o MongoDB com o Spring Boot. Ou acesse os fóruns da comunidade e veja o que outras pessoas estão fazendo com o MongoDB.
Principais comentários nos fóruns
Ainda não há comentários sobre este artigo.
Iniciar a conversa

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Início rápido

Java - Change Streams


Oct 01, 2024 | 10 min read
Tutorial

Introdução à paginação de dados com o Quarkus e o MongoDB: um tutorial abrangente


Apr 25, 2024 | 7 min read
Artigo

Construindo um aplicativo Quartokus para realizar a Vector Search do MongoDB


Oct 07, 2024 | 9 min read
Tutorial

Usando a autenticação AWS IAM com o conector MongoDB para Apache Kafka


Jul 01, 2024 | 4 min read
Sumário