Um dos problemas mais comuns ao tentar usar IA na empresa é este: o modelo sabe muita coisa sobre o mundo, mas não sabe nada sobre você. Não conhece seus produtos, seus processos, seus contratos, sua base de conhecimento interna. Quando você pergunta sobre algo específico do seu negócio, o modelo inventa — o fenômeno conhecido como alucinação.
Há duas formas tradicionais de resolver isso:
- Fine-tuning: treinar (ou ajustar) o modelo com seus dados. Caro, lento, e os dados ficam "congelados" no momento do treinamento.
- Colocar tudo no contexto: enviar seus documentos inteiros na chamada ao modelo. Funciona para documentos pequenos, mas fica caro e impraticável com grandes volumes.
RAG (Retrieval-Augmented Generation) é a terceira via — e na maioria dos casos é a certa. A ideia é simples: antes de chamar o modelo, você busca apenas os trechos relevantes dos seus documentos e passa só eles como contexto. O modelo responde com base no que foi recuperado, não com base no que "imagina".
Por que RAG funciona bem
A intuição é a mesma de um consultor humano com acesso a uma boa base de conhecimento. Você não contrata alguém que memorizou todos os seus contratos — você contrata alguém inteligente que sabe onde procurar a informação certa e como raciocinar sobre ela.
RAG funciona bem porque:
- Documentos podem ser atualizados sem retreinar o modelo
- O modelo cita as fontes usadas para responder (rastreabilidade)
- O custo marginal de adicionar novos documentos é baixo
- Alucinações diminuem drasticamente — o modelo tem âncoras de texto reais
Arquitetura de um sistema RAG
Um pipeline RAG tem duas fases:
Fase de indexação (offline)
- Carregamento: ler os documentos (PDFs, Word, páginas web, registros de banco)
- Chunking: dividir em pedaços menores (chunks) com sobreposição para não perder contexto nas bordas
- Embedding: transformar cada chunk em um vetor numérico que representa seu significado semântico
- Armazenamento: salvar os vetores num banco de dados vetorial (pgvector, Pinecone, Qdrant, Weaviate)
Fase de recuperação (online, em tempo real)
- Embedding da pergunta: transformar a pergunta do usuário no mesmo espaço vetorial
- Busca por similaridade: encontrar os chunks mais próximos semanticamente da pergunta
- Augmentation: montar o prompt com os chunks recuperados como contexto
- Generation: o LLM gera a resposta baseada no contexto fornecido
Implementando RAG do zero com pgvector e Python
Vamos construir um sistema RAG simples para indexar documentos internos e responder perguntas sobre eles.
1. Setup do banco com pgvector
-- Habilita a extensão no PostgreSQL
CREATE EXTENSION IF NOT EXISTS vector;
-- Tabela para armazenar chunks e seus embeddings
CREATE TABLE documentos_chunks (
id BIGSERIAL PRIMARY KEY,
fonte TEXT NOT NULL, -- nome do arquivo original
pagina INTEGER, -- página (se PDF)
conteudo TEXT NOT NULL, -- texto do chunk
embedding vector(1536), -- vetor do embedding (1536 dims = OpenAI/Cohere)
metadados JSONB DEFAULT '{}',
criado_em TIMESTAMPTZ DEFAULT NOW()
);
-- Índice HNSW para busca aproximada de alta performance
CREATE INDEX ON documentos_chunks
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
2. Pipeline de indexação
import anthropic
import psycopg2
import PyPDF2
from pathlib import Path
client = anthropic.Anthropic()
def extrair_texto_pdf(caminho: str) -> list[dict]:
"""Extrai texto de um PDF, página por página."""
chunks = []
with open(caminho, "rb") as f:
reader = PyPDF2.PdfReader(f)
for i, pagina in enumerate(reader.pages):
texto = pagina.extract_text()
if texto.strip():
chunks.append({
"fonte": Path(caminho).name,
"pagina": i + 1,
"conteudo": texto.strip()
})
return chunks
def chunkar_texto(texto: str, tamanho: int = 800, sobreposicao: int = 100) -> list[str]:
"""Divide o texto em chunks com sobreposição."""
palavras = texto.split()
chunks = []
inicio = 0
while inicio < len(palavras):
fim = min(inicio + tamanho, len(palavras))
chunk = " ".join(palavras[inicio:fim])
chunks.append(chunk)
if fim == len(palavras):
break
inicio += tamanho - sobreposicao
return chunks
def gerar_embedding(texto: str) -> list[float]:
"""Gera embedding usando a API da Anthropic (via Voyage AI integrado)."""
# Anthropic usa Voyage AI para embeddings
# Alternativa: usar OpenAI text-embedding-3-small ou Cohere embed-v3
import voyageai
vo = voyageai.Client()
resultado = vo.embed([texto], model="voyage-3")
return resultado.embeddings[0]
def indexar_documento(caminho_pdf: str, conn):
"""Indexa um documento PDF completo."""
print(f"Indexando {caminho_pdf}...")
paginas = extrair_texto_pdf(caminho_pdf)
with conn.cursor() as cur:
for pagina in paginas:
chunks = chunkar_texto(pagina["conteudo"])
for chunk in chunks:
if len(chunk.split()) < 20: # ignora chunks muito pequenos
continue
embedding = gerar_embedding(chunk)
cur.execute("""
INSERT INTO documentos_chunks (fonte, pagina, conteudo, embedding)
VALUES (%s, %s, %s, %s::vector)
""", (pagina["fonte"], pagina["pagina"], chunk, embedding))
conn.commit()
print(f" ✓ {caminho_pdf} indexado")
3. Motor de busca e resposta
def buscar_chunks_relevantes(
pergunta: str,
conn,
top_k: int = 5,
threshold: float = 0.7
) -> list[dict]:
"""Busca os chunks mais semanticamente próximos da pergunta."""
embedding_pergunta = gerar_embedding(pergunta)
with conn.cursor() as cur:
cur.execute("""
SELECT
fonte,
pagina,
conteudo,
1 - (embedding <=> %s::vector) AS similaridade
FROM documentos_chunks
WHERE 1 - (embedding <=> %s::vector) > %s
ORDER BY embedding <=> %s::vector
LIMIT %s
""", (embedding_pergunta, embedding_pergunta, threshold, embedding_pergunta, top_k))
return [
{
"fonte": row[0],
"pagina": row[1],
"conteudo": row[2],
"similaridade": float(row[3])
}
for row in cur.fetchall()
]
def responder_com_rag(pergunta: str, conn) -> str:
"""Responde uma pergunta usando RAG."""
# 1. Recupera chunks relevantes
chunks = buscar_chunks_relevantes(pergunta, conn)
if not chunks:
return "Não encontrei informações relevantes nos documentos indexados para responder essa pergunta."
# 2. Monta o contexto
contexto = "\n\n---\n\n".join([
f"Fonte: {c['fonte']}, Página {c['pagina']} (similaridade: {c['similaridade']:.2f})\n\n{c['conteudo']}"
for c in chunks
])
# 3. Chama o modelo com o contexto
resposta = client.messages.create(
model="claude-opus-4-5-20251101",
max_tokens=2048,
system="""Você é um assistente especializado nos documentos da empresa.
Responda APENAS com base nos trechos fornecidos como contexto.
Se a informação não estiver no contexto, diga claramente que não encontrou essa informação nos documentos.
Sempre cite a fonte (nome do arquivo e página) ao final da resposta.""",
messages=[{
"role": "user",
"content": f"""Contexto dos documentos:
{contexto}
---
Pergunta: {pergunta}"""
}]
)
return resposta.content[0].text
# Exemplo de uso
conn = psycopg2.connect("postgresql://usuario:senha@localhost/empresa_db")
# Indexar documentos
indexar_documento("contratos/contrato_servico_2026.pdf", conn)
indexar_documento("manuais/politica_rh.pdf", conn)
indexar_documento("financeiro/relatorio_q1_2026.pdf", conn)
# Responder perguntas
pergunta = "Qual é o prazo de aviso prévio previsto na política de RH?"
print(responder_com_rag(pergunta, conn))
RAG avançado: técnicas que melhoram a qualidade
Chunking híbrido
Em vez de dividir por tamanho fixo, use estrutura do documento: parágrafos, seções, tabelas. Um contrato dividido por cláusula funciona muito melhor do que dividido por 800 palavras no meio de uma cláusula.
Re-ranking
Após recuperar os top-K chunks por similaridade vetorial, use um modelo de re-ranking (Cohere Rerank, cross-encoders) para reordenar por relevância real. A busca vetorial é rápida mas aproximada — o re-ranking é mais preciso.
Busca híbrida (vetorial + BM25)
Combine busca semântica (vetorial) com busca por palavra-chave (BM25/FTS). Busca vetorial é melhor para significado; busca por keyword é melhor para termos técnicos específicos, números e siglas. O pgvector combinado com o FTS nativo do PostgreSQL resolve isso sem precisar de um segundo banco.
HyDE (Hypothetical Document Embeddings)
Antes de buscar, peça ao modelo para gerar um "documento hipotético" que responderia à pergunta, e use o embedding desse documento hipotético para a busca. Parece contra-intuitivo, mas melhora significativamente a recuperação para perguntas abstratas.
def busca_com_hyde(pergunta: str, conn) -> list[dict]:
"""RAG com HyDE: gera documento hipotético antes de buscar."""
# Gera resposta hipotética para melhorar o embedding de busca
hipotetico = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=300,
messages=[{
"role": "user",
"content": f"Escreva um parágrafo técnico que responderia diretamente à seguinte pergunta:\n\n{pergunta}\n\nEscreva como se fosse um trecho de um documento oficial."
}]
).content[0].text
# Usa o documento hipotético para busca (não a pergunta original)
return buscar_chunks_relevantes(hipotetico, conn)
Casos de uso por tipo de empresa
Escritório de advocacia
Base de dados de jurisprudência, contratos-modelo e pareceres indexados. Advogados consultam precedentes em segundos em vez de horas de pesquisa manual.
E-commerce com catálogo grande
Fichas técnicas, manuais e especificações de produtos indexados. Atendimento responde perguntas técnicas específicas ("esse produto funciona com 110V?") sem precisar de especialista humano.
RH e onboarding
Políticas, benefícios, guias de processos e FAQs indexados. Novos funcionários consultam o assistente em vez de abrir chamado para o RH para cada dúvida.
Suporte técnico de software
Documentação, changelogs, tickets resolvidos e notas de release indexados. Suporte N1 resolve mais chamados sem escalar para engenharia.
Quando RAG não é suficiente
RAG tem limitações. Se você precisa que o modelo aprenda um estilo específico (tom de voz, formato de resposta muito particular), RAG sozinho pode não bastar e fine-tuning faz sentido. Se a base de conhecimento é muito pequena e estática, colocar tudo no contexto do sistema pode ser mais simples. E se a pergunta requer raciocínio sobre todos os documentos ao mesmo tempo (estatísticas agregadas, análises comparativas), RAG recupera amostras — para isso, um data pipeline analítico é mais adequado.
Para a maioria dos casos de "IA que sabe sobre nossa empresa", porém, RAG é o ponto de partida correto. É o projeto de IA com melhor custo-benefício disponível hoje para empresas que querem resultados práticos sem investir meses em infraestrutura de ML.