Slack AI Bot with AWS Bedrock

Mahmood Rahimi
5 min readJan 7, 2024

--

Want to impress your colleagues? If so, let's build a Slack AI bot using AWS Bedrock. To start, you need, at the very least, admin access to a Slack workspace.

Head to the administration part of Slack. This is the same as managing members and then click on the build.

Click on Create New App.

Name your App and select your Workspace. For security reasons, the name of my Workspace is hidden.

Once the App is created, we will do two things to it. We will select the permissions and make this a bot. Start by clicking on Permissions.

Scroll down to scopes and select IM: READ, IM: Write and CHAT: WRITE.

Go back to the main page and select Add Features and functionality. Both permission and Bots should have a green checkmark. Now, go ahead and install it in Workspace.

Once you are prompted to install, click allow. Again, for security reasons, my workspace name is hidden.

That is for Slack; for now, let's head over to AWS and request access to an AWS bedrock Model.

I will request Jurassic-2 Ultra for now, but you can go ahead and do a different model. The steps are the same but with minor changes.

You should get access instantly.

NOTE: The AWS boto does not have the Bedrock runtime when writing this blog. You can download the layer zip here: https://github.com/mahmoodr786/boto3-bedrock-lambda-layer or create your own Lambda layer zip.

Head over to AWS Lamba, where we will use Python code to communicate between Slack and AWS Bedrock. Create a Lambda function with Python 3.11. I let Lambda create a role for me, and we will add the Bedrock permissions using IAM.

Click on Advanced Settings and enable the function URL. This will be an HTTPS endpoint that Slack will use for communication. I will be using None for the Auth type, but if you are doing this for production use, consider using AWS_IAM or building your authentication mechanism. Click on the create function.

Copy your function URL and return to Slack, where we will add it to event subscriptions.

Turn event subscriptions on.

Now, add the function URL, and once you do that, it will give an error because the Lambda function does not know the correct code to respond to Slack.

Let's correct the code to pass the challenge. Deploy the code and click retry in Slack next to the URL.

import json

def lambda_handler(event, context):
body = json.loads(event['body'])

# TODO implement
return {
'statusCode': 200,
'body': body['challenge']
}

You should now see the Verified with a checkmark.

Scroll down, select Subscribe to bot events, and select the app_mention and message.im. Save Changes.

Head to AWS IAM, find your Lambda role and add permission for AWS Bedrock. I'm using AmazonBedrockFullAccess because I will be deleting everything after this blog; however, I recommend you use the permission you need and not full access.

Let's get our Python code to connect with Bedrock when we get a message from Slack. We need the Slack Bot token and will save it in the environment variables in our lambda function.

We also need to give our Lambda function a timeout of more than 3 seconds, so I'll set it to 30 seconds.

Let's update the Python code to receive messages from Slack and send responses back.

import json
import boto3
import urllib3
import os
from botocore.response import StreamingBody


bedrock = boto3.client(service_name='bedrock-runtime')
slackUrl = 'https://slack.com/api/chat.postMessage'
slackToken = os.environ.get('token')

http = urllib3.PoolManager()

def call_bedrock(question):
body = json.dumps({
"prompt": f"\n\nHuman: Act as a slack bot. {question}\n\nAssistant:",
"maxTokens": 3000,
"temperature": 0.5,
"topP": 1,
})

modelId = 'ai21.j2-ultra-v1'
accept = 'application/json'
contentType = 'application/json'

response = bedrock.invoke_model(body=body, modelId=modelId, accept=accept, contentType=contentType)

if isinstance(response.get('body'), StreamingBody):
response_content = response['body'].read().decode('utf-8')
else:
response_content = response.get('body')

response_body = json.loads(response_content)


return response_body.get('completions')[0].get('data').get('text')

def lambda_handler(event, context):

slackBody = json.loads(event['body'])
slackText = slackBody.get('event').get('text')
slackUser = slackBody.get('event').get('user')
channel = slackBody.get('event').get('channel')
# replace bot username U06D5B8AR8R
msg = call_bedrock(slackText.replace('<@U06D5B8AR8R>',''))

data = {'channel': channel, 'text': f"<@{slackUser}> {msg}"}

headers = {
'Authorization': f'Bearer {slackToken}',
'Content-Type': 'application/json',
}

response = http.request('POST', slackUrl, headers=headers, body=json.dumps(data))

return {
'statusCode': 200,
'body': json.dumps({'msg': "message recevied"})
}

Deploy your function.

Create a channel, and invite the AI and your colleagues. Start writing a message to AI, and you should get a response.

That is it. Currently, the AI does not have the state of your previous questions or your chat history with new subsequent messages. In the next blog, we will fix this by using a DynamoDB.

--

--

Mahmood Rahimi

I'm a DevSecOps Engineer with 13 years of experience in development, operations, security, and cloud.