Tools
Como Criar um Chatbot com RAG do Zero: Guia Prático com OpenAI e Qdrant
2025-12-29
0 views
admin
Disclaimer ## Introdução ## O Problema: Por Que LLMs Alucinam? ## Por Que Isso Acontece? ## A Solução: RAG ## Construindo um Chatbot Básico ## Preparando os Dados para o RAG ## Gerando Embeddings ## Integrando o Banco de Dados Vetorial ## Conectando a Base de Conhecimento ao LLM ## Criando o Prompt com RAG ## Considerações Sobre o RAG ## Alternativa com Groq para Inferência Rápida ## Conclusão ## Especialização em Engenharia de IA 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. 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. 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. 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. É 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. Vamos à prática. Primeiro, instalamos os pacotes necessários: 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: Para ver o texto da resposta: A resposta pode ser adicionada ao histórico para continuar a conversa: 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. 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): Para indexar os chunks no banco de dados vetorial, precisamos transformá-los em vetores (embeddings). Usamos a API de embeddings da OpenAI: Agora vamos adicionar tudo isso ao banco de dados vetorial. Usaremos o Qdrant diretamente: Agora indexamos os chunks. Para cada chunk, geramos o embedding e inserimos no banco: 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: 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. Configuramos uma função que recupera os itens mais relevantes do banco de dados vetorial e monta o prompt com contexto: Agora, no System Prompt, informamos ao LLM para utilizar o contexto para responder: 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! 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: 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: O SDK do Groq segue a mesma estrutura da OpenAI, então a migração é simples: 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. 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: 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 . Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse COMMAND_BLOCK:
pip install openai qdrant-client Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
pip install openai qdrant-client COMMAND_BLOCK:
pip install openai qdrant-client COMMAND_BLOCK:
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 COMMAND_BLOCK:
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
) COMMAND_BLOCK:
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
) CODE_BLOCK:
print(response.choices[0].message.content) Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
print(response.choices[0].message.content) CODE_BLOCK:
print(response.choices[0].message.content) CODE_BLOCK:
messages.append({ "role": "assistant", "content": response.choices[0].message.content
}) Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
messages.append({ "role": "assistant", "content": response.choices[0].message.content
}) CODE_BLOCK:
messages.append({ "role": "assistant", "content": response.choices[0].message.content
}) COMMAND_BLOCK:
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 COMMAND_BLOCK:
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() COMMAND_BLOCK:
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() CODE_BLOCK:
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 CODE_BLOCK:
def get_embedding(text): response = client.embeddings.create( model="text-embedding-ada-002", input=text ) return response.data[0].embedding CODE_BLOCK:
def get_embedding(text): response = client.embeddings.create( model="text-embedding-ada-002", input=text ) return response.data[0].embedding COMMAND_BLOCK:
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 COMMAND_BLOCK:
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)
) COMMAND_BLOCK:
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)
) CODE_BLOCK:
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 CODE_BLOCK:
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) CODE_BLOCK:
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) CODE_BLOCK:
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 CODE_BLOCK:
query = "O que tem de tão especial no Mistral 7B?" results = qdrant.search( collection_name="chatbot", query_vector=get_embedding(query), limit=3
) CODE_BLOCK:
query = "O que tem de tão especial no Mistral 7B?" results = qdrant.search( collection_name="chatbot", query_vector=get_embedding(query), limit=3
) COMMAND_BLOCK:
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 COMMAND_BLOCK:
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 COMMAND_BLOCK:
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 CODE_BLOCK:
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 CODE_BLOCK:
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) CODE_BLOCK:
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) COMMAND_BLOCK:
pip install groq Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
pip install groq COMMAND_BLOCK:
pip install groq COMMAND_BLOCK:
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 COMMAND_BLOCK:
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) COMMAND_BLOCK:
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) - 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 - 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
how-totutorialguidedev.toaimachine learningopenaillmgptgit