~ / blog / ia / rag-pgvector
Atualizado em 16 set 2026 ia 14 min de leitura

IA: RAG com pgvector — assistente com documentos da empresa

Retrieval-Augmented Generation permite que o modelo responda com base em PDFs, wikis e bancos internos — com citações e menos alucinações. Este guia monta um protótipo funcional usando PostgreSQL + pgvector.

RAGpgvectorPostgreSQLembeddingsOpenAI

1. O que é RAG (e o que não é)

RAG (Retrieval-Augmented Generation) combina busca + geração. Em vez de depender só do conhecimento treinado no modelo, o sistema:

  1. Recebe a pergunta do usuário
  2. Busca trechos relevantes em uma base de documentos (PDFs, Markdown, tickets, etc.)
  3. Injeta esses trechos no contexto do prompt
  4. Pede ao modelo para responder usando apenas o material recuperado, citando fontes

Não é fine-tuning. Não é treinar um modelo do zero. É um padrão de arquitetura que reduz alucinações e permite atualizar a base sem re-treinar nada.

tip · Comece com documentos estáveis (manuais, políticas, runbooks). Tickets e chats mudam rápido e exigem pipeline de atualização.

2. Arquitetura mínima

Para um protótipo sério, quatro peças bastam:

  • Store de documentos — arquivos + metadados (título, origem, data)
  • Índice vetorial — embeddings dos chunks (aqui: pgvector)
  • Retriever — busca por similaridade + filtros opcionais
  • Gerador — LLM com prompt que força uso das fontes e citações

PostgreSQL + extensão pgvector é uma escolha sólida: você já tem o banco, ACID, backups e SQL. Para muitos casos (até dezenas/centenas de milhares de chunks) não precisa de Pinecone ou similar.

3. Preparar o PostgreSQL + pgvector

No Postgres 16+ (ou 15 com a extensão instalada):

-- Habilitar a extensão
CREATE EXTENSION IF NOT EXISTS vector;

-- Tabela de documentos (metadados)
CREATE TABLE documents (
  id          bigserial PRIMARY KEY,
  title       text NOT NULL,
  source      text,          -- caminho do arquivo, URL, etc.
  created_at  timestamptz DEFAULT now()
);

-- Chunks com embedding
-- dimension 1536 = text-embedding-3-small (OpenAI)
CREATE TABLE chunks (
  id          bigserial PRIMARY KEY,
  document_id bigint REFERENCES documents(id) ON DELETE CASCADE,
  content     text NOT NULL,
  embedding   vector(1536),
  chunk_index int,
  token_count int
);

-- Índice HNSW para busca aproximada (bom equilíbrio qualidade/velocidade)
CREATE INDEX ON chunks
  USING hnsw (embedding vector_cosine_ops);

Ajuste a dimensão conforme o modelo de embedding que você usar (OpenAI, Voyage, nomic, etc.).

4. Chunking e embeddings

Documentos longos precisam ser divididos. Regras práticas:

  • Tamanho típico: 400–800 tokens por chunk
  • Overlap de 10–20% para não cortar frases no meio
  • Preferir quebrar em títulos/parágrafos (não no meio de tabelas ou código)
  • Guardar chunk_index e referência ao documento original

Exemplo mínimo em Python (usando a API oficial da OpenAI):

from openai import OpenAI
import psycopg2

client = OpenAI()

def embed(texts: list[str]) -> list[list[float]]:
    res = client.embeddings.create(
        model="text-embedding-3-small",
        input=texts
    )
    return [d.embedding for d in res.data]

# após chunkar o PDF/Markdown:
embeddings = embed([c["content"] for c in chunks])
# inserir no Postgres com psycopg2 ou SQLAlchemy
tip · Rode o embedding em batch (até 100–200 textos por request). Guarde o modelo e a dimensão usados — trocar depois exige reindexar tudo.

5. Retrieval + geração

Fluxo de uma pergunta:

-- 1. Embedding da pergunta
-- (feito na aplicação)

-- 2. Busca dos k chunks mais próximos
SELECT content, document_id, 1 - (embedding <=> $1) AS score
FROM chunks
ORDER BY embedding <=> $1
LIMIT 6;

O operador <=> é distância de cosseno no pgvector. Depois monte o prompt:

Você é um assistente técnico. Responda APENAS com base nos trechos abaixo.
Se a informação não estiver nos trechos, diga que não encontrou.
Cite a fonte no formato [doc:ID].

Trechos:
---
[doc:42] ...conteúdo do chunk...
[doc:17] ...conteúdo do chunk...
---

Pergunta do usuário: {pergunta}

Envie para o modelo (GPT-4o-mini, Claude, Gemini, etc.) com temperatura baixa (0–0.3) e, se possível, response format estruturado.

6. Quando RAG não é a solução certa

  • Perguntas que exigem raciocínio multi-hop complexo sem evidência direta nos docs
  • Base muito pequena e estável — às vezes um prompt com o texto inteiro basta
  • Necessidade de respostas em tempo real com dados transacionais (aí é melhor function calling + SQL)
  • Documentos extremamente ruidosos ou mal estruturados (priorize limpeza antes de indexar)

7. Próximos passos

  1. Adicionar hybrid search (BM25 + vetor) para termos exatos (códigos de erro, nomes de serviços)
  2. Filtros por metadados (departamento, data, produto)
  3. Avaliação: conjunto de perguntas + respostas esperadas e métricas de retrieval (recall@k, MRR)
  4. Observabilidade: logar quais chunks foram usados e se o usuário aceitou a resposta

Com esse esqueleto você já tem um assistente interno utilizável. O resto é iteração sobre qualidade dos chunks, modelo de embedding e prompt de geração.

8. O SQL e o código explicados

TrechoO que faz
CREATE EXTENSION IF NOT EXISTS vector;Ativa a extensão pgvector no banco. O IF NOT EXISTS evita erro se ela já estiver ativa.
id bigserial PRIMARY KEYIdentificador inteiro que se auto-incrementa e é a chave primária.
text NOT NULLColuna de texto obrigatória.
created_at timestamptz DEFAULT now()Data e hora com fuso, preenchidas automaticamente.
document_id bigint REFERENCES documents(id) ON DELETE CASCADELiga cada chunk ao seu documento. Se o documento for apagado, os chunks vão junto.
embedding vector(1536)Coluna de vetor com 1536 dimensões, o tamanho do modelo text-embedding-3-small.
CREATE INDEX ... USING hnsw (embedding vector_cosine_ops)Índice HNSW para busca aproximada dos vizinhos mais próximos, usando distância de cosseno.
1 - (embedding <=> $1) AS scoreConverte a distância de cosseno em similaridade: quanto mais perto de 1, mais parecido.
ORDER BY embedding <=> $1 LIMIT 6Devolve os 6 chunks mais próximos da pergunta. O $1 é o vetor da pergunta, enviado pela aplicação.
OperadorDistânciaClasse do índice
<->euclidiana (L2)vector_l2_ops
<=>cossenovector_cosine_ops
<#>produto interno (negativo)vector_ip_ops
Confira a dimensão na documentação do modelo antes de criar a coluna
Modelo de embeddingDimensão
text-embedding-3-small (OpenAI)1536
text-embedding-3-large (OpenAI)3072
nomic-embed-text768

No Python, OpenAI() lê a chave da variável de ambiente OPENAI_API_KEY. A chamada client.embeddings.create(model=..., input=texts) aceita uma lista de textos e devolve um vetor por texto em res.data, o que permite processar em lote.

9. Erros comuns e como resolver

SintomaCausa provávelSolução
type "vector" does not existA extensão não está instalada ou ativada neste banco.Instale o pacote do pgvector no servidor e rode CREATE EXTENSION vector; no banco certo.
expected 1536 dimensions, not 768O modelo de embedding gera uma dimensão diferente da coluna.Use o mesmo modelo na indexação e na busca, ou recrie a coluna e reindexe.
A busca fica lenta com muitos chunksNão existe índice HNSW ou a consulta não usa ORDER BY ... LIMIT.Crie o índice e mantenha o LIMIT na consulta.
Os trechos recuperados fogem do temaChunks grandes ou pequenos demais.Ajuste o tamanho e a sobreposição e meça com um conjunto de perguntas de teste.
O modelo inventa informaçãoO prompt permite usar conhecimento fora dos trechos.Mantenha a instrução de responder apenas com os trechos, a citação de fonte e a temperatura baixa.

Quer implementar isso na sua empresa?

Ajudamos times a colocar RAG, agentes e modelos locais em produção com segurança e observabilidade.

Falar com a IRN Devs Como contratar →

Quer aplicar isso ao seu contexto?

Se esse problema existe na sua operação, podemos analisar o cenário em um diagnóstico gratuito de 30 minutos.

solicitar diagnóstico gratuito →

Leia também