Bancos vetoriais em produção para sistemas RAG

1. Fundamentos e Arquitetura de Bancos Vetoriais para RAG

1.1. Conceitos básicos: embeddings, similaridade vetorial e indexação

Sistemas RAG (Retrieval-Augmented Generation) dependem de bancos vetoriais para recuperar informações relevantes antes de alimentar modelos de linguagem. O coração dessa arquitetura são os embeddings — representações numéricas densas de texto, imagens ou áudio em um espaço vetorial de alta dimensionalidade (tipicamente 768 a 1536 dimensões). A similaridade entre embeddings é calculada por métricas como cosseno, produto escalar ou distância euclidiana.

Para viabilizar buscas em milhões de vetores com latência de milissegundos, bancos vetoriais utilizam algoritmos de indexação aproximada:

  • HNSW (Hierarchical Navigable Small World): Cria grafos em múltiplas camadas, onde nós de nível superior permitem saltos rápidos para regiões promissoras. Oferece excelente recall (>99%) com latência de 1-10ms para 1M vetores.
  • IVF (Inverted File Index): Particiona o espaço vetorial em clusters (usando k-means) e busca apenas nos clusters mais próximos ao query vector. Mais eficiente em memória, mas com recall ligeiramente inferior.
# Exemplo: Configuração de índice HNSW no Qdrant
{
  "vectors": {
    "size": 768,
    "distance": "Cosine"
  },
  "optimizers_config": {
    "indexing_threshold": 10000
  },
  "hnsw_config": {
    "m": 16,          # Conexões por nó (mais = recall maior)
    "ef_construct": 200,  # Tamanho da lista dinâmica durante construção
    "full_scan_threshold": 10000
  }
}

1.2. Integração com pipelines RAG

Um pipeline RAG típico envolve:

  1. Ingestão: Documentos são processados em chunks de 256-1024 tokens
  2. Embedding: Cada chunk é convertido em vetor via modelos como text-embedding-3-small ou all-MiniLM-L6-v2
  3. Armazenamento: Vetores + metadados (fonte, timestamp, chunk_id) são inseridos no banco vetorial
  4. Recuperação: Query do usuário é embedada e busca-se os top-k chunks mais similares
  5. Geração: Chunks recuperados são inseridos no contexto do LLM
# Pipeline de ingestão com pgvector (PostgreSQL)
CREATE EXTENSION vector;

CREATE TABLE documents (
    id SERIAL PRIMARY KEY,
    content TEXT NOT NULL,
    metadata JSONB,
    embedding vector(768),
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_documents_embedding 
ON documents 
USING ivfflat (embedding vector_cosine_ops) 
WITH (lists = 100);

1.3. Diferenças entre bancos vetoriais especializados e extensões relacionais

Característica Pinecone/Weaviate/Qdrant pgvector (PostgreSQL)
Latência pura 2-5ms para 1M vetores 10-50ms para 1M vetores
Escalabilidade Sharding automático, multi-região Requer extensões como Citus
Filtros híbridos Suporte nativo a metadados SQL completo, joins complexos
Manutenção Gerenciado (SaaS) Auto-gerenciado ou RDS
Custo $70-500/mês para 1M vetores Incluso no custo do RDS

2. Estratégias de Indexação e Performance em Escala

2.1. Parâmetros críticos de indexação

A escolha de parâmetros impacta diretamente o trade-off precisão vs. latência:

# Configuração para alta precisão (recall > 99%)
{
  "hnsw_config": {
    "m": 32,
    "ef_construct": 400,
    "ef_search": 200  # Aumentar para consulta mais precisa
  }
}

# Configuração para baixa latência (recall ~95%)
{
  "hnsw_config": {
    "m": 8,
    "ef_construct": 100,
    "ef_search": 50
  }
}

2.2. Particionamento e sharding

Para datasets com mais de 10M vetores, o sharding por critérios como tenant, região geográfica ou hash do ID do documento é essencial:

# Sharding por tenant no Weaviate
{
  "class": "Document",
  "shardingConfig": {
    "desiredCount": 16,
    "virtualPerPhysical": 128,
    "replicationFactor": 2
  }
}

2.3. Cache de embeddings e consultas híbridas

Consultas que combinam similaridade vetorial com filtros escalares (ex: "documentos de 2024 sobre machine learning") podem ser otimizadas com:

  • Pré-filtragem: Aplicar filtros SQL antes da busca vetorial (reduz o espaço de busca)
  • Pós-filtragem: Buscar top-k vetores e depois filtrar (útil quando filtros são seletivos)
  • Índices compostos: No pgvector, criar índices parciais por categoria
# Consulta híbrida otimizada no pgvector
SELECT content, metadata, 
       1 - (embedding <=> '[0.1, 0.2, ...]') AS similarity
FROM documents
WHERE metadata->>'year' = '2024'
  AND metadata->>'category' = 'machine-learning'
ORDER BY embedding <=> '[0.1, 0.2, ...]'
LIMIT 10;

3. Garantia de Consistência e Atualizações em Tempo Real

3.1. Sincronização entre fontes de dados e banco vetorial

Para manter consistência entre a fonte primária (ex: banco relacional, S3) e o banco vetorial:

  • CDC (Change Data Capture): Use Debezium + Kafka para capturar mudanças em tempo real
  • Batch processing: Para dados que mudam com menos frequência, jobs Airflow diários
  • Streaming: Para aplicações de busca em tempo real (ex: documentos colaborativos)
# Exemplo: Pipeline CDC com Debezium para Qdrant
{
  "name": "postgres-connector",
  "config": {
    "connector.class": "io.debezium.connector.postgresql.PostgresConnector",
    "database.hostname": "postgres-primary",
    "database.dbname": "documents",
    "table.include.list": "public.documents",
    "transforms": "unwrap,vectorize",
    "transforms.vectorize.type": "org.apache.kafka.connect.transforms.VectorizeEmbedding",
    "transforms.vectorize.model": "text-embedding-3-small"
  }
}

3.2. Versionamento de embeddings e rollback

Modelos de embedding evoluem (ex: de ada-002 para text-embedding-3-small). Estratégias de versionamento:

  • Coleções separadas por versão: documents_v1, documents_v2
  • Metadata version: Adicionar campo embedding_version e filtrar nas consultas
  • Rollback: Manter snapshot do índice anterior por 7 dias

3.3. Tratamento de dados obsoletos

Políticas de TTL e garbage collection são críticas para evitar degradação:

# Política de TTL no Qdrant (expira após 30 dias)
{
  "collection_name": "documents",
  "optimizers_config": {
    "default_segment_number": 2,
    "memmap_threshold_kb": 200000,
    "indexing_threshold": 10000
  },
  "hnsw_config": {
    "payload_m": 16,
    "payload_ef_construct": 100
  },
  "wal_config": {
    "wal_capacity_mb": 1024
  }
}

# Garbage collection manual via API
POST /collections/documents/points/delete
{
  "filter": {
    "must": [
      {"key": "created_at", "range": {"lt": 1700000000}}
    ]
  }
}

4. Observabilidade e Monitoramento de Sistemas RAG

4.1. Métricas essenciais

  • Latência de busca: p50 < 20ms, p99 < 100ms
  • Recall: Proporção de chunks relevantes recuperados entre top-k
  • Taxa de acerto: Percentual de consultas onde o chunk recuperado foi usado na resposta final
  • Drift de embeddings: Monitorar distribuição das similaridades ao longo do tempo
# Métricas exportadas via Prometheus (exemplo com Qdrant)
# HELP qdrant_vectors_count_total Total number of vectors
# TYPE qdrant_vectors_count_total gauge
# HELP qdrant_search_latency_seconds Latency of search operations
# TYPE qdrant_search_latency_seconds histogram
# HELP qdrant_ef_search_avg Average ef_search parameter used
# TYPE qdrant_ef_search_avg gauge

4.2. Logging e tracing distribuído

Para depurar cadeias RAG complexas, use OpenTelemetry para rastrear:

  1. Query do usuário → embedding → busca vetorial → chunks recuperados → prompt do LLM → resposta
  2. Cada etapa deve registrar: timestamp, latência, modelo usado, tokens consumidos

4.3. Alertas para degradação de qualidade

Configure alertas baseados em:

  • Queda de similaridade média: Se a similaridade média dos top-5 resultados cai > 10% em 1 hora
  • Aumento de falsos positivos: Quando chunks irrelevantes aparecem consistentemente no top-k
  • Timeout de busca: Mais de 1% das consultas excedem 500ms

5. Segurança e Controle de Acesso em Produção

5.1. Isolamento de tenants

Para aplicações multi-tenant, cada consulta deve ser filtrada por tenant_id:

# Filtro obrigatório por tenant no Qdrant
{
  "filter": {
    "must": [
      {"key": "tenant_id", "match": {"value": "acme_corp"}}
    ]
  },
  "limit": 10,
  "with_payload": true
}

5.2. Proteção contra injeção via embeddings

Embora raro, ataques podem tentar manipular embeddings para recuperar chunks de outros tenants. Mitigações:

  • Validar que o embedding da query está dentro do range esperado (ex: norma L2 entre 0.8 e 1.2)
  • Usar embeddings assinados criptograficamente para verificar origem

5.3. Criptografia

  • Em repouso: Ativar criptografia AES-256 no disco do banco vetorial
  • Em trânsito: TLS 1.3 obrigatório para todas as conexões
  • Chaves: Gerenciar via AWS KMS ou HashiCorp Vault

6. Custos e Otimização de Recursos em Nuvem

6.1. Dimensionamento de recursos

Para 10M vetores de 768 dimensões:
- Memória: ~30 GB para índice HNSW (sem quantização)
- CPU: 8-16 cores para operações simultâneas
- Armazenamento: ~15 GB para vetores + metadados

6.2. Compressão de embeddings

Reduza custos em 60-80% com quantização:

# Quantização escalar (float32 → int8)
{
  "quantization_config": {
    "scalar": {
      "type": "int8",
      "always_ram": true
    }
  }
}

# Redução de dimensionalidade via PCA (1536 → 256)
from sklearn.decomposition import PCA
pca = PCA(n_components=256)
embeddings_reduced = pca.fit_transform(embeddings_original)

6.3. Modelos de precificação

Provedor Modelo Custo mensal (1M vetores)
Pinecone Pod-based (p1) $70-150
Weaviate Serverless (per request) $50-200
Qdrant Dedicated cluster $100-300
pgvector RDS (incluso) $30-100 (adicional ao RDS)

7. Casos de Uso Avançados e Padrões de Arquitetura

7.1. RAG multimodal

Armazene embeddings de texto, imagem e áudio na mesma coleção, diferenciando por metadados:

{
  "vectors": {
    "text": [0.1, 0.2, ...],  # 768 dims
    "image": [0.3, 0.4, ...], # 512 dims
    "audio": [0.5, 0.6, ...]  # 384 dims
  },
  "payload": {
    "modality": "image",
    "source": "product_catalog_2024"
  }
}

7.2. Híbrido vetorial + lexical (BM25)

Para melhor recall em consultas com termos raros, combine busca vetorial com BM25:

# Weaviate: busca híbrida nativa
{
  "query": "machine learning transformers",
  "alpha": 0.5,  # 0 = only BM25, 1 = only vector
  "limit": 10
}

7.3. Failover e replicação geográfica

Para alta disponibilidade, configure replicação cross-region:

# Replicação no Qdrant (3 nós, 2 regiões)
{
  "cluster": {
    "nodes": [
      {"host": "qdrant-us-east-1", "port": 6333},
      {"host": "qdrant-us-west-2", "port": 6333},
      {"host": "qdrant-eu-west-1", "port": 6333}
    ],
    "replication_factor": 2,
    "write_consistency": "majority"
  }
}

Referências