Grandes modelos de linguagem (LLMs) têm revolucionado a forma como aplicativos modernos geram texto em resposta a prompts ou consultas do usuário. Exemplos comuns de LLMs incluem GPT-3.5, GPT-4, Bard e Llama, entre outros. Para melhorar os recursos dos LLMs para responder a consultas do usuário usando informações contextualmente relevantes e precisas, engenheiros e desenvolvedores de IA desenvolveram duas abordagens principais.
A primeira é ajustar o LLM de linha de base com dados relevantes de propriedade e contexto. A segunda abordagem, e mais econômica, é conectar o LLM a um armazenamento de dados com um modelo de recuperação que extrai informações semanticamente relevantes do banco de dados para adicionar contexto à entrada do usuário do LLM. Essa abordagem é chamada de geração aumentada de recuperação (RAG).
Recuperação de Geração Aumentada (RAG) Explicada
Os sistemas de geração aumentada de recuperação (RAG) conectam um LLM a um armazenamento de dados com um modelo de recuperação que retorna resultados semanticamente relevantes. Ele melhora as respostas adicionando informações relevantes de fontes de informação às consultas do usuário. Os sistemas RAG são uma abordagem econômica para desenvolver aplicativos LLM que fornecem informações atualizadas e relevantes em comparação ao ajuste fino.
Os sistemas RAG são uma melhoria nos modelos de ajuste fino pelos seguintes motivos:
- O ajuste fino requer uma quantidade substancial de dados proprietários e específicos do domínio, cuja coleta e preparação podem exigir muitos recursos.
- O ajuste fino de um LLM para cada novo contexto ou aplicação exige recursos computacionais e tempo consideráveis, tornando-o uma solução menos escalável para aplicações que precisam se adaptar rapidamente a vários domínios ou conjuntos de dados.
O que é Geração Aumentada de Recuperação (RAG)?
RAG é um padrão de design de sistema que alavanca técnicas de recuperação de informações e modelos de IA generativos para fornecer respostas precisas e relevantes às consultas do usuário. Ele faz isso recuperando dados semanticamente relevantes para suplementar as consultas do usuário por contexto adicional, combinados como entrada para LLMs.
O padrão de design arquitetônico RAG para aplicativos LLM aprimora a relevância e a precisão das respostas LLM ao incorporar contexto de fontes de dados externas. No entanto, integrar e manter o componente de recuperação pode aumentar a complexidade, o que pode aumentar a latência geral do sistema. Além disso, a qualidade das respostas geradas depende muito da relevância e da qualidade dos dados na fonte externa.
Os pipelines e sistemas RAG dependem de uma pilha de IA, também conhecida como pilha de IA moderna ou pilha de IA Gen. Isso se refere à composição de modelos, bancos de dados, bibliotecas e frameworks usados para construir aplicativos modernos com capacidades de IA generativas. A infraestrutura alavanca conhecimento paramétrico do LLM e conhecimento não paramétrico de dados para aumentar as consultas do usuário.
Os componentes do AI Stack incluem o seguinte: Modelos, orquestradores ou integradores e bancos de dados operacionais e vetoriais. Neste tutorial, MongoDB atuará como banco de dados operacional e vetorial.
Como criar um sistema RAG
O tutorial descreve as etapas para implementar os estágios padrão de um pipeline RAG. Especificamente, ele se concentra na criação de um sistema tipo chatbot para responder a perguntas de usuários sobre anúncios do Airbnb, oferecendo recomendações ou informações gerais.
1. Instalar bibliotecas
O seguinte snippet de código instala várias bibliotecas que fornecem funcionalidades para acessar grandes modelos de linguagem, modelos de reclassificação e métodos de conexão de banco de dados. Essas bibliotecas abstraem algumas complexidades associadas à escrita de código extenso para atingir resultados que as bibliotecas importadas condensam em apenas algumas linhas e chamadas de método.
!pip install llama-index
!pip install llama-index-vector-stores-mongodb
!pip install llama-index-embeddings-openai
!pip install pymongo
!pip install datasets
!pip install pandas
2. Carregamento de dados e configuração de chave OpenAI
O comando abaixo atribui uma chave de API OpenAI à variável de ambiente OPENAI_API_KEY
. Isso é necessário para garantir que o LlamaIndex crie um cliente OpenAI com a chave de API OpenAI fornecida para acessar recursos como modelos LLM (GPT-3, GPT-3.5-turbo e GPT-4) e modelos de incorporação (text-embedding-ada-002, text-embedding-3-small, text-embedding-3-large).
import os
os.environ["OPENAI_API_KEY"] = ""
O próximo passo é carregar os dados dentro do ambiente de desenvolvimento. Os dados para este tutorial são originados da plataforma Hugging Face, mais especificamente, o Conjunto de dados do Airbnb disponibilizado via MongoDB. Este conjunto de dados compreende listagens do Airbnb, completas com descrições de propriedades, avaliações e vários metadados.
Além disso, ele apresenta embeddings de texto para as descrições de propriedade e embeddings de imagem para as fotos da listagem. Os embeddings de texto foram gerados usando o modelo text-embedding-3-small
da OpenAI, enquanto as incorporações de imagens são produzidas com o modelo CLIP-ViT-B/32
da OpenAI, ambos acessíveis no Hugging Face.
from datasets import load_dataset
import pandas as pd# https://huggingface.co/datasets/MongoDB/embedded_movies
# Make sure you have an Hugging Face token (HF_TOKEN) in your development environment
dataset = load_dataset("MongoDB/airbnb_embeddings")
# Convert the dataset to a pandas dataframe
dataset_df = pd.DataFrame(dataset['train'])
dataset_df.head(5)
Para demonstrar totalmente as capacidades e a utilização do LlamaIndex, o campo text-embeddings
deve ser removido do conjunto de dados original. Esta etapa permite a criação de um novo campo de incorporação adaptado aos atributos especificados pela configuração do documento LlamaIndex.
O seguinte trecho de código remove efetivamente o atributo text-embeddings
de cada ponto de dados no conjunto de dados.
dataset_df = dataset_df.drop(columns=['text_embeddings'])
3. Configuração LLM do LlamaIndex
O trecho de código abaixo foi projetado para configurar os modelos fundamentais e de incorporação necessários para o pipeline RAG.
Dentro desta configuração, o modelo base selecionado para geração de texto é o modelo gpt-3.5-turbo
da OpenAI, que é a escolha padrão dentro da biblioteca LlamaIndex. Enquanto a classe OpenAIEmbedding
do LlamaIndex normalmente assume como padrão o modelo text-embedding-ada-002
para recuperação e incorporação, este tutorial mudará para o uso do modelo text-embedding-3-small
, que apresenta uma dimensão de incorporação de 256.
Os modelos de incorporação e base são delimitados globalmente usando o módulo Settings
do LlamaIndex. Isso significa que os processos downstream do pipeline RAG não precisam especificar os modelos utilizados e, por padrão, usarão os modelos especificados globalmente.
from llama_index.core.settings import Settings
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbeddingembed_model = OpenAIEmbedding(model="text-embedding-3-small", dimensions=256)
llm = OpenAI()
Settings.llm = llm
Settings.embed_model = embed_model
4. Criando documentos e nós personalizados do LlamaIndex
Em seguida, criaremos documentos e nós personalizados, que são considerados cidadãos de primeira classe dentro do ecossistema LlamaIndex. Documentos são estruturas de dados que fazem referência a um objeto criado a partir de uma fonte de dados, permitindo a especificação de metadados e o comportamento dos dados quando fornecidos a LLMs para geração e incorporação de texto.
O snippet de código fornecido abaixo cria uma lista de documentos, com atributos específicos extraídos de cada ponto de dados no conjunto de dados. Além disso, o snippet de código demonstra como os tipos originais de alguns atributos no conjunto de dados são convertidos em tipos apropriados reconhecidos pelo LlamaIndex.
import json
from llama_index.core import Document
from llama_index.core.schema import MetadataMode# Convert the DataFrame to a JSON string representation
documents_json = dataset_df.to_json(orient='records')
# Load the JSON string into a Python list of dictionaries
documents_list = json.loads(documents_json)
llama_documents = []
for document in documents_list:
# Value for metadata must be one of (str, int, float, None)
document["amenities"] = json.dumps(document["amenities"])
document["images"] = json.dumps(document["images"])
document["host"] = json.dumps(document["host"])
document["address"] = json.dumps(document["address"])
document["availability"] = json.dumps(document["availability"])
document["review_scores"] = json.dumps(document["review_scores"])
document["reviews"] = json.dumps(document["reviews"])
document["image_embeddings"] = json.dumps(document["image_embeddings"])
# Create a Document object with the text and excluded metadata for llm and embedding models
llama_document = Document(
text=document["description"],
metadata=document,
excluded_llm_metadata_keys=["_id", "transit", "minimum_nights", "maximum_nights", "cancellation_policy", "last_scraped", "calendar_last_scraped", "first_review", "last_review", "security_deposit", "cleaning_fee", "guests_included", "host", "availability", "reviews", "image_embeddings"],
excluded_embed_metadata_keys=["_id", "transit", "minimum_nights", "maximum_nights", "cancellation_policy", "last_scraped", "calendar_last_scraped", "first_review", "last_review", "security_deposit", "cleaning_fee", "guests_included", "host", "availability", "reviews", "image_embeddings"],
metadata_template="{key}=>{value}",
text_template="Metadata: {metadata_str}\n-----\nContent: {content}",
)
llama_documents.append(llama_document)
# Observing an example of what the LLM and Embedding model receive as input
print("\nThe LLM sees this:\n", llama_documents[0].get_content(metadata_mode=MetadataMode.LLM),)
print("\nThe Embedding model sees this:\n", llama_documents[0].get_content(metadata_mode=MetadataMode.EMBED),)
O próximo passo é criar nós a partir dos documentos após criar uma lista de documentos e especificar os metadados e o comportamento do LLM. Os nós são os objetos que são ingeridos no banco de dados vetorial do MongoDB e permitirão a utilização de dados vetoriais e dados operacionais para conduzir pesquisas.
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.schema import MetadataModeparser = SentenceSplitter(chunk_size=5000)
nodes = parser.get_nodes_from_documents(llama_documents)
for node in nodes:
node_embedding = embed_model.get_text_embedding(
node.get_content(metadata_mode=MetadataMode.EMBED)
)
node.embedding = node_embedding
5. Conexão e configuração do banco de dados vetorial MongoDB
O MongoDB atua como um banco de dados operacional e vetorial para o sistema RAG. O MongoDB Atlas fornece especificamente uma solução de banco de dados que armazena, consulta e recupera embeddings vetoriais de forma eficiente.
Criar um banco de dados e uma coleção no MongoDB é simplificado com o MongoDB Atlas.
Primeiro, registre-se para uma Conta MongoDB Atlas. Para usuários existentes, faça login em Atlas MongoDB. Siga as instruções. Selecione Atlas UI como o procedimento para implantar seu primeiro cluster. Crie o banco de dados: airbnb
. Dentro do banco de dados airbnb
, crie a coleção ‘listings_reviews’. Criar um índice de pesquisa vetorial nomeado vector_index
para a coleção ‘listings_reviews’. Este índice permite que o aplicativo RAG recupere registros como contexto adicional para suplementar consultas de usuários por meio de pesquisa vetorial. Abaixo está a definição JSON do índice de pesquisa vetorial da coleta de dados.
{
"fields": [
{
"numDimensions": 256,
"path": "embedding",
"similarity": "cosine",
"type": "vector"
}
]
}
Abaixo está uma explicação de cada campo de definição JSON do índice de pesquisa vetorial.
- fields: Esta é uma lista que especifica os campos a serem indexados na coleção do MongoDB e define as características do próprio índice.
- numDimensions: Dentro de cada item de campo,
numDimensions
especifica o número de dimensões dos dados vetoriais. Neste caso, é definido como 256. Este número deve corresponder à dimensionalidade dos dados vetoriais armazenados no campo, e também é uma das dimensões que o modelotext-embedding-3-small
da OpenAI cria embeddings vetoriais. - path: O campo
path
indica o caminho para os dados dentro dos documentos do banco de dados a serem indexados. Aqui, ele é definido comoembedding
. - similarity: O campo
similarity
define o tipo de métrica de distância de similaridade que será usada para comparar vetores durante o processo de busca. Aqui, ele é definido comocosine
, que mede o cosseno do ângulo entre dois vetores, determinando efetivamente quão semelhantes ou diferentes esses vetores são em sua orientação no espaço vetorial. Outras medidas métricas de distância de similaridade são Euclidiana e produtos de ponto. - type: Este campo especifica o tipo de dados que o índice manipulará. Neste caso, ele é definido como
vector
, indicando que este índice foi projetado especificamente para manipular e otimizar pesquisas em dados vetoriais.
Ao final desta etapa, você deve ter um banco de dados com uma coleção e um índice de pesquisa vetorial definido. A etapa final desta seção é obter a conexão com a string do identificador uniforme de recursos (URI) para o cluster Atlas criado para estabelecer uma conexão entre os bancos de dados e o ambiente de desenvolvimento atual.
Não se esqueça de colocar na lista de permissões o IP do host Python ou 0.0.0.0/0
para qualquer IP ao criar provas de conceito.
Siga as etapas do MongoDB para obter a string de conexão da Atlas UI. Após configurar o banco de dados e obter o URI de conexão do cluster Atlas, armazene o URI com segurança dentro do seu ambiente de desenvolvimento.
Este guia usa o Google Colab, que oferece um recurso para armazenar segredos de ambiente com segurança. Esses segredos podem ser acessados dentro do ambiente de desenvolvimento. Especificamente, a linha mongo_uri = userdata.get('MONGO_URI')
recupera o URI do armazenamento seguro.
As etapas a seguir utilizam o PyMongo para criar uma conexão com o cluster e obter objetos de referência para o banco de dados e a coleção.
import pymongo
from google.colab import userdatadef get_mongo_client(mongo_uri):
"""Establish connection to the MongoDB."""
try:
client = pymongo.MongoClient(mongo_uri, appname="devrel.content.python")
print("Connection to MongoDB successful")
return client
except pymongo.errors.ConnectionFailure as e:
print(f"Connection failed: {e}")
return None
mongo_uri = userdata.get('MONGO_URI')
if not mongo_uri:
print("MONGO_URI not set in environment variables")
mongo_client = get_mongo_client(mongo_uri)
DB_NAME = "airbnb"
COLLECTION_NAME = "listings_reviews"
db = mongo_client[DB_NAME]
collection = db[COLLECTION_NAME]
A próxima etapa exclui todos os registros existentes na coleção para garantir que o destino de ingestão de dados esteja vazio.
# Delete any existing records in the collection
collection.delete_many({})
6. Ingestão de dados
Usando LlamaIndex, a inicialização do armazenamento de vetores e a ingestão de dados é um processo trivial que pode ser estabelecido em apenas duas linhas de código. O snippet abaixo inicializa um objeto de armazenamento de vetores do MongoDB atlas por meio do construtor LlamaIndex MongoDBAtlasVectorSearch
. Usando o método add()
da instância de armazenamento vetorial, os nós são ingeridos diretamente no banco de dados MongoDB.
É importante observar que nesta etapa, referenciamos o nome do índice de pesquisa vetorial que será criado por meio da interface MongoDB Cloud Atlas. Para este caso de uso específico, o nome do índice: vector_index
.
Inicialização do Vector Store
from llama_index.vector_stores.mongodb import MongoDBAtlasVectorSearchvector_store = MongoDBAtlasVectorSearch(mongo_client, db_name=DB_NAME, collection_name=COLLECTION_NAME, index_name="vector_index")
vector_store.add(nodes)
Crie uma instância de um MongoDB Atlas Vector Store: vector_store
usando o construtor MongoDBAtlasVectorSearch
.
- db_name=”airbnb”: Isso especifica o nome do banco de dados dentro do MongoDB Atlas onde os documentos, juntamente com as incorporações de vetores, são armazenados.
- collection_name=”listings_reviews”: Especifica o nome da coleção dentro do banco de dados onde os documentos são armazenados.
- index_name=”vector_index”: Esta é uma referência ao nome do índice de pesquisa vetorial criado para a coleção MongoDB.
Ingestão de dados
vector_store.add(nodes)
7. Consultando o índice com consultas do usuário
Para utilizar os recursos de armazenamento de vetores com o LlamaIndex, um índice é inicializado a partir do armazenamento de vetores do MongoDB, conforme demonstrado no trecho de código abaixo.
from llama_index.core import VectorStoreIndexindex = VectorStoreIndex.from_vector_store(vector_store)
O próximo passo envolve a criação de um mecanismo de consulta LlamaIndex. O mecanismo de consulta habilita a funcionalidade para utilizar linguagem natural para recuperar informações relevantes e contextualmente apropriadas de um vasto índice de dados. O LlamaIndex as_query_engine
método abstrai as complexidades de Engenheiros de IA e desenvolvedores escrevendo o código de implementação para processar consultas adequadamente para extrair informações de uma fonte de dados.
Para nosso caso de uso, o mecanismo de consulta satisfaz o requisito de construção de um aplicativo de perguntas e respostas, embora o LlamaIndex forneça a capacidade de construir um aplicativo semelhante a um bate-papo com a funcionalidade ChatEngine
.
import pprint
from llama_index.core.response.notebook_utils import display_responsequery_engine = index.as_query_engine(similarity_top_k=3)
query = "I want to stay in a place that's warm and friendly, and not too far from restaurants, can you recommend a place? Include a reason as to why you've chosen your selection"
response = query_engine.query(query)
display_response(response)
pprint.pprint(response.source_nodes)
Inicialização do mecanismo de consulta
O processo começa com a inicialização de um mecanismo de consulta a partir de um índice existente, chamando index.as_query_engine(similarity_top_k=3)
. Isso prepara o mecanismo para examinar os dados indexados para identificar os principais (3 neste caso) entradas que são mais semelhantes ao conteúdo da consulta.
Para este cenário, a consulta proposta é: “Quero ficar em um lugar que seja aconchegante e amigável e não muito longe de restaurantes, você pode recomendar um lugar? Inclua um motivo pelo qual você escolheu sua seleção.”
O mecanismo de consulta processa a consulta de entrada para encontrar e recuperar respostas relevantes.
Observando os nós de origem
O objeto de resposta LlamaIndex do mecanismo de consulta fornece informações adicionais sobre os pontos de dados que contribuem para a resposta gerada.
Compreendendo como construir um sistema RAG
Neste tutorial, desenvolvemos um sistema de consulta simples que alavanca o padrão de design arquitetônico RAG para aplicativos LLM, utilizando o LlamaIndex como a estrutura LLM e o MongoDB como o banco de dados vetorial. Também demonstramos como personalizar metadados para consumo por bancos de dados e LLMs usando as estruturas de dados primárias, documentos e nós do LlamaIndex. Isso permite mais controlabilidade de metadados ao construir aplicativos LLM e processos de ingestão de dados. Além disso, mostra como usar o MongoDB como um banco de dados vetorial e realizar a ingestão de dados.
Ao focar em um caso de uso prático — um sistema tipo chatbot para listagens do Airbnb — este guia o orienta pelo processo passo a passo de implementação de um pipeline RAG e destaca as vantagens distintas do RAG sobre os métodos tradicionais de ajuste fino. Por meio do POLM AI Stack — combinando Python, modelos OpenAI, LlamaIndex para orquestração e MongoDB Atlas como o banco de dados de dupla finalidade, os desenvolvedores recebem um kit de ferramentas abrangente para inovar e implantar aplicativos de IA generativos de forma eficiente.
Perguntas Frequentes
1. O que é a Geração Aumentada de Recuperação (RAG)?
RAG é um padrão de design de sistema que alavanca técnicas de recuperação de informações e modelos de IA generativos para fornecer respostas precisas e relevantes às consultas do usuário. Ele faz isso recuperando dados semanticamente relevantes para suplementar as consultas do usuário por contexto adicional, combinados como entrada para LLMs.
2. Quais são as vantagens do RAG sobre o ajuste fino?
O RAG oferece várias vantagens sobre o ajuste fino:
- Economia de Recursos: O ajuste fino requer uma quantidade substancial de dados proprietários e específicos do domínio, cuja coleta e preparação podem exigir muitos recursos.
- Escalabilidade: O ajuste fino de um LLM para cada novo contexto ou aplicação exige recursos computacionais e tempo consideráveis, tornando-o uma solução menos escalável para aplicações que precisam se adaptar rapidamente a vários domínios ou conjuntos de dados.
3. Como o LlamaIndex facilita a criação de um sistema RAG?
O LlamaIndex é uma estrutura que facilita a criação de sistemas RAG ao fornecer funcionalidades para conectar fontes de dados a modelos de linguagem grandes. Ele abstrai complexidades associadas à ingestão de dados, implementação de pipeline RAG e desenvolvimento de aplicativos LLM.
4. Quais são os componentes principais do AI Stack para sistemas RAG?
Os componentes principais do AI Stack para sistemas RAG incluem:
- Modelos: Modelos de linguagem grandes e modelos de incorporação.
- Orquestradores ou Integradores: Ferramentas como LlamaIndex que facilitam a integração de diferentes componentes.
- Bancos de Dados Operacionais e Vetoriais: Bancos de dados que armazenam e recuperam dados vetoriais de forma eficiente, como MongoDB.
5. Como o MongoDB é usado em um sistema RAG?
O MongoDB atua como um banco de dados operacional e vetorial para o sistema RAG. Ele armazena, consulta e recupera embeddings vetoriais de forma eficiente, permitindo que o aplicativo RAG recupere registros como contexto adicional para suplementar consultas de usuários por meio de pesquisa vetorial.
Conclusão
A geração aumentada de recuperação (RAG) é uma abordagem inovadora para melhorar a precisão e a relevância das respostas geradas por grandes modelos de linguagem (LLMs). Ao conectar LLMs a armazenamentos de dados com modelos de recuperação, os sistemas RAG podem fornecer informações atualizadas e contextualmente relevantes de forma econômica e escalável. Este artigo explorou os fundamentos do RAG, destacou suas vantagens sobre o ajuste fino e forneceu um tutorial detalhado sobre como criar um sistema RAG usando o LlamaIndex e o MongoDB.
Em resumo, a geração aumentada de recuperação é uma abordagem poderosa para melhorar a capacidade dos LLMs de fornecer respostas precisas e relevantes. Com as ferramentas e técnicas adequadas, os desenvolvedores podem criar sistemas RAG eficazes que se adaptam rapidamente a vários domínios e conjuntos de dados, oferecendo uma experiência de usuário superior.