From 9228f8d99be24bcee2e14d6ea9417240cbd41828 Mon Sep 17 00:00:00 2001 From: Nithin Chandran Rajashankar Date: Thu, 18 Jun 2026 18:41:43 +0000 Subject: [PATCH] feat(agentcore-memory): Personalized AI assistant with Memory + Bedrock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deploy Amazon Bedrock AgentCore Memory with an AWS Lambda function that stores conversations, retrieves relevant context via semantic search, and generates personalized responses using Amazon Bedrock. Two-step composition: Memory provides user context recall, Bedrock provides reasoning and personalization. Neither works alone for this use case — memory without inference is just storage, inference without memory has no personalization. --- agentcore-memory-cdk/.gitignore | 6 ++ agentcore-memory-cdk/README.md | 73 ++++++++++++++ agentcore-memory-cdk/bin/app.ts | 8 ++ agentcore-memory-cdk/cdk.json | 3 + agentcore-memory-cdk/example-pattern.json | 98 +++++++++++++++++++ .../lib/agentcore-memory-stack.ts | 56 +++++++++++ agentcore-memory-cdk/package.json | 19 ++++ agentcore-memory-cdk/src/handler/index.py | 93 ++++++++++++++++++ agentcore-memory-cdk/tsconfig.json | 19 ++++ 9 files changed, 375 insertions(+) create mode 100644 agentcore-memory-cdk/.gitignore create mode 100644 agentcore-memory-cdk/README.md create mode 100644 agentcore-memory-cdk/bin/app.ts create mode 100644 agentcore-memory-cdk/cdk.json create mode 100644 agentcore-memory-cdk/example-pattern.json create mode 100644 agentcore-memory-cdk/lib/agentcore-memory-stack.ts create mode 100644 agentcore-memory-cdk/package.json create mode 100644 agentcore-memory-cdk/src/handler/index.py create mode 100644 agentcore-memory-cdk/tsconfig.json diff --git a/agentcore-memory-cdk/.gitignore b/agentcore-memory-cdk/.gitignore new file mode 100644 index 0000000000..888fdf2586 --- /dev/null +++ b/agentcore-memory-cdk/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +cdk.out/ +build/ +*.js +*.d.ts +cdk.context.json diff --git a/agentcore-memory-cdk/README.md b/agentcore-memory-cdk/README.md new file mode 100644 index 0000000000..4b84db6637 --- /dev/null +++ b/agentcore-memory-cdk/README.md @@ -0,0 +1,73 @@ +# Amazon Bedrock AgentCore Memory with AWS Lambda (CDK) + +This pattern deploys an Amazon Bedrock AgentCore Memory resource with an AWS Lambda function that demonstrates storing conversation events and retrieving memory records. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/agentcore-memory-cdk + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Node.js 20+](https://nodejs.org/en/download/) installed +* [AWS CDK v2](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html) installed (`npm install -g aws-cdk`) +* CDK bootstrapped in your account/region (`cdk bootstrap`) + +## Deployment Instructions + +1. Clone the repository and navigate to the pattern directory: + ```bash + cd agentcore-memory-cdk + ``` + +2. Install dependencies: + ```bash + npm install + ``` + +3. Build and deploy: + ```bash + npx cdk deploy + ``` + +## How it works + +Amazon Bedrock AgentCore Memory provides persistent memory for AI agents: + +- **Short-term memory**: Stores raw conversation events (messages, tool calls, results) per session. Available immediately after `create_event`. +- **Long-term memory**: Automatically extracted from short-term memory using configurable strategies (semantic, summary, user profile). Enables agents to recall facts and preferences across sessions. + +The AWS Lambda function demonstrates two operations: +1. **Store**: Writes conversation events to short-term memory via `create_event` +2. **Retrieve**: Searches long-term memory records via `retrieve_memory_records` using semantic similarity + +## Testing + +Store a conversation event: +```bash +aws lambda invoke --function-name FUNCTION_NAME \ + --payload '{"action": "store", "messages": [{"role": "user", "content": "My name is Alice and I prefer Python"}, {"role": "assistant", "content": "Nice to meet you, Alice! I will remember your Python preference."}]}' \ + --cli-binary-format raw-in-base64-out output.json && cat output.json +``` + +Retrieve memory (after extraction completes, typically a few minutes): +```bash +aws lambda invoke --function-name FUNCTION_NAME \ + --payload '{"action": "retrieve", "query": "What programming language does the user prefer?"}' \ + --cli-binary-format raw-in-base64-out output.json && cat output.json +``` + +## Cleanup + +```bash +npx cdk destroy +``` + +⚠️ This will delete the Amazon Bedrock AgentCore Memory resource and all stored memory records. + +--- + +Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/agentcore-memory-cdk/bin/app.ts b/agentcore-memory-cdk/bin/app.ts new file mode 100644 index 0000000000..15f268fa14 --- /dev/null +++ b/agentcore-memory-cdk/bin/app.ts @@ -0,0 +1,8 @@ +#!/usr/bin/env node +import * as cdk from 'aws-cdk-lib'; +import { AgentcoreMemoryStack } from '../lib/agentcore-memory-stack'; + +const app = new cdk.App(); +new AgentcoreMemoryStack(app, 'AgentcoreMemoryStack', { + env: { region: 'us-east-1' }, +}); diff --git a/agentcore-memory-cdk/cdk.json b/agentcore-memory-cdk/cdk.json new file mode 100644 index 0000000000..debd1380e0 --- /dev/null +++ b/agentcore-memory-cdk/cdk.json @@ -0,0 +1,3 @@ +{ + "app": "node build/bin/app.js" +} diff --git a/agentcore-memory-cdk/example-pattern.json b/agentcore-memory-cdk/example-pattern.json new file mode 100644 index 0000000000..9bca58ff8f --- /dev/null +++ b/agentcore-memory-cdk/example-pattern.json @@ -0,0 +1,98 @@ +{ +<<<<<<< Updated upstream:agentcore-memory-cdk/example-pattern.json + "title": "Persistent AI agent memory with Amazon Bedrock AgentCore Memory", + "description": "Deploy Amazon Bedrock AgentCore Memory with an AWS Lambda function that stores conversation events and retrieves memory records, enabling AI agents to maintain context across sessions.", +======= + "title": "Structured intelligence extraction from JavaScript-rendered pages with Amazon Bedrock AgentCore Browser and Amazon Bedrock", + "description": "Deploy an AWS Lambda function that navigates JavaScript-rendered web pages via Amazon Bedrock AgentCore Browser, extracts dynamic content that simple HTTP GET cannot reach, and uses Amazon Bedrock to analyze and structure the data.", +>>>>>>> Stashed changes:agentcore-browser-cdk/example-pattern.json + "language": "TypeScript", + "level": "300", + "framework": "AWS CDK", + "introBox": { + "headline": "How it works", + "text": [ +<<<<<<< Updated upstream:agentcore-memory-cdk/example-pattern.json + "Amazon Bedrock AgentCore Memory provides managed short-term and long-term memory for AI agents.", + "The AWS Lambda function stores conversation events (messages, tool calls) as short-term memory.", + "AgentCore asynchronously extracts long-term memory (facts, preferences, summaries) from stored events using configurable strategies.", + "Agents retrieve relevant memory records using semantic search to maintain personalized, consistent experiences across sessions." +======= + "The AWS Lambda function starts a managed Amazon Bedrock AgentCore Browser session and navigates to a target URL.", + "Unlike simple HTTP requests, the Browser renders JavaScript, executes SPAs, and waits for dynamic content to load — handling React, Angular, and framework-rendered pages.", + "The fully rendered page content is extracted and passed to Amazon Bedrock for structured analysis.", + "Amazon Bedrock extracts key facts, prices, dates, and entities — returning structured JSON intelligence from pages that would otherwise require a headless browser infrastructure." +>>>>>>> Stashed changes:agentcore-browser-cdk/example-pattern.json + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/agentcore-memory-cdk", + "templateURL": "serverless-patterns/agentcore-memory-cdk", + "projectFolder": "agentcore-memory-cdk", + "templateFile": "lib/agentcore-memory-stack.ts" + } + }, + "resources": { + "bullets": [ + { + "text": "Amazon Bedrock AgentCore Memory documentation", + "link": "https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/memory.html" + }, + { + "text": "Amazon Bedrock InvokeModel API", + "link": "https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_InvokeModel.html" + } + ] + }, + "deploy": { + "text": [ + "npx cdk deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "npx cdk destroy" + ] + }, + "authors": [ + { + "name": "Nithin Chandran R", + "bio": "Technical Account Manager at AWS", + "linkedin": "nithin-chandran-r" + } + ], + "patternArch": { + "icon1": { + "x": 20, + "y": 50, + "service": "lambda", + "label": "AWS Lambda" + }, + "icon2": { + "x": 55, + "y": 30, + "service": "bedrock", + "label": "AgentCore Memory" + }, + "icon3": { + "x": 55, + "y": 70, + "service": "bedrock", + "label": "Amazon Bedrock" + }, + "line1": { + "from": "icon1", + "to": "icon2" + }, + "line2": { + "from": "icon1", + "to": "icon3" + } + } +} diff --git a/agentcore-memory-cdk/lib/agentcore-memory-stack.ts b/agentcore-memory-cdk/lib/agentcore-memory-stack.ts new file mode 100644 index 0000000000..4ab31e2934 --- /dev/null +++ b/agentcore-memory-cdk/lib/agentcore-memory-stack.ts @@ -0,0 +1,56 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as agentcore from 'aws-cdk-lib/aws-bedrockagentcore'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; + +export class AgentcoreMemoryStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + // Amazon Bedrock AgentCore Memory with built-in semantic extraction + const memory = new agentcore.Memory(this, 'AgentMemory', { + memoryName: 'agent_memory', + memoryStrategies: [ + agentcore.MemoryStrategy.usingBuiltInSemantic(), + ], + }); + + // AWS Lambda function: personalized AI assistant with persistent memory + const fn = new lambda.Function(this, 'MemoryFunction', { + runtime: lambda.Runtime.PYTHON_3_12, + handler: 'index.handler', + code: lambda.Code.fromAsset('src/handler'), + timeout: cdk.Duration.seconds(60), + environment: { + MEMORY_ID: memory.memoryId, + MODEL_ID: 'us.anthropic.claude-sonnet-4-20250514-v1:0', + }, + }); + + // Grant permissions to use Amazon Bedrock AgentCore Memory + fn.addToRolePolicy(new iam.PolicyStatement({ + actions: [ + 'bedrock-agentcore:CreateEvent', + 'bedrock-agentcore:GetEvent', + 'bedrock-agentcore:ListEvents', + 'bedrock-agentcore:RetrieveMemoryRecords', + 'bedrock-agentcore:ListMemoryRecords', + ], + resources: [memory.memoryArn, `${memory.memoryArn}/*`], + })); + + // Grant permission to invoke Amazon Bedrock models + fn.addToRolePolicy(new iam.PolicyStatement({ + actions: ['bedrock:InvokeModel'], + resources: [ + `arn:aws:bedrock:*::foundation-model/*`, + `arn:aws:bedrock:*:${this.account}:inference-profile/*`, + ], + })); + + // Outputs + new cdk.CfnOutput(this, 'MemoryId', { value: memory.memoryId }); + new cdk.CfnOutput(this, 'FunctionName', { value: fn.functionName }); + } +} diff --git a/agentcore-memory-cdk/package.json b/agentcore-memory-cdk/package.json new file mode 100644 index 0000000000..207c6e94e2 --- /dev/null +++ b/agentcore-memory-cdk/package.json @@ -0,0 +1,19 @@ +{ + "name": "agentcore-memory-cdk", + "version": "1.0.0", + "bin": { + "app": "build/bin/app.js" + }, + "scripts": { + "build": "tsc", + "synth": "cdk synth" + }, + "dependencies": { + "aws-cdk-lib": "^2.260.0", + "constructs": "^10.3.0" + }, + "devDependencies": { + "aws-cdk": "^2.1128.0", + "typescript": "~5.4.0" + } +} diff --git a/agentcore-memory-cdk/src/handler/index.py b/agentcore-memory-cdk/src/handler/index.py new file mode 100644 index 0000000000..c972700c26 --- /dev/null +++ b/agentcore-memory-cdk/src/handler/index.py @@ -0,0 +1,93 @@ +""" +AWS Lambda function: Personalized AI assistant with persistent memory. +Stores conversations in Amazon Bedrock AgentCore Memory, retrieves relevant +context via semantic search, and generates personalized responses using Amazon Bedrock. +""" + +import json +import os +import uuid +from datetime import datetime, timezone +import boto3 + + +def handler(event, context): + """Personalized AI assistant with persistent memory across sessions.""" + try: + memory_id = os.environ['MEMORY_ID'] + model_id = os.environ.get('MODEL_ID', 'us.anthropic.claude-sonnet-4-20250514-v1:0') + region = os.environ.get('AWS_REGION', 'us-east-1') + action = event.get('action', 'chat') + + agentcore_client = boto3.client('bedrock-agentcore', region_name=region) + + if action == 'store': + session_id = event.get('sessionId', str(uuid.uuid4())) + messages = event.get('messages', [ + {'role': 'user', 'content': 'My name is Alice and I prefer Python for data work'}, + {'role': 'assistant', 'content': 'Noted! I will remember your Python preference, Alice.'}, + ]) + actor_id = event.get('actorId', 'demo-user') + + payload = [] + for m in messages: + role = 'USER' if m['role'] == 'user' else 'ASSISTANT' + payload.append({'conversational': {'content': {'text': m['content']}, 'role': role}}) + + agentcore_client.create_event( + memoryId=memory_id, actorId=actor_id, + sessionId=session_id, eventTimestamp=datetime.now(timezone.utc), + payload=payload, + ) + + return { + 'statusCode': 200, + 'body': json.dumps({'action': 'store', 'sessionId': session_id}), + } + + elif action == 'chat': + question = event.get('question', 'What programming language should I use?') + actor_id = event.get('actorId', 'demo-user') + + # Step 1: Retrieve relevant memories about this user + memory_response = agentcore_client.retrieve_memory_records( + memoryId=memory_id, namespace=actor_id, + searchCriteria={'searchQuery': question, 'topK': 5}, + ) + records = memory_response.get('memoryRecords', []) + memory_context = '\n'.join(str(r) for r in records) if records else 'No memories stored yet.' + + # Step 2: Generate personalized response using Amazon Bedrock + bedrock_client = boto3.client('bedrock-runtime', region_name=region) + + prompt = f"""You are a personalized AI assistant. Use the following memories about the user to give a tailored, relevant response. + +User Memories: +{memory_context} + +User Question: {question} + +Respond naturally, referencing what you know about the user from memory. If no memories exist, respond helpfully and note you're still learning about them.""" + + response = bedrock_client.invoke_model( + modelId=model_id, contentType='application/json', accept='application/json', + body=json.dumps({ + 'anthropic_version': 'bedrock-2023-05-31', + 'max_tokens': 512, + 'messages': [{'role': 'user', 'content': prompt}], + }), + ) + + body = json.loads(response['body'].read()) + answer = body['content'][0]['text'] + + return { + 'statusCode': 200, + 'body': json.dumps({'action': 'chat', 'question': question, 'answer': answer, 'memoriesUsed': len(records)}), + } + + else: + return {'statusCode': 400, 'body': json.dumps({'error': f'Unknown action: {action}. Use store or chat.'})} + + except Exception as e: + return {'statusCode': 500, 'body': json.dumps({'error': str(e)})} diff --git a/agentcore-memory-cdk/tsconfig.json b/agentcore-memory-cdk/tsconfig.json new file mode 100644 index 0000000000..5eccfb04e8 --- /dev/null +++ b/agentcore-memory-cdk/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "commonjs", + "lib": ["es2022"], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "outDir": "./build", + "rootDir": ".", + "skipLibCheck": true + }, + "exclude": ["node_modules", "cdk.out", "build"] +}