# ADR: OpenSearch Serverless vs Banco Vetorial Dedicado para RAG Agêntico

Este ADR avalia as opções de infraestrutura de busca vetorial para uma plataforma RAG agêntica multi-tenant na AWS, comparando OpenSearch Serverless, bancos vetoriais dedicados (Pinecone, pgvector) e busca híbrida própria. A decisão considera custo, latência p99, filtros por permissão, ingestão incremental e integração nativa com Bedrock Knowledge Bases.

- URL: https://fernando.moretes.com/studies/adr-opensearch-serverless-vs-vector-db-agentic-rag

- Markdown: https://fernando.moretes.com/studies/adr-opensearch-serverless-vs-vector-db-agentic-rag/study.md?lang=pt

- Type: Decisão (ADR)

- Company: Agentic RAG platform (cenário)

- Domain: IA / Dados

- Status: accepted

- Date: 2026-06-04

- Tags: rag, opensearch-serverless, vector-search, bedrock, multi-tenancy, agentic-ai, aws, data-platform

- Reading time: 8 min

---

Quando você começa a construir uma plataforma RAG agêntica multi-tenant, a escolha do motor de busca vetorial não é um detalhe de infraestrutura — é uma decisão arquitetural que define custo operacional, segurança de dados entre tenants, latência de recuperação e a velocidade com que o sistema aprende com novos documentos. Este ADR documenta o raciocínio que me levou a escolher OpenSearch Serverless com integração nativa ao Bedrock Knowledge Bases, e os trade-offs que aceitei conscientemente ao fazê-lo.

## Ficha Técnica

- **Sistema:** Plataforma RAG Agêntica (cenário de referência)
- **Domínio:** IA / Dados — recuperação aumentada por geração com agentes autônomos
- **Escala estimada:** 50–200 tenants, 10M–500M vetores, 5k–50k consultas/dia (estimativa de projeto)
- **Região primária:** us-east-1 (Bedrock GA, OpenSearch Serverless disponível)
- **Stack de IA:** Amazon Bedrock (Claude 3, Titan Embeddings v2), Bedrock Knowledge Bases, Bedrock Agents
- **Stack de dados:** S3 (fonte), OpenSearch Serverless (índice vetorial), DynamoDB (metadados de tenant), Lambda (ingestão)
- **Status da decisão:** Aceita — implementação em andamento
- **Data:** 2025-Q1

## Contexto e Forças em Jogo

Uma plataforma RAG agêntica difere de um chatbot RAG simples em duas dimensões críticas: **autonomia** e **escopo dinâmico**. Os agentes não apenas recuperam documentos — eles decidem *quando* recuperar, *quais coleções* consultar e *como combinar* múltiplas fontes para construir um raciocínio multi-hop. Isso impõe requisitos que um índice vetorial estático não consegue satisfazer sozinho.

O primeiro conjunto de forças vem do **modelo multi-tenant**. Cada tenant possui sua própria base de conhecimento — documentos jurídicos, manuais técnicos, políticas internas — e não pode, sob nenhuma circunstância, ver dados de outro tenant. Isso não é apenas isolamento lógico via filtro de metadados; é um requisito de segurança que precisa ser auditável e demonstrável. A abordagem ingênua de colocar todos os vetores em um índice único e filtrar por `tenant_id` cria um risco residual: uma query malformada ou um bug no filtro expõe dados cruzados. Índices separados por tenant eliminam esse risco, mas explodem o custo operacional em bancos vetoriais provisionados.

O segundo conjunto de forças é **econômico**. Bancos vetoriais dedicados como Pinecone operam em modelo de pods provisionados. Para 50 tenants com volumes heterogêneos — alguns com 100k vetores, outros com 50M — o over-provisioning é inevitável. O custo de manter pods p2 ou p3 ociosos durante horários de baixa demanda é real e recorrente. OpenSearch Serverless, por outro lado, cobra por OCU (OpenSearch Compute Unit) efetivamente consumida, com escalonamento automático até zero em períodos sem tráfego — o que para workloads com padrão diurno forte representa uma economia estrutural.

O terceiro conjunto de forças é a **integração com o ecossistema Bedrock**. A AWS anunciou suporte nativo de OpenSearch Serverless como backend de Bedrock Knowledge Bases, o que significa que o pipeline de ingestão (chunking, embedding com Titan Embeddings v2, indexação) é gerenciado pelo próprio serviço. Isso elimina uma camada inteira de código de orquestração que eu teria que escrever e manter se escolhesse um banco vetorial externo.

## O Problema Real: Filtros de Permissão em Tempo de Consulta

O aspecto mais subestimado na escolha de um motor vetorial para RAG empresarial é o **filtro de permissão em tempo de consulta** — o que a literatura chama de *pre-filtering* vs *post-filtering*. A distinção importa muito mais do que parece.

**Post-filtering** é simples de implementar: você recupera os top-K vetores mais similares e depois descarta os que o usuário não tem permissão de ver. O problema é que se 60% dos documentos mais relevantes semanticamente estão fora do escopo de permissão do usuário, o resultado final pode ter recall catastroficamente baixo — você retorna 2 documentos quando deveria retornar 10, e os 2 que retornou não são os melhores.

**Pre-filtering** resolve isso aplicando o filtro *antes* da busca vetorial, restringindo o espaço de busca apenas aos documentos que o usuário pode ver. OpenSearch Serverless suporta pre-filtering via `filter` no contexto de `knn_vector` queries usando a API `_search` com `knn` + `filter` combinados. Isso é crítico: significa que o agente nunca vê um documento que o usuário não deveria ver, e a relevância semântica é calculada dentro do espaço correto de permissão.

Além disso, a plataforma precisa suportar **ingestão incremental** — documentos novos chegam continuamente via S3 events, e o índice precisa refletir isso em minutos, não horas. OpenSearch Serverless lida com isso nativamente: o Bedrock Knowledge Bases tem suporte a sync incremental via S3 data source, onde apenas os chunks modificados ou adicionados são re-embeddados e re-indexados. Isso contrasta com soluções como pgvector em RDS, onde ingestão incremental de alta frequência cria pressão de write IOPS e pode degradar performance de leitura sem tuning cuidadoso de vacuum e índices HNSW.

Um ponto que muitos arquitetos ignoram: **busca híbrida** (vetorial + BM25 lexical) não é opcional para RAG de qualidade em domínios técnicos. Documentos com números de série, códigos de produto, siglas técnicas ou nomes próprios específicos são mal recuperados por busca puramente semântica. OpenSearch suporta busca híbrida nativamente com `hybrid` query type, combinando scores de kNN e BM25 via normalização min-max ou RRF (Reciprocal Rank Fusion). Pinecone requer que você mantenha um índice de texto separado (tipicamente Elasticsearch ou OpenSearch) e faça a fusão na camada de aplicação — adicionando latência, complexidade operacional e um ponto extra de falha.

## Matriz de Decisão: Opções Avaliadas

### OpenSearch Serverless (AOSS) + Bedrock Knowledge Bases

**Pros**
- Integração nativa com Bedrock Knowledge Bases — pipeline de ingestão gerenciado (chunking, embedding, indexação)
- Autoscaling real por OCU — sem over-provisioning para tenants de baixo volume
- Busca híbrida nativa (kNN + BM25) com RRF sem camada extra
- Pre-filtering por metadados suportado nativamente na query kNN
- Isolamento por collection por tenant — sem risco de vazamento cross-tenant

**Cons**
- Custo mínimo de 2 OCUs por collection (~$700/mês por collection ativa) — penaliza tenants muito pequenos
- Menos controle sobre parâmetros HNSW (ef_construction, m) comparado a OpenSearch provisionado
- Latência de cold start para collections sem tráfego recente
- Vendor lock-in moderado no ecossistema AWS/Bedrock

**Verdict:** Escolha principal — melhor equilíbrio entre operação zero, segurança multi-tenant e integração Bedrock

### Pinecone (SaaS dedicado)

**Pros**
- Latência p99 consistentemente baixa em workloads de alta escala (>100M vetores)
- Namespaces para isolamento lógico de tenant sem custo de collection separada
- SDK maduro, documentação excelente, suporte a metadados ricos

**Cons**
- Sem integração nativa com Bedrock Knowledge Bases — pipeline de ingestão 100% custom
- Busca híbrida requer índice lexical externo (custo e complexidade adicionais)
- Modelo de pods provisionados — over-provisioning inevitável para tenants heterogêneos
- Dados fora da VPC AWS — implicações de compliance e latência de rede
- Custo elevado em escala: p2 pod ~$1.4k/mês por pod, sem escala a zero

**Verdict:** Descartado — custo estrutural alto, sem integração Bedrock nativa, dados fora da VPC

### pgvector em Aurora PostgreSQL Serverless v2

**Pros**
- Familiaridade operacional — equipes já conhecem PostgreSQL
- Dados dentro da VPC, integração com RDS IAM auth
- Serverless v2 com escala por ACU — melhor que RDS provisionado
- Suporte a metadados ricos via colunas SQL e filtros complexos

**Cons**
- HNSW em pgvector ainda imaturo para >10M vetores — índice IVFFlat requer rebuild periódico
- Sem busca híbrida nativa — BM25 requer extensão pg_trgm ou FTS separado
- Sem integração com Bedrock Knowledge Bases — pipeline de ingestão custom
- Escala a zero do Aurora Serverless v2 ainda tem latência de cold start de 15-30s
- Isolamento de tenant por schema ou banco separado — complexidade operacional cresce com número de tenants

**Verdict:** Descartado para uso primário — adequado como fallback para tenants com volume muito baixo (<100k vetores)

### Busca Híbrida Própria (OpenSearch provisionado + Lambda)

**Pros**
- Controle total sobre parâmetros de índice, sharding, tuning de relevância
- Potencialmente menor custo por vetor em altíssima escala (>1B vetores)

**Cons**
- Overhead operacional massivo — patches, rebalanceamento, blue/green upgrades
- Time de engenharia desviado de features de produto para infraestrutura
- Sem integração com Bedrock Knowledge Bases — pipeline completamente custom
- Risco de under-provisioning em picos de ingestão ou consulta

**Verdict:** Descartado — custo de engenharia não justificado na fase atual; revisitar se escala >500M vetores/tenant

## Comparativo Técnico Detalhado
| Critério | Critério | AOSS + Bedrock KB | Pinecone | pgvector Aurora |
| --- | --- | --- | --- | --- |
| Integração Bedrock KB | ✅ Nativa | ❌ Custom pipeline | ❌ Custom pipeline | — |
| Busca Híbrida (kNN + BM25) | ✅ Nativa com RRF | ⚠️ Requer índice externo | ⚠️ FTS parcial, sem RRF nativo | — |
| Pre-filtering por permissão | ✅ kNN + filter nativo | ✅ Metadata filter | ✅ SQL WHERE clause | — |
| Isolamento multi-tenant | ✅ Collection por tenant | ⚠️ Namespace (lógico) | ⚠️ Schema/DB separado | — |
| Autoscaling / custo ocioso | ✅ OCU por uso (mín 0.5 OCU) | ❌ Pod provisionado fixo | ⚠️ ACU serverless, cold start | — |
| Ingestão incremental | ✅ S3 sync via Bedrock KB | ⚠️ Custom upsert pipeline | ⚠️ Custom upsert + reindex | — |
| Latência p99 (estimativa) | ~80-150ms (warm) | ~20-50ms (warm) | ~50-120ms (warm) | — |
| Dados dentro da VPC AWS | ✅ VPC endpoint disponível | ❌ SaaS externo | ✅ VPC nativa | — |

## Decisão Arquitetural

**Status:** accepted

**Contexto**

A plataforma RAG agêntica precisa servir 50-200 tenants com volumes de documentos heterogêneos, garantir isolamento de dados entre tenants, suportar busca híbrida para domínios técnicos, integrar com Bedrock Agents para raciocínio multi-hop, e escalar custo proporcionalmente ao uso real — não ao pior caso provisionado.

**Decisão**

Adotar **Amazon OpenSearch Serverless (AOSS)** como backend de busca vetorial, integrado ao **Amazon Bedrock Knowledge Bases** para gerenciamento do pipeline de ingestão. Cada tenant recebe uma **collection AOSS dedicada** com data access policy isolada. A busca utiliza **hybrid query (kNN + BM25) com RRF** para todos os domínios técnicos. Filtros de permissão são aplicados via **pre-filter na query kNN**. Ingestão incremental é gerenciada pelo **S3 data source sync do Bedrock Knowledge Bases**.

**Consequências**
- ✅ Pipeline de ingestão gerenciado — zero código de chunking, embedding e indexação para manter
- ✅ Isolamento físico por collection — auditável, demonstrável, sem risco de vazamento cross-tenant
- ✅ Custo proporcional ao uso — tenants inativos não geram custo de compute significativo
- ✅ Busca híbrida nativa elimina a necessidade de índice lexical separado
- ⚠️ Custo mínimo por collection ativa (~2 OCUs) penaliza tenants com volume muito baixo — mitigado por estratégia de collection compartilhada para tenants tier-free
- ⚠️ Latência p99 de 80-150ms é aceitável para RAG agêntico (o LLM domina a latência total), mas requer monitoramento em consultas complexas com muitos filtros

## Estratégia de Multi-Tenancy: Collections vs Namespaces vs Índices Compartilhados

A decisão de usar uma **collection AOSS por tenant** merece justificativa detalhada, porque vai contra a intuição de otimização de custo à primeira vista.

A alternativa óbvia seria um único índice compartilhado com campo `tenant_id` como filtro. Isso funciona em ambientes de baixo risco, mas falha em três cenários reais: (1) **auditoria de compliance** — quando um auditor pergunta "como você garante que o Tenant A não vê dados do Tenant B?", a resposta "temos um filtro na query" não é satisfatória; a resposta "cada tenant tem sua própria collection com data access policy IAM isolada" é; (2) **blast radius de operações de manutenção** — uma reindexação ou migração de schema em um índice compartilhado afeta todos os tenants simultaneamente; (3) **isolamento de performance** — um tenant com ingestão massiva em um índice compartilhado pode degradar latência de consulta de outros tenants via contenção de recursos de indexação.

A collection por tenant resolve os três. O custo mínimo de ~2 OCUs por collection ativa é real, mas para tenants pagantes em plataformas B2B, esse custo é absorvido no pricing do plano. Para tenants em tier gratuito ou de muito baixo volume (menos de 50k vetores, menos de 100 queries/dia), a estratégia é diferente: esses tenants são agrupados em uma **collection compartilhada de tier-free**, onde o isolamento é feito por metadados (`tenant_id` como campo de filtro obrigatório em todas as queries). A troca de garantia de isolamento por custo é explícita e documentada no contrato de nível de serviço do tier gratuito.

Essa estratégia em duas camadas — collections dedicadas para tenants pagantes, collection compartilhada para tier-free — é um padrão que uso consistentemente em plataformas multi-tenant: você não precisa de uma solução única que sirva todos os casos, precisa de uma política clara que define qual solução se aplica a qual segmento.

## Arquitetura: Plataforma RAG Agêntica com AOSS e Bedrock

Fluxo completo: ingestão incremental de documentos via S3 → Bedrock Knowledge Bases → AOSS, e fluxo de consulta agêntica via Bedrock Agents → AOSS hybrid search → resposta gerada pelo LLM. Isolamento por collection por tenant destacado.

### 👤 Tenant Layer

- End User Tenant A/B/N (user)
- API Gateway + Cognito JWT (security)

### 🤖 Agentic Layer (Bedrock)

- Bedrock Agent Orchestrator (ai)
- Claude 3 (Bedrock) (ai)
- Bedrock Knowledge Bases (ai)

### 🔍 Vector Search Layer (AOSS)

- AOSS Collection Tenant A (dedicated) (data)
- AOSS Collection Tenant B (dedicated) (data)
- AOSS Collection Free-tier (shared) (data)

### 📥 Ingestion Layer

- S3 Bucket Documents (per-tenant prefix) (storage)
- S3 Event Notification (messaging)
- Bedrock KB Sync Job (incremental) (ai)
- Titan Embeddings v2 (managed by KB) (ai)

### 🔐 Security & Metadata

- IAM Data Access Policy (per collection) (security)
- DynamoDB Tenant Metadata + ACL (data)
- Lambda Permission Resolver (compute)

### Fluxos

- user -> api_gw: HTTPS + JWT
- api_gw -> bedrock_agent: query + tenant_id
- bedrock_agent -> lambda_auth: resolve permissions
- lambda_auth -> dynamo_meta: get ACL
- bedrock_agent -> bedrock_kb: retrieve(query, filters)
- bedrock_kb -> aoss_tenant_a: hybrid kNN+BM25
+ pre-filter
- bedrock_kb -> aoss_tenant_b: hybrid kNN+BM25
+ pre-filter
- bedrock_kb -> aoss_shared: tenant_id filter
(free-tier)
- bedrock_agent -> bedrock_llm: context + prompt
- bedrock_llm -> bedrock_agent: generated response
- bedrock_agent -> api_gw: final answer
- s3_docs -> s3_event: PutObject event
- s3_event -> kb_sync: trigger sync
- kb_sync -> titan_emb: chunk → embed
- titan_emb -> aoss_tenant_a: index vectors
- aoss_tenant_a -> iam_policy: access control

## Avaliação Well-Architected

- **security**: Isolamento físico por collection AOSS com data access policies IAM por tenant. Pre-filtering na query kNN garante que o modelo nunca vê documentos fora do escopo de permissão. Dados trafegam dentro da VPC via VPC endpoint do AOSS. Credenciais gerenciadas por IAM roles — sem chaves estáticas.
- **reliability**: AOSS é um serviço gerenciado com SLA de 99.9%. Bedrock Knowledge Bases gerencia retry de ingestão automaticamente. Sem SPOF na camada de busca — o serviço é multi-AZ por design. Risco: cold start de collection inativa; mitigado por warm-up periódico via CloudWatch Events para tenants críticos.
- **performance**: Busca híbrida kNN+BM25 com RRF melhora recall em domínios técnicos. Latência p99 estimada de 80-150ms (warm) é dominada pelo LLM (~1-3s), tornando a busca não o gargalo. OCU autoscaling previne degradação em picos de consulta.
- **cost**: Modelo OCU por uso elimina over-provisioning. Estratégia de two-tier (collection dedicada para pagantes, compartilhada para free) otimiza custo por segmento. Monitoramento de OCU por collection via CloudWatch é essencial para detectar tenants com crescimento anômalo.
- **sustainability**: Autoscaling a near-zero para collections inativas reduz consumo de energia em períodos ociosos — relevante para plataformas com padrão diurno forte e tenants em fusos horários distintos.

> **Minha Perspectiva Sênior:** Se eu fosse começar essa plataforma hoje, faria exatamente essa escolha — mas com os olhos abertos para dois riscos que a maioria dos arquitetos subestima.

O primeiro é o **custo de OCU em escala de collections**. O modelo de precificação do AOSS cobra 2 OCUs mínimas por collection ativa. Isso é ótimo para tenants médios e grandes, mas se você chegar a 200 tenants pagantes com collections ativas 24/7, o custo mínimo de compute pode surpreender. Minha recomendação: implemente uma política de **hibernação de collection** para tenants sem atividade por mais de 30 dias — o AOSS não tem isso nativo ainda, então você precisa de um job que monitore atividade e desativa collections via API. Isso pode reduzir o custo de compute em 30-40% em plataformas com churn de tenants.

O segundo risco é a **dependência do pipeline de ingestão do Bedrock Knowledge Bases**. Ele é conveniente, mas é uma caixa preta: você não controla o chunking strategy, o overlap, nem o modelo de embedding usado (está amarrado ao Titan Embeddings v2 ou Cohere). Para a maioria dos casos isso é aceitável, mas se você tiver documentos com estrutura muito específica — tabelas financeiras, código-fonte, diagramas técnicos — o chunking padrão vai destruir a semântica. Nesse caso, eu manteria o Bedrock KB para o fluxo padrão e teria um **pipeline de ingestão custom via Lambda** para tipos de documento especiais, que escreve diretamente na collection AOSS via API. A coexistência dos dois pipelines é suportada — o Bedrock KB não é o único que pode escrever em uma collection AOSS.

Sobre a latência p99: 80-150ms para busca vetorial parece alto comparado ao Pinecone (~20-50ms), mas em contexto de RAG agêntico, o LLM consome 1-3 segundos por chamada. A busca não é o gargalo. Gastar engenharia para reduzir latência de busca de 120ms para 40ms em detrimento de complexidade operacional é uma otimização prematura clássica. Meça primeiro, otimize depois.

## Referências

- [AWS News Blog — OpenSearch Serverless for Agentic AI](https://aws.amazon.com/blogs/aws/)
- [Amazon OpenSearch Serverless — Features & Pricing](https://aws.amazon.com/opensearch-service/features/serverless/)
- [Amazon Bedrock Knowledge Bases](https://aws.amazon.com/bedrock/knowledge-bases/)
- [OpenSearch kNN + filter documentation](https://opensearch.org/docs/latest/search-plugins/knn/filter-search-knn/)
- [OpenSearch Hybrid Search with RRF](https://opensearch.org/docs/latest/search-plugins/hybrid-search/)
- [Pinecone Pricing — Pod-based indexes](https://www.pinecone.io/pricing/)
- [pgvector — Open-source vector similarity search for Postgres](https://github.com/pgvector/pgvector)
- [Amazon Bedrock — Supported foundation models](https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html)

## Veredicto

OpenSearch Serverless com Bedrock Knowledge Bases é a escolha correta para uma plataforma RAG agêntica multi-tenant na AWS em 2025 — não porque seja perfeita, mas porque resolve os problemas certos com o menor custo operacional. O isolamento físico por collection é a única resposta auditável para multi-tenancy de dados sensíveis. A busca híbrida nativa elimina uma camada de complexidade que qualquer outra opção exigiria. O autoscaling por OCU transforma o custo de infraestrutura de fixo em variável, alinhando-o ao modelo de negócio da plataforma.

Os trade-offs são reais: custo mínimo por collection ativa, latência p99 maior que Pinecone, e lock-in moderado no ecossistema Bedrock. Nenhum deles é bloqueante para o estágio atual. O custo mínimo é gerenciável com política de hibernação. A latência é irrelevante frente ao tempo de inferência do LLM. O lock-in é mitigado pela portabilidade dos dados brutos em S3.

A decisão seria diferente em dois cenários: (1) se a plataforma precisasse de latência de busca sub-20ms para casos de uso de tempo real (recomendação em tempo real, autocomplete), onde Pinecone ou um OpenSearch provisionado com tuning de HNSW seria necessário; (2) se a escala ultrapassasse 500M vetores por tenant com padrões de acesso altamente imprevisíveis, onde o controle fino sobre sharding e parâmetros de índice justificaria o overhead operacional de um cluster provisionado. Para o cenário descrito — 50-200 tenants, domínios técnicos, integração Bedrock, equipe de engenharia focada em produto — esta é a decisão certa.

## Fontes do caso

- [AWS News Blog — OpenSearch Serverless for agentic AI](https://aws.amazon.com/blogs/aws/)
- [Amazon OpenSearch Serverless](https://aws.amazon.com/opensearch-service/features/serverless/)
- [Amazon Bedrock Knowledge Bases](https://aws.amazon.com/bedrock/knowledge-bases/)
