Acho que não é surpresa pra ninguém que muitas vezes quando perguntamos algo para os LLMs eles acabam inventando fatos, estudos imports de código que não existe — e tudo isso com uma grande certeza?

Esse comportamento conhecido como alucinação, é um dos maiores obstáculos para a adoção de IA em aplicações críticas. Quando seu produto precisa de fatos, e não de ficção, como garantir que o LLM permaneça fiel à realidade?

A resposta não é treinar um modelo maior, mas sim dar a ele uma "cola" em tempo real.

RAG — A prova com consulta

Imagine que seu LLM está fazendo uma prova. Mais especificamente uma prova de memória, ele responde com base apenas no que foi "estudado" durante a fase de treinamento. Se por acaso a informação com a qual o modelo foi treinado acaba ficando desatualizada, ele improvisa — e alucina.

A Geração Aumentada por Recuperação (RAG) transforma esse cenário em uma prova com consulta. Em vez de depender apenas da memória, o modelo primeiro recupera informações relevantes de uma base de conhecimento confiável — seus documentos, sua base de dados, sua API — e só então gera a resposta com base nesses fatos concretos.

Ele não está mais adivinhando. Ele está citando.

O RAG ancora as respostas do LLM em sua fonte de verdade, reduzindo drasticamente as alucinações e permitindo que você entregue respostas precisas e baseadas em dados atualizados. Pesquisas recentes, como o estudo "The Power of Noise" (Jan 2024), mostram que a qualidade dessa recuperação é o fator mais crítico. Se você entregar os documentos certos, o LLM brilha. Se entregar "ruído", ele se perde. O famoso garbage in, garbage out.

Construir um sistema RAG robusto não é apenas sobre conectar um LLM a um banco de vetores. É sobre dominar três pilares:

  • Chunking Inteligente: Como você divide seus documentos? A pesquisa aponta para técnicas semânticas e baseadas em conteúdo, que preservam o contexto muito melhor do que uma simples divisão por contagem de caracteres.

  • Recuperação Eficiente: A busca vetorial inicial que traz um conjunto amplo de candidatos.

  • Reranking Cirúrgico: Onde a mágica acontece. Um segundo filtro que analisa os documentos recuperados e os reordena, garantindo que o contexto mais relevante chegue ao topo.

Dominar esse fluxo é o que separa um chatbot que "acha" de uma aplicação de IA que "sabe".

Código na prática

A qualidade do RAG é diretamente impactada pela qualidade dos chunks. Se você dividir uma frase importante no meio, o contexto se perde. Se seus chunks forem muito grandes, você introduz ruído e pode estourar a janela de contexto do modelo.

A forma como você "fatia" seus documentos antes de inseri-los no banco de dados vetorial é uma decisão de engenharia crucial. Vamos explorar três estratégias, da mais simples à mais avançada, com código prático.

Chunking de Tamanho Fixo

A abordagem mais simples: cortar o texto a cada N caracteres. É rápido e fácil, mas perigoso, pois pode quebrar frases e ideias ao meio, destruindo o contexto.

import langchain
from langchain.text_splitter import CharacterTextSplitter

text = "..." # String completa do seu documento (que pode ser um pdf, txt, csv, etc)

# Define o tamanho do chunk e uma sobreposição (overlap) para tentar manter o contexto
text_splitter = CharacterTextSplitter(
    separator="\n\n", # Tenta separar por parágrafos primeiro
    chunk_size=512,
    chunk_overlap=100 
)

docs = text_splitter.create_documents([text])
print(f"Documento dividido em {len(docs)} chunks de tamanho fixo.")

Quando usar? Para uma prova de conceito muito rápida ou com textos que não têm uma estrutura clara. Em geral, evite em produção.

Chunking Recursivo

Essa é a abordagem mais recomendada como linha de base. Ele tenta dividir o texto usando uma hierarquia de separadores, como \n\n (parágrafo), \n (linha), (espaço). Isso o torna muito mais propenso a manter blocos de texto semanticamente coesos.

from langchain.text_splitter import RecursiveCharacterTextSplitter

text = "..." # String completa do seu documento (que pode ser um pdf, txt, csv, etc)

# Tenta dividir recursivamente para manter a coesão
recursive_splitter = RecursiveCharacterTextSplitter(
    chunk_size=512,
    chunk_overlap=100,
    separators=["\n\n", "\n", "(?<=\. )", " ", ""] # Hierarquia de separadores
)

docs_recursive = recursive_splitter.create_documents([text])
print(f"Documento dividido em {len(docs_recursive)} chunks recursivos.")

Quando usar? Quase sempre. É o melhor equilíbrio entre simplicidade, desempenho e preservação de contexto para a maioria dos casos de uso.

Chunking Semântico

Em vez de usar contagem de caracteres ou regras, o chunking semântico usa um modelo de embedding para decidir onde dividir. Ele agrupa frases que são semanticamente relacionadas, criando chunks que são conceitualmente completos.

from llama_index.experimental.text_splitter import SemanticSplitterNodeParser
from llama_index.core import SimpleDirectoryReader
from llama_index.embeddings.huggingface import HuggingFaceEmbedding

# Carrega um modelo de embedding open-source
embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")

# O splitter usará o modelo de embedding para encontrar os pontos de quebra
semantic_splitter = SemanticSplitterNodeParser.from_defaults(
    embed_model=embed_model,
    buffer_size=1,
    breakpoint_percentile_threshold=95 # Ajuste para controlar o tamanho dos chunks
)

# Carrega o documento e aplica o chunking semântico
documents = SimpleDirectoryReader("./data").load_data()
nodes = semantic_splitter.get_nodes_from_documents(documents)
print(f"Documento dividido em {len(nodes)} chunks semânticos.")

Quando usar? Quando a precisão do contexto é crítica. Ideal para documentos densos e complexos (artigos científicos, contratos legais) onde a relação entre as frases não é óbvia.

Ferramenta da semana

Para implementar o passo de reranking com uma abordagem open source, a combinação da biblioteca sentence-transformers com os modelos disponíveis no HuggingFace é a melhor saída. Você ganha poder e controle total sobre seu pipeline.

Por que é a ferramenta da semana?

  • Controle Total e Custo Zero: Por ser open source, você hospeda o modelo, elimina custos de API e evita o vendor lock-in. Seus dados nunca saem da sua infraestrutura.

  • Implementação Simples: A biblioteca sentence-transformers abstrai a complexidade. Com a classe CrossEncoder, você pode carregar um modelo de ponta e fazer o reranking com poucas linhas de código.

  • Vasta Seleção de Modelos: O HuggingFace é um repositório gigantesco de modelos cross-encoder pré-treinados. Você pode escolher um que seja otimizado para o seu domínio (jurídico, financeiro, etc.) ou idioma. Um ótimo ponto de partida é o cross-encoder/ms-marco-MiniLM-L-6-v2.

from sentence_transformers.cross_encoder import CrossEncoder

# Carregue um modelo pré-treinado do Hugging Face Hub
model = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')

# Documentos recuperados da primeira fase
retrieved_docs_content = [doc.page_content for doc in retrieved_docs]

# Crie pares (pergunta, documento) para o modelo pontuar
model_input = [(user_query, doc_content) for doc_content in retrieved_docs_content]

# Calcule as pontuações de relevância
scores = model.predict(model_input)

# Agora, reordene seus documentos com base nos scores!

Se você quer controle máximo e custo zero, mergulhe no mundo dos rerankers open source. Comece explorando a documentação do Sentence-Transformer e navegue pelos modelos cross-encoder disponíveis no Huggingface.

Uma dica que posso dar, é ainda procurar pelos modelos reranker disponíveis no Ollama. Dessa forma, é possível subir uma API para servir o modelo de forma muito simples e direta ao ponto.

Espero que esta edição tenha sido útil para desmistificar como construir aplicações de IA mais confiáveis utilizando RAG. Para quem chegou até aqui, deixo um desafio que fiz para uma vaga de emprego na Hotmart.

O objetivo foi fazer um aplicação baseada em RAG na qual uma API poderia receber arquivos com o objetivo de carregá-los no banco de dados vetorial e ao ser enviado um prompt, também fosse capaz de buscar o conteúdo no banco de dados vetorial, incrementar o contexto para daí sim o LLM realizar a inferência.

Gostou do conteúdo? Compartilhe esta newsletter com um colega se não gostou mande pra alguém que você desgoste.

Até a próxima

Reply

Avatar

or to participate

Keep Reading