RAG, de retrieval-augmented generation, é o nome de uma ideia simples: antes de o modelo responder, o seu sistema busca os trechos de texto mais relevantes para a pergunta e os coloca no contexto. O modelo responde com base no que recebeu, não só no que lembra do treino. É a forma mais comum de fazer um assistente falar sobre os documentos da sua empresa, e é também onde a maioria dos projetos tropeça, porque o problema difícil não é o modelo: é a busca.
Este é o sétimo artigo da trilha Aprenda IA construindo software. O artigo RAG para pequenas empresas discute quando vale a pena; este mostra como se constrói, com um experimento que rodamos de verdade sobre os artigos deste site.
As quatro etapas
- Dividir: cada documento vira pedaços menores, em inglês chunks. O modelo não precisa do manual inteiro, precisa do parágrafo certo.
- Vetorizar: cada pedaço vira uma lista de números que representa o seu conteúdo. Pedaços parecidos geram vetores parecidos.
- Buscar: a pergunta também vira vetor, e o sistema devolve os pedaços com vetores mais próximos, normalmente pela similaridade de cosseno.
- Montar o contexto: os pedaços encontrados entram na chamada ao modelo, cada um com a sua fonte, junto com a instrução de responder só com base neles e citar de onde veio.
Como fizemos aqui: buscando nos artigos deste site
Para que os números deste artigo não fossem inventados, montamos um experimento pequeno: um programa Java de um arquivo só que baixa todos os artigos publicados pela API pública do site, divide, vetoriza e busca. Em 21 de outubro de 2026 o acervo tinha 71 artigos e 107.886 palavras, que viraram 694 pedaços e um vocabulário de 6.816 termos.
A divisão usa pedaços de 200 palavras com sobreposição de 40. A sobreposição existe para que uma frase cortada no fim de um pedaço apareça inteira no começo do seguinte:
static List<String> dividir(String texto, int tamanho, int sobreposicao) {
List<String> todas = palavras(texto);
List<String> pedacos = new ArrayList<>();
for (int inicio = 0; inicio < todas.size(); inicio += tamanho - sobreposicao) {
int fim = Math.min(inicio + tamanho, todas.size());
pedacos.add(String.join(" ", todas.subList(inicio, fim)));
if (fim == todas.size()) break;
}
return pedacos;
}
Para vetorizar, o experimento usa TF-IDF, uma técnica clássica de busca por palavras: cada termo do pedaço recebe um peso que cresce com a frequência dele no pedaço e diminui com a frequência dele no acervo inteiro. Palavra rara pesa mais que palavra comum. A comparação entre pergunta e pedaço é o cosseno entre os dois vetores:
static double cosseno(Map<String, Double> a, Map<String, Double> b) {
double produto = 0;
for (var e : a.entrySet()) produto += e.getValue() * b.getOrDefault(e.getKey(), 0.0);
double normaA = Math.sqrt(a.values().stream().mapToDouble(v -> v * v).sum());
double normaB = Math.sqrt(b.values().stream().mapToDouble(v -> v * v).sum());
return normaA == 0 || normaB == 0 ? 0 : produto / (normaA * normaB);
}
Escolhemos TF-IDF de propósito: roda na hora, sem chave de API nem custo, e serve de linha de base. Qualquer sistema de embeddings que você adotar depois precisa ganhar dele nas suas perguntas, senão não se paga.
O que a busca acertou e o que errou
Estes são os resultados reais, com a pontuação de cosseno do primeiro pedaço devolvido:
- como publicar um app na Google Play: acertou. Os três primeiros pedaços são de artigos sobre revisão de loja e teste interno na Play (0,292).
- onde guardar a senha do banco de dados: acertou em parte. O artigo certo, sobre segredos e variáveis de ambiente, veio em segundo e terceiro; o primeiro foi um pedaço sobre arquitetura de app que menciona senha de passagem.
- assistente que responde no idioma errado: acertou. Devolveu o trecho do artigo de prompt que fala da regra de idioma do Porto (0,253).
- app perdeu aparelhos compatíveis por causa de permissão: quase errou. O pedaço certo, que conta como uma permissão de microfone tirou o app de 16 aparelhos, ficou em segundo, com pontuação baixa (0,084), atrás de um pedaço sobre desempenho de listas.
- a IA inventou uma biblioteca que não existe: errou. Os três pedaços devolvidos são de artigos de React Native, porque a palavra biblioteca aparece muito neles. Nenhum fala de alucinação.
- resposta inventada pelo modelo: a mesma dúvida com outras palavras acertou o artigo certo, o de como um LLM funciona.
Os dois últimos casos mostram o limite da busca por palavras: ela encontra o texto que usa as mesmas palavras da pergunta, não o que quer dizer a mesma coisa. O artigo sobre alucinação não usa a palavra biblioteca, e o caso do microfone fala em compatibilidade e modelos de telefone, não em aparelhos perdidos.
Onde entram os embeddings
É exatamente esse limite que os embeddings resolvem. Um modelo de embeddings transforma o texto num vetor que representa o significado, de forma que inventou uma biblioteca e alucinação fiquem próximos mesmo sem palavra em comum. No código, a troca é localizada: o método que gera o vetor passa a chamar a API de um provedor de embeddings, e a busca por cosseno continua igual. A Anthropic não oferece modelo de embeddings próprio e indica provedores como a Voyage AI; OpenAI, Google e modelos abertos também oferecem.
Duas consequências práticas da troca:
- Custo e tempo de indexação: cada pedaço precisa ser enviado uma vez ao provedor, e de novo sempre que o texto mudar. Com 694 pedaços, é pouco; com milhões, vira projeto.
- Onde guardar: para alguns milhares de vetores, uma lista em memória e uma busca linear bastam, como no experimento. Passando disso, entra um índice vetorial, como a extensão pgvector do PostgreSQL ou um banco vetorial dedicado.
O caminho que funciona em produção costuma ser híbrido: busca por palavras e por embeddings ao mesmo tempo, combinando as listas. A busca por palavras acerta nomes próprios, códigos e termos técnicos exatos; a semântica acerta paráfrases.
Montar o contexto e citar a fonte
Com os pedaços em mãos, o contexto enviado ao modelo deixa cada trecho identificado e a instrução explícita:
Responda usando apenas os trechos abaixo. Cite o endereço do artigo de onde
tirou cada informação. Se os trechos não respondem à pergunta, diga isso.
[fonte: /artigos/agente-de-codigo-na-pratica-app-android]
...texto do pedaço...
[fonte: /artigos/permissoes-app-react-native-camera-notificacoes-arquivos]
...texto do pedaço...
A frase se os trechos não respondem, diga isso é a mais importante: é ela que transforma uma busca ruim, como a da biblioteca inventada, em uma resposta honesta em vez de uma resposta inventada com fonte errada. O artigo Fontes confiáveis para IA corporativa trata desse cuidado do lado do processo.
E o Porto, precisa de RAG?
O Porto, assistente deste site, hoje não busca nos artigos. Com 694 pedaços e perguntas que quase sempre citam o tema pelo nome, o primeiro passo mais barato não é um índice vetorial, e sim uma ferramenta de busca simples no banco, como a descrita em Tool use e MCP. O experimento deste artigo dá o critério para mudar de ideia: se as perguntas reais dos visitantes começarem a parecer com a da biblioteca inventada, a busca semântica passa a se pagar.
Checklist
- Medi o acervo antes de escolher a arquitetura: quantos documentos, palavras e pedaços.
- Os pedaços têm tamanho definido e sobreposição, e guardam a fonte de onde vieram.
- Tenho uma linha de base simples, como busca por palavras, para comparar.
- Testei com perguntas reais, inclusive paráfrases, e registrei onde a busca errou.
- O contexto identifica a fonte de cada trecho e manda o modelo dizer quando não sabe.
- Sei quanto custa reindexar quando o conteúdo muda.
- Permissões de acesso valem para a busca: ninguém recupera trecho que não poderia ler.
Próximo passo da trilha: Como testar respostas de IA: avaliações para times pequenos.
Termos tecnicos desta leitura
Alguns conceitos aparecem com frequencia neste tema. Abrir o glossario ajuda a comparar definicoes, exemplos e leituras relacionadas sem sair do contexto do artigo.