Back to posts
Building a Serverless QnABot with Custom Response Bots on AWS

Building a Serverless QnABot with Custom Response Bots on AWS

Sunil Biradar ยท September 3, 2024

Introduction

In this blog post, we'll walk through the process of creating a serverless QnABot using AWS Lambda, the Serverless Framework, and Amazon Lex. We'll cover everything from initial setup to implementing custom response bots for handling multi-turn conversations. This guide is based on a real-world project and addresses common challenges and roadblocks you might encounter along the way.

Table of Contents

  1. Setting Up the Serverless Framework
  2. Creating the Initial Serverless Project
  3. Understanding and Configuring Deployment Stages
  4. Implementing the Lambda Function
  5. Configuring IAM Roles
  6. Deploying to Different AWS Regions
  7. Integrating with QnABot
  8. Implementing Custom Response Bots
  9. Testing and Troubleshooting
  10. Best Practices and Tips

Setting Up the Serverless Framework

To get started, you'll need to install the Serverless Framework and configure your AWS credentials.

  1. Install Node.js and npm from https://nodejs.org/
  2. Install the Serverless Framework globally:
    npm install -g serverless
  3. Configure your AWS credentials:
    • Create an AWS account if you don't have one
    • Create an IAM user with programmatic access and AdministratorAccess policy
    • Run aws configure and enter your AWS access key ID and secret access key

Creating the Initial Serverless Project

Let's create a new Serverless project for our QnABot:

  1. Create a new directory for your project and navigate to it:
    mkdir qnabot-project
    cd qnabot-project
  2. Initialize a new Serverless project:
    serverless create --template aws-nodejs --path .
  3. Open the generated serverless.yml file and update it with the following content:
org: sunilb0575p
app: e-shop-response-bots
service: qna-chatbot

provider:
  name: aws
  runtime: nodejs20.x
  region: us-west-2

functions:
  return-status:
    handler: handler.returnStatus

This configuration sets up a basic serverless service with a single Lambda function named return-status.

Understanding and Configuring Deployment Stages

By default, the Serverless Framework uses a stage called "dev". You can specify different stages for different environments (e.g., development, staging, production).

To deploy to a specific stage, use the --stage option:

serverless deploy --stage production

You can also configure the stage in your serverless.yml:

provider:
  stage: ${opt:stage, 'dev'}

This allows CLI override while defaulting to 'dev' if not specified.

Implementing the Lambda Function

Now, let's implement our Lambda function in the handler.js file:

'use strict'

module.exports.returnStatus = async event => {
  console.log('Input:', JSON.stringify(event, null, 2))

  return {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Return status check function executed successfully!'
    })
  }
}

This is a basic implementation that logs the input and returns a success message.

Configuring IAM Roles

To integrate with QnABot, we need to use a specific IAM role. Update your serverless.yml:

functions:
  return-status:
    handler: handler.returnStatus
    role: arn:aws:iam::YOUR_ACCOUNT_ID:role/dev-qnabot-6-0-1-FulfillmentLambdaRole-trLi2DR59osV

Replace YOUR_ACCOUNT_ID with your actual AWS account ID.

Deploying to Different AWS Regions

To deploy to a specific AWS region, add the region field under the provider section in your serverless.yml:

provider:
  name: aws
  runtime: nodejs20.x
  region: us-west-2

You can also override this using the --region flag during deployment:

serverless deploy --region us-west-2

Integrating with QnABot

To integrate with QnABot, ensure your function name starts with "qna-". In our case, the service name already starts with "qna-", so the deployed function name will be qna-chatbot-dev-return-status, which satisfies this requirement.

Implementing Custom Response Bots

To implement custom response bots for multi-turn conversations, we'll use Amazon Lex. Here's how to set it up:

  1. Create a new Amazon Lex bot named "QNAReturnStatus" with intents for capturing Return Status number and DOB.

  2. Update your serverless.yml to include permissions for Lex:

provider:
  name: aws
  runtime: nodejs20.x
  region: us-west-2
  iamRoleStatements:
    - Effect: Allow
      Action:
        - lex:PostText
      Resource: 'arn:aws:lex:${self:provider.region}:${aws:accountId}:bot:QNAReturnStatus:*'

functions:
  return-status:
    handler: handler.returnStatus
    role: arn:aws:iam::YOUR_ACCOUNT_ID:role/dev-qnabot-6-0-1-FulfillmentLambdaRole-trLi2DR59osV
  1. Modify your handler.js to handle the multi-turn conversation:
const AWS = require('aws-sdk')
const lex = new AWS.LexRuntime()

exports.returnStatus = async (event, context) => {
  const sessionAttributes = event.sessionAttributes || {}
  const returnNumber = sessionAttributes.return_info
    ? sessionAttributes.return_info.ReturnNumber
    : null
  const dob = sessionAttributes.return_info
    ? sessionAttributes.return_info.DOB
    : null

  if (!returnNumber) {
    return elicitSlot(
      event,
      'What is your Return number?',
      'QNAReturnStatus',
      'ReturnNumber'
    )
  }

  if (!dob) {
    return elicitSlot(
      event,
      'What is your date of birth? (MM/DD/YYYY)',
      'QNAReturnStatus',
      'DOB'
    )
  }

  // Here you would typically make an API call to check the Return status
  const status = await checkReturnStatus(returnNumber, dob)

  return {
    statusCode: 200,
    body: JSON.stringify({ message: `Your Return status is ${status}` })
  }
}

function elicitSlot(event, message, botName, slotToElicit) {
  return lex
    .postText({
      botName: botName,
      botAlias: '$LATEST',
      userId: event.userId || 'user1',
      inputText: event.inputText || '',
      sessionAttributes: event.sessionAttributes || {},
      requestAttributes: {
        'x-amz-lex:qnabot-response': JSON.stringify({
          elicitResponse: {
            responsebot: botName,
            namespaceattribute: 'return_info',
            slottoelicit: slotToElicit
          }
        })
      }
    })
    .promise()
    .then(lexResponse => {
      return {
        statusCode: 200,
        body: JSON.stringify({ message: lexResponse.message }),
        sessionAttributes: lexResponse.sessionAttributes
      }
    })
}

async function checkReturnStatus(returnNumber, dob) {
  // Implement your Return status check logic here
  return 'VALID'
}
  1. In the QnABot Content Designer:
    • Create a new item for the Return status question
    • Set the Lambda hook to your function
    • Set up Elicit Response to use your custom Lex bot

Testing and Troubleshooting

To test your function locally:

serverless invoke local -f return-status

To pass custom event data:

serverless invoke local -f return-status --path event.json

Where event.json contains your test event data.

Common issues and solutions:

  1. Deployment fails: Ensure your AWS credentials are correctly configured and you have the necessary permissions.
  2. Function not recognized by QnABot: Verify that your function name starts with "qna-".
  3. Lex integration not working: Check the IAM role permissions and ensure the Lex bot name is correct.

Best Practices and Tips

  1. Use environment variables for configuration values.
  2. Implement proper error handling and input validation.
  3. Use AWS CloudWatch for logging and monitoring.
  4. Regularly update your dependencies and runtime versions.
  5. Use AWS X-Ray for tracing and performance optimization.
  6. Implement CI/CD for automated testing and deployment.

Conclusion

Building a serverless QnABot with custom response bots on AWS involves several components working together seamlessly. By following this guide, you should be able to set up a basic implementation and extend it to suit your specific needs. Remember to always follow AWS best practices for security and performance optimization.

Happy coding!