Vector databases: o que são, como funcionam e quando usar com LLMs

1. O problema que os bancos vetoriais resolvem

1.1. Limitações das buscas tradicionais com LLMs

Modelos de linguagem de grande escala (LLMs) possuem conhecimento limitado ao seu corte temporal e não conseguem acessar bases de dados privadas ou atualizadas em tempo real. Quando um usuário pergunta sobre um documento interno da empresa ou sobre um evento recente, o LLM simplesmente não tem essa informação. Buscas tradicionais por palavra-chave (como SQL LIKE ou Elasticsearch) falham em capturar significado semântico: uma busca por "carro elétrico" não retorna resultados sobre "veículo movido a bateria".

1.2. A necessidade de representar significado semântico

A busca semântica exige que o sistema entenda o significado por trás das palavras, não apenas a correspondência literal. É aqui que os bancos vetoriais entram: eles armazenam representações matemáticas densas (vetores) que capturam o sentido de textos, imagens ou áudios. Dois textos com significados similares geram vetores próximos no espaço multidimensional, mesmo que usem palavras diferentes.

1.3. Contexto na arquitetura de RAG e agentes

Os bancos vetoriais são o coração do padrão Retrieval-Augmented Generation (RAG). Em um sistema RAG típico:
1. Documentos são divididos em chunks e convertidos em embeddings
2. Esses embeddings são armazenados em um banco vetorial
3. Quando chega uma pergunta, ela também é convertida em embedding
4. O banco retorna os chunks mais similares semanticamente
5. O LLM usa esses chunks como contexto para gerar a resposta

Agentes autônomos (como os construídos com LangGraph) usam bancos vetoriais como memória de longo prazo, permitindo recuperar informações de interações passadas.

2. Fundamentos: embeddings e vetores

2.1. O que é um embedding vetorial

Um embedding é um vetor de números reais (por exemplo, 1536 dimensões no modelo text-embedding-ada-002 da OpenAI). Cada dimensão captura um aspecto semântico latente do texto. Vetores densos significam que a maioria das dimensões tem valores não-zero, ao contrário de representações esparsas como bag-of-words.

2.2. Como LLMs geram embeddings

Modelos de embedding são treinados para produzir vetores onde textos similares ficam próximos. Exemplos populares:
- OpenAI: text-embedding-3-small (512 ou 1536 dimensões)
- HuggingFace: BAAI/bge-base-en-v1.5 (768 dimensões)
- Google: text-embedding-gecko (768 dimensões)

2.3. Métricas de similaridade

As três métricas mais comuns para comparar vetores:

# Similaridade por cosseno (mais comum)
similaridade = cos(θ) = (A · B) / (||A|| * ||B||)

# Distância euclidiana (L2)
distancia = sqrt(Σ(Ai - Bi)²)

# Produto escalar (dot product)
produto = Σ(Ai * Bi)

A similaridade por cosseno é a mais usada por ser invariante à magnitude dos vetores, focando apenas na direção (significado).

3. Arquitetura interna de um banco vetorial

3.1. Indexação: algoritmos de aproximação

Para busca eficiente em milhões de vetores, usam-se índices aproximados (ANN - Approximate Nearest Neighbors):

  • HNSW (Hierarchical Navigable Small World): Cria múltiplas camadas de grafos, permitindo busca logarítmica. Alta precisão, mas maior consumo de memória.
  • IVF (Inverted File Index): Divide o espaço em clusters (Voronoi). Rápido, mas menos preciso que HNSW.
  • PQ (Product Quantization): Comprime vetores para reduzir memória. Útil para datasets muito grandes.

Trade-off principal: precisão vs. velocidade vs. memória. HNSW oferece ~99% de recall com latências de milissegundos.

3.2. Operações fundamentais

# Inserção de vetor
INSERT INTO vectordb (id, embedding, metadata)
VALUES ('doc1', [0.12, 0.45, ..., 0.89], '{"titulo": "Relatório 2024"}')

# Busca por similaridade (k-NN)
SELECT id, metadata, cosine_similarity(embedding, query_embedding) AS score
FROM vectordb
ORDER BY score DESC
LIMIT 5

# Filtragem híbrida
SELECT id, metadata, cosine_similarity(embedding, query_embedding) AS score
FROM vectordb
WHERE metadata->>'ano' = '2024'
ORDER BY score DESC
LIMIT 5

4. Principais soluções do mercado

4.1. Bancos dedicados

Solução Prós Contras
Pinecone Gerenciado, escalável, baixa latência Caro em escala
Weaviate Open-source, schema flexível Configuração mais complexa
Qdrant Rust, performance alta Ecossistema menor
Milvus Suporte a GPU, bilhões de vetores Operação complexa

4.2. Extensões em bancos relacionais

  • pgvector (PostgreSQL): Permite usar PostgreSQL como banco vetorial com índices IVFFlat e HNSW. Ideal para quem já usa PostgreSQL.
  • MongoDB Atlas Vector Search: Integração nativa com MongoDB, suporta filtros híbridos.

4.3. Critérios de escolha

  • Volume de dados: até 100k vetores → pgvector; até 10M → Qdrant/Weaviate; acima → Milvus/Pinecone
  • Latência: aplicações em tempo real → HNSW com banco em memória
  • Gerenciamento: equipe pequena → Pinecone (serverless); equipe experiente → Milvus auto-hospedado

5. Integrando bancos vetoriais com LLMs na prática

5.1. Pipeline típico

1. Chunking: Dividir documentos em partes de ~500 tokens com overlap de 50 tokens
2. Embedding: Converter cada chunk em vetor usando modelo de embedding
3. Inserção: Armazenar vetor + chunk + metadados no banco vetorial
4. Consulta: Embedding da pergunta → busca k-NN → recuperação dos chunks
5. Geração: Contexto (chunks) + pergunta → LLM → resposta final

5.2. Exemplo de fluxo RAG (pseudocódigo conceitual)

# 1. Preparação dos dados
documentos = carregar_pdfs("relatorios/")
chunks = chunk_documentos(documentos, tamanho=500, overlap=50)

# 2. Criação dos embeddings
modelo_embedding = carregar_modelo("BAAI/bge-base-en-v1.5")
vetores = modelo_embedding.embed(chunks)

# 3. Inserção no banco vetorial (Qdrant)
for chunk, vetor in zip(chunks, vetores):
    banco.inserir(
        id=chunk.id,
        vetor=vetor,
        payload={"texto": chunk.texto, "fonte": chunk.arquivo}
    )

# 4. Consulta do usuário
pergunta = "Qual foi o faturamento em 2024?"
vetor_pergunta = modelo_embedding.embed([pergunta])

# 5. Busca semântica
resultados = banco.buscar(
    vetor=vetor_pergunta[0],
    limite=3,
    filtro={"ano": "2024"}
)

# 6. Geração com contexto
contexto = "\n".join([r.payload["texto"] for r in resultados])
resposta = llm.gerar(f"Contexto:\n{contexto}\n\nPergunta: {pergunta}")

5.3. Desafios comuns

  • Tamanho do contexto: chunks muito grandes excedem o limite de tokens do LLM
  • Atualização de embeddings: ao trocar o modelo, todos os vetores precisam ser recalculados
  • Filtragem por metadados: bancos vetoriais puros têm suporte limitado; soluções híbridas (pgvector) são melhores

6. Quando usar (e quando evitar) bancos vetoriais com LLMs

6.1. Casos de uso ideais

  • Chatbots com base de conhecimento: suporte técnico, FAQ interno, manuais de produtos
  • Pesquisa em documentos: contratos, artigos científicos, relatórios financeiros
  • Sistemas de recomendação semântica: recomendar artigos baseados em conteúdo similar
  • Memória para agentes: agentes que precisam lembrar de interações anteriores

6.2. Cenários onde não são recomendados

  • Dados puramente tabulares: buscas exatas em colunas (SQL resolve melhor)
  • Baixo volume de dados: menos de 1000 documentos → uma busca simples com embeddings em memória é suficiente
  • Busca por metadados complexa: consultas SQL com múltiplos joins são mais eficientes em bancos relacionais

6.3. Alternativas

  • Busca híbrida (BM25 + vetorial): combina busca lexical e semântica (ex: Elasticsearch com plugin de embeddings)
  • Bancos relacionais com índices simples: para dados estruturados com poucos registros

7. Boas práticas e armadilhas comuns

7.1. Escolha do modelo de embedding

Para datasets pequenos (< 100k documentos), modelos com 384-768 dimensões (como all-MiniLM-L6-v2) oferecem bom custo-benefício. Para datasets grandes, modelos maiores (1536 dimensões) capturam mais nuances, mas aumentam custo de armazenamento.

7.2. Estratégias de chunking

# Chunking com overlap
tamanho_chunk = 500 tokens
overlap = 50 tokens

# Exemplo de chunks de um documento de 1200 tokens:
# Chunk 1: tokens 0-500
# Chunk 2: tokens 450-950
# Chunk 3: tokens 900-1200

O overlap evita que informações importantes sejam cortadas no meio de um chunk.

7.3. Monitoramento em produção

  • Drift de embeddings: verifique periodicamente se a distribuição dos vetores mudou (pode indicar mudança no domínio dos dados)
  • Latência: buscas devem levar < 100ms para aplicações interativas
  • Custo de armazenamento: vetores 1536-dim em float32 ocupam ~6KB cada; para 1M documentos, são ~6GB

8. Tendências futuras

8.1. Embeddings multimodais

Modelos como CLIP e ImageBind geram embeddings que representam texto, imagem e áudio no mesmo espaço. Bancos vetoriais estão se adaptando para buscas multimodais: "encontre imagens que combinem com este texto descritivo".

8.2. Integração com agentes LangGraph

Agentes com loops de feedback podem usar bancos vetoriais como memória episódica, armazenando embeddings de ações e resultados para melhorar decisões futuras.

8.3. Relação com temas vizinhos

Bancos vetoriais são fundamentais para RAG avançado, busca semântica e vibe coding (onde o código é gerado a partir de descrições em linguagem natural). Eles formam a ponte entre a compreensão semântica dos LLMs e dados estruturados e não estruturados.

Referências