Disclaimer
Este texto foi inicialmente concebido pela IA Generativa em função da transcrição de um vídeo do canal de Daniel Romero(a pessoa que lidera nossa especialização em Engenharia de IA). Se preferir acompanhar por vídeo, é só dar o play.
Um detalhe importante é que o vídeo exemplifica com LangChain e aqui nós preferimos deixar apenas com o suficiente.
Introdução
Retrieval-Augmented Generation, ou simplesmente RAG, é uma técnica que tem revolucionado a forma como construímos aplicações de inteligência artificial. Neste post, vamos criar um chatbot do zero usando essa técnica — sem frameworks intermediários, direto nos SDKs. Ao final, você será capaz de criar um chatbot usando modelos GPT da OpenAI (ou alternativas como Groq) integrado a um banco de dados vetorial. Esse chatbot poderá responder perguntas relacionadas a qualquer documentação interna de uma empresa, por exemplo.
O Problema: Por Que LLMs Alucinam?
Antes de colocar a mão na massa, vale entender por que precisamos do RAG em primeiro lugar.
Aplicações mais simples fazem uma consulta direta em um LLM para obter respostas. Isso funciona bem para conhecimento geral ou informações que o modelo viu durante a fase de treino. O problema? Muitos LLMs simplesmente não viram informações suficientes sobre tópicos que gostaríamos de entender.
Se você perguntar ao GPT-3.5 Turbo o que tem de tão especial no modelo Mistral 7B, provavelmente não vai receber uma boa resposta. O Mistral é um modelo de linguagem recente que não foi incluído nos dados de treino da maioria dos LLMs. Resultado: eles fornecem informações incorretas ou simplesmente inventam algo.
Outro exemplo interessante: o Qdrant é um sistema de banco de dados que indexa e recupera dados vetoriais em alta dimensão. Porém, ao perguntar ao GPT-4 o que é Qdrant, ele pode dizer que não encontra informações sobre o termo e sugerir que você errou a digitação. Isso indica claramente que o modelo não tem ideia do que é Qdrant. O conhecimento de um LLM é limitado ao que foi aprendido durante o treinamento — ele não tem acesso ao mundo exterior.
E sim, o termo técnico para esse comportamento é alucinação.
Por Que Isso Acontece?
LLMs alucinam porque dependem exclusivamente do conhecimento aprendido durante o processo de treino. O modelo não aprende explorando o mundo. Se algo não estiver em seus dados de treino, ele não vai saber — e mesmo que esteja, pode não ser preciso, levando a ambiguidades.
O propósito de um LLM é comprimir as informações dos dados de treino em um modelo interno do mundo. Esse tipo de conhecimento é chamado de conhecimento paramétrico, porque é armazenado nos parâmetros do modelo. E esses parâmetros são congelados após o treinamento, ou seja, o LLM não pode aprender coisas novas ou se adaptar a novas situações.
A Solução: RAG
É aqui que entra o RAG(Retrieval Augmented Generation) para apoiar na resolução desse problema. A ideia é adicionar um componente intermediário — que pode ser um Pipeline RAG, uma busca no Google, ou uma comunicação com uma fonte externa como um banco de dados SQL.
Com o RAG, adicionamos um componente de memória externa que pode ser modificado e atualizado: uma espécie de memória de longo prazo. Isso permite que o sistema se adapte a novas situações, indo além do que o modelo aprendeu originalmente.
No caso do RAG, é comum usar um banco de dados vetorial como memória externa. A vantagem? Podemos adicionar, excluir e gerenciar a memória e o conhecimento da aplicação. É quase como gerenciar ou atualizar informações no cérebro de uma pessoa — uma analogia um pouco distópica, mas que ilustra bem o conceito.
Esse método é chamado de Source Knowledge, diferente do conhecimento paramétrico, porque o conhecimento não é armazenado nos parâmetros do modelo.
Construindo um Chatbot Básico
Vamos à prática. Primeiro, instalamos os pacotes necessários:
pip install openai qdrant-client
Para trabalhar com a API da OpenAI, o chatlog é representado como uma lista de dicionários. Cada dicionário contém uma role (que pode ser system, user ou assistant) e o content com o texto correspondente:
from openai import OpenAI
client = OpenAI() # usa a variável de ambiente OPENAI_API_KEY
messages = [
{"role": "system", "content": "Você é um assistente útil que responde a perguntas."},
{"role": "user", "content": "Olá, bot, como você está hoje?"},
{"role": "assistant", "content": "Estou bem, obrigado! Como posso ajudar?"},
{"role": "user", "content": "Gostaria de entender o que é Machine Learning."}
]
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=messages
)
Para ver o texto da resposta:
print(response.choices[0].message.content)
A resposta pode ser adicionada ao histórico para continuar a conversa:
messages.append({
"role": "assistant",
"content": response.choices[0].message.content
})
Agora, se fizermos uma nova pergunta sobre "a diferença entre supervisionado e não supervisionado" — sem mencionar a palavra "aprendizado" — o modelo consegue manter o contexto e responder sobre "aprendizado supervisionado e não supervisionado". Isso mostra que o histórico de conversação está funcionando corretamente.
Montar esse chatbot básico é relativamente fácil. Não tem nada complicado acontecendo aqui.
Preparando os Dados para o RAG
Para adicionar conhecimento externo ao chatbot, precisamos de uma base de conhecimento. Neste exemplo, usamos um dataset do Hugging Face que contém o paper do Mistral 7B já dividido em chunks — ou seja, o texto do artigo já foi pré-processado e separado em pedaços menores, prontos para serem indexados.
O dataset tem várias colunas (como id, title, summary), mas para esse exemplo só precisamos de duas: chunk (o pedaço de texto) e source (a fonte de onde veio):
import pandas as pd
# Carrega o dataset e isola as colunas relevantes
df = df[["chunk", "source"]]
# Cada registro contém o texto e sua origem
chunks = df["chunk"].tolist()
sources = df["source"].tolist()
Gerando Embeddings
Para indexar os chunks no banco de dados vetorial, precisamos transformá-los em vetores (embeddings). Usamos a API de embeddings da OpenAI:
def get_embedding(text):
response = client.embeddings.create(
model="text-embedding-ada-002",
input=text
)
return response.data[0].embedding
Integrando o Banco de Dados Vetorial
Agora vamos adicionar tudo isso ao banco de dados vetorial. Usaremos o Qdrant diretamente:
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct
# Inicializa o cliente (em memória para este exemplo)
qdrant = QdrantClient(":memory:")
# Cria a coleção
qdrant.create_collection(
collection_name="chatbot",
vectors_config=VectorParams(size=1536, distance=Distance.COSINE)
)
Agora indexamos os chunks. Para cada chunk, geramos o embedding e inserimos no banco:
points = []
for i, (chunk, source) in enumerate(zip(chunks, sources)):
embedding = get_embedding(chunk)
points.append(PointStruct(
id=i,
vector=embedding,
payload={"text": chunk, "source": source}
))
qdrant.upsert(collection_name="chatbot", points=points)
Conectando a Base de Conhecimento ao LLM
Estamos quase terminando. Falta conectar a base de conhecimento ao nosso LLM.
Podemos testar a comunicação com o banco de dados vetorial executando uma consulta:
query = "O que tem de tão especial no Mistral 7B?"
results = qdrant.search(
collection_name="chatbot",
query_vector=get_embedding(query),
limit=3
)
Isso retorna os três chunks mais similares semanticamente à pergunta. Lembra que antes o LLM não conseguia responder essa pergunta? Agora é diferente — estamos obtendo chunks diretamente do artigo sobre o Mistral 7B.
O resultado é útil, mas um pouco difícil de ler. Para resolver isso, deixamos o LLM processar essa informação.
Criando o Prompt com RAG
Configuramos uma função que recupera os itens mais relevantes do banco de dados vetorial e monta o prompt com contexto:
def augmented_prompt(query):
# Recupera os chunks relevantes
results = qdrant.search(
collection_name="chatbot",
query_vector=get_embedding(query),
limit=3
)
# Extrai o texto dos resultados
source_knowledge = "\n".join([r.payload["text"] for r in results])
# Monta o prompt com contexto
prompt = f"""Use o contexto abaixo para responder a pergunta.
Contexto:
{source_knowledge}
Pergunta: {query}"""
return prompt
Agora, no System Prompt, informamos ao LLM para utilizar o contexto para responder:
query = "O que tem de tão especial no Mistral 7B?"
messages = [
{"role": "system", "content": "Use o contexto abaixo para responder a pergunta."},
{"role": "user", "content": augmented_prompt(query)}
]
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=messages
)
print(response.choices[0].message.content)
E agora temos uma resposta baseada no conteúdo do paper: "Mistral 7B é um modelo de linguagem com 7 bilhões de parâmetros, projetado para oferecer desempenho superior e eficiência." Pelo menos ele não está dizendo que é um vinho!
Considerações Sobre o RAG
Implementar o RAG de forma ingênua — assumindo que toda consulta exige uma busca na base de conhecimento — nem sempre é necessário. Se um usuário cumprimentar o chatbot com "Oi, tudo bem? Como você está?", não faz sentido consultar uma base externa para responder.
Além disso, o tempo de inferência pode ser lento dependendo do modelo e da API escolhida. Essa é uma desvantagem, mas os benefícios compensam:
- Melhor desempenho de retrieval
- Maior precisão nas respostas
- Possibilidade de fornecer citações das fontes
- Mais rápido e eficiente que outras abordagens, como Agents
- Controle sobre o número de tokens
Alternativa com Groq para Inferência Rápida
Pensando em aplicações reais, usar apenas a OpenAI pode trazer dois problemas: custo (que sobe rapidamente) e tempo de inferência (às vezes crítico).
Uma alternativa com inferência muito mais rápida é o Groq, capaz de gerar cerca de 800 tokens por segundo. Para usá-lo, basta instalar o pacote:
pip install groq
O SDK do Groq segue a mesma estrutura da OpenAI, então a migração é simples:
from groq import Groq
groq_client = Groq() # usa a variável de ambiente GROQ_API_KEY
response = groq_client.chat.completions.create(
model="llama-3.1-70b-versatile",
messages=messages,
temperature=0
)
print(response.choices[0].message.content)
A execução é muito mais rápida que o GPT. E como ambos os SDKs seguem a mesma interface, podemos reutilizar exatamente a mesma estrutura de mensagens e a função augmented_prompt que criamos antes — basta trocar o cliente.
Conclusão
Um pipeline RAG basicamente recupera informações, utiliza essas informações como contexto e gera respostas. Essa abordagem resolve o problema fundamental das alucinações em LLMs ao adicionar uma fonte de conhecimento externa que pode ser atualizada e gerenciada independentemente do modelo.
Os pontos principais:
- LLMs têm conhecimento limitado ao que foi aprendido durante o treinamento (conhecimento paramétrico)
- O RAG adiciona uma "memória de longo prazo" que pode ser modificada (Source Knowledge)
- Bancos de dados vetoriais são a escolha comum para armazenar esse conhecimento
- Os SDKs da OpenAI e Groq seguem interfaces similares, facilitando a troca entre provedores
Especialização em Engenharia de IA
Este conteúdo é um trecho de uma das aulas da Especialização em Engenharia de IA, uma parceria com a Dev Mais Eficiente. O curso aborda RAG, Vector Search, Busca Híbrida, Agents, Tools e muito mais, sempre com aulas 100% práticas e com exemplos reais.
Faça sua inscrição em https://deveficiente.com/especializacao-engenharia-ia .
Top comments (0)