Welcome back, doston! 👋
In Part 1 and Part 2, We created our Vector Bucket and Vector Index using the AWS Console. Now in Part 3, it’s time to make the vector index actually useful — by inserting embeddings into it and then querying it to find the most relevant matches!
And what better way to explain this than using your favorite Indian snacks: Samosa, Vada Pav, and Masala Dosa? 🥔🌶️🥪
🍽️ Goal of This Post
We’ll:
- Generate embeddings for snack descriptions using Amazon Bedrock (Titan Model).
- Insert those embeddings into our S3 Vector Index.
- Attach metadata (like the region or original text) for smarter querying later.
- Query the index with new text inputs and retrieve the most similar snacks based on vector similarity.
⚠️ Important Note Before You Begin
Currently, AWS Lambda does not support the required version of
boto3
(1.39.9) needed to interact with the Amazon S3 Vector Index service. Since this feature is very new, the latestboto3
features (likes3vectors
client) aren’t yet available in the Lambda execution environment.🧠 So for this hands-on demo, we’ll use an Amazon SageMaker Notebook, which gives us the flexibility to install the latest
boto3
version and test Bedrock + S3 Vector integrations smoothly.Once AWS Lambda supports this version, you can port the logic there using the similar IAM Role and code logic.
🛠 Step 1: Setup IAM Role for Using CloudFormation
Before our SageMaker Notebook can communicate with Amazon Bedrock and insert vectors into the S3 Vector Index, we need to set up an IAM Role with all the required permissions.
📜 Why IAM Role?
IAM role gives secure access to your Lambda function so it can invoke models from Bedrock and interact with your S3 Vector bucket/index.
AWSTemplateFormatVersion: "2010-09-09"
Description: IAM role used in the S3Vectors project.
Parameters:
SagemakerRoleName:
Type: String
Default: bedrock-s3vectors-sagemaker-role
Description: Lambda role for S3Vectors and Bedrock
Resources:
SagemakerRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Ref SagemakerRoleName
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: sagemaker.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSageMakerFullAccess
- arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
Policies:
- PolicyName: BedrockAndS3VectorsPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- bedrock:*
Resource: "*"
- Effect: Allow
Action:
- s3vectors:*
Resource: "*"
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: "*"
Tags:
- Key: Project
Value: S3Vectors
- Key: Owner
Value: UtkarshR
🧪 Step 2: Deploy IAM Role with CloudFormation via AWS CLI
Now that we have our IAM role template ready in a file (e.g., iam-role-s3vector.yaml
), let’s deploy it using the AWS CLI.
✅ Prerequisites
Make sure you have:
- AWS CLI installed and configured with appropriate permissions
- The CloudFormation YAML file saved locally (e.g.,
iam-role-s3vector.yaml
)
🚀 Run the Deployment Command
aws cloudformation deploy
--template-file iam-role-s3vector.yaml
--stack-name S3Vectors-Sagemaker-IAM-Stack
--capabilities CAPABILITY_NAMED_IAM
🧠 Step 3: Launch SageMaker Notebook in us-east-1
Create a SageMaker notebook instance (ml.t3.medium
) in us-east-1, attach the IAM role from Step 2,
🔧 Step 4: Install Updated boto3 in SageMaker
In your SageMaker notebook cell, run the following to install the required boto3 version:
pip install boto3==1.39.9
pip install botocore==1.39.9
✅ Step 5: Enable Access to the Titan Embedding Model
Before using amazon.titan-embed-text-v1
, ensure you’ve enabled access to this model in your Bedrock console:
- Go to Amazon Bedrock → Model access in the us-east-1 region.
- Enable access to
amazon.titan-embed-text-v1
.
🔐 Without this, your SageMaker code will fail with
AccessDeniedException
.
🪄 Step 6: Insert Vector Embeddings into S3 Vector Index via SageMaker
Run the following code in your SageMaker notebook to:
- Generate a vector embedding using Amazon Bedrock (Titan)
- Insert the embedding into your S3 Vector Index
import boto3
import json
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
s3vectors = boto3.client("s3vectors", region_name="us-east-1")
try:
# Desi snacks to embed
snacks = [
"Samosa: A spicy deep-fried snack filled with potato masala.",
"Vada Pav: Mumbai's favorite street food – a batata vada in a pav.",
"Masala Dosa: A crispy South Indian crepe with spicy potato stuffing."
]
# Step 1: Generate vector embeddings using Amazon Titan
embeddings = []
for snack in snacks:
response = bedrock.invoke_model(
modelId="amazon.titan-embed-text-v1",
body=json.dumps({"inputText": snack})
)
response_body = json.loads(response["body"].read())
embeddings.append(response_body["embedding"])
# print(f"Embedding: {embeddings}")
# Step 2: Insert vectors into the vector index with metadata
s3vectors.put_vectors(
vectorBucketName="awslearner-vector-bucket",
indexName="snack-index",
vectors=[
{
"key": "Samosa",
"data": {"float32": embeddings[0]},
"metadata": {"source_text": snacks[0], "region": "North India"}
},
{
"key": "Vada Pav",
"data": {"float32": embeddings[1]},
"metadata": {"source_text": snacks[1], "region": "Maharashtra"}
},
{
"key": "Masala Dosa",
"data": {"float32": embeddings[2]},
"metadata": {"source_text": snacks[2], "region": "South India"}
}
]
)
print(f"Process Completed")
except Exception as e:
print(f"Error occurred: {str(e)}")
🔍 Step 7: Query S3 Vector Index and Retrieve Similar Results
Now let’s query the vector index using a sample input, get the embedding using Bedrock Titan, and find the most similar items from the S3 Vector Index:
import boto3
import json
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
s3vectors = boto3.client("s3vectors", region_name="us-east-1")
# Desi snack to search
input_text = "crispy spicy street food with potato filling"
# Step 1: Generate embedding using Amazon Titan
response = bedrock.invoke_model(
modelId="amazon.titan-embed-text-v1",
body=json.dumps({"inputText": input_text})
)
model_response = json.loads(response["body"].read())
embedding = model_response["embedding"]
# print(f"embedding:{embedding}")
# Step 2: Query the vector index
print("Top 3 closest matches for snack query:")
response = s3vectors.query_vectors(
vectorBucketName="awslearner-vector-bucket",
indexName="snack-index",
queryVector={"float32": embedding},
topK=3,
returnDistance=True,
returnMetadata=True
)
print(json.dumps(response["vectors"], indent=2))
# Step 3: Query with metadata filter (e.g., region = North India)
print("nTop 3 matches from North India only:")
response = s3vectors.query_vectors(
vectorBucketName="awslearner-vector-bucket",
indexName="snack-index",
queryVector={"float32": embedding},
topK=3,
filter={"region": "North India"},
returnDistance=True,
returnMetadata=True
)
print(json.dumps(response["vectors"], indent=2))
🧾 Wrap-Up
In this part, we explored how to:
✅ Generate text embeddings using Amazon Bedrock’s Titan model
✅ Insert those embeddings into an S3 Vector Index using SageMaker Notebook
✅ Attach meaningful metadata (like snack names and regions)
✅ Perform a query on the index to find similar snacks based on user input!
This hands-on demo shows the real power of combining Bedrock, S3 Vector Indexing, and modern vector search for practical, intelligent search experiences.
📚 More Learning
👨💻 About Me
Hi! I’m Utkarsh, a Cloud Specialist & AWS Community Builder who loves turning complex AWS topics into fun chai-time stories ☕