Designing lifecycle policies for AgentCore memory
Memory lifecycle policies help long-running agents on Amazon Bedrock AgentCore stay effective by systematically managing what they remember and forget. Your agent generates memories from every conversation it conducts. If you don’t actively manage these memories, your agents will accumulate…
Memory lifecycle policies help long-running agents on Amazon Bedrock AgentCore stay effective by systematically managing what they remember and forget. Your agent generates memories from every conversation it conducts. If you don’t actively manage these memories, your agents will accumulate outdated context, which can degrade response quality and create compliance risks for your deployment. After months of production use, problems emerge. We observed a customer support agent reference a billing dispute resolved four months earlier, treating it as active. Another agent repeated outdated deployment advice because its memory still contained a superseded runbook. In this post, we introduce memory lifecycle management for AI agents: the practice of systematically scoring, consolidating, and pruning agent memories over time. We walk through a deployable architecture using AgentCore memory (a capability of Amazon Bedrock AgentCore), AWS Step Functions, and Amazon Bedrock to run a nightly lifecycle workflow. By the end, you will have an AWS Cloud Development Kit (AWS CDK) stack and a framework for managing agent memory as a managed resource. The complete code is available in the GitHub repository. This solution targets agents that accumulate high volumes of interaction data over weeks or months, such as customer support agents, sales advisors, and IT helpdesk bots. For lower-volume agents like personal assistants, you might start with time-to-live (TTL) expiration and General Data Protection Regulation (GDPR) compliance alone. All thresholds are configurable to match your agent’s needs. Solution overview This solution combines a shared memory taxonomy with three lifecycle policies that run as a nightly workflow. We begin with the memory types that shape those policies. Memory types Before designing lifecycle policies, we need a shared vocabulary for what agents remember. We categorize agent memory into three types, each with different retention requirements. Episodic memory: Episodic memories capture what happened, it’s the record of past conversations. These are timestamped, session-bound, and high-volume. Agentcore memory stores this information in two strategies, Summary and Episodic. Both strategies store memories as individual entries tied to specific agent-user sessions. Episodes and Summary provide short-term continuity but individually they become less relevant as time progresses. When designing your lifecycle policies, prioritize these memories for expiration first. Semantic memory: Semantic memories are distilled facts and preferences extracted from interactions but decoupled from any single conversation. “The user prefers the US East (N. Virginia) AWS Region (us-east-1) for deployments.” These are durable, high value, and compact. In your lifecycle policies, retain semantic memories longer than episodic memories. These are prime candidates for consolidation, where you merge multiple episodic observations into a single, authoritative fact. Procedural memory: Procedural memories encode learned workflows and tool-use patterns. “When the user asks about costs, query the AWS Cost Explorer API first, then summarize.” These represent the agent’s operational expertise. Procedural memories are lower volume but the most valuable type for certain use cases. They have the longest retention and the highest bar for pruning. AgentCore memory stores procedural knowledge as reflections tied to episodic memory. Read more about it in Episodic memory deep dive blog. You should check these for validity as your procedures evolve. Lifecycle policies With our taxonomy in place, we can design three complementary lifecycle policies. Each targets a different failure mode of unbounded memory. Policy 1: TTL-based expiration The first policy automatically deletes memories older than a configured TTL. We default to 90 days for episodic memories. TTL does not consider whether a memory is still useful, but it provides a hard ceiling on accumulation and is essential for compliance. In production, differentiate TTL by memory type. Configure your summary memories to expire after 30–60 days, semantic memories after 6–12 months, and consider setting no TTL for procedural memories. This post delivers a single configurable memoryTtlDays parameter as a starting point. TTL expiration runs first, before scoring or consolidation, which helps avoid wasting compute on memories that should already be gone. AgentCore memory doesn’t provide a built-in auto-delete TTL. However, it exposes system-generated timestamp fields that support BEFORE and AFTER filter operators on ListMemoryRecords. Our pruner uses x-amz-agentcore-memory-createdAt with a BEFORE filter to retrieve only records older than the configured TTL, then deletes them. cutoff = (now - timedelta(days=ttl_days)).isoformat() response = client.list_memory_records( memoryId=memory_id, namespace=agent_id, metadataFilters=[{ "left": {"metadataKey": "x-amz-agentcore-memory-createdAt"}, "operator": "BEFORE", "right": {"metadataValue": {"dateTimeValue": cutoff}}, }], ) Policy 2: Relevance decay scoring Not all memories age at the same rate. A memory accessed yesterday is more relevant than one untouched for weeks. We score each memory using a three-term weighted formula that combines creation recency, last-access recency, and access frequency: score = W_RECENCY * exp(-decay_rate * days_since_creation) + W_ACCESS * exp(-decay_rate * days_since_last_access) + W_FREQUENCY * min(access_count / MAX_ACCESS_BASELINE, 1.0) Rather than exposing a raw decay constant, we provide one intuitive parameter: pruneDays, the approximate number of days after which an unaccessed memory’s score drops below the relevance threshold: import math def decay_rate_from_prune_days(prune_days: int, threshold: float) -> float: """Convert pruneDays to an exponential decay rate. decay_rate = -ln(threshold) / prune_days """ if prune_days float: """Compute relevance score using the 3-term weighted decay formula. score = w_recency * exp(-decay_rate * days_since_creation) + w_access * exp(-decay_rate * days_since_last_access) + w_frequency * min(access_count / max_access_baseline, 1.0) Returns a float in [0.0, 1.0] when weights sum to 1.0. Raises ValueError if max_access_baseline is zero or negative. """ if max_access_baseline WriteRunOutput once (both branches converge here) const emitAndWrite = emitMetrics.next(writeRunOutput); const definition = ttlExpiration .next(scoreMemories) .next( checkLowScoreMemories .when( sfn.Condition.isPresent('$.scoringResult.below_threshold[0]'), batchConsolidate.next(emitAndWrite), ) .otherwise(emitAndWrite), ); const stateMachine = new sfn.StateMachine(this, 'MemoryLifecycleStateMachine', { definitionBody: sfn.DefinitionBody.fromChainable(definition), timeout: cdk.Duration.hours(1), tracingEnabled: true, }); Nightly trigger: An Amazon EventBridge rule fires the workflow at 2 AM UTC every day: new events.Rule(this, 'NightlyMemoryLifecycleRule', { schedule: events.Schedule.expression('cron(0 2 * * ? *)'), targets: [new targets.SfnStateMachine(stateMachine)], }); All configurable parameters (memoryTtlDays, relevanceThreshold, consolidationBatchSize, pruneDays, bedrockModelId, and the scoring weights) are read from CDK context, so you can tune them at deploy time without changing code: npx cdk deploy \ -c memoryTtlDays=60 \ -c relevanceThreshold=0.25 \ -c consolidationBatchSize=15 \ -c pruneDays=45 \ -c wRecency=0.4 \ -c wAccess=0.35 \ -c wFrequency=0.25 \ -c maxAccessBaseline=50 Cost considerations The primary cost driver is Amazon Bedrock invocations during consolidation. For an agent with 1,000 memories where 20 percent score below the threshold, expect roughly 20 Bedrock invocations per nightly run (about $0.01–$0.02). At 100,000 memories, this could reach $50–$100 per month. Start with a higher relevance threshold to limit consolidation volume, and review Amazon Bedrock pricing for your specific workload. Testing memory quality Pruning and consolidation are only useful if the agent still answers correctly afterward. We measure whether lifecycle operations degrade response quality using a regression test suite. Memory regression test suite We define test cases as question-and-criteria pairs (code/test/test_regression_suite.py). Each test case specifies a question, the criteria the agent’s response should satisfy, and a minimum quality score: DEFAULT_TEST_FIXTURES = [ { "question": "What are the user's preferred programming languages?", "expected_criteria": "Response mentions specific languages previously discussed with the user", "min_quality_score": 0.7, }, { "question": "Summarize the last project we worked on together.", "expected_criteria": "Response includes project name, key milestones, and outcome", "min_quality_score": 0.6, }, ] The regression suite follows a before-and-after pattern: Baseline: Query the agent with each test question before the lifecycle run. Record the quality score using AgentCore Evaluations, a capability of Amazon Bedrock AgentCore. Run lifecycle: Execute the nightly workflow (scoring, consolidation, pruning). Post-lifecycle: Query the agent again with the same questions. Record new quality scores. Evaluate: A test case passes if the post-lifecycle score meets or exceeds the configured minimum. We also compute the quality delta (post_lifecycle_score - baseline_score) for reporting. def determine_pass_fail(test_case: RegressionTestCase) -> RegressionTestCase: if test_case.post_lifecycle_score is None: test_case.passed = None return test_case test_case.passed = test_case.post_lifecycle_score >= test_case.min_quality_score return test_case AgentCore Evaluations integration The regression suite integrates with Amazon Bedrock AgentCore Evaluations to compute quality scores programmatically. AgentCore Evaluations works as an LLM-as-judge system: you provide the agent’s response and human-defined criteria, and the service returns a normalized quality score between 0.0 and 1.0. This makes the suite fully automated and suitable for continuous integration and continuous delivery (CI/CD) pipelines. Running the suite produces a per-test-case report that pairs the baseline and post-lifecycle scores so you can see the quality delta at a glance: Memory regression suite (2 test cases) ------------------------------------------------------------ [PASS] Preferred programming languages baseline=0.82 post=0.85 delta=+0.03 min=0.70 [PASS] Summary of last project baseline=0.74 post=0.71 delta=-0.03 min=0.60 ------------------------------------------------------------ Result: 2/2 passed In this sample run, both test cases stay above their configured minimums. A test case fails only when the post-lifecycle score drops below its min_quality_score, signaling that pruning or consolidation went too far. Privacy and compliance Memory lifecycle management is not only about performance. It’s a compliance requirement. When your agent stores personal data in memory, you inherit obligations under regulations like GDPR. GDPR right-to-be-forgotten A dedicated GDPR Deletion Handler (code/lambdas/gdpr_deletion/handler.py) deletes all memories for a specific user. It lists every memory for that user in AgentCore memory and deletes them individually: def handler(event: dict, context) -> dict: user_id = event["user_id"] memory_id = event["memory_id"] client = boto3.client("bedrock-agentcore") response = client.list_memory_records( memoryId=memory_id, namespace=user_id, ) memories = response.get("memoryRecordSummaries", []) deleted_count = 0 failed_memory_ids = [] for memory in memories: record_id = memory["memoryRecordId"] try: client.delete_memory_record(memoryId=memory_id, memoryRecordId=record_id) deleted_count += 1 logger.info(json.dumps({ "action": "gdpr_delete", "user_id": user_id, "memory_id": record_id, "timestamp": datetime.now(timezone.utc).isoformat(), })) except Exception as exc: failed_memory_ids.append(record_id) status = "success" if len(failed_memory_ids) == 0 else "partial_failure" return { "status": status, "user_id": user_id, "deleted_count": deleted_count, "failed_memory_ids": failed_memory_ids, } The handler returns a confirmation with the count of deleted memories and any failed IDs. On partial failure, the response includes the failed memory identifiers so operators can investigate and retry. Audit logging with CloudTrail Every memory mutation (scoring, consolidation, pruning, GDPR deletion) produces structured JSON logs in Amazon CloudWatch Logs with action type, memory ID, and ISO 8601 timestamp. The CDK stack also configures AWS CloudTrail to log AgentCore memory API calls, providing an immutable audit trail for compliance demonstrations: new cloudtrail.Trail(this, 'MemoryLifecycleTrail', { bucket: trailBucket, trailName: 'MemoryLifecycleAuditTrail', isMultiRegionTrail: false, includeGlobalServiceEvents: false, enableFileValidation: true, }); The stack creates an Amazon CloudWatch dashboard displaying memories processed, consolidated, pruned, and workflow execution status for real-time operational visibility. Clean up To remove all resources created by this solution, run: cd code npx cdk destroy This removes all resources created by the stack. You might need to delete CloudWatch log groups created by Lambda executions separately. Conclusion We showed how to build memory lifecycle policies for Amazon Bedrock AgentCore agents using AWS Step Functions and Amazon Bedrock. The solution applies three complementary policies: TTL expiration for hard time limits, relevance decay scoring for intelligent prioritization, and LLM-based consolidation for preserving knowledge. With the pruneDays parameter, you can tune decay aggressiveness. We also covered testing to confirm pruning doesn’t degrade quality, and GDPR compliance at the memory layer. The full code is available in the GitHub repository. Deploy it with npx cdk deploy -c pruneDays=45 and start running nightly memory lifecycle management for your agents. To learn more, see the Amazon Bedrock AgentCore documentation, the Amazon Bedrock AgentCore detail page, the AWS Step Functions Developer Guide, and the Amazon Bedrock User Guide. About the authors Himanshu Sah Himanshu is an Associate Delivery Consultant in AWS Professional Services, specialising in Application Development and Generative AI solutions. Based in India, he helps customers architect and implement cutting-edge applications leveraging AWS services and generative AI capabilities. Outside of work, he is passionate about exploring new technologies and contributing to the tech community. Akarsha Sehwag Akarsha is a Sr. Generative AI Data Scientist, Tech Lead for AgentCore memory GTM team. With over seven years of experience in AI/ML, she has built and guided production-ready enterprise solutions across diverse customer segments in Generative AI, Deep Learning and Computer Vision domains. Nicolò Cosimo Albanese Nicolò is a Sr. Data Scientist and ML Engineer at Amazon Web Services Professional Services. With a Master of Science in Engineering and postgraduate degrees in Machine Learning and Biostatistics, he specializes in developing AI/ML solutions that drive business value for enterprise customers. His expertise lies at the intersection of statistical modeling, cloud technologies, and scalable machine learning systems.Source: AWS Machine Learning — Published — Category: Tools