Build a Slack­bot....................

Chat­bots are ev­ery­where. Ama­zon Web Ser­vices’ Lex makes it easy to build and de­ploy bots to Slack, Face­book and more, as Dan Frost demon­strates

Linux Format - - CONTENTS -

Dan Frost is back wield­ing his Server­less Frame­work like an im­mor­tal High­lander, un­leashed and cre­at­ing Slack­bots to tor­ment and to tit­il­late your on­line en­e­mies.

Bots have a var­ied pres­ence on­line, from Face­book mes­sen­ger to web­sites, the Quartz news app and on Slack. Done well, they’re help­ful, can save you time and glue to­gether ex­ist­ing data and in­fra­struc­ture.

How­ever, build­ing a bot can be fid­dly. Thank­fully, Ama­zon Web Ser­vices (AWS) re­leased Lex, which is the nat­u­ral lan­guage pro­cess­ing en­gine and bot sys­tem be­hind Alexa. It en­ables you to build and de­ploy a bot in very few lines of code. In fact, once you have Lex wired up to your chat plat­form it­er­at­ing and adding fea­tures is ex­tremely easy.

To get you started build­ing in­ter­ac­tive chat­bots we’re go­ing to put to­gether a re­ally sim­ple recipe-lookup bot called chef­bot. We’re go­ing to do this us­ing AWS Lex’s plat­form, with Slack as the chat plat­form us­ing the server­less frame­work.

Chef­bot has the an­swers

All the code is on github so you can it­er­ate on the code. Chef­bot is both sim­ple and generic enough to be a good­start­ing point for any bot that asks hu­mans ques­tions and looks up data from a MySQL (or any other data­base).

For this recipe you’ll need an AWS ac­count, a Slack ac­count, a MySQL server run­ning some­where and the Server­less frame­work (see LXF228) in­stalled. We as­sume that you have ad­min ac­cess to the AWS and Slack ac­counts.

Be­fore we dive into the code, let’s un­der­stand what we’re about to build. In user in­ter­face terms a bot is a com­puter that sits on a mes­sen­ger plat­form (Slack, Face­book mes­sen­ger) and in­ter­acts with hu­mans in a con­ver­sa­tional style us­ing text, im­ages and other me­dia. All the in­ter­ac­tion is chrono­log­i­cal and lin­ear, un­like apps and web pages where the in­ter­ac­tion is directed by the user.

In sys­tem ar­chi­tec­ture terms, a bot is piece of code that ei­ther re­sponds to a mes­sage com­mand or pushes a mes­sage to a hu­man user in the hope of a fol­low-up com­mand. In terms of our ar­chi­tec­ture here, a python func­tion will be called each time the user sends a mes­sage in Slack. This means that we need to cre­ate an API end­point, con­fig­ure Slack to know about that end­point and then code the end­point to re­spond use­fully to mes­sages from users.

But if we’re do­ing any­thing more com­plex than re­spond­ing to lit­eral, per­fectly match­ing strings then we’ll have to build a whole stack of nat­u­ral lan­guage pro­cess­ing us­ing ma­chine learn­ing. This is non-triv­ial, so it’s nice to off­load the work onto Lex, which does this for us.

Build­ing the Lex bot

Nav­i­gate to https://con­sole.aws.ama­zon.com/lex/ home?re­gion=us-east-1#bots and cre­ate a bot, choos­ing Cus­tom Bot. En­ter the bot name as chef­bot, se­lect None for voice, 5 for time­out, se­lect No for COPPA, then click Cre­ate.

Now we cre­ate an in­tent which is some­thing the user wants to get out of the bot. This might be a hol­i­day, the an­swer to a ques­tion, the weather to­mor­row or any­thing else that you can an­swer. For our ex­am­ple, it’s go­ing to be a recipe.

The in­tent might re­quire a few more de­tails which the bot gets out of the user by ask­ing ques­tions, such as “What is the main in­gre­di­ent?” Each of these ex­tra de­tails fill what Lex calls “slots”. Once the in­tent is clear and the slots are filled, Lex will pass both to our func­tion which should then be able to pro­vide a fi­nal re­sponse to the user.

First, cre­ate a few in­tents which re­flect how our users might ex­press their need for a recipe: Find my a recipe for fish I want to cook with fish Fish recipe

Add each of these by typ­ing the sen­tence into the ut­ter­ance in­put and click­ing the + icon.

Next we need to de­scribe the two slots of in­for­ma­tion that we need. We’ll cre­ate a dummy slot type called In­gre­di­ent

with an ex­am­ple value Fish. Now add one re­quired slot called

main_in­gre­di­ent of type In­gre­di­ent and make it re­quired. Then cre­ate an­other slot called cook­ing_­time of type AMA­ZON.

NUM­BER and also make it re­quired. Now, re­turn­ing to our sam­ple ut­ter­ances we need to la­bel which of the words in the sen­tences re­late to our slots, since all we’re re­ally in­ter­ested in is get­ting the slot val­ues. La­bel fish as an in­gre­di­ent and the num­bers as cook­ing_­time.

(It’s pos­si­ble to use NLP to turn hu­man du­ra­tion phrases into num­bers, but that’s out­side the scope of this tu­to­rial. Have a look at AMA­ZON.DU­RA­TION and play around!)

We aren’t go­ing to bother with a con­fir­ma­tion prompt since what we do with the in­tent isn’t ex­actly life-chang­ing: we aren’t or­der­ing a pizza, tak­ing down a server or mov­ing money be­tween bank ac­counts. In those more se­ri­ous sit­u­a­tions you would have a con­fir­ma­tion like “Okay – I’m go­ing to push the big red but­ton. Are you sure about that?” be­fore do­ing it.

For now, leave ful­fil­ment as Re­turn Pa­ram­e­ters to Client, which does what it sounds like. We get the slots back so we can see if our lit­tle con­ver­sa­tion worked.

To test your bot, first click Build to build it and then open the Test Bot di­a­log in the bot­tom right of the con­sole. Type one of the sam­ple ut­ter­ances or a slight vari­a­tion such as “Got a recipe for fish?” You should see that the slight vari­a­tions in lan­guage are dealt with by the Lex NLP, and af­ter an­swer­ing a cou­ple of ques­tions you get the slot val­ues back.

Our next task is to plumb in Slack so the same re­sult can be seen there. Af­ter that, we’ll do some­thing more ex­cit­ing with the data.

Plumb in Slack

Get­ting Lex tied into Slack re­quires copy­ing a few keys from Lex to Slack and vice versa. This is don­key work, but nec­es­sary so let’s get on with it…

In the Set­tings tab cre­ate a new alias by giv­ing it a name – for ex­am­ple Beta – and se­lect­ing a ver­sion. Se­lect the most re­cent ver­sion you’ve built. Click the Chan­nels tab and then click Slack.

You’ll need some in­for­ma­tion from slack first, so open a new tab and go to https://api.slack.com and lo­gin. Click Add a Bot and then on the next screen click Add a Bot User. On the next screen set the bot name to chef­bot and set Al­ways Show my Bot as On­line to On and click Add Bot User.

Click In­ter­ac­tive Mes­sages in the left menu and then click En­able In­ter­ac­tive Mes­sages. For now, just put any valid URL in the URL field – we’ll come back to this later. Now click Ba­sic In­for­ma­tion in the left menu and copy the val­ues for Client ID, Client Se­cret and Ver­i­fi­ca­tion to­ken from the Slack in­ter­face into the cor­re­spond­ing fields in Lex and, in Lex click Ac­ti­vate.

We now have two val­ues to copy back to Slack: Post­back URL, which is used for event sub­scrip­tions and in­ter­ac­tive mes­sages; and oAuth URL, which is used for the oAuth handshake to au­then­ti­cate with Slack. Copy these into a text file as we’ll need them in a few places.

Go back to the Slack tab (we’re nearly done, we prom­ise) and click oAuth Per­mis­sions. Click Add Re­di­rect URL and paste in the oAuth URL. Click Save. Now add the scope per­mis­sions: `Chat:write:bot, team:read` and save changes. Click In­ter­ac­tive Mes­sages and copy the post­back URL from Lex into the Re­quest URL and click Save changes.

Fi­nally (yes, re­ally…), click Event Sub­scrip­tions and en­able them with the tog­gle. Paste in the post­back URL to the re­quest URL field. In Sub­scribe to Team Events add mes­sage. chan­nels, and in Sub­scribe to Bot Events type mes­sage.im and se­lect the op­tion that comes up. Save the changes.

So that’s the con­fig done. Now we need to de­ploy it to Slack. In Man­age Dis­tri­bu­tions click Add to Slack and then Autho­rize on the fol­low­ing screen. You’re then redi­rected to the Slack web UI where you can test the bot. Se­lect the bot from the left list of chan­nels and start chat­ting us­ing the ut­ter­ances that you con­fig­ured ear­lier.

(Be­fore we go any fur­ther, if you get stuck or if the process has changed, con­sult the AWS doc­u­men­ta­tion on in­te­grat­ing Slack, http://docs.aws.ama­zon.com/lex/lat­est/dg/slack­bot-as­so­ci­a­tion.html.)

Okay, this took some bor­ing con­fig­u­ra­tion but the up­shot is that you can eas­ily mes­sage the bot and ob­tain a re­sponse. We now have Slack send­ing mes­sages to Lex, Lex work­ing out what we need from the user and then dump­ing the slots of data back to the user. The only un­cool part of this is that the Lambda func­tion isn’t do­ing any­thing very in­ter­est­ing, so let’s solve that next.

Bring our Franken­bot to life

In­stead of just dump­ing the val­ues back to the user, Lex can hand off to Lambda, AWS’s Server­less en­vi­ron­ment. I’m go­ing to use the Server­less frame­work which does lots of the com­plex or­ches­tra­tion re­quired to use AWS, so get your­self an AWS ac­count and install the Server­less frame­work and let’s boot­strap the project. We’re go­ing to use the Python 3

en­vi­ron­men­tLet’s kick thingsas that’soff... our pre­ferred lan­guage these days. npm install -g server­less server­less cre­ate --tem­plate aws-python --path MyChatBot Now cre­ate an IAM pro­file for your­self and set up your cre­den­tials as fol­lows: server­less con­fig cre­den­tials -p aws -k XXX -s XXXXX --pro­file tu­to­rial-pro­file Now mod­ify the server­less.yml file to con­tain the fol­low­ing. You can re­move all the boil­er­plate con­fig if you wish. provider: name: aws run­time: python3.6 pro­file: tu­to­rial-pro­file … func­tions: han­dle_lookup: han­dler: han­dler.han­dle_lookup events: - http: path: lookup method: any Now add the han­dler func­tion to han­dler.py: def han­dle_lookup(event, con­text): log­ger.info(str(event)) re­turn { ‘ses­sionAt­tributes': event['ses­sionAt­tributes'], ‘di­alogAc­tion': { ‘type': ‘Close’, ‘ful­fill­men­tS­tate': ‘Ful­filled’, ‘mes­sage': { ‘con­tentType': ‘PlainText’, ‘con­tent': ‘Look at my bot!’ } } }

And de­ploy: server­less de­ploy -v (At this stage it’s worth tail­ing the log in your ter­mi­nal: server­less logs -t f han­dle_lookup.)

In the Lex UI, change ful­fil­ment to “AWS lambda func­tion” and se­lect your func­tion from the drop­down. Save the in­tent. Give it a whirl in slack and you should see the few slot-fill­ing steps per­formed by Lex and then the fi­nal “Whoa!” re­sponse from our Python method.

Let’s fin­ish off by mak­ing this do some­thing in­ter­est­ing.

Pulling in some real data

We’ve cre­ated a sim­ple MySQL dataset that you can put into any MySQL data­base in­stance. For the pur­poses of this, I cre­ated an AWS RDS in­stance, but your MySQL server can be any­where so long as Lambda can see it. There aren’t enough words in the ar­ti­cle to get MySQL setup and do the chat stuff

so just do what works for you (we love a chal­lenge!–Ed). All you need is the host­name, user, pass­word and data­base name and to have the DB open to the in­ter­net… (Warn­ing: the method of open­ing the data­base to the in­ter­net is not fit for pro­duc­tion sys­tems. This is just for demon­stra­tion only.)

Install the MySQL con­nec­tor and then the code to con­nect, se­lect the records and re­turn them. First install the pack­age: vir­tualenv .myenv source .myenv/bin/ac­ti­vate pip install mysql-con­nec­tor-python-rf Now add the fol­low­ing to the top of the han­dler file: sys.path.ap­pend(’.myenv/lib/python3.6/site-pack­ages') import mysql.con­nec­tor

The first line is be­cause we need to bun­dle up all de­pen­den­cies in­side the Lambda and then in­clude the site pack­ages di­rec­tory in our path. The sec­ond is a nor­mal Python import state­ment.

Now we can get down to the task of writ­ing the code to con­nect and re­turn re­sults. For this ex­am­ple, we’re us­ing a plain MySQL con­nec­tor, but you can em­ploy which­ever fancy data­base con­nec­tor takes your fancy. def han­dle_lookup(event, con­text): log­ger.info(str(event)) host­name = ‘...’ user­name = ‘...’ pass­word = ‘...’ data­base = ‘...’

main_in­gre­di­ent = event['cur­ren­tIn­tent']['slots']['main_ in­gre­di­ent']

cook­ing_­time = event['cur­ren­tIn­tent']['slots']['cook­ing_ time']

cnx = mysql.con­nec­tor.con­nect(user=user­name, pass­word=pass­word, host=host­name, data­base=data­base) cur­sor = cnx.cur­sor(buffered=True) query = ‘se­lect * from recipes where main_in­gre­di­ent = %s and cook­ing_­time <=%s'#.for­mat(main_in­gre­di­ent, cook­ing_ time)

cur­sor.ex­e­cute(query, (main_in­gre­di­ent, cook­ing_­time)) re­ply = “\n*Here’s what I found:*\n” for r in cur­sor: log­ger.info(r) recipe = “\n- *{}* which re­quires the in­gre­di­ents: {}”. for­mat(r[1], r[3])

re­ply = re­ply + recipe re­turn { ‘ses­sionAt­tributes': event['ses­sionAt­tributes'], ‘di­alogAc­tion': { ‘type': ‘Close’, ‘ful­fill­men­tS­tate': ‘Ful­filled’, ‘mes­sage': { ‘con­tentType': ‘PlainText’, ‘con­tent': ‘Here\'s a recipe for ' + main_in­gre­di­ent + ' tak­ing ' + cook­ing_­time + “\n\n== " + re­ply } } }

Now let’s take it for a spin. We’ve loaded up the recipe data­base with a few en­tirely non­sense recipes, but the re­sults should give you an idea of what you can do with bots. Start ask­ing chef­bot for recipes and you’ll be asked for a main in­gre­di­ent and cook­ing time, and get recipes in re­sponse. The only piece that’s coded is the fi­nal query to the data­base and the re­sponse to the user. This is sim­ple, but there’s a lot of po­ten­tial.

Where to go from here

In the ar­ti­cle we’ve con­fig­ured a nat­u­ral lan­guage pro­ces­sor, hooked up a Lambda func­tion to re­act to the ut­ter­ances of users and hooked in a MySQL data­base of recipes via a Server­less Lambda func­tion. All this is far more con­fig­u­ra­tion than pro­gram­ming, so it’s im­por­tant to keep in mind what can be achieved if you take this fur­ther.

The de­ploy­ment we ran had the in­ter­ac­tion take place in pri­vate hu­man-bot chan­nels, but bots can sit in on pub­lic chan­nels as well. This can be use­ful if you’re pulling in data for your team to ref­er­ence such as “@ is­sue­tracker­bot how many open is­sues are there?” or “@ up­time much much down­time on server X last week”.

The in­ter­ac­tion we built was also en­tirely text based with a text in­put and text out­put. This is nice for proof of con­cept, but you can also add card re­sponses which make the process more vis­ual and, on a plat­form like Face­book mes­sen­ger, much more en­gag­ing. Ex­plor­ing places such as Google docs, Dropbox, traf­fic mon­i­tor­ing, billing, so­cial and other data sources can broaden the scope for what just a cou­ple of ques­tions to a bot can do for you and your users.

Our ex­pert

Dan Frost Dan works at Cam­bridge As­sess­ment, ex­per­i­ment­ing with tech­nol­ogy in ed­u­ca­tion. He’s a writer and ex­plorer of ideas and tech­nolo­gies. He’s of­ten found on Twit­ter (@ dan­frost) dis­miss­ing new fads in com­put­ing. Un­til he cham­pi­ons them.

The AWS Lex con­sole, where you de­sign your chat­bot’s con­ver­sa­tions. Play around with new phras­ings and forms of chat to keep the ex­pe­ri­ence en­gag­ing.

Chef­bot in ac­tion. Sim­ple ad­di­tions like emoji make a dry bot chat seem fun or more hu­man to deal with.

It’s im­por­tant for peo­ple to know what your bot does. Cre­at­ing a sim­ple icon, adding a colour and a com­pelling short de­scrip­tion will get users chat­ting.

Ex­per­i­ment with events – we’ve only scratched the sur­face of the events you can use here, but it’s pos­si­ble to have your Lambda func­tion re­act to other data. Play around in the Slack con­fig and see what else you can cre­ate.

Newspapers in English

Newspapers from Australia

© PressReader. All rights reserved.