Building a Serverless Application with AWS Lambda and Qdrant for Semantic Search
![Building a Serverless Application with AWS Lambda and Qdrant for Semantic Search](https://cdn.gradual.com/images/https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/pexels-pembegul-dal-3320222-9801351-5436e427-13c1-4cba-a674-c153f409469b-1719853546162.jpeg?fit=scale-down&width=2400)
Deploying and LLM Application with AWS Lambda and Qdrant
July 2, 2024![Benito Martin](https://cdn.gradual.com/images/https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/216453b4-d22b-4e55-b945-61f9fe994d4d-4f06b6b6-4163-449c-ba5d-40154eaee7d3-1719918888839.jpeg?fit=scale-down&width=60)
![Building a Serverless Application with AWS Lambda and Qdrant for Semantic Search](https://cdn.gradual.com/images/https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/pexels-pembegul-dal-3320222-9801351-5436e427-13c1-4cba-a674-c153f409469b-1719853546162.jpeg?fit=scale-down&width=2400)
![](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-1a48f8d9-3122-422e-9d90-34fb96437667-1719498553596.jpeg)
Author: Benito Martin
In this post, I’ll explain how to build a serverless application to perform semantic search over academic papers using AWS Lambda and Qdrant. I used LangChain and OpenAI’s embeddings to create vector representations of document chunks and store them in Qdrant. A simple shell script helps build and push the Docker image to AWS ECR and deploy it as an AWS Lambda function. After testing the Lambda function, I created an API Gateway endpoint and built a Streamlit application to interact with our Lambda function.
The complete code can be found in this repository.
Let’s dive in!!!
Prerequisites
Before we start, ensure you have the following:
- An AWS account with necessary permissions to create Lambda functions and ECR repositories: AWSLambda_FullAccess, AmazonEC2ContainerRegistryFullAccess
- One Qdrant cluster and the corresponding API Key and URL
- OpenAI API keys
- Docker installed for building and pushing the Lambda Function and Docker image
Step 1: Setting Up Environment Variables
Create a .env file in your project directory with the following content:
![some-file-22065cad-ebe2-419a-8f7c-eb9709e4210e](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-22065cad-ebe2-419a-8f7c-eb9709e4210e-1720181049875.png)
The LAMBDA_ROLE_ARN is previously created in the AWS account. The API_ENDPOINT will be provided once you create the endpoint, which will be explained in Step 7.
Step 2: Creating the Vector Store
File name: create_vector_store.py
To create the vector store, we first need to load our academic paper. Fortunately, all papers follow the same structure, so by selecting the corresponding paper number, the following code will fetch it and save it locally on your computer.
![some-file-5fc72e54-aa9f-4fde-9037-09f8a50328b2](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-5fc72e54-aa9f-4fde-9037-09f8a50328b2-1720181150603.png)
Next, we need to check if the collection exists in our cluster. If it doesn’t, it will be created.
![some-file-3dee0ce6-92a2-4e81-bddd-61ab74bcc4a8](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-3dee0ce6-92a2-4e81-bddd-61ab74bcc4a8-1720181466095.png)
All academic papers contain the same structure, so each document that is loaded will contain the page content, source, and page. As metadata, I selected these three and added an additional unique identifier to easily find the specific chunk.
![some-file-40e7c8db-6417-42c7-b600-6430c9750853](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-40e7c8db-6417-42c7-b600-6430c9750853-1720181731011.png)
Now it’s time to trigger the function by selecting the corresponding paper number, and the chunks will be ready to be retrieved in the cluster collection.
![some-file-8abf5271-ac11-4f32-b69d-1bd39e21f6a1](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-8abf5271-ac11-4f32-b69d-1bd39e21f6a1-1720181793323.png)
![some-file-b3b54d97-fabf-45fb-a172-b2bf2c7ba25c](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-b3b54d97-fabf-45fb-a172-b2bf2c7ba25c-1720181968973.png)
Step 3: Creating the RAG Application
File name: rag_app.py
Now we need to create a function for retrieval and generation of responses using the Qdrant vector store as a retriever and an LLM model, in our case GPT-3.5-turbo.
![some-file-fa08e972-79a1-498d-a943-87e33ca4c165](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-fa08e972-79a1-498d-a943-87e33ca4c165-1720182159248.png)
This function initializes the vector store, defines a prompt template for the LLM, and sets up the retrieval QA chain. It then invokes the chain with the provided query to retrieve context from Qdrant and generate an answer using GPT-3.5-turbo.
Here is an example of how to use this function:
![some-file-fe8924c5-053e-45ad-9898-57d91aa4c0ae](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-fe8924c5-053e-45ad-9898-57d91aa4c0ae-1720182295837.png)
Step 4: Creating the AWS Lambda
File name: lambda_function.py
Next, we need to create an AWS Lambda function that processes incoming events and generates responses using a retrieval and generation method.
Why Lambda?
AWS Lambda is a serverless compute service that allows you to run code without provisioning or managing servers. Lambda functions are event-driven, executing the code in response to events, and can be triggered by various AWS services, in our case, AWS API Gateway. Some other benefits of Lambda functions are:
- Automatic Scaling: Scales the applications by running code in parallel in response to each event. As default it provides 1'000 concurrent executions but the quota can be increased based on needs.
- Stateless: Each Lambda function invocation is stateless, meaning each execution is independent.
- No Server Management: The cloud provider AWS handles all the underlying infrastructure.
For this specific use case, this type of architecture offers numerous benefits:
1. Handling Simultaneously Requests: Due to simplified automatic scaling, which handle the incoming workload and adjust it accordingly.
2. Speeding Up Deployment: Rapid deployment and iteration of code, enabling continuous integration and continuous deployment (CI/CD) practices.
3. Cost Efficiency: You only pay for the compute time your code consumes.
4. Integration with Other AWS Services: Seamless integration with other AWS services such AWS API Gateway.
5. Improved Focus on Business Logic: Developers can focus more on writing and optimizing business logic, developing features, and enhancing the application, instead of managing infrastructure.
Lambda Function Code
Now, if we focus on the code, this Lambda function will be responsible for handling HTTP requests (events) made to our REST API, parsing the input, invoking our retrieval and generation function, and returning the response. The lambda_handler function is defined to handle incoming Lambda events. This is the main entry point for the Lambda function. We log the incoming event for debugging purposes.
![some-file-58bd035e-ddb0-407c-9762-65440fa156cf](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-58bd035e-ddb0-407c-9762-65440fa156cf-1720182738038.png)
This Lambda function integrates seamlessly with AWS API Gateway (configured as a REST API) to handle HTTP requests, invoke the retrieval and generation logic, and return the results in a structured format.
Step 5: Creating and Deploying the Docker Image
File name: build_and_deploy.sh
Now we need a shell script to build and push the Docker image to AWS ECR and deploy it as an AWS Lambda function. This step involves several important tasks: building the Docker image, authenticating with AWS ECR, tagging the Docker image, pushing it to the ECR repository, and finally creating or updating the Lambda function with the new Docker image.
![some-file-bda0e02f-4190-4adb-8530-705264e1f5bc](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-bda0e02f-4190-4adb-8530-705264e1f5bc-1720182861532.png)
By automating this process, we ensure a smooth deployment by handling all necessary steps and checks. You should run these commands on the terminal. A specific Dockerfile is available in the repo with all requirements.
![some-file-3b9a0946-4725-44de-89e3-01a57fc08c2a](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-3b9a0946-4725-44de-89e3-01a57fc08c2a-1720183203793.png)
![some-file-ddf78d91-89e2-4f45-9dfb-c54111071132](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-ddf78d91-89e2-4f45-9dfb-c54111071132-1720183267526.png)
Step 6: Testing the Lambda Function
File name: test_lambda.py
Before we move forward to the Streamlit app, we need to ensure that our Lambda function is correctly set up and working. The following test script will invoke the AWS Lambda function and print the response.
![some-file-4e5b66d1-ff7b-450e-b4e5-536823e7fd44](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-4e5b66d1-ff7b-450e-b4e5-536823e7fd44-1720183399688.png)
If everything is working correctly, you should see the following output. If there are any issues, the logs on the terminal and AWS CloudWatch will help in debugging and identifying the root cause.
![some-file-d2e0ac6c-d8e3-401a-9c79-6b0eaba86dd1](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-d2e0ac6c-d8e3-401a-9c79-6b0eaba86dd1-1720183639250.png)
Step 7: Create an API Gateway Endpoint
Once the lambda function is working, we need to create an API Gateway endpoint to interact with your Lambda function. Follow these steps to create the endpoint:
1. Navigate to the API Gateway Console:
- Go to the AWS Management Console.
- Open the API Gateway service.
2. Create a New REST API:
- Click on “Create API”.
- Select “REST API” and then click “Build”.
- Choose “New API”.
- Provide a name for your API.
- Click “Create API”.
3. Create a Resource:
- Click on “Create Resource”.
- Provide a Resource Name (query)
- Click “Create Resource”.
4. Create a Method:
- Select the newly created resource (/query).
- Click on “Actions” and select “Create Method”.
- Choose “POST” from the dropdown and click the checkmark.
- In the “Integration type” , select “Lambda Function”.
- Check the box for “Lambda Proxy integration”.
- In the “Lambda Function” field, select the name of your Lambda function as per LAMBDA_FUNCTION_NAMEin the .env file
- Click “Create Method”.
- You will be prompted to grant API Gateway permission to invoke your Lambda function. Click “OK”.
6. Deploy the API:
- Click on “Deploy API”.
- Create a new deployment stage (choose a name, dev).
- Click “Deploy”.
7. Get the Endpoint URL:
- After deployment, you will be provided with an Invoke URL (https://<api-id>.execute-api.<region>.amazonaws.com/dev/query), that you shall add in the .env file:
![some-file-6cd44454-be05-4ebb-b5f1-aeee65a2444a](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-6cd44454-be05-4ebb-b5f1-aeee65a2444a-1720183787494.png)
Step 8: Building the Streamlit App
File name: streamlit_app.py
Finally, let’s build a Streamlit app to interact with our Lambda function and Qdrant collection. The Streamlit app provides a user-friendly interface to enter queries and display the responses from the Lambda function. The previously created API_ENDPOINT is integrated in the file.
![some-file-11d3f0f7-7dbb-445b-b9d9-c394fa58f53b](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-11d3f0f7-7dbb-445b-b9d9-c394fa58f53b-1720184037559.png)
To run the Streamlit app, use the following command in the terminal:
![some-file-cc0573d4-05e7-468a-8f04-ece1bf6ca798](https://d2xo500swnpgl1.cloudfront.net/uploads/mlops/some-file-cc0573d4-05e7-468a-8f04-ece1bf6ca798-1720184094226.png)
Now you can interact with the app and ask questions about the paper!
![](https://miro.medium.com/v2/resize:fit:1050/1*nvxB9KNWEC9qOIiLwFDkQQ.png)
Author: Benito Martin
Don’t forget to delete the endpoint as this will incur unnecessary costs.
Conclusion
In this post, we walked through building a serverless application using AWS Lambda, AWS API Gateway and Qdrant for semantic search. We created a Python script to download and process academic papers, a Lambda function to handle search queries, and a Streamlit app for user interaction. This architecture allows for scalable and efficient semantic search over large document collections.
If you enjoyed reading this content you can support it by:
- Clapping and following me on Medium! 👏 👏 👏
- Follow my Github 🎶 🎷 🎶
- Staring the repo ⭐⭐⭐
- Share my content on LinkedIn! 💯💯💯
Happy coding!
Originally posted at: