Serverless RAG Architecture: Combining MongoDB Atlas Vector Search, Amazon Bedrock, and AWS Lambda

Serverless RAG Architecture: Combining MongoDB Atlas Vector Search, Amazon Bedrock, and AWS Lambda

A Practical Guide to Building a Serverless RAG System with AWS Services and MongoDB Atlas

·

15 min read

Building production-ready RAG (Retrieval-Augmented Generation) systems doesn't have to be complex. In this guide, we'll explore how to combine MongoDB Atlas Vector Search, Amazon Bedrock, and AWS Lambda to create an efficient and scalable RAG pipeline. By leveraging MongoDB's vector search capabilities, Bedrock's foundation models, and Lambda's serverless architecture, we'll demonstrate a practical approach to implementing RAG that can handle real-world workloads while keeping operational overhead minimal. Let's build a system that turns your documents into actionable AI-powered insights.

You may refer to the GitHub Link below for the code and Devpost Link for the Demo.
GitHub Link

Devpost Link

RAG Architecture

RAG System Architecture

Before I start explaining the implementation, let me talk about the RAG architecture.

This architecture presents a modern RAG (Retrieval-Augmented Generation) system implemented on AWS Cloud, combining several key components. In the upper section, it integrates AI21's LLM model (AI21-Jumbo-1.5-Mini) with Amazon Bedrock Knowledge Base, utilizing LangChain's RAG Chain Agentic Framework for orchestration. The Knowledge Base serves as an intelligent data layer that enhances the LLM's responses by providing relevant context from your stored documents – it indexes and organizes information from MongoDB Atlas, making it quickly accessible for the LLM to reference when generating responses. The system leverages MongoDB Atlas for document storage and vector search capabilities, enabling efficient similarity searches across your data.

For the backend, it implements a serverless architecture where user requests flow through API Gateway to AWS Lambda, which uses Docker containers and Mangum Handler to run a FastAPI application. This setup ensures scalable, efficient handling of RAG operations while maintaining a serverless infrastructure for cost-effectiveness and automatic scaling.

💡
Note: The reason why I choose AI21-Jumbo-1.5-Mini Model is because I’m having service quota error where other models and this is the only one model that I can access. You can replace it to Claude Model according to your needs.

Tech Stack Used

  • Amazon Bedrock: Core LLM & knowledge base

  • Amazon S3 Storage : Storage for Data

  • MongoDB Atlas: Vector search & data storage

  • LangChain: RAG framework & chain management

  • FastAPI + Lambda: Serverless API

  • Docker: Containerization

  • AWS CDK: Infrastructure

Data Source Used

TheMealDB

For this blog, Im using recipe data from TheMealDB website as data source. I created a script to ingest the data into the S3 storage. You can refer to the GitHub Repo for more details.

Setup

Setup the Python Environment for Local Development

git clone https://github.com/intelli-foods/intellifoods_backend.git
cd image
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt

Configure AWS

You need to have an AWS account, and AWS CLI set up on your machine. You'll also need to have Bedrock enabled on AWS (and granted model access to Claude or whatever you want to use). For my case, I used AI21-Jamba-1.5-Mini

Update .env File with AWS Credentials

Create a file named .env in image/. Do NOT commit the file to .git. The file should have content like this:

AWS_ACCESS_KEY_ID=XXXXX
AWS_SECRET_ACCESS_KEY=XXXXX
AWS_REGION=us-east-1

This will be used by Docker for when we want to test the image locally. The AWS keys are just your normal AWS credentials and region you want to run this in (even when running locally you will still need access to Bedrock LLM).

Prerequisties

  • set your AWS region into us-east-1 region as Atlas Vector Search is currently available as a knowledge base only in AWS regions located in the United States.

  • Enable MongoDB Atlas in AWS Marketplace

  • Create an Atlas M10+ cluster running MongoDB version 6.0.11, 7.0.2, or later.

  • Access to the following foundation models used in this project in your Bedrock:

    • Amazon Titan Embeddings G1 - Text

    • Jamba 1.5 Mini

Data Ingestion into S3 Bucket

Create the S3 Bucket that store the recipe data.

Run the Data Preparation Python Script to ingest the data and metadata.json into the S3 Bucket

cd image
python code/data_collection.py

metadata.json is needed for metadata filtering.

Metadata filtering is like adding smart labels to your documents to help find information more efficiently. Just as you would tag photos with dates, locations, or people's names, metadata filtering in Amazon Bedrock Knowledge Bases lets you attach properties like year, category, or version to your documents. When searching, you can use these tags to narrow down results before looking at the actual content - for example, filtering technical documentation to only show guides from 2023 for Windows 10, rather than searching through all versions and years. This pre-filtering helps you get more accurate and relevant results faster, while reducing processing costs.

For more details can refer this Amazon Bedrock Metadata Filtering Documentation

can check your S3 Bucket to ensure that the data is ingested correctly.

Create the Atlas Vector Search Index and Atlas Vector as a vector database in MongoDB Atlas

create a new database called recipe_db create a new collection called recipe_vector

Note: the name can change accordingly.

go to Atlas Search page and define Atlas Vector Search index

put the index name as vector_index_recipe

and put the definition below for the index

{
  "fields": [
    {
      "numDimensions": 1536,
      "path": "embedding",
      "similarity": "cosine",
      "type": "vector"
    },
    {
      "path": "metadata",
      "type": "filter"
    },
    {
      "path": "text_chunk",
      "type": "filter"
    }
  ]
}

Review the index definition and then Click Create Search Index then we are good to go.

Create a Bedrock Knowledge Base

go to the Amazon Bedrock Console and click Knowledge bases.

Click Create knowledge base and put its name as recipe-mongodb-atlas-knowledge-base. You can change it accordingly.

Add a data source

Enter the URI for the S3 bucket that you created just now as the data source. For the chunking strategy, choose No Chunking as we have preprocessed the data in the data ingestion step already.

After that, choose Titan Embeddings G1-Text as embedding models to convert the data source into vector embeddings.

Connect Atlas to the Knowledge Base

choose mongoDB Atlas as the vector database.

Configuration for MongoDB Atlas as Vector DB in knowledge bases
  • For the Hostname, enter the URL for your Atlas cluster located in its connection string. The hostname uses the following format:
<clusterName>.mongodb.net
  • For Database name, enter recipe_db

  • For Collection name, enter recipe_vector

  • For the Credentials secret ARN, enter the ARN for the secret that contains your Atlas cluster credentials.

AWS Secret Manager to store the Atlas Credentials

go to your AWS Secret Manager and store the Atlas Credentials in the format below.

Store in Key/Value format

Secret KeySecret Value
username"mongoDB atlas-username"
password"mongoDB atlas-password"

then retrieve the Secret ARN for this credentials and use it in the knowledge bases creation.

Metadata Field Mapping

In the Metadata field mapping section, configure the following options to determine the search index and field names that Atlas uses to embed and store your data source:

  • For the Vector search index name, enter vector_index.

  • For the Vector embedding field path, enter embedding.

  • For the Text field path, enter text_chunk.

  • For the Metadata field path, enter metadata.

Review and create the knowledge base

After reviewing the details for your knowledge base, click Create knowledge base to finalize your creation.

Sync the data source

After Amazon Bedrock creates the knowledge base, it prompts you to sync your data. In the Data source section, select your data source and click Sync to sync the data from the S3 bucket and load it into Atlas.

When the sync completes, you can view your vector embeddings in the Atlas UI by navigating to the recipe_db.recipe_vector collection in your cluster.

now you have created the knowledge bases successfully. Can proceed to the next step which is running the FastAPI server to test the knowledge base.

Change the KNOWLEDGE_BASE_ID accordingly

once you created the knowledge base successfully, go to image/src and find the variable KNOWLEDGE_BASE_ID and change the variable to your own KNOWLEDGE_BASE_ID accordingly.

Running the FastAPI Server Locally

cd image
python src/main.py

Then go to http://0.0.0.0:8000/docs to try it out.

Run in Docker Environment

Build the Docker Image

docker build -t recipe-api .

Run the Docker Image as a Server Locally

docker run --rm -p 8000:8000 \
    --entrypoint python \
    --env-file .env \
    recipe-api main.py

After running the Docker container on localhost, you can access an interactive API page locally to test it: http://0.0.0.0:8000/docs.

Deploy to AWS Lambda

Install the Node dependencies

cd lambda-rag-cdk-infra

npm install

Deploy the FastAPI into Lambda Serverless Function

cdk deploy

You will get the deployed Lambda URL and you can test out the deployed fastAPI endpoint using that URL. You can also find it out from Lambda Functions page and get the Function URL.

Note for Lambda Deployment

Make sure that you have setup the AWS CLI in your machine and AWS CDK already bootstrapped. If haven't bootstrap, run the code below.

cdk bootstrap

Delete the lambda deployment

If you want to stop the lambda deployment, run the code below.

cdk destroy

API Response

We have 2 endpoints which are /recommend and /substitute. I will only show the response for endpoint /substitute as both endpoints actually have similar output. The only difference is /recommend will just recommend the most suitable recipe based on the input ingredients while /substitute will recommend the most suitable recipe based on the input ingredients while introducing the substitution ingredients if the required ingredients are not found in the input ingredients.

Input Endpoint for /substitute

Input JSON Payload

{
    "main_ingredients": ["chicken"],
    "pantry_ingredients": [
        "egg"
    ]
}

Output Response

💡
Note that you will get different result with the same input JSON payload to ensure that you can get different recipes.
{
    "status": "success",
    "data": {
        "recipe": {
            "recipe_name": "Chicken Basquaise",
            "category": "Chicken",
            "cuisine": "French",
            "match_scores": {
                "main_score": 100.0,
                "pantry_score": 0.0,
                "total_score": 70.0,
                "main_matches": 1,
                "pantry_matches": 0
            },
            "ingredients": [
                {
                    "name": "chicken",
                    "measure": "1.5kg",
                    "is_main": true,
                    "available": true
                },
                {
                    "name": "butter",
                    "measure": "25g",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "olive oil",
                            "measure": "20g"
                        },
                        {
                            "ingredient": "coconut oil",
                            "measure": "20g"
                        },
                        {
                            "ingredient": "applesauce",
                            "measure": "3 tablespoons"
                        }
                    ]
                },
                {
                    "name": "olive oil",
                    "measure": "6 tblsp",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "unsweetened applesauce",
                            "measure": "6 tablespoons"
                        },
                        {
                            "ingredient": "avocado",
                            "measure": "3/4 cup mashed"
                        }
                    ]
                },
                {
                    "name": "red onions",
                    "measure": "2 sliced",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "sweet onion",
                            "measure": "2"
                        },
                        {
                            "ingredient": "shallots",
                            "measure": "6"
                        }
                    ]
                },
                {
                    "name": "red pepper",
                    "measure": "3 Large",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "1 large yellow pepper",
                            "measure": "1"
                        },
                        {
                            "ingredient": "1 large orange pepper",
                            "measure": "1"
                        },
                        {
                            "ingredient": "1 large green pepper",
                            "measure": "1"
                        }
                    ]
                },
                {
                    "name": "chorizo",
                    "measure": "130g",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "smoked paprika",
                            "measure": "1 tbsp"
                        },
                        {
                            "ingredient": "cooked chicken sausage",
                            "measure": "1 link"
                        },
                        {
                            "ingredient": "plant-based sausage",
                            "measure": "1 link"
                        }
                    ]
                },
                {
                    "name": "sun-dried tomatoes",
                    "measure": "8",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "dried tomatoes",
                            "measure": "1/2 cup"
                        },
                        {
                            "ingredient": "tomato paste",
                            "measure": "1/4 cup"
                        }
                    ]
                },
                {
                    "name": "garlic",
                    "measure": "6 cloves sliced",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "garlic powder",
                            "measure": "1 teaspoon"
                        },
                        {
                            "ingredient": "granulated garlic",
                            "measure": "1 teaspoon"
                        }
                    ]
                },
                {
                    "name": "basmati rice",
                    "measure": "300g",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "couscous",
                            "measure": "300g"
                        },
                        {
                            "ingredient": "quinoa",
                            "measure": "300g"
                        }
                    ]
                },
                {
                    "name": "tomato puree",
                    "measure": "drizzle",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "tomato paste",
                            "measure": "1:1 ratio"
                        },
                        {
                            "ingredient": "canned crushed tomatoes",
                            "measure": "1:1 ratio"
                        }
                    ]
                },
                {
                    "name": "paprika",
                    "measure": "tsp",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "cayenne pepper",
                            "measure": "1/4 tsp"
                        },
                        {
                            "ingredient": "smoked paprika",
                            "measure": "1 tsp"
                        }
                    ]
                },
                {
                    "name": "bay leaves",
                    "measure": "4",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "dried thyme",
                            "measure": "1 teaspoon"
                        },
                        {
                            "ingredient": "dried rosemary",
                            "measure": "1 teaspoon"
                        }
                    ]
                },
                {
                    "name": "thyme",
                    "measure": "Handful",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "rosemary",
                            "measure": "2 teaspoons"
                        },
                        {
                            "ingredient": "oregano",
                            "measure": "2 teaspoons"
                        },
                        {
                            "ingredient": "savory",
                            "measure": "2 teaspoons"
                        }
                    ]
                },
                {
                    "name": "chicken stock",
                    "measure": "350ml",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "water",
                            "measure": "350ml"
                        },
                        {
                            "ingredient": "vegetable broth",
                            "measure": "350ml"
                        }
                    ]
                },
                {
                    "name": "dry white wine",
                    "measure": "180g",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "chicken broth",
                            "measure": "240ml"
                        },
                        {
                            "ingredient": "white grape juice",
                            "measure": "180ml"
                        }
                    ]
                },
                {
                    "name": "lemons",
                    "measure": "2",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "lime juice",
                            "measure": "2 limes"
                        },
                        {
                            "ingredient": "vinegar",
                            "measure": "2 tablespoons vinegar"
                        }
                    ]
                },
                {
                    "name": "black olives",
                    "measure": "100g",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "capers",
                            "measure": "50g"
                        },
                        {
                            "ingredient": "canned artichoke hearts",
                            "measure": "50g"
                        }
                    ]
                },
                {
                    "name": "salt",
                    "measure": "to serve",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "salt-free seasoning blend",
                            "measure": "1 teaspoon"
                        },
                        {
                            "ingredient": "kosher salt",
                            "measure": "3/4 teaspoon"
                        }
                    ]
                },
                {
                    "name": "pepper",
                    "measure": "to serve",
                    "is_main": false,
                    "available": false,
                    "substitutes": [
                        {
                            "ingredient": "paprika",
                            "measure": "1:1"
                        },
                        {
                            "ingredient": "cayenne pepper",
                            "measure": "1/2 tsp per tsp pepper"
                        }
                    ]
                }
            ],
            "steps": [
                "Preheat the oven to 180C/Gas mark 4. Have the chicken joints ready to cook. Heat the butter and 3 tbsp olive oil in a flameproof casserole or large frying pan. Brown the chicken pieces in batches on both sides, seasoning them with salt and pepper as you go. Don't crowd the pan - fry the chicken in small batches, removing the pieces to kitchen paper as they are done.",
                "Add a little more olive oil to the casserole and fry the onions over a medium heat for 10 minutes, stirring frequently, until softened but not browned. Add the rest of the oil, then the peppers and cook for another 5 minutes.",
                "Add the chorizo, sun-dried tomatoes and garlic and cook for 2-3 minutes. Add the rice, stirring to ensure it is well coated in the oil. Stir in the tomato paste, paprika, bay leaves and chopped thyme. Pour in the stock and wine. When the liquid starts to bubble, turn the heat down to a gentle simmer. Press the rice down into the liquid if it isn't already submerged and place the chicken on top. Add the lemon wedges and olives around the chicken.",
                "Cover and cook in the oven for 50 minutes. The rice should be cooked but still have some bite, and the chicken should have juices that run clear when pierced in the thickest part with a knife. If not, cook for another 5 minutes and check again."
            ],
            "image_url": "https://www.themealdb.com/images/media/meals/wruvqv1511880994.jpg",
            "total_score": 70.0,
            "missing_ingredients": [
                {
                    "name": "butter",
                    "measure": "25g",
                    "substitutes": [
                        {
                            "ingredient": "olive oil",
                            "measure": "20g"
                        },
                        {
                            "ingredient": "coconut oil",
                            "measure": "20g"
                        },
                        {
                            "ingredient": "applesauce",
                            "measure": "3 tablespoons"
                        }
                    ]
                },
                {
                    "name": "olive oil",
                    "measure": "6 tblsp",
                    "substitutes": [
                        {
                            "ingredient": "unsweetened applesauce",
                            "measure": "6 tablespoons"
                        },
                        {
                            "ingredient": "avocado",
                            "measure": "3/4 cup mashed"
                        }
                    ]
                },
                {
                    "name": "red onions",
                    "measure": "2 sliced",
                    "substitutes": [
                        {
                            "ingredient": "sweet onion",
                            "measure": "2"
                        },
                        {
                            "ingredient": "shallots",
                            "measure": "6"
                        }
                    ]
                },
                {
                    "name": "red pepper",
                    "measure": "3 Large",
                    "substitutes": [
                        {
                            "ingredient": "1 large yellow pepper",
                            "measure": "1"
                        },
                        {
                            "ingredient": "1 large orange pepper",
                            "measure": "1"
                        },
                        {
                            "ingredient": "1 large green pepper",
                            "measure": "1"
                        }
                    ]
                },
                {
                    "name": "chorizo",
                    "measure": "130g",
                    "substitutes": [
                        {
                            "ingredient": "smoked paprika",
                            "measure": "1 tbsp"
                        },
                        {
                            "ingredient": "cooked chicken sausage",
                            "measure": "1 link"
                        },
                        {
                            "ingredient": "plant-based sausage",
                            "measure": "1 link"
                        }
                    ]
                },
                {
                    "name": "sun-dried tomatoes",
                    "measure": "8",
                    "substitutes": [
                        {
                            "ingredient": "dried tomatoes",
                            "measure": "1/2 cup"
                        },
                        {
                            "ingredient": "tomato paste",
                            "measure": "1/4 cup"
                        }
                    ]
                },
                {
                    "name": "garlic",
                    "measure": "6 cloves sliced",
                    "substitutes": [
                        {
                            "ingredient": "garlic powder",
                            "measure": "1 teaspoon"
                        },
                        {
                            "ingredient": "granulated garlic",
                            "measure": "1 teaspoon"
                        }
                    ]
                },
                {
                    "name": "basmati rice",
                    "measure": "300g",
                    "substitutes": [
                        {
                            "ingredient": "couscous",
                            "measure": "300g"
                        },
                        {
                            "ingredient": "quinoa",
                            "measure": "300g"
                        }
                    ]
                },
                {
                    "name": "tomato puree",
                    "measure": "drizzle",
                    "substitutes": [
                        {
                            "ingredient": "tomato paste",
                            "measure": "1:1 ratio"
                        },
                        {
                            "ingredient": "canned crushed tomatoes",
                            "measure": "1:1 ratio"
                        }
                    ]
                },
                {
                    "name": "paprika",
                    "measure": "½ tsp",
                    "substitutes": [
                        {
                            "ingredient": "cayenne pepper",
                            "measure": "1/4 tsp"
                        },
                        {
                            "ingredient": "smoked paprika",
                            "measure": "1 tsp"
                        }
                    ]
                },
                {
                    "name": "bay leaves",
                    "measure": "4",
                    "substitutes": [
                        {
                            "ingredient": "dried thyme",
                            "measure": "1 teaspoon"
                        },
                        {
                            "ingredient": "dried rosemary",
                            "measure": "1 teaspoon"
                        }
                    ]
                },
                {
                    "name": "thyme",
                    "measure": "Handful",
                    "substitutes": [
                        {
                            "ingredient": "rosemary",
                            "measure": "2 teaspoons"
                        },
                        {
                            "ingredient": "oregano",
                            "measure": "2 teaspoons"
                        },
                        {
                            "ingredient": "savory",
                            "measure": "2 teaspoons"
                        }
                    ]
                },
                {
                    "name": "chicken stock",
                    "measure": "350ml",
                    "substitutes": [
                        {
                            "ingredient": "water",
                            "measure": "350ml"
                        },
                        {
                            "ingredient": "vegetable broth",
                            "measure": "350ml"
                        }
                    ]
                },
                {
                    "name": "dry white wine",
                    "measure": "180g",
                    "substitutes": [
                        {
                            "ingredient": "chicken broth",
                            "measure": "240ml"
                        },
                        {
                            "ingredient": "white grape juice",
                            "measure": "180ml"
                        }
                    ]
                },
                {
                    "name": "lemons",
                    "measure": "2",
                    "substitutes": [
                        {
                            "ingredient": "lime juice",
                            "measure": "2 limes"
                        },
                        {
                            "ingredient": "vinegar",
                            "measure": "2 tablespoons vinegar"
                        }
                    ]
                },
                {
                    "name": "black olives",
                    "measure": "100g",
                    "substitutes": [
                        {
                            "ingredient": "capers",
                            "measure": "50g"
                        },
                        {
                            "ingredient": "canned artichoke hearts",
                            "measure": "50g"
                        }
                    ]
                },
                {
                    "name": "salt",
                    "measure": "to serve",
                    "substitutes": [
                        {
                            "ingredient": "salt-free seasoning blend",
                            "measure": "1 teaspoon"
                        },
                        {
                            "ingredient": "kosher salt",
                            "measure": "3/4 teaspoon"
                        }
                    ]
                },
                {
                    "name": "pepper",
                    "measure": "to serve",
                    "substitutes": [
                        {
                            "ingredient": "paprika",
                            "measure": "1:1"
                        },
                        {
                            "ingredient": "cayenne pepper",
                            "measure": "1/2 tsp per tsp pepper"
                        }
                    ]
                }
            ]
        },
        "metadata": {
            "main_ingredients": [
                "chicken"
            ],
            "pantry_ingredients": [
                "egg"
            ]
        }
    },
    "error": null,
    "details": null
}
💡
You can try the /recommend endpoint by yourself. Both accepts the same JSON Payload format.

Conclusion

Congratulations! You've successfully built a production-ready RAG system that combines the power of MongoDB Atlas Vector Search, Amazon Bedrock's foundation models, and AWS Lambda's serverless capabilities.

This architecture not only ensures scalable and efficient data retrieval but also provides cost-effective, always-available access to your AI-powered insights from anywhere in the world. Whether you're expanding this system for different use cases or using it as a foundation for more complex AI applications, you now have a robust, enterprise-grade solution that can grow with your needs. The journey doesn't end here – feel free to explore, modify, and enhance this system to better suit your specific requirements. Happy building!

Feel free to put your comment if you have any questions/suggestions!

Thank you for reading.

Resources

MongoDB Documentation for Bedrock Integration

Amazon Bedrock Metadata Filtering Documentation

Deploy RAG App in AWS Youtube

Deploy RAG to AWS GitHub Repo

MongoDB and Amazon Bedrock Seminar Youtube

MongoDB and Amazon Bedrock Agent Integration

Simplify RAG application with MongoDB Atlas and Amazon Bedrock Dev Blog

ChatBedrock Langchain Docs

GitHub Link

Devpost Link

TheMealDB Data Source

MongoDB Atlas in AWS Marketplace