RAG com Atlas Vector Search, LangChain e OpenAI
Harshad Dhavale10 min read • Published Sep 18, 2024 • Updated Sep 18, 2024
Avalie esse Tutorial
Com todos os desenvolvimentos recentes (e frenesi!) em torno da IA generativa, os LLMs ganharam muita atenção. No entanto, há também outra tendência emergente que muitos desconhecem: o aumento do armazenamento vetorial. O armazenamento vetorial ou bancos de dados vetoriais desempenham uma função importante na criação das aplicações de LLM. Isso coloca o Atlas Vector Search no setor de armazenamento vetorial com muitos concorrentes.
O objetivo deste tutorial é fornecer uma visão geral dos principais conceitos do Atlas Vector Search como um armazenamento de vetor, e LLMs e suas limitações. Também analisaremos um paradigma futuro que está ganhando rápida adoção, chamado "geração aumentada de recuperação" (RAG). Discutiremos ainda brevemente o framework LangChain, os modelos OpenAI e o Gradio. Por fim, vamos unir tudo usando esses conceitos, arquitetura e componentes em um aplicativo do mundo real. Ao final deste tutorial, os leitores sairão com uma compreensão de alto nível dos conceitos mencionados acima e com mais admiração pelo Atlas Vector Search!
Os grandes modelos de linguagem (LLMs) são uma classe de modelos de rede neural profunda que foram treinados em grandes quantidades de dados em texto, o que permite que eles entendam e gerem textos semelhantes aos de humanos.Os LLMs revolucionaram o campo do processamento de linguagem natural, mas eles vêm com certas limitações:
- Alucinações: os LLMs às vezes geram informações imprecisas ou sem fundamentos, um fenômeno conhecido como "hallucinations.".
- Dados obsoletos: os LLMs são treinados em um conjunto de dados estático que são atuais somente até um determinado momento. Isso significa que eles podem não ter informações sobre eventos ou desenvolvimentos que ocorreram depois que os dados de treinamento foram coletados.
- Sem acesso aos dados locais dos usuários: os LLMs não têm acesso aos dados locais ou bancos de dados pessoais de um usuário.Eles só podem gerar respostas com base no conhecimento em que foram treinados, o que pode limitar sua capacidade de fornecer respostas personalizadas ou específicas do contexto.
- Limites de token: os LLMs têm um limite máximo do número de tokens (pedaços de texto) que podem processar em uma única interação. Os tokens nos LLMs são as unidades básicas de texto que os modelos processam e geram. Eles podem representar caracteres individuais, palavras, subpalavras ou unidades linguísticas ainda maiores. Por exemplo, o limite de token para o gpt-3.5-turbo da OpenAI é 4096.
Geração aumentada de recuperação (RAG)
A arquitetura de geração aumentada de recuperação (RAG) foi desenvolvida para resolver esses problemas. A RAG usa pesquisa vetorial para recuperar documentos relevantes com base na query de entrada. Em seguida, fornece esses documentos recuperados como contexto para o LLM para ajudar a gerar uma resposta mais informada e precisa. Ou seja, em vez de gerar respostas a partir de padrões aprendidos durante o treinamento, a RAG usa os documentos relevantes recuperados para ajudar a gerar uma resposta mais informada e precisa. Isso ajuda a resolver as limitações citadas acima nos LLMs. Especificamente:
- Os RAGs minimizam as alucinações ao fundamentar as respostas do modelo em informações factuais.
- Ao recuperar informações de fontes atualizadas, a RAG garante que as respostas do modelo reflitam as informações mais atuais e precisas disponíveis.
- Embora a RAG não forneça acesso de LLMs direto aos dados locais de um usuário, ele permite que utilize bancos de dados externos ou bases de conhecimento que podem ser atualizadas com informações específicas do usuário.
- Além disso, embora o RAG não aumente o limite de tokens de um LLM, ele torna o uso de tokens pelo modelo mais eficiente, recuperando apenas os documentos mais relevantes para gerar uma resposta.
Este tutorial demonstra como a arquitetura RAG pode ser aproveitada com o Atlas Vector Search para criar uma aplicação de perguntas e respostas com base nos seus próprios dados.
A arquitetura da aplicação é:
Observação: para simplificar, o componente da Gradio usado para gerar a interface da web do aplicativo não foi mostrado neste diagrama de arquitetura.
Na seção a seguir, falaremos brevemente sobre os diferentes frameworks, modelos e componentes que usaremos nesse tutorial enquanto tentamos entender as nuances de alguns desses componentes.
O Atlas Vector Search do MongoDB permite que você realize pesquisas de similaridade semântica em seus dados, que podem ser integrados a LLMs para construir aplicativos baseados em IA. Dados de várias fontes e em diferentes formatos podem ser representados numericamente como incorporações vetoriais. O Atlas Vector Search permite que você armazene incorporações vetoriais ao lado de seus dados de origem e metadados, aproveitando o poder do document model. Essas incorporações vetoriais podem então ser alvo de uma query usando um pipeline de agregação para realizar uma pesquisa rápida de similaridade semântica nos dados, usando um algoritmo aproximado dos vizinhos mais próximos.
O LangChain é uma estrutura projetada para simplificar a criação de aplicações de LLM. Ele fornece uma interface padrão para cadeias, integrações com outras ferramentas e cadeias de ponta a ponta para aplicações comuns. Isso permite que os desenvolvedores de IA criem aplicações de LLM que aproveitam fontes de dados externas (por exemplo, fontes de dados privadas).
Algumas coisas importantes para serem observadas sobre o LangChain:
- No LangChain, uma "chain" é uma sequência de componentes que podem ser combinados para resolver um problema específico/realizar uma tarefa específica.
- Uma cadeia pode incluir componentes e módulos como wrappers para LLMs e armazenamento vetorial, modelos de prompt, carregadores, módulos de divisão e fragmentação de texto, recuperadores, etc. Componentes diferentes podem ser alocados juntos.
- A cadeia pega a entrada do usuário e a processa por meio de cada componente da sequência.
Essa abordagem modular de alocar diferentes componentes ou módulos simplifica o desenvolvimento complexo das aplicações, debugging e manutenção. - O LangChain é um projeto de código aberto lançado em outubro de 2022. O projeto ganhou popularidade rapidamente. Ele está sendo muito aceito, incluindo contribuições de centenas de desenvolvedores no GitHub, e tem um número cada vez maior de integrações com sistemas externos. Ele está ganhando ampla adoção e evoluindo rapidamente para atender às necessidades da sua base de usuários crescente.
A Gradio é uma biblioteca Python de código aberto usada para criar uma interface da web para aplicativos de ML e ciência de dados.
A OpenAI é uma empresa de pesquisa de IA e está entre as empresas que têm sido fundamentais no avanço dos LLMs e da IA generativa. A OpenAI não só desenvolveu os Grandes Modelos de Linguagem que estão ganhando uma adoção rápida, mas também desenvolveu outros modelos que executam uma variedade de outras tarefas, como vetorização de texto, geração de imagens com base em prompt de linguagem, conversão de áudio para texto, etc.
Quando alguém se refere a “using OpenAI” em seu aplicativo, alguma desambiguação é necessária porque não há um modelo único que possa ser usado para todos os casos de uso. Em geral, modelos diferentes servem a finalidades diferentes. Por exemplo, os modelos GPT da OpenAI são modelos de idioma que entendem e geram idiomas, o modelo DALL-E pode criar imagens com base em um prompt de texto, o Writer é um modelo que pode converter áudio em texto, etc.
Neste tutorial, usaremos o modelo de incorporação e o modelo de linguagem da OpenAI. Portanto, é importante entender a distinção entre os dois:
Componentes principais
Observação: as etapas de configuração, bem como os scripts Python compartilhados abaixo, podem ser baixados do repositório do Github.
- Instale os seguintes pacotes:
1 pip3 install langchain pymongo bs4 openai tiktoken gradio requests lxml argparse unstructured - Crie a chave API da OpenAI. É necessário ter uma conta paga na OpenAI, com créditos suficientes. As solicitações da API da OpenAI param de funcionar se o saldo atingir $0.
- Salve a chave API da OpenAI no arquivo key_param.py. Você pode escolher o nome do arquivo.
- Como opção, também salve o MongoDB URI no arquivo.
- Crie dois scripts do Python:
- load_data.py: esse roteiro será usado para carregar seus documentos, ingerir o texto e incorporar os vetores em uma coleção MongoDB.
- extract_information.py: este roteiro irá gerar a interface de usuário e permitirá que você execute as perguntas e respostas nos seus dados, utilizando Atlas Vector Search e OpenAI.
- Importe as seguintes bibliotecas:
1 from pymongo import MongoClient 2 from langchain.embeddings.openai import OpenAIEmbeddings 3 from langchain.vectorstores import MongoDBAtlasVectorSearch 4 from langchain.document_loaders import DirectoryLoader 5 from langchain.llms import OpenAI 6 from langchain.chains import RetrievalQA 7 import gradio as gr 8 from gradio.themes.base import Base 9 import key_param
Documentos de amostra
Neste tutorial, carregaremos três arquivos de texto de um diretório usando o DirectoryLoader. Esses arquivos devem ser salvos em um diretório chamado sample_files. O conteúdo desses arquivos de texto é o seguinte (nenhum desses textos contém PII ou CI):
- Log_example.txt
1 2023-08-16T16:43:06.537+0000 I MONGOT [63528f5c2c4f78275d37902d-f5-u6-a0 BufferlessChangeStreamApplier] [63528f5c2c4f78275d37902d-f5-u6-a0 BufferlessChangeStreamApplier] Starting change stream from opTime=Timestamp{value=7267960339944178238, seconds=1692203884, inc=574}2023-08-16T16:43:06.543+0000 W MONGOT [63528f5c2c4f78275d37902d-f5-u6-a0 BufferlessChangeStreamApplier] [c.x.m.r.m.common.SchedulerQueue] cancelling queue batches for 63528f5c2c4f78275d37902d-f5-u6-a02023-08-16T16:43:06.544+0000 E MONGOT [63528f5c2c4f78275d37902d-f5-u6-a0 InitialSyncManager] [BufferlessInitialSyncManager 63528f5c2c4f78275d37902d-f5-u6-a0] Caught exception waiting for change stream events to be applied. Shutting down.com.xgen.mongot.replication.mongodb.common.InitialSyncException: com.mongodb.MongoCommandException: Command failed with error 286 (ChangeStreamHistoryLost): 'Executor error during getMore :: caused by :: Resume of change stream was not possible, as the resume point may no longer be in the oplog.' on server atlas-6keegs-shard-00-01.4bvxy.mongodb.net:27017.2023-08-16T16:43:06.545+0000 I MONGOT [indexing-lifecycle-3] [63528f5c2c4f78275d37902d-f5-u6-a0 ReplicationIndexManager] Transitioning from INITIAL_SYNC to INITIAL_SYNC_BACKOFF.2023-08-16T16:43:18.068+0000 I MONGOT [config-monitor] [c.x.m.config.provider.mms.ConfCaller] Conf call response has not changed. Last update date: 2023-08-16T16:43:18Z.2023-08-16T16:43:36.545+0000 I MONGOT [indexing-lifecycle-2] [63528f5c2c4f78275d37902d-f5-u6-a0 ReplicationIndexManager] Transitioning from INITIAL_SYNC_BACKOFF to INITIAL_SYNC. - Chat_conversation.txt
1 Alfred: Hi, can you explain to me how compression works in MongoDB? Bruce: Sure! MongoDB supports compression of data at rest. It uses either zlib or snappy compression algorithms at the collection level. When data is written, MongoDB compresses and stores it compressed. When data is read, MongoDB uncompresses it before returning it. Compression reduces storage space requirements. Alfred: Interesting, that's helpful to know. Can you also tell me how indexes are stored in MongoDB? Bruce: MongoDB indexes are stored in B-trees. The internal nodes of the B-trees contain keys that point to children nodes or leaf nodes. The leaf nodes contain references to the actual documents stored in the collection. Indexes are stored in memory and also written to disk. The in-memory B-trees provide fast access for queries using the index.Alfred: Ok that makes sense. Does MongoDB compress the indexes as well?Bruce: Yes, MongoDB also compresses the index data using prefix compression. This compresses common prefixes in the index keys to save space. However, the compression is lightweight and focused on performance vs storage space. Index compression is enabled by default.Alfred: Great, that's really helpful context on how indexes are handled. One last question - when I query on a non-indexed field, how does MongoDB actually perform the scanning?Bruce: MongoDB performs a collection scan if a query does not use an index. It will scan every document in the collection in memory and on disk to select the documents that match the query. This can be resource intensive for large collections without indexes, so indexing improves query performance.Alfred: Thank you for the detailed explanations Bruce, I really appreciate you taking the time to walk through how compression and indexes work under the hood in MongoDB. Very helpful!Bruce: You're very welcome! I'm glad I could explain the technical details clearly. Feel free to reach out if you have any other MongoDB questions. - aerodynamics.txt
1 Boundary layer control, achieved using suction or blowing methods, can significantly reduce the aerodynamic drag on an aircraft's wing surface.The yaw angle of an aircraft, indicative of its side-to-side motion, is crucial for stability and is controlled primarily by the rudder.With advancements in computational fluid dynamics (CFD), engineers can accurately predict the turbulent airflow patterns around complex aircraft geometries, optimizing their design for better performance.
Carregando os documentos
- Defina os nomes de URI, banco de dados e coleção do MongoDB:
1 client = MongoClient(key_param.MONGO_URI) 2 dbName = "langchain_demo" 3 collectionName = "collection_of_text_blobs" 4 collection = client[dbName][collectionName] - Inicialize o DirectoryLoader:
1 loader = DirectoryLoader( './sample_files', glob="./*.txt", show_progress=True) 2 data = loader.load() - Defina o Modelo de incorporação da OpenAI que usaremos para os dados de origem. O modelo de incorporação é diferente do modelo de geração de linguagem:
1 embeddings = OpenAIEmbeddings(openai_api_key=key_param.openai_api_key) - Inicialize o VectorStore. Vetorize o texto dos documentos usando o modelo de incorporação especificado e o insira na coleção especificada do MongoDB.
1 vectorStore = MongoDBAtlasVectorSearch.from_documents( data, embeddings, collection=collection ) - Crie o seguinte índice do Atlas Search na coleção, certifique-se de que o nome do seu índice esteja definido como
default
:
1 { 2 "fields": [{ 3 "path": "embedding", 4 "numDimensions": 1536, 5 "similarity": "cosine", 6 "type": "vector" 7 }] 8 }
Executando pesquisa vetorial usando o Atlas Vector Search
- Defina os nomes do URI, banco de dados e coleção do MongoDB:
1 client = MongoClient(key_param.MONGO_URI) 2 dbName = "langchain_demo" 3 collectionName = "collection_of_text_blobs" 4 collection = client[dbName][collectionName] - Defina o Modelo de incorporação da OpenAI que usaremos. O modelo de incorporação é diferente do modelo de geração de linguagem:
1 embeddings = OpenAIEmbeddings(openai_api_key=key_param.openai_api_key) - Inicialize a armazenagem vetorial:
1 vectorStore = MongoDBAtlasVectorSearch( collection, embeddings ) - Defina uma função que a) executa pesquisa de similaridade semântica usando o Atlas Vector Search (observe que estou incluindo esta etapa apenas para destacar as diferenças entre a saída apenas da pesquisa semântica versus a saída gerada com a arquitetura RAG usando RetrieverQA):
1 def query_data(query): 2 # Convert question to vector using OpenAI embeddings 3 # Perform Atlas Vector Search using Langchain's vectorStore 4 # similarity_search returns MongoDB documents most similar to the query 5 6 docs = vectorStore.similarity_search(query, K=1) 7 as_output = docs[0].page_content e b) use geração aumentada de recuperação para realizar as perguntas e respostas nos dados:1 # Leveraging Atlas Vector Search paired with Langchain's QARetriever 2 3 # Define the LLM that we want to use -- note that this is the Language Generation Model and NOT an Embedding Model 4 # If it's not specified (for example like in the code below), 5 # then the default OpenAI model used in LangChain is OpenAI GPT-3.5-turbo, as of August 30, 2023 6 7 llm = OpenAI(openai_api_key=key_param.openai_api_key, temperature=0) 8 9 10 # Get VectorStoreRetriever: Specifically, Retriever for MongoDB VectorStore. 11 # Implements _get_relevant_documents which retrieves documents relevant to a query. 12 retriever = vectorStore.as_retriever() 13 14 # Load "stuff" documents chain. Stuff documents chain takes a list of documents, 15 # inserts them all into a prompt and passes that prompt to an LLM. 16 17 qa = RetrievalQA.from_chain_type(llm, chain_type="stuff", retriever=retriever) 18 19 # Execute the chain 20 21 retriever_output = qa.run(query) 22 23 24 # Return Atlas Vector Search output, and output generated using RAG Architecture 25 return as_output, retriever_output - Crie uma interface da web para o aplicativo utilizando a Gradio:
1 with gr.Blocks(theme=Base(), title="Question Answering App using Vector Search + RAG") as demo: 2 gr.Markdown( 3 """ 4 # Question Answering App using Atlas Vector Search + RAG Architecture 5 """) 6 textbox = gr.Textbox(label="Enter your Question:") 7 with gr.Row(): 8 button = gr.Button("Submit", variant="primary") 9 with gr.Column(): 10 output1 = gr.Textbox(lines=1, max_lines=10, label="Output with just Atlas Vector Search (returns text field as is):") 11 output2 = gr.Textbox(lines=1, max_lines=10, label="Output generated by chaining Atlas Vector Search to Langchain's RetrieverQA + OpenAI LLM:") 12 13 # Call query_data function upon clicking the Submit button 14 15 button.click(query_data, textbox, outputs=[output1, output2]) 16 17 demo.launch()
As capturas de tela a seguir mostram os resultados gerados para várias perguntas feitas. Observe que uma pesquisa de similaridade semântica retorna o conteúdo do texto dos documentos de origem como ele é, enquanto o resultado do aplicativo de perguntas e respostas usando a arquitetura RAG gera respostas precisas para as perguntas feitas.
Exemplo de análise de log
Exemplo de conversa por chat
Exemplo de análise de sentimento
Exemplo de recuperação de resposta precisa
Nesse tutorial, vimos como construir um aplicativo de perguntas e respostas para conversar com seus dados privados utilizando o Atlas Vector Search como um armazenamento vetorial, enquanto aproveita a arquitetura de geração aumentada de recuperação com o LangChain e a OpenAI.
Os armazenamentos vetoriais ou bancos de dados vetoriais desempenham uma função fundamental na criação de aplicações de LLM e a geração aumentada de recuperação (RAG) é um avanço importante no campo da IA, especialmente no processamento de linguagem natural. Ao combiná-los, é possível criar aplicações poderosas com IA para vários casos de uso.
Caso tenha perguntas ou comentários, junte-se a nós nos fóruns de desenvolvedores para continuar a conversa!