DEV Community

Alberto Luiz Souza
Alberto Luiz Souza

Posted on

Como Criar um Chatbot com RAG do Zero: Guia Prático com OpenAI e Qdrant

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
Enter fullscreen mode Exit fullscreen mode

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
)
Enter fullscreen mode Exit fullscreen mode

Para ver o texto da resposta:

print(response.choices[0].message.content)
Enter fullscreen mode Exit fullscreen mode

A resposta pode ser adicionada ao histórico para continuar a conversa:

messages.append({
    "role": "assistant", 
    "content": response.choices[0].message.content
})
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)