Filtragem estruturada de memória com metadados no AgentCore Memory

O problema: busca semântica não é suficiente sozinha

Imagine um agente de suporte ao cliente que recebe a consulta “problemas de cobrança” e devolve uma mistura de tickets técnicos, conversas de vendas com reclamações de recibo e disputas de faturamento — tudo junto. Esse é o limite da busca por similaridade semântica quando agentes acumulam semanas de histórico de interações: o sistema encontra tudo que é semanticamente próximo, mas não consegue filtrar pelo que realmente importa, como tipo de problema, status ou período de tempo.

O Amazon Bedrock AgentCore Memory é um serviço gerenciado de memória que dá aos agentes de IA a capacidade de lembrar e recuperar informações entre conversas. Ele organiza os registros de memória em namespaces que definem escopos isolados — como clients/client-123 — garantindo que os dados de cada entidade fiquem separados. Para entender mais sobre organização de namespaces, a AWS publicou o post Organizando a memória de agentes em escala: padrões de design de namespace no AgentCore Memory.

À medida que as memórias crescem, sinais relevantes se perdem em resultados semanticamente similares, mas contextualmente irrelevantes. O isolamento por namespace sozinho não resolve isso. A filtragem por metadados fecha essa lacuna: é possível aplicar filtros baseados em atributos de negócio — como prioridade, departamento ou intervalo de tempo — antes que a busca semântica seja executada.

Em avaliações realizadas com um conjunto de 151 perguntas baseado em um benchmark de memória de longo prazo (no estilo LoCoMo, com múltiplas sessões de conversa), a precisão geral nas respostas subiu de 40% para 64% com a filtragem por metadados habilitada. Para perguntas que dependem de fronteiras contextuais — como buscas por período de tempo, filtragem por prioridade ou escopo por departamento — o ganho foi ainda maior: de 16% para 69%.

Imagem original — fonte: Aws

Como namespaces e metadados se complementam

O AgentCore Memory usa namespaces para organizar e isolar memórias ao longo das fronteiras das entidades primárias. O namespace define quem é o dono dos dados — por exemplo, patients/patient-456. Já os metadados lidam com o sub-agrupamento dentro dessas fronteiras: a categoria, o status de resolução, a data, a prioridade e as tags.

Em ambientes multi-tenant, o padrão fica claro: os namespaces já fornecem separação completa de dados entre tenants. Dentro do namespace de cada tenant, os agentes ainda precisam filtrar por tipo de ticket antes de buscar padrões de resolução. Os namespaces respondem ao quem; os metadados respondem ao o quê, quando e quão urgente.

Como os metadados funcionam no AgentCore Memory

Os metadados operam tanto na memória de curto prazo quanto na de longo prazo, seguindo um ciclo de vida em três fases: configuração, ingestão e recuperação.

Imagem original — fonte: Aws

Na memória de curto prazo, é possível anexar pares de chave-valor baseados em strings aos eventos, marcando interações com informações contextuais que não fazem parte da conversa em si, mas são críticas para recuperação posterior. Essas tags são carregadas para a memória de longo prazo durante a extração e consolidação, onde se tornam dimensões filtráveis.

Fase 1: Configuração

Ao criar um recurso de memória, é necessário declarar quais chaves de metadados serão indexadas para filtragem e recuperação rápida. As chaves indexadas são armazenadas em um formato otimizado para filtragem de consultas, enquanto as chaves não indexadas ficam armazenadas junto aos registros de memória apenas para fins informativos. O exemplo a seguir cria um recurso de memória de suporte ao cliente com configuração de metadados:

response = agentcore_client.create_memory(
    name="CustomerSupportMemory",
    eventExpiryDuration=30,
    indexedKeys=[
        {"key": "priority", "type": "STRING"},
        {"key": "agent_type", "type": "STRING"},
        {"key": "channel", "type": "STRING"},
        {"key": "ticket_id", "type": "STRING"}
    ],
    memoryStrategies=[{
        "semanticMemoryStrategy": {
            "name": "SupportSemanticStrategy",
            "description": "Captures support interaction details",
            "namespaces": ["support/{actorId}"],
            "memoryRecordSchema": {
                "metadataSchema": [
                    {
                        "key": "priority",
                        "type": "STRING",
                        "extractionType": "STRICTLY_CONSISTENT",
                        "extractionConfig": {
                            "llmExtractionConfig": {
                                "definition": "Issue priority level based on customer impact.",
                                "llmExtractionInstruction": "LATEST_VALUE",
                                "validation": {
                                    "stringValidation": {
                                        "allowedValues": ["critical", "high", "medium", "low"]
                                    }
                                }
                            }
                        }
                    },
                    {
                        "key": "agent_type",
                        "type": "STRING",
                        "extractionType": "STRICTLY_CONSISTENT",
                        "extractionConfig": {
                            "llmExtractionConfig": {
                                "definition": "Support agent classification.",
                                "llmExtractionInstruction": "Prefer the most specialized agent type. Hierarchy: specialist > tier3 > tier2 > tier1 > bot."
                            }
                        }
                    },
                    {
                        "key": "sentiment",
                        "type": "STRING",
                        "extractionType": "STRICTLY_CONSISTENT",
                        "extractionConfig": {
                            "llmExtractionConfig": {
                                "definition": "Customer sentiment during the interaction.",
                                "llmExtractionInstruction": "Classify the overall customer sentiment based on tone and language used.",
                                "validation": {
                                    "stringValidation": {
                                        "allowedValues": ["positive", "neutral", "negative", "frustrated"]
                                    }
                                }
                            }
                        }
                    }
                ]
            }
        }
    }]
)

Cada entrada do schema tem um campo definition, que descreve o que o campo representa, e um llmExtractionInstruction, que fornece orientação adicional de extração e comportamento de resolução de conflitos ao Modelo de Linguagem de Grande Escala (LLM). A operação embutida LATEST_VALUE fornece resolução baseada em recência, enquanto instruções personalizadas em linguagem natural tratam lógicas específicas do domínio. O campo opcional validation restringe os valores extraídos — como allowedValues para STRING, maxItems para STRINGLIST, ou mínimo-máximo para NUMBER.

Um ponto importante: ticket_id é declarado como chave indexada no nível da memória, mas não está incluído no memoryRecordSchema da estratégia. Isso significa que essa chave não será populada nos registros de memória extraídos. Somente chaves definidas no schema da estratégia aparecem nos registros após a extração. Para mais detalhes sobre as restrições de configuração, consulte a documentação do AgentCore Memory.

Imagem original — fonte: Aws

Além das chaves configuradas, a filtragem temporal está disponível por meio dos campos x-amz-agentcore-memory-createdAt e x-amz-agentcore-memory-updatedAt, gerados automaticamente pelo sistema, que suportam os operadores BEFORE e AFTER sem exigir que você declare chaves de data/hora indexadas.

Extração determinística com STRICTLY_CONSISTENT

Algumas chaves de metadados são classificadores organizacionais — como department, compliance_level ou interaction_type — cujos valores a aplicação já conhece no momento da criação do evento. Para esses casos, a extração via LLM introduz variabilidade indesejada: a mesma conversa pode gerar "eng" em um registro e "Engineering" em outro.

O AgentCore Memory resolve isso com o tipo de extração STRICTLY_CONSISTENT. Quando uma chave é configurada dessa forma, o valor fornecido no evento é propagado sem alteração através da extração e consolidação — o LLM não é consultado para essa chave. Além disso, eventos com os mesmos valores determinísticos são sempre extraídos juntos e isolados de eventos com valores diferentes, eliminando ambiguidade. A consolidação respeita a mesma separação: um registro com department: "billing" não será mesclado com um de department: "engineering", por mais similar que seja o conteúdo.

Esse mecanismo é ideal para isolamento de conformidade (registros HIPAA não se misturam com registros padrão), roteamento organizacional e qualquer cenário em que o valor seja conhecido no momento do evento e precise ser preservado exatamente nos registros resultantes. O AgentCore Memory suporta até três chaves STRICTLY_CONSISTENT por estratégia, e cada uma consome um dos dez slots de chaves indexadas do recurso de memória — chaves indexadas não podem ser removidas depois de adicionadas.

Fase 2: Ingestão

Os metadados entram no sistema por dois caminhos. O caminho orientado a eventos anexa metadados aos eventos, e o AgentCore Memory os propaga automaticamente pela extração e consolidação para os registros de memória de longo prazo:

# Initial contact event
agentcore_client.create_event(
    memoryId="mem-support-abc123",
    actorId="customer-123",
    sessionId="session-001",
    eventTimestamp="2024-01-23T10:00:00Z",
    payload=[{
        "conversational": {
            "role": "USER",
            "content": {"text": "I have a question about my bill"}
        }
    }],
    metadata={
        "priority": {"stringValue": "medium"},
        "channel": {"stringValue": "email"},
        "ticket_id": {"stringValue": "TKT-5001"}
    }
)

Quando múltiplos eventos em uma sessão carregam valores diferentes para a mesma chave, o LLM resolve os conflitos usando o llmExtractionInstruction definido no schema. Por exemplo, se um evento posterior escalar a prioridade de "medium" para "high", a instrução LATEST_VALUE mantém o valor mais recente.

Imagem original — fonte: Aws

Vale destacar que os metadados do evento não são estritamente necessários para que as chaves do schema produzam valores. Quando uma chave do schema não tem metadados correspondentes nos eventos de origem, o LLM deriva o valor diretamente do conteúdo da conversa, usando a definition e o llmExtractionInstruction da chave. As regras de validação ainda se aplicam: a saída do LLM é restringida aos allowedValues declarados, independentemente de o valor ter vindo de metadados do evento ou de inferência de conteúdo.

O caminho direto de escrita — via BatchCreateMemoryRecords e BatchUpdateMemoryRecords — ignora a extração por completo. Quando memoryStrategyId é fornecido em cada registro, o serviço filtra os metadados de entrada de acordo com o schema daquela estratégia, descartando silenciosamente chaves não definidas. Quando omitido, os metadados do payload são armazenados como estão — mas apenas chaves indexadas são filtráveis.

Fase 3: Recuperação

Com os metadados indexados e populados nos registros de memória, é possível combinar busca semântica com filtros de metadados para refinar os resultados. O AgentCore Memory usa uma arquitetura de pré-filtragem: os filtros de metadados são aplicados antes da busca por similaridade vetorial. Isso reduz o conjunto de candidatos primeiro, para que a busca K-Vizinhos Mais Próximos (KNN) opere em um subconjunto menor e mais relevante:

results = agentcore_client.retrieve_memory_records(
    memoryId="mem-support-abc123",
    namespace="support/customer-123",
    searchCriteria={
        "searchQuery": "billing issues",
        "topK": 10,
        "metadataFilters": [{
            "left": {"metadataKey": "priority"},
            "operator": "EQUALS_TO",
            "right": {"metadataValue": {"stringValue": "high"}}
        }, {
            "left": {"metadataKey": "x-amz-agentcore-memory-createdAt"},
            "operator": "AFTER",
            "right": {"metadataValue": {"dateTimeValue": "2026-01-01T00:00:00Z"}}
        }]
    }
)

O AgentCore Memory disponibiliza múltiplos operadores para cobrir padrões comuns de consulta. Para recuperação sem busca semântica, o ListMemoryRecords oferece filtragem por metadados para enumerar registros que atendam a critérios específicos — por exemplo, listar registros de alta prioridade criados após uma determinada data.

Nos experimentos da AWS, consultas com restrições de tempo mostraram os maiores ganhos com a filtragem por metadados. A filtragem estruturada com os operadores BEFORE e AFTER converte a busca temporal em uma operação determinística de índice, evitando ambiguidades da busca semântica.

Casos de uso empresariais

Aplicações SaaS multi-tenant

Em uma empresa SaaS com assistentes de IA para múltiplos clientes corporativos, os namespaces já fornecem isolamento primário de tenants. Os metadados adicionam filtragem refinada dentro dessas fronteiras — por segmento de cliente, departamento ou nível de assinatura. Por exemplo, um tenant de nível enterprise pode obter recuperação completa do histórico, enquanto um tenant de nível inicial tem a recuperação restrita a memórias recentes por meio de filtros AFTER no timestamp do sistema.

Saúde e domínios com conformidade regulatória

Em um agente de saúde que gerencia interações de pacientes em múltiplos departamentos, uma busca ampla por “histórico de medicação” retorna resultados de cardiologia, endocrinologia e clínica geral ao mesmo tempo. Indexando chaves como department, record_type e symptoms, o agente consegue restringir a recuperação a dimensões clínicas específicas. O operador CONTAINS em uma chave do tipo STRINGLIST verifica se a lista inclui o valor especificado, permitindo escopo por indicadores clínicos. Para conformidade, a filtragem por metadados ajuda a atender requisitos como HIPAA (isolamento por departamento), GDPR (identificação de registros fora da janela de retenção) e SOC 2 (trilha verificável de escopo correto na recuperação).

Suporte ao cliente com roteamento por prioridade

Em organizações de suporte que lidam com milhares de tickets, os metadados suportam padrões de recuperação como “encontre memórias de cobrança de alta prioridade dos últimos 30 dias”, combinando filtros de metadados personalizados com filtros de timestamp do sistema. À medida que tickets escalam de baixo para crítico, as regras de mesclagem mantêm os metadados nos registros consolidados alinhados com o estado de escalação mais recente.

Serviços financeiros e precisão temporal

Dados financeiros são inerentemente sensíveis ao tempo. Uma consulta sobre “discussões de portfólio do terceiro trimestre” deve retornar registros específicos daquele trimestre. Um agente de gestão de patrimônio pode combinar filtragem DATETIME com metadados personalizados para delimitar a recuperação com precisão, evitando ruído de outras classes de ativos e períodos de tempo.

Sistemas multi-agente e coordenação de memória

Em fluxos de trabalho com múltiplos agentes, saber qual agente criou uma memória é essencial para confiança, depuração e roteamento. Indexando chaves como source_agent, agent_role e workflow_step, um agente supervisor pode filtrar memórias armazenadas por um agente específico. Num pipeline com bot de triagem, agente de nível 1 e especialista, os metadados controlam quais memórias cada agente recupera — o especialista filtra por workflow_step: "triage" para entender a classificação inicial, evitando trabalho duplicado.

Evolução do schema de metadados

O AgentCore Memory suporta evolução de schema por um modelo apenas-aditivo. É possível adicionar novas chaves de metadados indexadas a um recurso de memória existente:

agentcore_client.update_memory(
    memoryId="mem-support-abc123",
    addIndexedKeys=[
        {"metadataKey": "customer_segment", "metadataValueType": "STRING"}
    ]
)

Novas chaves ficam disponíveis imediatamente para eventos e registros de memória futuros. Registros existentes não recebem o novo campo retroativamente, mas à medida que memórias antigas passam por consolidação com as mais novas, elas naturalmente adquirem os novos metadados. Não é possível remover uma chave indexada previamente adicionada.

Boas práticas

  • Comece pelas dimensões que seus agentes realmente precisam filtrar. Evite indexar todos os campos possíveis de antemão. Cada campo indexado consome um dos slots disponíveis e adiciona custo em ambas as direções: mais trabalho por escrita durante a ingestão e compactação de consulta nas leituras. Comece com três a cinco chaves que impactam diretamente a qualidade da recuperação.
  • Escreva definições claras e específicas. Em vez de “A prioridade do ticket”, escreva “Nível de prioridade do problema com base no impacto ao cliente. Use ‘critical’ para interrupções de serviço em produção, ‘high’ para degradação de desempenho, ‘medium’ para solicitações de funcionalidades, ‘low’ para problemas de documentação ou cosméticos.”
  • Escolha regras de mesclagem que correspondam à semântica do domínio. LATEST_VALUE é um padrão seguro para a maioria dos campos, mas nem sempre correto. Para agent_type em um fluxo de escalonamento, o tipo de agente mais sênior deve ser retido, não o mais recente.
  • Restrinja a saída do LLM com regras de validação. Defina allowedValues para evitar que o LLM produza “High”, “high”, “HIGH” ou “critical” para o mesmo conceito, o que quebraria a correspondência de filtros downstream.
  • Use extração determinística para valores conhecidos no momento do evento. Se uma chave representa um atributo organizacional fixo — como departamento, nível de tenant ou escopo de conformidade — configure-a como STRICTLY_CONSISTENT e forneça o valor em cada evento. Reserve llmExtractionConfig para dimensões que precisam ser inferidas do conteúdo da conversa, como sentimento ou tópico.
  • Evite estes antipadrões: não indexe campos de texto livre com alta cardinalidade (como descrições ou nomes completos); não use metadados para valores que mudam a cada interação; não replique isolamento de namespace apenas por metadados — um campo tenant_id sem isolamento de namespace é um modelo de segurança frágil.

Conclusão

A filtragem por metadados no AgentCore Memory resolve um desafio fundamental de recuperação. Os namespaces já isolam memórias por entidades primárias — usuários, tenants ou projetos. Com a filtragem estruturada por metadados aplicada sobre o escopo de namespace, é possível restringir a recuperação do agente a fronteiras contextuais precisas antes que a correspondência por similaridade seja executada, entregando precisão mensurável e uma base prática para conformidade, gerenciamento de contexto baseado em prioridade e filtragem organizacional refinada.

Para começar, identifique três a cinco dimensões de filtragem que mais impactam a qualidade da recuperação para o seu caso de uso. Comece com uma Prova de Conceito (PoC) usando um recurso de memória de teste para validar as estratégias relevantes, e depois expanda o schema conforme necessidades concretas surgirem. Os recursos a seguir oferecem orientação prática:

Fonte

Structured memory filtering with metadata in AgentCore Memory (https://aws.amazon.com/blogs/machine-learning/structured-memory-filtering-with-metadata-in-agentcore-memory/)

Comments

Leave a Reply

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