Última notícia há desconhecido
Tutorialpython-ia· Médio· PT35M

Bancos de Dados Vetoriais: Como Escolher e Usar

Chroma vs Pinecone vs pgvector: embeddings e busca semântica para RAG.

11 de maio de 2026· Atualizado em 14 de maio de 2026por Análises SWEN.AI

O Que São Bancos de Dados Vetoriais?

Um banco de dados vetorial é um sistema de armazenamento especializado em guardar e buscar vetores de alta dimensão — arrays numéricos que representam o significado semântico de textos, imagens, áudios ou qualquer outro dado.

Quando você passa um texto por um modelo de embedding (como o text-embedding-3-small da OpenAI ou o all-MiniLM-L6-v2 do HuggingFace), ele retorna um vetor com centenas ou milhares de dimensões. Textos com significados similares produzem vetores matematicamente próximos no espaço vetorial — mesmo que não compartilhem nenhuma palavra em comum.

Por exemplo:

  • "O cão latiu alto" → vetor próximo de "O cachorro fez barulho"
  • "Receita de bolo de chocolate" → vetor distante de "Política econômica brasileira"

Um banco vetorial armazena esses vetores e permite buscar os N mais similares a um vetor de consulta em milissegundos — mesmo com milhões de registros.

---

Por Que Precisamos de Vector Databases?

Bancos relacionais tradicionais (PostgreSQL, MySQL) não foram projetados para busca por similaridade. Uma query WHERE texto LIKE '%cão%' não encontra "cachorro". Uma busca full-text encontra palavras exatas, não conceitos.

Os casos de uso que tornaram os vector databases essenciais:

RAG (Retrieval-Augmented Generation): a técnica mais usada hoje para conectar LLMs a documentos privados. O documento é fragmentado, cada fragmento vira um vetor, e na hora da pergunta você busca os fragmentos mais relevantes e envia para o LLM como contexto.

Busca semântica: um usuário digita "problema com pagamento" e o sistema encontra tickets categorizados como "erro de cobrança", "falha no checkout" e "cartão recusado" — mesmo sem nenhuma palavra em comum.

Sistemas de recomendação: encontrar produtos, artigos ou usuários similares baseado em preferências convertidas em vetores.

Detecção de duplicatas: identificar textos ou imagens semanticamente idênticos mesmo com redação diferente.

Base de memória para agentes de IA: armazenar e recuperar memórias relevantes com base no contexto atual da conversa.

Sem vector databases, construir esses sistemas exigiria re-indexar tudo a cada consulta ou usar aproximações muito menos precisas.

---

Principais Opções: Chroma, FAISS, Pinecone, Weaviate, Qdrant

| Banco | Tipo | Hospedagem | Open Source | Ideal Para | Escala |

|-------|------|------------|-------------|------------|--------|

| Chroma | Embarcado/Servidor | Local ou self-hosted | Sim | Prototipagem, RAG local | Até ~1M vetores |

| FAISS | Biblioteca | Local (in-memory/disco) | Sim (Meta) | Velocidade máxima, pesquisa | Bilhões de vetores |

| Pinecone | Managed cloud | SaaS | Não | Produção sem ops, escala | Ilimitada (pago) |

| Weaviate | Servidor | Local ou cloud | Sim | GraphQL, multi-modal | Alta |

| Qdrant | Servidor | Local ou cloud | Sim | Produção, filtros complexos | Alta |

| pgvector | Extensão SQL | PostgreSQL | Sim | Já usa Postgres | Média |

| Milvus | Servidor | Local ou cloud | Sim | Empresarial, alta escala | Muito alta |

Recomendação prática:

  • Começando um projeto ou fazendo RAG local → Chroma
  • Precisa de velocidade máxima em pesquisa acadêmica → FAISS
  • Produção sem querer gerenciar infraestrutura → Pinecone
  • Produção self-hosted com recursos avançados → Qdrant
  • Já tem PostgreSQL e quer evitar novo serviço → pgvector

---

Chroma DB: Começando do Zero

Chroma é o banco vetorial mais fácil de começar. Não exige servidor externo — funciona embutido no processo Python.

pip install chromadb
import chromadb
from chromadb.utils import embedding_functions

# Modo in-memory (dados perdidos ao reiniciar)
client = chromadb.Client()

# Modo persistente (dados salvos em disco)
client = chromadb.PersistentClient(path='./meu_chroma_db')

# Criar uma coleção (equivalente a uma tabela)
# Por padrão usa o modelo all-MiniLM-L6-v2 (gratuito, local)
collection = client.create_collection(
    name='documentos',
    metadata={'hnsw:space': 'cosine'}  # métrica de distância
)

# Adicionar documentos (Chroma gera os embeddings automaticamente)
collection.add(
    documents=[
        'Python é uma linguagem de programação interpretada.',
        'Machine learning é um subcampo da inteligência artificial.',
        'O Brasil é o maior país da América do Sul.',
        'Redes neurais são inspiradas no cérebro humano.',
        'O Rio de Janeiro foi capital do Brasil até 1960.'
    ],
    ids=['doc1', 'doc2', 'doc3', 'doc4', 'doc5'],
    metadatas=[
        {'categoria': 'tecnologia', 'fonte': 'wikipedia'},
        {'categoria': 'ia', 'fonte': 'wikipedia'},
        {'categoria': 'geografia', 'fonte': 'wikipedia'},
        {'categoria': 'ia', 'fonte': 'wikipedia'},
        {'categoria': 'historia', 'fonte': 'wikipedia'}
    ]
)

# Busca por similaridade
results = collection.query(
    query_texts=['aprendizado de máquina'],
    n_results=3
)

print('Documentos mais similares:')
for i, (doc, distance) in enumerate(zip(
    results['documents'][0],
    results['distances'][0]
)):
    print(f'{i+1}. Distância: {distance:.4f} | {doc}')

# Busca com filtro de metadados
results_filtrados = collection.query(
    query_texts=['inteligência artificial'],
    n_results=2,
    where={'categoria': 'ia'}  # só busca na categoria 'ia'
)

# Adicionar mais documentos a uma coleção existente
collection.add(
    documents=['TensorFlow é uma biblioteca de deep learning do Google.'],
    ids=['doc6'],
    metadatas=[{'categoria': 'ia', 'fonte': 'google'}]
)

# Deletar documento
collection.delete(ids=['doc6'])

# Contar documentos
print(f'Total de documentos: {collection.count()}')

Usando embeddings da OpenAI com Chroma:

import chromadb
from chromadb.utils.embedding_functions import OpenAIEmbeddingFunction

ef = OpenAIEmbeddingFunction(
    api_key='sua-api-key',
    model_name='text-embedding-3-small'
)

collection = client.create_collection(
    name='docs_openai',
    embedding_function=ef
)

---

FAISS: Velocidade Máxima para Busca Vetorial

FAISS (Facebook AI Similarity Search) é uma biblioteca desenvolvida pela Meta para busca vetorial extremamente eficiente. Não é um banco de dados completo — é um índice vetorial de alta performance.

pip install faiss-cpu  # ou faiss-gpu para NVIDIA GPU
pip install numpy
import faiss
import numpy as np

# Dimensão dos vetores (depende do modelo de embedding)
DIMENSAO = 384  # all-MiniLM-L6-v2 produz vetores de 384 dimensões

# Criar índice
# IndexFlatL2: busca exata por distância euclidiana (lento para muitos vetores)
index_exato = faiss.IndexFlatL2(DIMENSAO)

# IndexIVFFlat: busca aproximada (mais rápido, leve perda de precisão)
quantizer = faiss.IndexFlatL2(DIMENSAO)
index_aprox = faiss.IndexIVFFlat(quantizer, DIMENSAO, 100)  # 100 clusters

# Simular vetores de documentos (em produção viriam de um modelo de embedding)
np.random.seed(42)
vetores = np.random.random((10000, DIMENSAO)).astype('float32')

# Treinar índice aproximado (obrigatório para IVF)
index_aprox.train(vetores)

# Adicionar vetores ao índice
index_aprox.add(vetores)
print(f'Vetores no índice: {index_aprox.ntotal}')

# Buscar os 5 mais similares
query = np.random.random((1, DIMENSAO)).astype('float32')
distancias, indices = index_aprox.search(query, k=5)

print('Top 5 mais similares:')
for dist, idx in zip(distancias[0], indices[0]):
    print(f'  Índice {idx}: distância {dist:.4f}')

# Salvar e carregar índice
faiss.write_index(index_aprox, 'meu_indice.faiss')
index_carregado = faiss.read_index('meu_indice.faiss')

# FAISS com IDs customizados (mapeando para IDs do seu banco)
index_com_ids = faiss.IndexIDMap(faiss.IndexFlatL2(DIMENSAO))
ids_customizados = np.array([100, 200, 300, 400, 500], dtype='int64')
vetores_sample = np.random.random((5, DIMENSAO)).astype('float32')
index_com_ids.add_with_ids(vetores_sample, ids_customizados)

distancias, ids_encontrados = index_com_ids.search(query, k=3)
print(f'IDs encontrados: {ids_encontrados[0]}')

---

Pinecone: Vector Database na Nuvem

Pinecone é um serviço gerenciado — você não instala nada, apenas usa a API. Ideal para produção sem querer gerenciar infraestrutura.

pip install pinecone
from pinecone import Pinecone, ServerlessSpec

# Inicializar cliente
pc = Pinecone(api_key='sua-pinecone-api-key')

# Criar índice (só precisa fazer uma vez)
if 'meu-indice' not in pc.list_indexes().names():
    pc.create_index(
        name='meu-indice',
        dimension=1536,  # dimensão do text-embedding-3-small da OpenAI
        metric='cosine',
        spec=ServerlessSpec(
            cloud='aws',
            region='us-east-1'
        )
    )

# Conectar ao índice
index = pc.Index('meu-indice')

# Inserir vetores com metadados
vetores_para_inserir = [
    ('vec1', [0.1, 0.2, ...], {'texto': 'Documento sobre Python', 'categoria': 'tech'}),
    ('vec2', [0.3, 0.1, ...], {'texto': 'Documento sobre IA', 'categoria': 'ia'}),
]
index.upsert(vectors=vetores_para_inserir)

# Buscar similares
resultados = index.query(
    vector=[0.2, 0.15, ...],  # vetor de consulta
    top_k=5,
    include_metadata=True,
    filter={'categoria': 'ia'}  # filtro opcional
)

for match in resultados['matches']:
    print(f'ID: {match["id"]} | Score: {match["score"]:.4f} | {match["metadata"]}')

# Ver estatísticas do índice
print(index.describe_index_stats())

---

Embeddings: Convertendo Texto em Vetores

Antes de armazenar em qualquer vector database, você precisa converter texto em vetores. Aqui estão as principais opções:

OpenAI Embeddings:

from openai import OpenAI

client = OpenAI(api_key='sua-api-key')

def gerar_embedding_openai(texto: str) -> list[float]:
    response = client.embeddings.create(
        input=texto,
        model='text-embedding-3-small'  # 1536 dims, mais barato
        # model='text-embedding-3-large'  # 3072 dims, mais preciso
    )
    return response.data[0].embedding

vector = gerar_embedding_openai('Olá, como você está?')
print(f'Dimensões: {len(vector)}')  # 1536

HuggingFace (gratuito, roda localmente):

pip install sentence-transformers
from sentence_transformers import SentenceTransformer

# Baixa automaticamente na primeira execução (~90 MB)
model = SentenceTransformer('all-MiniLM-L6-v2')  # 384 dims, rápido
# model = SentenceTransformer('all-mpnet-base-v2')  # 768 dims, mais preciso
# model = SentenceTransformer('paraphrase-multilingual-mpnet-base-v2')  # multilingual

# Embedding de um texto
vector = model.encode('Texto para converter em vetor')
print(f'Tipo: {type(vector)}, Dimensões: {len(vector)}')

# Batch de textos (muito mais eficiente)
textos = [
    'Python é uma linguagem de programação',
    'Machine learning usa dados para aprender',
    'O Brasil tem 210 milhões de habitantes'
]
vectors = model.encode(textos, batch_size=32, show_progress_bar=True)
print(f'Shape: {vectors.shape}')  # (3, 384)

# Calcular similaridade entre dois textos
from sentence_transformers import util
sim = util.cos_sim(vectors[0], vectors[1])
print(f'Similaridade Python x ML: {sim.item():.4f}')

Modelos gratuitos recomendados por caso de uso:

| Modelo | Dims | Idiomas | Melhor Para |

|--------|------|---------|-------------|

| all-MiniLM-L6-v2 | 384 | Inglês | Velocidade, prototipagem |

| all-mpnet-base-v2 | 768 | Inglês | Qualidade geral |

| paraphrase-multilingual-mpnet-base-v2 | 768 | 50+ | PT-BR, multilingual |

| nomic-ai/nomic-embed-text-v1 | 768 | Inglês | Textos longos |

---

Construindo um Sistema de Busca Semântica Completo

Aqui está um sistema funcional de busca semântica em documentos usando Chroma + SentenceTransformers:

import chromadb
from sentence_transformers import SentenceTransformer
from pathlib import Path
import json

class BuscaSemantica:
    def __init__(self, caminho_db: str = './vector_db'):
        self.model = SentenceTransformer('paraphrase-multilingual-mpnet-base-v2')
        self.client = chromadb.PersistentClient(path=caminho_db)
        self.collection = self.client.get_or_create_collection(
            name='documentos',
            metadata={'hnsw:space': 'cosine'}
        )
    
    def indexar_documentos(self, documentos: list[dict]) -> None:
        """
        documentos: lista de {'id': str, 'texto': str, 'metadados': dict}
        """
        textos = [d['texto'] for d in documentos]
        ids = [d['id'] for d in documentos]
        metadados = [d.get('metadados', {}) for d in documentos]
        
        # Gerar embeddings em batch
        print(f'Gerando embeddings para {len(textos)} documentos...')
        embeddings = self.model.encode(textos, batch_size=32).tolist()
        
        # Inserir no banco vetorial
        self.collection.add(
            documents=textos,
            embeddings=embeddings,
            ids=ids,
            metadatas=metadados
        )
        print(f'Indexação concluída. Total: {self.collection.count()} documentos')
    
    def buscar(self, query: str, n_resultados: int = 5, filtro: dict = None) -> list[dict]:
        # Gerar embedding da consulta
        query_embedding = self.model.encode(query).tolist()
        
        kwargs = {
            'query_embeddings': [query_embedding],
            'n_results': n_resultados
        }
        if filtro:
            kwargs['where'] = filtro
        
        results = self.collection.query(**kwargs)
        
        # Formatar resultados
        saida = []
        for doc, meta, dist in zip(
            results['documents'][0],
            results['metadatas'][0],
            results['distances'][0]
        ):
            saida.append({
                'texto': doc,
                'metadados': meta,
                'similaridade': 1 - dist  # converter distância em similaridade
            })
        
        return saida

# Uso prático
if __name__ == '__main__':
    busca = BuscaSemantica()
    
    # Indexar documentos de exemplo
    docs = [
        {'id': '1', 'texto': 'Como cancelar minha assinatura do plano mensal?',
         'metadados': {'categoria': 'cancelamento', 'prioridade': 'alta'}},
        {'id': '2', 'texto': 'Erro ao processar pagamento com cartão de crédito',
         'metadados': {'categoria': 'pagamento', 'prioridade': 'alta'}},
        {'id': '3', 'texto': 'Não consigo fazer login na minha conta',
         'metadados': {'categoria': 'acesso', 'prioridade': 'media'}},
        {'id': '4', 'texto': 'Quero encerrar meu contrato com a empresa',
         'metadados': {'categoria': 'cancelamento', 'prioridade': 'alta'}},
        {'id': '5', 'texto': 'Boleto bancário não chegou por email',
         'metadados': {'categoria': 'pagamento', 'prioridade': 'baixa'}},
    ]
    
    busca.indexar_documentos(docs)
    
    # Buscar
    print('\n--- Busca: "quero desistir do serviço" ---')
    resultados = busca.buscar('quero desistir do serviço', n_resultados=3)
    for r in resultados:
        print(f'Sim: {r["similaridade"]:.3f} | {r["texto"]}')
    
    print('\n--- Busca só em pagamentos ---')
    resultados = busca.buscar('problema com cobrança', filtro={'categoria': 'pagamento'})
    for r in resultados:
        print(f'Sim: {r["similaridade"]:.3f} | {r["texto"]}')

---

Boas Práticas em Produção

1. Tamanho dos chunks em RAG

Fragmentar documentos corretamente é crítico. Chunks muito pequenos perdem contexto; muito grandes diluem a relevância.

  • Recomendação geral: 300–500 tokens com overlap de 50–100 tokens
  • Para código: por função/método
  • Para tabelas: manter a tabela inteira em um chunk

2. Normalizar os vetores

Sempre normalize seus vetores antes de inserir quando usar distância cosine. A maioria dos modelos SentenceTransformers já normaliza, mas verifique.

3. Não misture modelos de embedding

Se você indexou com all-MiniLM-L6-v2, use o mesmo modelo para gerar o embedding da consulta. Modelos diferentes produzem espaços vetoriais incompatíveis.

4. Metadados para filtros eficientes

Adicione metadados relevantes (categoria, data, autor, tipo de documento) para poder filtrar antes ou depois da busca vetorial. Isso reduz o espaço de busca e melhora a precisão.

5. Reindexação periódica

Se o conteúdo dos documentos muda frequentemente, implemente uma estratégia de upsert baseada em hash do conteúdo para evitar duplicatas e manter o índice atualizado.

6. Cache de embeddings

Gerar embeddings é caro (tempo e dinheiro com APIs). Armazene os vetores e evite regenerar para documentos que não mudaram.

7. Avalie a qualidade da busca

Use métricas como MRR (Mean Reciprocal Rank) e NDCG para medir se sua busca está retornando os resultados certos. Uma boa busca semântica deve ter precisão superior a uma busca por palavras-chave no seu domínio.

---

Perguntas Frequentes

1. Qual a diferença entre busca vetorial e busca full-text?

Busca full-text encontra documentos que contêm as palavras exatas da consulta (com stemming e sinônimos limitados). Busca vetorial encontra documentos com significado similar, mesmo sem palavras em comum. Para melhor resultado em produção, combine as duas abordagens (busca híbrida).

2. Preciso de GPU para gerar embeddings?

Não é obrigatório. Modelos menores como all-MiniLM-L6-v2 rodam em CPU de forma aceitável (centenas de documentos por segundo em hardware moderno). Para indexar milhões de documentos, GPU acelera significativamente.

3. Quantos documentos o Chroma suporta em produção?

O Chroma funciona bem até alguns milhões de vetores em modo servidor. Para escala maior (dezenas de milhões+), migre para Qdrant, Weaviate ou Pinecone.

4. O pgvector é uma boa alternativa se já uso PostgreSQL?

Sim, especialmente para projetos que já têm PostgreSQL e não querem adicionar um novo serviço. A extensão pgvector suporta busca aproximada (HNSW e IVFFlat) e integra bem com queries SQL normais. A limitação é escala: acima de 1–2 milhões de vetores, soluções dedicadas têm melhor performance.

5. Como atualizar um documento que mudou?

A maioria dos bancos vetoriais suporta upsert (insert + update). Passe o mesmo ID com o novo embedding e o documento é substituído automaticamente. No Chroma, use collection.update() para documentos existentes.

6. Qual a diferença entre distância euclidiana e cosseno?

Distância euclidiana mede a distância geométrica no espaço vetorial. Similaridade cosseno mede o ângulo entre os vetores, ignorando a magnitude. Para embeddings de texto, similaridade cosseno costuma ser mais adequada porque o comprimento do texto não deve afetar a similaridade semântica.

Tutoriais relacionados