Lately I’ve been spending a lot of time thinking about my career and where it’s going. I don’t want to give the impression that I have never thought about my career before, but now the thoughts are becoming constant.
Reference: http://docs.botframework.com/sdkreference/csharp/index.html
Chatbots have been around for a long time. Their usefulness ranges from terrible to moderate. None have really been able to fool humans (at least not openly available ones). However, with Microsoft's introduction of the Bot Framework, we may start to see some really awesome interactions with computer systems. We have Siri and Cortana. But now it's really open to the masses.
The Bot Framework is open-source and available as a NuGet package (Reference: https://github.com/Microsoft/botbuilder, Install-Package Microsoft.Bot.Builder). So we have the source and the ability to tinker with this neat tool. Maybe we should give it a try and see how difficult it is to hook up. The rest of this post will show you how to set up your own Bot with the framework, step by step.
Bot Builder is the framework for constructing bots that handle freeform interactions (or guided ones). There are a number of built-in dialogs for things like yes/no answers, number responses, etc. There is also built-in support for LUIS (Reference: https://www.luis.ai/) although we're not going to explore that today. Let's start by creating a new project to explore the beginnings of this framework.
First things first. Let's create a new web api project. Open up Visual Studio 2015 Community Update 2 RC. Create a new C# ASP.NET Web Application using the .NET Framework 4.6.1.
Select Web Api 4 and click 'OK'. If you are going to host in Azure, go ahead and select your service here. I will be but for the sake of brevity, I will not be including this here.
Finally, let's add the NuGet Package. In the Package Manager Console, Type Install-Package Microsoft.Bot.Builder. If the Console is not visible you can show it by going to Tools > NuGet Package Manager > Package Manager Console.
We will be showing you the Echo Bot in this post. Something simple and already out there. Then, in a later post, we will expand to show more complex bots (because bots can have state too!)
Dialogs are conversation models. Basically, you will handle all the interaction of the bot and how it responds from this class (or group of classes). Below is the EchoDialog:
using System; using System.Threading.Tasks; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; namespace SparcPoint.BotFrameworkExample { [Serializable] public class EchoDialog : IDialog<object> { public async Task StartAsync(IDialogContext context) { context.Wait(MessageReceivedAsync); } public async Task MessageReceivedAsync(IDialogContext context, IAwaitable<Message> argument) { Message message = await argument; await context.PostAsync($"You said: {message.Text}"); context.Wait(MessageReceivedAsync); } } }
There are a number of things to note here. The StartAsync routine starts the conversation off. In some cases, this may have the bot say things like "Hello, <username>" or "What would you like to know?" But once you're ready for a user response, we must call context.Wait(MessageReceivedAsync). This will wait until a response from the user occurs and will run the Message through the handler you specify.
In the MessageReceivedAsync routine, we await the message and then post the response back using context.PostAsync(...). This is how the bot interacts with the user.
In order to utilize the bot, you have 1 of 2 real options: Create a console actor (or any application/emulator), or a web service that can then be posted in the Microsoft Directory. We will opt for the latter.
To accomplish this, we are going to create a Message controller to handle the posts.
using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; using System.Threading.Tasks; using System.Web.Http; namespace SparcPoint.BotFrameworkExample.Controllers { public class MessageController : ApiController { public async Task<Message> Post([FromBody]Message message) { if (message.Type == "Message") { return await Conversation.SendAsync(message, () => new EchoDialog()); } else { // Handle System Message (do nothing) return null; } } } }
If you've ever done Web Api, this should be fairly straight forward. We create a post action to the url /api/Message that will start the conversation with the user. If we happened to have a system message, we return nothing since we don't want to handle it. For this demonstration it isn't important. Normally, though you would have a handle routine to properly deal with the conversation.
Conversation.SendAsync is used to start the conversation with the user. We pass in a new EchoDialog that will then keep state and be used forever more until the dialog completes. Since our dialog is infinite in nature (since the message received routine references itself) we will always be continuing this conversation when we hit the service. It must be noted that we still hit this service during interactions from the user. However, the EchoDialog is only created once. This is why it is a lambda expression. The engine keeps track of where the conversation is currently stopped and resumes when a new message is received.
I'm a big fan of swagger/swashbuckle (Reference: https://github.com/domaindrivendev/Swashbuckle). It creates an interactive help page for web api. So we're going to use this to test our new conversation API!
To install, simply install the Nuget Package via the Package Manager Console.
This will bootstrap everything and should be ready to go out of the box.
Note: You may have to update Newtonsoft.Json to 8.0.0 to make this work properly. Go to your manager and update accordingly
Without having to create a client and everything to try this out (which is arduous at best), I am going to use Swagger to make this work.
Start debugging (F5) the project and let the browser come up. Once it comes up, go to http://localhost:<port>/swagger. You should see a screen like below.
From here, click 'Post' next to /api/Message. You should see a nice interface with a complete description of the api Post call.
On the left, there is a box that has "(required)" in it. Replace it with the following text request:
{ "type": "Message", "conversationId": "1", "language": "en", "text": "Hello Good Sir!", "from": { "name": "webuser", "channelId": "emulator", "address": "webuser", "isBot": false }, "to": { "name": "exbot", "channelId": "emulator", "address": "exbot", "isBot": true }, "replyToMessageId": "1500", "participants": [ { "name": "webuser", "channelId": "emulator", "address": "webuser", }, { "name": "exbot", "channelId": "emulator", "address": "exbot" } ], "totalParticipants": 2, "place": "string", "channelMessageId": "2000", "channelConversationId": "Conv1" }
This request is very basic but we should get a response back from the service with the text "You Said: Hello Good Sir!" To test this theory, click 'Try it out'. If all goes well you will get a response similar to the one below:
{ "type": null, "id": null, "conversationId": "1", "created": null, "sourceText": null, "sourceLanguage": null, "language": "en", "text": "You said: Hello Good Sir!", "attachments": null, "from": { "name": "exbot", "channelId": "emulator", "address": "exbot", "id": null, "isBot": true }, "to": { "name": "webuser", "channelId": "emulator", "address": "webuser", "id": null, "isBot": false }, "replyToMessageId": null, "participants": [ { "name": "webuser", "channelId": "emulator", "address": "webuser", "id": null, "isBot": null }, { "name": "exbot", "channelId": "emulator", "address": "exbot", "id": null, "isBot": null } ], "totalParticipants": 2, "mentions": null, "place": null, "channelMessageId": "2000", "channelConversationId": "Conv1", "channelData": null, "botUserData": null, "botConversationData": null, "botPerUserInConversationData": { "DialogState": "H4sIAAAAAAAEAO1Y3XLbVBC2bEm2nASSQjuU6YAbCtNgR7XjOEk7E4jjJMU0LSV22s4EjzmWjx0RWcpIclr3DYAZhlfglp9X4IIn4A246PSCp4Bd/SUhTixl6p908IxWmqOjc3a/3f12j0NMKBT6B354x994GMST+7Kka4bWMMVVDa62rNSpnko8oroha+pyRsyIaTGdShTaitnW6bJK26ZOlFTiYbumyNI92ilre1RdzmZqjexSboHUswvzNJvjcJtc19XFNZkoWtMQi6pJdZUohjNS0GDgmRmBL4WaZpa1bYPqbJ2YhGvINXhkWdJ9xcOVNnCiIZaoDkvKz4kJViRLpqbTomqYRJXoaqfc2aeltq5rTWLS5Be1b6hkbtEG1Sm8RlQGs0vK5y7WDT/AS7DQYVFwILjIQNRFZ7I4I/J+qWOYtCVuq7LZObbgZxruaunIg2BQR0vZKAZbDETR/vYwuuYxtvxEV21xkeSk3ELmdnaeppduc9x58OPAVmkv2iASmN5h2d8Yx5iCpihgLqhkiHepClZJYgnnfp3Z2fG5S3FDJy2aSvQroSqV2MAi03KigCKOjkd39vI7eppdg1QVrBm4xXjeMGirpnQeADBMiInxYzBpxS8nlKha3ySGWVQVWaXVVY8QYBF+vJ/cxWDEotH8BIgFvwp/biMJkxAHK0VRTQ6BvGChht7kq0TXSYerGvJzGqse2KuCWbcCqblTwWiKxTB6hTed4MKLibsEMQkiiq9CFs/gNRtoDytep1BcAjERZiZdB74FIuuXJ3CpDYccPO9NBWEaVxmPY54S2RxU1fqe8bnNY1AKw80JSXupVKJlSJquyLVXwM+Vikcjl1FcQd+ib6a8oXdw6LIbAlcR6iujaASrU8NkVUrrEXba2WyNKhS90IUHkz7VfwALehi9i9a/hN4syNeYK9EDorRptRqKhZ3s4RDXX4OCeHw69GEqxfgV71PDIM0TVONNOEo2PlE+STaDQvkaQvQCUH7pdMHMVZcm3gMxFwCz/7IEix70oTfGesx9HzWJ3qRmOtqi5q5WT0fYSNqH6e67ddXUOz+d4ukeLXayvNtW97ao0W7RQSTRLWcD4Cun+kFotQDOotrQuhgpJBDZ6yimEd5rQ4L3j1cD79DTKzD+HyD0N1B8iPijOwKDh10Ua0LVihGnEeRtl1yyb1jQ3BbxjcMh7Bbjts/wcaJ+dE2GCTMME9yR/EdBagt4ru+1xeIN/iaSuBOo9sjHIC5AVvNJ9C4CFeeuXwyVrcZMsvfldWtLNnK+vyjC/vjIKjtYaYRZRGv6HJloJRHmwbHTlFBQIKesp5LcVAkiEfee5uL2kphNk84hI683wV7VNBj8hSKxCUcRnLRTEZJuKRMwAPkMiN/dM0t5V6ekLqtNsUyMPcOS4Cy/LToWzEoCQ+Wm764e76cccLpML4KWoNCx4KnM8HNgxN/hs40IZEU/j1yDAKiv6TWDJ7w4g5UDywWfDdKIuow71EppE3DuBCUvgngdOgF+6ZCzb7wmNg2T1O/gGQL7IyR2YcljUIwX/hMQf140Bu3hSuDUT8Gs7yL/c+ohpw41/G3SZWeHdD76oQeDFHaJrCY3NW3fHhnJs+YK5mwexSqI8QKI7dI+0aWHmqxadln/6z3V9L31Z6S1r9BzuE5tKwp7Z0humjnbHHFd2tUcKjwPgGuI3TqKDWTEFbcDuAvi2x4RYpeRfAOyazAnruKJ8n4PxKgFMr8JSo054BgdVYpz+RFUE8tgPA/UKNE6HC7YYIEWduOGWXWL6KZXRNEr/JcgfjyriCaOQHQK0XtV/1ixP43nvdl5/Oue1BTa5WCxBWr9NXy1BtHOr7nJXALxc7BkHoFmfvtItvOP+sbs/GNY2n/w809g+tsODFuQPPIBrTtpvh5oJZgcwlRiNtwMwqUFtFRAnfgdEL+cGard1OhfzPbuML/CqnHWqX3AGo9Cgzf2LwN6ihW+IwAA" }, "location": null, "hashtags": null, "eTag": null }
As you can see in the text field, we indeed get the proper response. There is a lot to explain here but that is beyond the scope of this post. Now all you need to do, is expand on this and create bigger and better bots! Keep in mind, if you want the conversation to continue, a lot of this data must be passed back into the next request so the service has a context to work with.
Bot Builder is a pretty intriguing invention of Microsoft. It isn't a new concept, and it does feel like they're playing catch-up a little bit, but still it is nice to have this available for us as another tool in the Natural Language Processing (NLP) tool belt to utilize. I think over time, as it is being developed, we will see it grow and culminate into a stronger Api that can do so much more. Hopefully, in the near future, I can show a more intricate example of how to do a more realistic bot that we can use for Home Automation and talking with our computer systems.
Til next time!
Check out our thoughts here.
Lately I’ve been spending a lot of time thinking about my career and where it’s going. I don’t want to give the impression that I have never thought about my career before, but now the thoughts are becoming constant.
There is always strong debate around databases and their role in development. Sometimes they are considered components, while others will consider them infrastructure. Is there a right answer? Let's discuss!
There is one, and only one, primary focus that any software developer acknowledge: the ability for software to be maintainable. Of course, correctness, functionality, and performance are all important, these will always be easier to address with maintainable software.