Building with Watson, Part 3

Tying actionable insights to meaningful next steps to build apps that enable efficient workflow.

joe russo
6 min readOct 25, 2018

Ok, time to bring everything together into a really cool, cognitive-infused app.

This is the conclusion to the Building with Watson introductory series. In Part 1, you learned the basics of developing apps using Watson Work Services and in Part 2, you were able to start applying what you’d learned to your own AI-infused app. Your app was leveraging AI to recognize actionable language in the conversation.

Now we are going to bring it full circle. In this article, we’ll describe the annotation ‘actionSelected’. Effectively, you’ll map the actionable language Watson has identified in the conversation to meaningful actions users can take within leaving the conversation. How’s that for productivity?

The use cases for this are endless (imagine, scheduling meetings, looking up lab results, finding sales opportunities, creating to dos, and so on), but here on the East coast we are smack dab in the middle of Hurricane season so keeping up with the weather is front and center on our minds, so we’ll continue our Weather App scenario.

Quick recap

In part 2, we built our Weather app to annotate the conversation as soon as someone posted a message that was related to the Weather or Forecast. Watson detected this weather-related language and underlined it to indicate that.

So now we want something to happen when the user actually clicks on that language. The obvious action would be to show the upcoming forecast to the user.

So how does this happen?

Making the conversation actionable

Whenever a user clicks the underlined message, Watson Work Services adds an actionSelected annotation which includes the action that was set up via the Watson Assistant dialog. Your code is creating a listener, for whenever an annotation-add event occurs and it checks to see if its actionSelected and then initiates that action in your code. In this case — to show the local forecast.

Building the user experience

With the help of targetedMessage and action cards we are able to build robust and interactive user experience to display this information to the users of our app.

targetedMessage
To start, the targetedMessage mutation lets your app have direct, private communication with a user. This means, when the user clicks on the underlined text in the conversation and the action is initiated, instead of everyone in the space seeing how the app responds, the interaction will pop up in a private panel that only the user who initiated the action can see and interact with.

Here’s the JSON for a targeted message mutation.

mutation {
createTargetedMessage(input: {
conversationId: "' + conversationId + '",
targetUserId: "' + userId + '",
targetDialogId: "' + dialogId + '",
attachments: [
{
type: CARD,
cardInput:
{
type: INFORMATION,
informationCardInput:
{
title: "'WEATHER'",
subtitle: "",
text: "WEATHER",
date: 1500573338000,
buttons: [
{
text: "Share the weather",
payload: "SHARE_WEATHER",
style: PRIMARY
}
]
}
}
}
]
}
)
{
successful
}
}
Note: the conversationId, userId and dialogId are placeholders for the actual values

Action Cards
Next, we can build a custom experience for the user to interact with in that private targeted message panel using Action cards. Action Cards are designed to allow you to facilitate a richer, more interactive experience for your end users. (Note: Action cards aren’t the only way to customize the user experience, check out our developer experience to learn more). An action card is essentially some copy with an action button contained in a visual box. There are many variations for action cards, in fact, your targeted message may actually display multiple cards in response to a single message, for example, displaying several cards could be used to show the recent news headlines.

However, for this example we’ll start with how to create a basic, one button action card. It’s pretty straight forward. To create an action card, you need to:

  1. Construct a targeted message mutation
  2. Add an attachment using type CARD.
  3. Add a button which initiates the action.

(see code block above)

User clicks the detected action

User sees available actions

User selects an action and views the targets message page with the action card.

But we don’t want the experience to stop there. The user wants to be able to take the action on the card. Once again, we’ll use actionSelected for to assign an action to the button on the card. In our example app, the card definition would look like this:

createGraphQLOptions.body += ‘payload: “SHARE_’ + operationWeather + ‘“,’;

That payload is the action, so if the user clicks the button, Watson Work Services will create an annotation for actionSelected and your app can listen for that action. Upon receiving this action, your app can fulfill that action.

In our example, the card will either display the Weather or the Forecast and provide an option for the user to post or share that information to the conversation for everyone to see.

Phew! Don’t need your rain coat ⛅️

The last thing your app has to do is a little clean up. In other words, once the user takes an action, you want the card to go away. To do this, you’ll need to create another targeted message that overrides the previous one. Here’s how:

  1. Fulfill the action
function processWeather(spaceId, accessToken, msgTitle, msgText) {
const appMessage = {
"type": "appMessage",
"version": "1",
"annotations": [{
"type": "generic",
"version": "1",
"title": "",
"text": "",
"color": "#ececec",
}]
};
const sendMessageOptions = {
"url": "https://api.watsonwork.ibm.com/v1/spaces/${space_id}/messages",
"headers": {
"Content-Type": "application/json",
"jwt": ""
},
"method": "POST",
"body": ""
};
sendMessageOptions.url = sendMessageOptions.url.replace("${space_id}", spaceId);
sendMessageOptions.headers.jwt = accessToken;
appMessage.annotations[0].title = msgTitle;
appMessage.annotations[0].text = msgText;
sendMessageOptions.body = JSON.stringify(appMessage);
request(sendMessageOptions, function(err, response, sendMessageBody) {if (err || response.statusCode !== 201) {
console.log("ERROR: Posting to " + sendMessageOptions.url + "resulted on http status code: " + response.statusCode + " and error " + err);
}
});}

2. Then, inform the user with this targeted message.

mutation {
createTargetedMessage(input:
{
conversationId: "' + conversationId + '",
targetUserId: "' + userId + '",
targetDialogId: "' + dialogId + '",
annotations: [
{
genericAnnotation:
{
title: "Weather shared. Please click X to close this panel.",
text: ""
}
}
]
}
)
{
successful
}
}
Note: the conversationId, userId and dialogId are placeholders for the actual values

Here’s what the user will see:

Good job! Time for the Prosecco! 🍾

Get the code

Want to peruse the code for this weather app? Go to Github “jarusso/weatherbotj. Nothing fancy here, but hopefully it serves as a good jumping off point for your super-charged apps.

--

--

joe russo

Designer, developer, writer, soccer fan, traveler, lover of food and cooking, quantum computing geek.