Strategies to make your digital assistant smarter!

This is an article written by my colleague Daniel Niculescu

How often have you talked to a chatbot and felt it sounds too robotic? How many times you’ve thought that it did not understand you well?

In this article, you will find a couple of ideas on how you could overcome this and enhance the conversations that users have with your Digital Assistant. Make sure you always try not to leave the user hanging and help him grab “low-hanging fruit” follow-up actions.

Reflect back on what the Digital Assistant understood

Sometimes when you are in a conversation with a Digital Assistant you may get the feeling that it did not understand you well and in most of cases this can be very frustrating for you as an end-user.

When building an FAQ (and not only), it is good to let the user know how the Digital Assistant interpreted his input together with the answer to his question:

The first message sent by the Digital assistant is a prelude message with an acknowledgment of what he understood: ‘You asked about “How to place an order”‘

You may also consider that over time when users keep having long conversations with the Digital Assistant you don’t want to overflow these conversations with “Acknowledgments”. One way you could do this is by lowering the chance the prelude message is displayed with every answer your Digital Assistant is sending back:

As the user keeps asking questions, the change of displaying the prelude is lowered

Randomization of responses

In some cases, the conversations that your users have with your Digital Assistant sound robotic, and this can have bad consequences on the user experience.

We as humans tend not to use the same words when we express the same ideas. Why wouldn’t a Digital Assistant do the same? What if we could make our prelude messages randomize every time they are sent to the user:

Control plane for intelligent buttons and links

When visiting a supermarket, at the counter you find those little candies, soda bottles, chewing gum, and other little packed products that you can pick on your way out.

Most FAQ Digital Assistants are implemented in a “question -> answer -> end of flow” pattern, leaving the user hanging after displaying the answer to his question. To make sure you keep the user engaged in the conversation, you can apply a technique by displaying some follow-up actions after the answer, like the “low-hanging fruits” at the supermarket counter.

Also one of these actions could be a “Learn more” button that the user can use to be redirected to a web page that gives more information about the subject.

In a real implementation, the follow-up actions and the follow-up links are known by business users and not by the developers. Why not use a Digital Assistant resource bundle to give the business users the ability to change these with no-code knowledge?

HOW TO

Here is an example on how you can configure a lowering chance prelude to reflect back what the Digital Assistant understood:

1. Create a new skill flow, I’ve called it “generic.answer.prelude” :

2. We start the flow with a Switch state that checks if the message should be displayed or not with the help of a Apache Freemarker expression:

<#if (((.now?long % 1000)/1000) <= (user.preludeChance?number)!1)>prelude<#else>skipPrelude</#if>

This uses a User scope variable to store the prelude display chance, called “preludeChance”. The Freemarker expression leverages “now” and a mathematical expression for the comparison against the “preludeChance”.

3. Configure a Common Response Component to display the prelude message as the next action for the previous Switch state.

Apache Freemarker code for the text:

<#attempt>${rb('answerPrelude'+((.now?long % 4) +1))} \"${rb('systemFlowName_'+skill.system.event.value.answerIntent.intentName,'channelType',skill.channelType.value)}\"<#recover>${rb('answerPrelude1')}</#attempt>

This Freemarker code will also help with the randomization of the responses. We’ve used 4 in this example, you can use more ( also change the mathematic formula to adapt for more or less). The Resource Bundles can be like this:

4. Add a new Set Variable state to update the value of “preludeChance” User scoped variable as the next action for the “displayPrelude” state and as the “skipPrelude” action of the Switch state.

${((user.preludeChance)!1)?number*(1-skill.system.config.da.preludeBackoff?number)}

The “preludeChance” will be updated and its value will be lowered every time a new answer is sent by the user. We use a skill-scoped variable called “da.preludeBackoff” to configure the frequency of displaying the prelude message.

5. Our service flow is ready and can be invoked by the flow that will handle the answer intents:

6. To display the answer intents we are using a Common Response Component and where we set the “Process User Message” value to “True”.

responseItems:
  - text: "${rb('systemAnswer_'+skill.system.event.value.answerIntent.intentName,'channelType',skill.channelType.value)}"
    type: text
globalActions:
  - visible:
      expression: "${(rb('intentActionMapping','intent',(skill.system.event.value.answerIntent.intentName)?replace('.','_')))?number/1%10?floor = 1}"
    payload:
      action: placeOrder
    label: "${rb('displayTopAnswer.placeOrder.label','channelType',skill.channelType.value)}"
    type: postback
  - visible:
      expression: "${(rb('intentActionMapping','intent',(skill.system.event.value.answerIntent.intentName)?replace('.','_')))?number/10%10?floor = 1}"
    payload:
      action: cancelOrder
    label: "${rb('displayTopAnswer.cancelOrder.label','channelType',skill.channelType.value)}"
    type: postback
  - visible:
      expression: "${(rb('intentActionMapping','intent',(skill.system.event.value.answerIntent.intentName)?replace('.','_')))?number/100%10?floor = 1}"
    payload:
      url: "${rb('learnMore.url.mappings','intent',(skill.system.event.value.answerIntent.intentName)?replace('.','_'))}"
    label: "${rb('displayTopAnswer.learnMore.label','channelType',skill.channelType.value)}"
    type: url

Using the above code in the CRC Metadata, we display the answer specific to the matched answer intent. We also leverage the visible property to check against a resource bundle if a specific global action should be displayed or not.

On the visible property, we use an Apache Freemarker expression that will check the remainder of a value from a Resource Bundle to see if the action should be displayed or not.

We use the same mathematic formula to get the URL that needs to be used by the “Learn more” action, from another Resource Bundle.

We also need to replace the dots (“.”) from the intent names with an underscore (“_”) since the Resource Bundles ICU message format doesn’t like dots.

7. The two used Resource Bundles used by this component are:

PICTURE

As the names suggest, the intentActionMapping handles the visibility of the global actions, and “learnMore.url.mappings” handle the URL mappings for the “learnMore” global action.

“learnMore.url.mappings” screenshot explains for itself how its value should be changed.

For the “intentActionMapping” we can add a “HOW TO” in the Annotation, where we explain that for specific actions, the user should perform a simple addition of the values: if he wants the “help_ans_order_cancel” answer intent to enable the “Cancel an order” and “Learn more” global actions, he can add 100+10 = 110 and use that in the ICU message of the Resource Bundle.

8. The rest of the flow that handles the answer intents is just a simple action mapping and Invoke flow states to navigate to those respective flows.

Conclusion

Every implementation has its own big challenges. However, the small details like: “Reflect back what the Digital assistant understood”, “Follow-up actions”, and “Randomize responses” are the ones that make your Digital Assistant look smart, and natural and keep the user engaged in its conversations.