Infraestrutura compartilhada, tenants isolados: multi-tenancy com Amazon Bedrock AgentCore

O desafio do multi-tenancy em aplicações de IA

Construir aplicações de IA para múltiplos clientes sobre a mesma infraestrutura é um dos desafios arquiteturais mais relevantes para quem desenvolve plataformas SaaS hoje. Sem os controles certos, os riscos são sérios: vazamento de dados entre clientes, qualidade de serviço inconsistente, custos impossíveis de rastrear por tenant e falta de visibilidade operacional.

A AWS publicou o segundo artigo de uma série sobre como endereçar esses desafios usando o Amazon Bedrock AgentCore. O Parte 1 da série abordou as considerações de design para arquiteturas multi-tenant com AgentCore. Este segundo artigo vai além e mostra padrões concretos de implementação, com código e configurações reais.

O exemplo central é um assistente de IA para saúde que atende múltiplas clínicas e hospitais. Mas os padrões apresentados se aplicam a qualquer plataforma SaaS, solução empresarial com múltiplas unidades de negócio ou serviço gerenciado para diferentes organizações clientes. O código de exemplo completo está disponível no repositório GitHub da AWS.

Visão geral da solução

A arquitetura proposta implementa uma hierarquia de três níveis: Tier → Tenant → Usuário. O isolamento é aplicado em cada camada — nos documentos da base de conhecimento, na memória conversacional, no acesso aos modelos e no rastreamento de custos.

A estratégia de tiers é um padrão comum em SaaS: os tenants são agrupados em níveis de serviço distintos com base em suas necessidades, padrões de uso ou planos de preço. No exemplo de saúde, a solução define dois tiers:

  • Tier Básico: voltado para clínicas pequenas que precisam principalmente de busca e recuperação de documentos. Usa o modelo Mistral Ministral 3 8B Instruct, mantendo custos baixos para consultas simples.
  • Tier Premium: voltado para hospitais e centros de especialidade que precisam de análise clínica mais complexa. Usa o OpenAI GPT OSS 120B com capacidades avançadas de raciocínio, incluindo acesso a uma ferramenta de busca na web exclusiva deste tier.

Dentro de cada tier, a solução adota o modelo de isolamento pool, onde os tenants compartilham a mesma infraestrutura e recursos computacionais. O isolamento entre eles é garantido por mecanismos lógicos: identificadores com escopo, políticas de acesso e particionamento de dados. Essa combinação de tiers com pool model permite equilibrar eficiência de custo com diferenciação de serviço.

Arquitetura e componentes principais

Os componentes da solução são:

  • Amazon Cognito: gerencia a autenticação dos usuários e armazena metadados do tenant (tier, clinic_id, role) como claims em Tokens Web JSON (JWT). Esses claims são propagados como contexto do tenant em toda a requisição.
  • Amazon API Gateway: roteia as requisições e aplica rate limiting por tier via usage plans.
  • AWS Lambda: extrai o contexto do tenant e invoca o agente AgentCore correspondente.
  • Componentes AgentCore: Runtime (execução do agente), Memory (estado da conversa), Identity (gerenciamento de identidade), Gateway (servidor de ferramentas) e Policy (limites de ação do agente).
  • Amazon Simple Storage Service (Amazon S3): armazena documentos clínicos em buckets separados por tier, com estrutura de prefixos hierárquicos para isolamento por tenant.
  • Amazon Bedrock Knowledge Bases: fornece busca semântica com filtragem por metadados para restringir consultas aos documentos do tenant solicitante.
  • Amazon Bedrock project: permite rastreamento de custos por tier via tags de alocação de custo.
Figura 1: Arquitetura multi-tenant com isolamento hierárquico (Tier → Tenant → Usuário) — Imagem original — fonte: Aws

Como o AgentCore endereça cada desafio de multi-tenancy

AgentCore Runtime: isolamento computacional por tenant

O AgentCore Runtime fornece o ambiente de execução dos agentes. Cada sessão de agente roda em uma micro-VM isolada, garantindo isolamento computacional por tenant. Instâncias separadas são mantidas por tier, cada uma configurada com os modelos e capacidades adequados ao seu nível de serviço. O ID do projeto Bedrock é carregado do SSM Parameter Store e passado na inicialização do modelo para garantir rastreamento de custos correto.

# Agent configuration
config = TIER_CONFIG.get(tier, TIER_CONFIG["basic"])
model_id = config["default_model"]

# Project ID is fetched from SSM
project_id = get_ssm_parameter(config["project_ssm"])

# Passed to OpenAIModel (premium tier) targeting the inference endpoint
self.model = OpenAIModel(
    client_args={"base_url": mantle_base_url, "api_key": api_key, "project": project_id},
    model_id=model_id,
)

AgentCore Identity: autenticação unificada baseada em JWT

O AgentCore Identity protege a arquitetura multi-tenant com um modelo de autenticação unificado baseado em JWT. O token de ID do Cognito valida o usuário tanto no Runtime quanto nas fronteiras do Gateway. Cada AgentCore Runtime é configurado com um autorizador JWT que valida os tokens do Cognito antes da execução do código do agente.

O token carrega metadados do tenant como claims customizados, incluindo custom:tier (que direciona para o modelo, base de conhecimento e gateway corretos), custom:clinic_id (que é o ID do tenant e garante isolamento de dados) e custom:role (para controle de acesso baseado em papéis). A configuração do autorizador é aplicada durante o deploy do agente:

AUTHORIZER_CONFIG='{"customJWTAuthorizer":{"discoveryUrl":"'$COGNITO_DISCOVERY_URL'","allowedAudience":["'$COGNITO_WEB_CLIENT_ID'"]}}'
agentcore configure --entrypoint main.py \
    --name healthcare_basic \
    --authorizer-config "$AUTHORIZER_CONFIG" \
    --request-header-allowlist "Authorization"

O Gateway também é configurado com autorização JWT usando a mesma URL de descoberta do Cognito. Quando o agente chama o gateway, ele encaminha o JWT original do usuário como Bearer token, junto com headers de contexto do tenant (X-Tier, X-Clinic-ID, X-S3-Prefix). A função Lambda de destino nunca recebe o JWT diretamente — ela lê apenas os headers de tenant confiáveis e assume uma role de Máquina de Distribuição de Tokens (TVM) com session tags derivadas desses headers.

AgentCore Memory: isolamento de memória conversacional em duas camadas

O histórico de conversas não pode vazar entre tenants nem entre usuários do mesmo tenant. A solução aplica isolamento de memória em duas camadas: escopo na aplicação e Controle de Acesso Baseado em Atributos (ABAC) com suporte em IAM.

Na camada de aplicação, o AgentCore Memory usa uma estrutura de namespace hierárquico com um actor_id composto para organizar os dados de conversa por tenant:

actor_id = f"{tier}-{clinic_id}-{user_id}"
# Example: "basic-clinic-a-dr.smith@clinic-a.com"

Os namespaces separam diferentes tipos de memória:

clinic/{actor_id}/facts/{session_id}   # SEMANTIC --- clinical facts
clinic/{actor_id}/preferences          # PREFERENCES -- user preferences

Para reforçar o isolamento na camada de infraestrutura, a solução usa o padrão TVM com ABAC. Em tempo de execução, o agente assume uma role TVM com Tier, ClinicId e UserId como session tags, recebendo credenciais temporárias com escopo restrito ao namespace daquele tenant:

sts = boto3.client("sts", region_name=region)
response = sts.assume_role(
    RoleArn=tvm_role_arn,
    RoleSessionName=f"mem-{tier}-{clinic_id}-{user_id}",
    DurationSeconds=900,
    Tags=[
        {"Key": "Tier",     "Value": tier},
        {"Key": "ClinicId", "Value": clinic_id},
        {"Key": "UserId",   "Value": user_id},
    ],
    TransitiveTagKeys=["Tier", "ClinicId", "UserId"],
)

AgentCore Gateway: ferramentas dinâmicas com contexto de tenant

O AgentCore Gateway transforma funções Lambda estáticas em ferramentas dinâmicas e conscientes do contexto, usando o Protocolo de Contexto de Modelo (MCP), um padrão open-source para conectar agentes de IA a ferramentas externas. Sem isso, seria necessário escrever código customizado para integrar APIs, gerenciar autenticação e propagação de contexto manualmente.

A Lambda expõe duas ferramentas pelo Gateway: patient_context (dados demográficos e histórico médico do paciente) e clinic_config (configurações e informações de provedores da clínica). O agente inicializa o cliente MCP Gateway com headers com escopo de tenant, de modo que toda chamada de ferramenta carrega automaticamente o contexto do tenant. Mais detalhes sobre os headers estão disponíveis na documentação de headers do gateway.

AgentCore Policy: controle de acesso declarativo com Cedar

O AgentCore Policy aplica limites de ação específicos por tier nas ferramentas do gateway usando políticas de autorização Cedar. A solução cria um motor de políticas compartilhado, anexado aos gateways basic e premium no modo ENFORCE.

Para o tier básico, uma política Cedar restringe o acesso à ferramenta patient_context ao horário comercial (8h–18h), avaliando o campo request_hour da entrada da ferramenta. Para o tier premium, a política permite acesso irrestrito 24/7. Ambos os tiers recebem permissão para a ferramenta clinic_config, que expõe apenas dados de configuração não sensíveis.

# Cedar policy: basic tier --- restrict patient_context to business hours
permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"HealthcareLambda-Basic___patient_context",
  resource == AgentCore::Gateway::"{gateway_arn}"
)
when {
  context.input has request_hour &&
  context.input.request_hour >= 8 &&
  context.input.request_hour < 18
};

# Cedar policy: premium tier --- 24/7 patient_context access
permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"HealthcareLambda-Premium___patient_context",
  resource == AgentCore::Gateway::"{gateway_arn}"
)
when {
  context.input has patient_id
};

AgentCore Observability: rastreamento por tenant com OpenTelemetry

A integração de observabilidade do AgentCore usa baggage do OpenTelemetry para propagar metadados do tenant por todo o ciclo de vida da requisição. O baggage do OpenTelemetry é um repositório de chave-valor que permite transportar dados adicionais junto ao contexto de rastreamento. A solução define os identificadores do tenant como baggage no ponto de entrada do Runtime, de modo que cada span e entrada de log carrega a atribuição do tenant:

from opentelemetry import baggage, context

# Set tenant context in OTel baggage (at AgentCore Runtime entrypoint)
ctx = baggage.set_baggage("tier", tier)
ctx = baggage.set_baggage("clinic_id", clinic_id, context=ctx)
ctx = baggage.set_baggage("actor_id", actor_id, context=ctx)
context.attach(ctx)

Com isso, é possível usar o Amazon CloudWatch Logs Insights para rastrear o volume de requisições por clínica, combinado com os Projetos Bedrock para atribuição de custos por tier e logging estruturado de uso para rastreamento de tokens por clínica.

Padrões de implementação multi-tenant

Isolamento de dados via buckets S3 por tier

A solução cria um bucket S3 separado por tier de serviço, com prefixos específicos por tenant dentro de cada bucket. Cada Knowledge Base de tier tem seu próprio bucket dedicado, fornecendo isolamento em nível de bucket entre tiers. Dentro de cada bucket, prefixos hierárquicos organizam os dados dos tenants:

s3://healthcare-basic-kb-{suffix}/
├── basic-tier/
│   ├── clinic-a/
│   │   ├── appointment-notes/
│   │   ├── lab-results/
│   │   ├── patient-intake/
│   │   └── prescriptions/
│   ├── clinic-b/
│   ├── clinic-c/
│   └── clinic-d/

s3://healthcare-premium-kb-{suffix}/
├── premium-tier/
│   ├── hospital-a/
│   │   ├── surgical-notes/
│   │   ├── pathology-reports/
│   │   └── imaging-studies/
│   ├── hospital-b/
│   ├── clinic-e/
│   └── clinic-f/

O prefixo S3 é construído a partir da identidade do tenant extraída dos claims JWT do Cognito. A ferramenta de recuperação de documentos aplica o isolamento através de filtro de metadados da Amazon Bedrock Knowledge Base no clinic_id:

response = client.retrieve(
    knowledgeBaseId=kb_id,
    retrievalQuery={"text": query},
    retrievalConfiguration={
        "vectorSearchConfiguration": {
            "filter": {"equals": {"key": "clinic_id", "value": clinic_id}},
        }
    },
)

Atribuição de custos via Projetos Bedrock e logging estruturado

A atribuição de custos opera em dois níveis: por tier através dos Projetos Bedrock, e por clínica através de logging estruturado de uso.

Cada tier tem um Projeto Bedrock dedicado com tags de alocação de custo (CostCenter, Tier, Application). O ID do projeto é passado em cada requisição de inferência pelo endpoint Bedrock Mantle, de modo que todos os custos de invocação de modelos são automaticamente segmentados por tier no AWS Cost Explorer. Após ativar as tags de alocação de custo no AWS Billing (as tags podem levar até 24 horas para se propagar), é possível filtrar e agrupar os custos de inferência por CostCenter, Tier ou Application.

Para granularidade por clínica, a solução registra o uso de tokens após cada invocação do agente como JSON estruturado com o contexto do tenant:

def _log_usage(self, result) -> None:
    usage = result.metrics.accumulated_usage
    logger.info(json.dumps({
        "event": "inference_usage",
        "tier": self.tier,
        "clinic_id": self.clinic_id,
        "user_id": self.user_id,
        "model_id": self.model_id,
        "input_tokens": usage.get("inputTokens", 0),
        "output_tokens": usage.get("outputTokens", 0),
        "total_tokens": usage.get("totalTokens", 0),
    }))

Esses logs chegam ao CloudWatch e podem ser consultados com Logs Insights para calcular o uso por clínica. Para estimar os custos, basta multiplicar as contagens de tokens pelo preço publicado por token de cada modelo.

Rate limiting via API Gateway

O rate limiting por tier é aplicado usando usage plans do API Gateway. A solução usa usage plans separados por tier:

basic-tier-plan:
  throttle: {rate_limit: 2, burst_limit: 5}
  quota: {limit: 50, period: DAY}

premium-tier-plan:
  throttle: {rate_limit: 10, burst_limit: 20}
  quota: {limit: 500, period: DAY}

Conclusão

O que a AWS demonstra neste artigo é que construir aplicações de IA multi-tenant seguras e escaláveis não exige lógica complexa de isolamento na camada de aplicação. Combinando serviços nativos como Cognito para identidade, prefixos S3 para isolamento de dados, API Gateway para rate limiting, Projetos Bedrock e logging estruturado para atribuição de custos, e o AgentCore para orquestração de IA, é possível entregar isolamento completo entre tenants com código customizado mínimo.

Os padrões apresentados — hierarquia Tier → Tenant → Usuário, modelo pool com isolamento lógico, TVM com ABAC, políticas Cedar declarativas e observabilidade via OpenTelemetry — são aplicáveis a qualquer plataforma SaaS que utilize agentes de IA.

Para explorar o código completo, acesse o repositório no GitHub ou consulte a documentação do Amazon Bedrock AgentCore. Para entender as considerações de design por trás desta arquitetura, confira também o artigo Building multi-tenant agents with Amazon Bedrock AgentCore.

Fonte

Shared infrastructure, isolated tenants: Pool model multi-tenancy with Amazon Bedrock AgentCore (https://aws.amazon.com/blogs/machine-learning/shared-infrastructure-isolated-tenants-pool-model-multi-tenancy-with-amazon-bedrock-agentcore/)

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *