Implementação básica de RAG: um tutorial passo a passo

Este tutorial para iniciantes explica como funciona uma implementação básica de RAG, aborda as práticas recomendadas e mostra como construir um sistema RAG de linha de comando funcional com Python, ChromaDB e os modelos de embedding e de linguagem da Perplexity.

Basic RAG implementation process diagram: the interface sends input to the orchestrator, which retrieves data from a vector database, builds the input context for the LLM, and returns the output to the interface.

O RAG é um componente essencial da infraestrutura moderna baseada em LLMs. Ao permitir que modelos de IA acessem dados externos, ele ajuda a resolver problemas como alucinações e dados de treinamento incompletos. Também permite que as empresas usem bases de conhecimento internas que contêm informações confidenciais ou proprietárias.

A implementação de RAG pode ser muito complexa. No entanto, um sistema RAG básico é surpreendentemente fácil de construir. Para iniciantes, criar uma arquitetura simples pode esclarecer como o RAG funciona no mundo real e servir de ponto de partida para novos experimentos.

Neste artigo, vamos mapear uma implementação básica de RAG, abordar as práticas recomendadas e, em seguida, construir um sistema funcional usando Python, ChromaDB e dois modelos da Perplexity para embedding e geração de respostas.

Processo básico de implementação de RAG: uma visão geral

Um sistema RAG básico tem três componentes principais: um orquestrador, uma fonte de informações e um LLM. Em um fluxo de trabalho típico, o usuário insere um prompt, e o orquestrador o modifica para pesquisar as fontes de informações conectadas. As informações recuperadas são então adicionadas ao contexto da entrada original e enviadas ao LLM para que uma resposta possa ser gerada.

Você precisará considerar estas etapas ao construir sua implementação de RAG:

  1. Indexação e embedding: Nesta etapa, seus documentos são convertidos em uma fonte de informações que o orquestrador pode pesquisar. Uma das formas mais comuns de fazer isso é criar um índice vetorial pesquisável. Usaremos um modelo dedicado da Perplexity para converter os trechos (chunks) dos seus documentos em embeddings. Em seguida, armazenaremos os embeddings no ChromaDB, um banco de dados vetorial de código aberto bastante popular.
  2. Orquestração: É a aplicação que fica entre a fonte de informações externa (no nosso caso, um banco de dados vetorial) e o LLM. Criaremos um script Python simples para atuar como orquestrador.
  3. Recuperação e geração: Quando um usuário faz uma pergunta pela interface de chat, o orquestrador a recebe, executa uma busca vetorial e envia a entrada expandida ao LLM. Em vez de criar um frontend dedicado, usaremos a linha de comando para enviar prompts e nos comunicar com nosso orquestrador em Python.

Práticas recomendadas: o que torna uma implementação de RAG eficaz?

Existem vários tipos de arquitetura RAG, e cada caso de uso exige uma abordagem diferente. Dito isso, várias práticas recomendadas se aplicam a todos os casos e garantem sistemas precisos, econômicos, escaláveis e fáceis de manter.

As seguintes práticas recomendadas se aplicam a todos os tipos de implementação de RAG:

  • Garanta um pipeline de ingestão limpo: Seus dados de origem devem estar o mais limpos possível antes da indexação. Você também deve usar um modelo de embedding adequado ao seu conteúdo.
  • Use busca híbrida: Os melhores métodos de recuperação, seja busca em banco de dados vetorial, busca na web via API, travessia de grafos de conhecimento, busca por palavras-chave ou outros, variam conforme o caso de uso. Escolher a combinação certa é importante para minimizar custos.
  • Avalie seu sistema: Após a implementação, você deve avaliá-lo com base em três medidas principais: precisão, relevância e latência. A precisão se refere à exatidão factual; a relevância, à aplicabilidade das informações recuperadas às consultas dos usuários; e a latência, à velocidade com que as informações são recuperadas.
  • Trate as informações com segurança: Se o seu sistema RAG utiliza informações internas privadas, garanta que elas sejam armazenadas com segurança e que o acesso à interface do sistema seja controlado. As chaves de API nunca devem ser compartilhadas.

Implementação básica de RAG: tutorial em 7 etapas

Neste tutorial, vamos criar uma aplicação de linha de comando que responde a perguntas sobre um único documento. Usaremos Python, ChromaDB e a API da Perplexity.

O objetivo não é construir um sistema RAG pronto para produção. Trata-se de um tutorial para iniciantes, criado para ilustrar como uma arquitetura RAG rudimentar funciona na prática.

Etapa 1: instale o software

Verifique se os itens a seguir estão instalados no seu computador e se você tem uma conta na API da Perplexity:

  • Python: Esta é a linguagem de programação que usaremos para construir o orquestrador, que coordenará o fluxo de dados entre o modelo de embedding, o ChromaDB e a Perplexity.
  • Visual Studio: Usaremos o Visual Studio como editor de código. Depois de instalá-lo, abra o painel Extensões e baixe a extensão Python.
  • Conta na API da Perplexity: A Perplexity fornecerá tanto o modelo de embedding (que transforma seus dados em embeddings vetoriais) quanto o LLM que processará o prompt expandido. Você não precisa de uma conta paga para isso, embora haja um pequeno custo em créditos.

Etapa 2: crie uma pasta de projeto

Comece criando uma pasta chamada rag-project e abra-a no VS Code selecionando Arquivo > Abrir Pasta. Em seguida, abra o terminal integrado em Terminal > Novo Terminal.

Digite o seguinte para criar um ambiente virtual Python:

bash
py -3.12 -m venv .venv

Agora você precisa instalar quatro bibliotecas Python: perplexityai, chromadb, python-dotenv e numpy. O SDK da Perplexity permitirá que você se conecte aos modelos da Perplexity via API. O ChromaDB cria o banco de dados vetorial e será executado localmente. O python-dotenv carrega suas chaves de API com segurança a partir de um arquivo local, e o NumPy é necessário durante o processo de ingestão.

Instale-as com o seguinte comando:

bash
.\.venv\Scripts\python.exe -m pip install perplexityai chromadb python-dotenv numpy

Etapa 3: configure seus arquivos

Adicione os seguintes arquivos a rag-project (raiz do projeto) selecionando Novo Arquivo… na barra lateral do Explorador:

  • index.py: Esta é a aplicação que indexará seu documento. Você pode executá-la novamente para adicionar mais texto.
  • rag.py: Esta é a aplicação Python que atuará como orquestrador.
  • document.txt: Este arquivo conterá a documentação que você converterá em um banco de dados vetorial.
  • .env: É aqui que você guardará sua chave da API da Perplexity.

Em .env, digite o seguinte:

plaintext
PERPLEXITY_API_KEY=your_api_key_here

Substitua your_api_key_here pela sua chave real. Para isso, acesse o painel inicial do Perplexity API Console e selecione Create an API key. Mantenha essa chave de API em sigilo.

Usaremos dois modelos: o pplx-embed-v1-0.6b da Perplexity, para gerar os embeddings da nossa documentação antes de enviá-la ao ChromaDB, e o GPT-5.6-Luna (acessado pela API da Perplexity), que processará as informações recuperadas.

Em document.txt, adicione o texto que seu pipeline de RAG recuperará. Se você não tiver um documento à mão, use Songs of Innocence and of Experience, de William Blake, disponível no Project Gutenberg, para este tutorial. É um texto curto, fácil de entender e de domínio público.

Etapa 4: escreva o programa de indexação

Copie e cole o código abaixo em index.py. Cada subtítulo traz uma breve explicação do que o trecho de código seguinte faz. Este é o programa que converterá seu material de origem em embeddings vetoriais e os armazenará em um banco de dados ChromaDB usando o modelo de embedding da Perplexity, pplx-embed-v1-0.6b, e a biblioteca NumPy.

Veja o que o programa faz:

  1. Abre document.txt.
  2. Divide o texto em trechos sobrepostos.
  3. Envia esses trechos ao modelo de embedding pplx-embed-v1-0.6b da Perplexity.
  4. Decodifica e normaliza os vetores retornados usando o NumPy, uma biblioteca Python de funções computacionais.
  5. Armazena os vetores e os trechos originais em um banco de dados ChromaDB (você o verá aparecer no diretório do projeto).

Ele foi configurado para não reindexar seu documento sempre que for executado. Se você adicionar informações a document.txt, ele gerará embeddings apenas do novo texto.

python
from pathlib import Path from collections import defaultdict, deque from hashlib import sha256 import base64 import numpy as np import chromadb from dotenv import load_dotenv from perplexity import Perplexity # 1. Connect to Perplexity and ChromaDB load_dotenv() ai = Perplexity() db = chromadb.PersistentClient(path="./rag_database_perplexity") collection = db.get_or_create_collection( name="documents", metadata={"hnsw:space": "cosine"} ) # Convert Perplexity's encoded embeddings into numerical vectors def decode_embedding(encoded): vector = np.frombuffer(base64.b64decode(encoded), dtype=np.int8).astype(np.float32) norm = np.linalg.norm(vector) if norm == 0: raise ValueError("Received a zero-length embedding vector.") return (vector / norm).tolist() # 2. Read the document and split it into chunks text = Path("document.txt").read_text(encoding="utf-8") words = text.split() chunk_size = 180 overlap = 30 step = chunk_size - overlap chunks = [" ".join(words[i:i + chunk_size]) for i in range(0, len(words), step)] if not chunks: raise ValueError("document.txt is empty.") # 3. Retrieve the chunks already stored in ChromaDB existing = collection.get(include=["documents"]) stored_chunks = defaultdict(deque) existing_ids = set(existing["ids"]) for chunk_id, document in zip(existing["ids"], existing["documents"]): stored_chunks[document].append(chunk_id) # 4. Identify unchanged and new chunks new_chunks = [] new_ids = [] kept_ids = set() unchanged = 0 for chunk in chunks: if stored_chunks[chunk]: chunk_id = stored_chunks[chunk].popleft() kept_ids.add(chunk_id) unchanged += 1 else: chunk_hash = sha256(chunk.encode("utf-8")).hexdigest() chunk_id = f"chunk-{chunk_hash}" suffix = 1 while chunk_id in existing_ids or chunk_id in kept_ids or chunk_id in new_ids: chunk_id = f"chunk-{chunk_hash}-{suffix}" suffix += 1 new_chunks.append(chunk) new_ids.append(chunk_id) kept_ids.add(chunk_id) # 5. Generate embeddings in batches of up to 100 chunks batch_size = 100 for i in range(0, len(new_chunks), batch_size): batch_chunks = new_chunks[i:i + batch_size] batch_ids = new_ids[i:i + batch_size] embeddings = ai.embeddings.create( model="pplx-embed-v1-0.6b", input=batch_chunks, encoding_format="base64_int8" ) vectors = [decode_embedding(item.embedding) for item in embeddings.data] # 6. Store each batch in ChromaDB collection.upsert( ids=batch_ids, documents=batch_chunks, embeddings=vectors ) print(f"Indexed batch {(i // batch_size) + 1}") # 7. Remove outdated chunks old_ids = [chunk_id for chunk_id in existing["ids"] if chunk_id not in kept_ids] if old_ids: collection.delete(ids=old_ids) # 8. Display indexing results print("Indexing complete!") print("Unchanged chunks:", unchanged) print("New or modified chunks:", len(new_chunks)) print("Removed chunks:", len(old_ids)) print("Total stored chunks:", collection.count())

Etapa 5: escreva o orquestrador do sistema RAG

Copie e cole o código abaixo em rag.py. Trata-se de uma implementação de RAG completa, porém mínima, que usa o SDK da Perplexity e a funcionalidade de banco de dados vetorial local do ChromaDB.

Veja o que acontecerá quando você executar o programa:

  1. Conecta-se à Perplexity e ao seu novo banco de dados ChromaDB.
  2. Verifica se o banco de dados contém documentos indexados.
  3. Aceita uma pergunta do usuário.
  4. Envia a pergunta ao modelo de embedding da Perplexity.
  5. Decodifica e normaliza o embedding resultante usando o NumPy.
  6. Pesquisa no ChromaDB os três trechos de documento mais semelhantes.
  7. Monta um prompt com os trechos recuperados e a pergunta do usuário.
  8. Envia o prompt ao GPT-5.6 Luna pela Agent API da Perplexity.
  9. Exibe a resposta e aguarda outra pergunta.
python
import base64 import numpy as np import chromadb from dotenv import load_dotenv from perplexity import Perplexity # 1. Connect to Perplexity and the existing ChromaDB database load_dotenv() ai = Perplexity() db = chromadb.PersistentClient(path="./rag_database_perplexity") collection = db.get_collection("documents") # Convert Perplexity's encoded embeddings into numerical vectors def decode_embedding(encoded): vector = np.frombuffer(base64.b64decode(encoded), dtype=np.int8).astype(np.float32) norm = np.linalg.norm(vector) if norm == 0: raise ValueError("Received a zero-length embedding vector.") return (vector / norm).tolist() # 2. Check that the database contains indexed documents count = collection.count() if count == 0: raise ValueError("The database is empty. Run index.py first.") print("Connected to existing ChromaDB database.") print("Stored chunks:", count) # 3. Accept questions from the user while True: question = input("\nAsk a question (or type exit): ") if question.lower().strip() == "exit": break if not question.strip(): continue # 4. Convert the question into a Perplexity embedding question_response = ai.embeddings.create( model="pplx-embed-v1-0.6b", input=[question], encoding_format="base64_int8" ) question_vector = decode_embedding(question_response.data[0].embedding) # 5. Retrieve the most similar document chunks from ChromaDB results = collection.query( query_embeddings=[question_vector], n_results=min(3, count) ) context = "\n\n".join(results["documents"][0]) # 6. Construct a prompt and send it to the fixed LLM response = ai.responses.create( model="openai/gpt-5.6-luna", instructions=( "Answer using only the supplied document excerpts. " "Do not use external information. " "If the answer is not present, say you don't know." ), input=f"Document excerpts:\n{context}\n\nQuestion: {question}" ) # 7. Display the answer print("\nAnswer:\n", response.output_text)

Etapa 6: execute as aplicações

No terminal do PowerShell no VS Code, digite o seguinte para gerar os embeddings do conteúdo de document.txt:

bash
.\.venv\Scripts\python.exe index.py

Se o documento tiver sido indexado com sucesso, você verá uma saída semelhante a esta:

plaintext
Indexing complete! Unchanged chunks: 0 New or modified chunks: 58 Removed chunks: 0 Total stored chunks: 58

Agora é hora de executar a aplicação principal do orquestrador. Digite este comando:

bash
.\.venv\Scripts\python.exe rag.py

Se a execução for bem-sucedida, você verá o seguinte:

plaintext
Connected to existing ChromaDB database. Stored chunks: 58 Ask a question (or type exit):

Agora você pode fazer perguntas que serão respondidas com base no banco de dados vetorial que você criou, como:

plaintext
Ask a question (or type exit): What animals does William Blake talk about? Answer: The excerpts mention sheep, lambs, birds, beasts, wolves, tigers, and a lion.

Etapa 7: entenda o que você construiu

O sistema que você construiu contém os dois pipelines principais presentes em toda arquitetura RAG: um pipeline de indexação, ou “ingestão”, e um pipeline de recuperação e geração.

Agora, sua implementação básica de RAG faz o seguinte:

  • Armazenamento vetorial: Os embeddings, gerados a partir do seu arquivo de conhecimento, são salvos no banco de dados ChromaDB do seu ambiente local.
  • Indexação incremental: Trechos de documento inalterados não passam novamente pelo processo de embedding, e você pode atualizar o documento sempre que quiser.
  • Recuperação semântica: O ChromaDB recupera trechos com base na similaridade vetorial (a pedido do orquestrador).
  • Geração de respostas fundamentadas: O LLM recebe as passagens recuperadas como contexto e as incorpora a uma resposta.
  • Indexação e consulta independentes: Você pode fazer perguntas sem reprocessar todo o texto contido no seu arquivo de conhecimento.
  • Conexão com o modelo via API: Todas as respostas são geradas pelo GPT-5.6 Luna por meio da Perplexity, e não por um modelo executado na sua máquina.

Próximos passos: como levar esta implementação adiante

O objetivo deste tutorial é construir uma implementação simples e básica. Ele aborda alguns dos componentes centrais da arquitetura RAG presentes nos sistemas modernos. Se quiser ir além, há várias formas de expandi-la.

Veja algumas ideias para ampliar o escopo da sua implementação:

  • Experimente o LangChain: O LangChain é um dos frameworks de orquestração de código aberto mais populares. Ele contém componentes prontos para criar aplicações capazes de executar pipelines de RAG complexos.
  • Amplie a funcionalidade de busca: Combine a busca vetorial densa com a busca lexical (por palavras-chave), como o BM25. Adicionar busca na web pela Search API da Perplexity também permitiria fundamentar as respostas em dados atuais.
  • Adicione um modelo de reranking: Integre um modelo de reranking que classifique as passagens recuperadas em relação ao prompt inicial e selecione as mais relevantes para inclusão no contexto de entrada fornecido ao LLM.
  • Construa um sistema RAG adaptativo: O RAG adaptativo usa um LLM ou um classificador (um componente do orquestrador) para determinar qual método de recuperação usar em um sistema RAG modular. Construir um é uma boa forma de explorar implementações de RAG mais sofisticadas.

Embora tudo isso possa parecer complexo, lembre-se de que plataformas de IA populares, como a Perplexity, podem oferecer assistência avançada de programação. Elas são excelentes para projetos pessoais e experimentação, e os custos podem ser mantidos relativamente baixos.

Observação: este tutorial usa o GPT-5.6 Luna. A disponibilidade dos modelos pode mudar, então talvez seja necessário atualizar a chamada de API.