QnA Maker is currently one of the most popular cognitive services our customers are enthusiastic about using. Back in August we published a blog post describing how to create card attachments for answers returned from QnA maker for .NET. In this post, we’ll be describing how to do the exact same thing for bots built with the Bot Builder SDK for Node.js. Previously, we provided a step-by-step for how to create and deploy a QnA service. This time we’ll skip over that step, but if you need a refresher, you can check out the QnA quick start video.
Prerequisites:
Click here to get started with the Bot Builder SDK for Node.js. Following the Node.js quick start, you’ll end up with a very simple echo bot with the following code in app.js.
var restify = require('restify'); var builder = require('botbuilder'); // Setup Restify Server var server = restify.createServer(); server.listen(process.env.port || process.env.PORT || 3978, function () { console.log('%s listening to %s', server.name, server.url); }); // Create chat connector for communicating with the Bot Framework Service var connector = new builder.ChatConnector({ appId: process.env.MICROSOFT_APP_ID, appPassword: process.env.MICROSOFT_APP_PASSWORD }); // Listen for messages from users server.post('/api/messages', connector.listen()); // Receive messages from the user and respond by echoing each message back (prefixed with 'You said:') var bot = new builder.UniversalBot(connector, function (session) { session.send("You said: %s", session.message.text); });
Install the botbuilder-cognitive services npm module by running the following command:
npm install –save botbuilder-cognitive services
Configure our bot’s connection to QnA service by creating a new instance of QnAMakerRecognizer as follows;
var recognizer = new builder_cognitiveservices.QnAMakerRecognizer({ knowledgeBaseId: 'Your-Qna-App-ID', // process.env.QnAKnowledgebaseId, subscriptionKey: 'Your-Qna-App-Password'}); //process.env.QnASubscriptionKey}); var basicQnAMakerDialog = new builder_cognitiveservices.QnAMakerDialog({ recognizers: [recognizer], defaultMessage: 'No match! Try changing the query terms!', qnaThreshold: 0.3} );
You can view the full list of functional prototypes in QnAMakerDialog.js. If you’ve read our previous article on QnA attachments in .NET, you’ll notice that the implementation is very similar. For this sample we’ll re-use the same QnA service from before, recall that we can use a QnA service to store data, and that we included a question answer pair where the answer was semi-colon ( ; ) delimited.
Below is an implementation of an overwrite to respondFromQnAMakerResult in order to add a hero card attachment to the response from the QnA service. Similar to the .NET implementation in the previous article, we perform a quick check on the result to verify our semi-colon delimited format, perform a string split, store the relevant properties into individual variables and use them to format the card before sending it back to the user.
basicQnAMakerDialog.respondFromQnAMakerResult = function(session, qnaMakerResult){ // Save the question var question = session.message.text; session.conversationData.userQuestion = question; // boolean to check if the result is formatted for a card var isCardFormat = qnaMakerResult.answers[0].answer.includes(';'); if(!isCardFormat){ // Not semi colon delimited, send a normal text response session.send(qnaMakerResult.answers[0].answer); }else if(qnaMakerResult.answers && qnaMakerResult.score >= 0.5){ var qnaAnswer = qnaMakerResult.answers[0].answer; var qnaAnswerData = qnaAnswer.split(';'); var title = qnaAnswerData[0]; var description = qnaAnswerData[1]; var url = qnaAnswerData[2]; var imageURL = qnaAnswerData[3]; var msg = new builder.Message(session) msg.attachments([ new builder.HeroCard(session) .title(title) .subtitle(description) .images([builder.CardImage.create(session, imageURL)]) .buttons([ builder.CardAction.openUrl(session, url, "Learn More") ]) ]); } session.send(msg).endDialog(); } bot.dialog('/', basicQnAMakerDialog);
Starting up our bot in the terminal:
node app.js
In the emulator, let’s give it a quick prompt – ‘what can you tell me about seattle?‘, we expect the same hero card render to be returned as in our .NET sample.
Neat! As you can see, whether you’re building bots using C# or Node.js, it’s all just a matter of parsing the response from the QnA service and formatting the properties to fit your application, and adding those properties as a message attachment. As a reminder, it is recommended to maintain a somewhat consistent data format in your QnA service. The more data formats you introduce to your service, the more custom handling you may need to introduce to your bot to format the response.
Adding an Adaptive Card
We first introduced adaptive cards at Build 2017 and discussed it in this blog post back in June. For this last section in the article, we’ll demonstrate how to add a simple adaptive card to a response from our QnA service.
Click here to read more about Adaptive Cards for bot developers.
This time, in the same app.js file, we’ll create an override for the defaultWaitNextMessage function by adding an adaptive card. An adaptive card attachment uses a simple JSON object to describe the content of the card. The adaptive card sample below just repeats the user’s question back, with a static response ‘Sorry, no answer found in QnA service’ if our QnA service fails to return anything.
basicQnAMakerDialog.defaultWaitNextMessage = function(session, qnaMakerResult){ // saves the user's question session.conversationData.userQuestion = session.message.text; if(!qnaMakerResult.answers){ let msg = new builder.Message(session) .addAttachment({ contentType: "application/vnd.microsoft.card.adaptive", content: { type: "AdaptiveCard", body: [ { "type": "TextBlock", "text": `${session.conversationData.userQuestion}`, "size": "large", "weight": "bolder", "color": "accent", "wrap": true }, { "type": "TextBlock", "text": `Sorry, no answer found in QnA service`, "size": "large", "weight": "regular", "color": "dark", "wrap": true } ] } }); session.send(msg); } session.endDialog(); }
Note that the adaptive card is not part of the BotBuilder SDK, unlike the rich card attachment. The entirety of the adaptive card is defined by a JSON card schema. The sample above only includes a small set of the features you can add to an adaptive card, for a full comprehensive list of currently supported adaptive card properties – click here.
We courage you to take a look at all of the functions available in QnAMakerDialog.js, there are a lot of other methods you can experiment with to make the most out of adding a QnA service to your bot!
Summary
Click here to view the source code for this sample on Git Hub.
Previously we released similar content for bots built using the .NET SDK. We’ve been receiving a lot of requests lately from customers to include more samples for Node.js, which we’ll try our best to include more in the future. We demonstrated how to leverage the botbuilder-cognitiveservices npm module to add more features using a QnA service to add media attachments, in addition to a simple demonstration of how to author an adaptive card. We hope these features will help you to create some really neat bots for your users.
Happy Making!
Matthew Shim from the Bot Framework team.