Sempre pensamos em trabalhar nossos dados integrados com modelos de LLMs, e esbarrar em um conceito chamado Embedding
RAG e Data Embedding
Sempre que pensarmos em trabalhar com nossos dados integrados com modelos de LLMs, vamos esbarrar em um conceito chamado Embeddings, que em um primeiro momento, pode ser algo complexo de entender (em um segundo, terceiro e quarto também, rs) - mas vamos tentar simplificar.
O custo para treinar um modelo de LLM é alto, fazer fine-tuning também pode ser trabalhoso, aí entra o conceito de RAG, que vamos entender um pouco mais pra frente.
Modelos de embedding transformam textos em números, é como um tradutor realmente, ele foi treinado com uma quantidade significativa de dados e quando eu envio um texto, ele simplesmente executa esse código que faz essa tradução.
E é isso que permite que frases diferentes, mas com um mesmo significado sejam identificadas de forma similar. Por exemplo:
Note que as quatro primeiras frases, mesmo escritas de formas diferentes, geram embeddings similares porque têm a mesma intenção/significado. Já a última frase, sobre pudim, gera números bem diferentes.
O processo funciona assim:
O texto é quebrado em tokens (pedaços que podem ser palavras ou sub-palavras)
Cada token é processado considerando seu contexto
O modelo gera uma lista de números que representa o significado do texto
Textos similares geram números similares
E porque isso importa? Quando eu pego por exemplo uma base de conhecimento antiga, com chamados resolvidos, manuais, histórico de aulas e quero que um modelo de LLM responda com base nessas informações, eu preciso basicamente:
Criar embeddings de todo esse conteúdo
Quebrar documentos em chunks (pedaços menores) - isso é importante, porque de forma parecida com um banco de dados, nós precisamos separar esses itens em “itens” de uma tabela, agrupado por registros,
Gerar embeddings para cada chunk
Armazenar em um banco de dados vetorial (como Pinecone, Qdrant, pgvector)
Quando uma pergunta chega:
Gerar embedding da pergunta
Pesquisar por similaridade no banco de dados vetorial
Recuperar os chunks mais relevantes
Enviar para o LLM:
A pergunta original
O conteúdo relevante encontrado
Um prompt que instrui como usar esse conteúdo
O LLM então gera uma resposta contextualizada
Este processo é o que chamamos de RAG (Retrieval Augmented Generation), porque:
Retrieval: recuperamos informação relevante do nosso banco de dados
Augmented: aumentamos/melhoramos o conhecimento do LLM
Generation: geramos uma resposta com base nesse contexto
E um ponto importante, quebrar documentos em chunks (pedaços menores) é uma etapa crucial no processo de RAG. Pense nisso como criar "unidades semânticas" do seu conteúdo - similar a um banco de dados, mas em vez de registros estruturados, queremos blocos de texto que mantenham significado coeso.
Por exemplo:
Um manual técnico pode ser dividido por seções ou procedimentos completos
Uma base de chamados pode ser dividida por problema/solução
Um material didático pode ser dividido por conceitos ou tópicos
O tamanho desses chunks geralmente varia entre 256 e 1024 tokens, com alguma sobreposição (overlap) de 10-20% para manter contexto entre eles. É um balanço entre:
Manter contexto suficiente para respostas precisas
Ter granularidade adequada para buscas eficientes
Otimizar o uso de recursos (custo, processamento)
Diferente de um banco de dados tradicional, onde os registros têm estrutura fixa, no RAG os chunks podem ter tamanhos variáveis para preservar a coerência do conteúdo. O importante é que cada chunk mantenha uma unidade lógica de informação completa o suficiente para ser útil em uma resposta.
E apenas para fixarmos nosso conteúdo, vejamos abaixo um exemplo de como isso funciona na prática com um exemplo extremamente simples.
Imagine que esse é o passo a passo para instalarmos o docker:
Como configurar o Docker
Passo 1: Instale o Docker
- Acesse docker.com
- Baixe a versão para seu SO
- Execute o instalador
Passo 2: Verifique a instalação
- Abra o terminal
- Digite docker --version
Dividido em Chunks
Chunk ID: chunk_1_1
Como configurar o Docker
Passo 1: Instale o Docker
- Acesse docker.com
- Baixe a versão para seu SO
- Execute o instalador
Metadados e Embedding
Embedding: [0.23, 0.45, -0.12, ...]
Metadata:
{
"source": "manual_docker.pdf",
"page": 1,
"topic": "instalação"}
Documento Original: Ticket de Suporte
Problema: Docker não inicia no Windows
Erro: daemon não responde
Solução: Reinicie o Docker Desktop e verifique se o WSL2 está habilitado
Dividido em Chunks
Chunk ID: chunk_2_1
Problema: Docker não inicia no Windows
Erro: daemon não responde
Solução: Reinicie o Docker Desktop e verifique se o WSL2 está habilitado
Metadados e Embedding
Embedding: [0.31, 0.52, -0.18, ...]
Metadata:
{
"source": "ticket_123",
"date": "2024-01-10",
"status": "resolved"}
E para fecharmos, alguns pontos importantes:
Cada chunk mantém uma unidade lógica completa
Embeddings são vetores de números que representam o significado do texto
Metadados ajudam a rastrear a origem e contexto do chunk
A busca é feita comparando o embedding da pergunta com os embeddings armazenados
Então é assim que podemos salvar nossas informações para trabalharmos com modelos de LLM:
Processo de Armazenamento:
Texto vira embedding (aquela "tradução" para números)
É armazenado em um banco de dados vetorial
Junto com metadados importantes sobre o conteúdo
Quando uma pergunta chega, nós fazemos o processo de RAG (Retrieval Augmented Generation):
Retrieval (Busca): transformamos a pergunta em embedding e buscamos conteúdo similar no banco
Augmented (Aumento): pegamos esse conteúdo relevante encontrado
Generation (Geração): enviamos para o modelo a pergunta + conteúdo encontrado, e ele gera uma resposta que faz sentido
É como dar ao modelo uma "cola" personalizada para cada pergunta, usando nossa própria base de conhecimento.