O Que É RAG e Por Que Importa
RAG é uma técnica que conecta um LLM a uma base de conhecimento externa. Em vez do modelo inventar respostas, ele primeiro busca informações relevantes nos seus documentos e depois gera a resposta com base neles.
O fluxo é simples:
- Cliente pergunta: "Qual o prazo de entrega para São Paulo?"
- O sistema busca nos seus documentos (FAQ, políticas, catálogo) os trechos relevantes sobre entrega
- O LLM recebe a pergunta + os trechos e gera uma resposta precisa e contextualizada
Dado-chave: Chatbots com RAG resolvem 2x mais atendimentos sem intervenção humana comparado a chatbots com LLM puro, porque respondem com dados reais do negócio em vez de respostas genéricas.
Passo 1: Prepare Sua Base de Conhecimento
Antes de qualquer código, organize os documentos que o agente precisa conhecer:
- FAQ: Perguntas frequentes com respostas objetivas
- Políticas: Entrega, troca, cancelamento, pagamento
- Catálogo: Produtos/serviços com preços e especificações
- Processos: Como agendar, como solicitar suporte, como fazer upgrade
Formato ideal: Markdown ou texto plano. Um documento por tema. Parágrafos curtos e diretos. Evite PDFs escaneados (OCR adiciona ruído).
Passo 2: Crie Embeddings e Vector Store
Embeddings transformam texto em vetores numéricos. Textos semanticamente similares ficam próximos no espaço vetorial. É assim que o sistema encontra os trechos relevantes para cada pergunta.
npm install openai @supabase/supabase-js
Script para indexar documentos:
const { createClient } = require('@supabase/supabase-js');
const OpenAI = require('openai');
const fs = require('fs');
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY);
const openai = new OpenAI({ apiKey: process.env.OPENAI_KEY });
async function indexDocument(filePath) {
const text = fs.readFileSync(filePath, 'utf-8');
// Divide em chunks de ~500 caracteres
const chunks = text.match(/.{1,500}/gs) || [];
for (const chunk of chunks) {
const embedding = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: chunk,
});
await supabase.from('documents').insert({
content: chunk,
embedding: embedding.data[0].embedding,
source: filePath,
});
}
console.log(`Indexado: ${filePath} (${chunks.length} chunks)`);
}
// Indexe todos os docs
indexDocument('./docs/faq.md');
indexDocument('./docs/politicas.md');
indexDocument('./docs/catalogo.md');
No Supabase, crie a tabela com a extensão pgvector habilitada:
create extension if not exists vector;
create table documents (
id bigserial primary key,
content text,
embedding vector(1536),
source text,
created_at timestamp default now()
);
create index on documents
using ivfflat (embedding vector_cosine_ops)
with (lists = 100);
Passo 3: Função de Busca Semântica
Quando o cliente pergunta algo, transforme a pergunta em embedding e busque os documentos mais similares:
async function searchDocs(query, limit = 3) {
const embedding = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: query,
});
const { data } = await supabase.rpc('match_documents', {
query_embedding: embedding.data[0].embedding,
match_threshold: 0.7,
match_count: limit,
});
return data.map(d => d.content).join('\n\n');
}
No Supabase, crie a função RPC:
create function match_documents(
query_embedding vector(1536),
match_threshold float,
match_count int
) returns table (content text, similarity float)
language plpgsql as $$
begin
return query
select documents.content,
1 - (documents.embedding <=> query_embedding) as similarity
from documents
where 1 - (documents.embedding <=> query_embedding) > match_threshold
order by similarity desc
limit match_count;
end;
$$;
Passo 4: Conecte RAG ao WhatsApp
No handler do webhook (do tutorial anterior), substitua a chamada direta ao LLM por busca + geração:
app.post('/webhook', async (req, res) => {
// ... extrai mensagem ...
// 1. Busca documentos relevantes
const context = await searchDocs(text);
// 2. Gera resposta com contexto
const completion = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{
role: 'system',
content: `Você é o assistente da [Empresa].
Responda APENAS com base no contexto abaixo.
Se não souber, diga que vai transferir para um atendente.
CONTEXTO:
${context}`
},
{ role: 'user', content: text }
],
});
await sendMessage(from, completion.choices[0].message.content);
res.sendStatus(200);
});
Dado-chave: O custo de embedding com text-embedding-3-small é US$ 0,02 por milhão de tokens. Indexar 100 páginas de documentação custa centavos. A busca semântica no Supabase é gratuita no free tier.
Erros Comuns e Como Evitar
| Erro | Causa | Solução |
|---|---|---|
| Respostas genéricas | Chunks muito grandes, contexto diluído | Chunks de 300-500 chars com overlap de 50 |
| Não encontra resposta | Threshold de similaridade alto demais | Reduza de 0.8 para 0.65-0.7 |
| Respostas inventadas | System prompt não restringe | Adicione "APENAS com base no contexto" |
| Lento demais | Muitos chunks retornados | Limite a 3-5 resultados, use cache |
Evolução: RAG + Ações
RAG sozinho informa. RAG + function calling age. O próximo passo é adicionar tools que o LLM pode chamar: consultar estoque, agendar horário, gerar boleto. O agente deixa de ser FAQ inteligente e vira assistente operacional completo.
A Verboo entrega RAG + function calling + WhatsApp nativo pronto para uso. Sem configurar Supabase, sem indexar documentos manualmente. Conheça os planos.
]]>


