Business requirement

I worked on this project with my colleague Albert Neumueller, and we were trying to improve a SAP Conversational AI Chatbot based on Skills, our Digital Purchase Assistant: this type of bot is very well suited to handle different conversation types, from direct answers to questions to complex user statements where the bot collects information and provides it to a human agent; the skill based bot can flexibly allow the conversation to change direction, but adding new topics to the Bot’s scope requires adding a new intents, which takes time and work to ensure that the recognition score remains high: when it comes to providing specific answers to a vast number of detailed questions, the best solution is an FAQ-type bot, which is optimized for exactly this task, although it does not have the same agility in the conversation.

To combine the advantages of both, me and Albert used the following approach:

  • Configure our Intent bot to recognize broad topics through Intents, such as “download”, “install”, “connect with data sources” and activated the dedicated Skills based on these Intents.
  • Configure an array of FAQ bots to provide specialized answers to a large (and potentially growing) number of detailed questions on each of these topics.
  • Connect the FAQ Bots to our Intent bot using API calls: this way the intent bot can handle a complex conversation, understand the general topic, trigger the relevant skill and, from there, launch the API call to extract the specialized answer from the relevant FAQ bot. This answer can be passed back within the more complex and lateral context of the Intent bot conversation.

If you are trying to do the same, we hope this article can be of use.

Basic Connection

Let’s start from simply passing the user statement to the FAQ bot and posting the answer in the Intent bot’s conversation.

NOTE: this section handles the scenario in which the FAQ bot retrieves a single answer with confidence above 90%. We will handle the case in which there are multiple answers from the FAQ bot later, in the section “Handling multiple answers from the FAQ bot”

 

  1. In the FAQ Bot
    • go to “settings”, and in the “Options” section copy the callback URL.
    • go to “tokens” and, create (if you haven’t already) a Runtime API token, then copy the Auth URL, Client ID and Client Secret.
    • go to the environment that you want to use and copy the environment Request token.
  2. In the Intent bot
    • Go to the relevant skill that you want to use to trigger the FAQ bot and create an answer group, click on the button “Connect External Service” and choose “Consume API Service”.
    • In the API drop-down menu type choose “POST” and, in the URL section, paste the callback URL copied from the FAQ bot.
    • In the Authentication section create an authentication of type OAuth2 and paste the Auth URL, Client ID and Client Secret copied from the FAQ Bot.
    • In the Headers section add a new line and mark the “key” box as “X-Token”. On the “value” box type “Token “, followed by the Request token copied from the FAQ bot.

 

    • In the API body section use this string:
{"message": {"content":"{{escape nlp.source}}","type":"text"}, "conversation_id": "{{nlp.uuid}}"}:

With {{escape nlp.source}} we pass the user question.

With {{nlp.uuid}} we pass the Intent bot’s conversation ID, so that the answer can be routed back to the correct conversation.

    • To pass the FAQ Bot’s answer in the Intent bot conversation, create a new answer (can be text, buttons etc.) and use this string:
    • {{api_service_response.default.body.results.messages[0].content}}
    • We are therefore passing the first of the “messages” from the FAQ bot as an answer.
    • Now you can test: from the intent bot, type a question that activates the desired skill, then see the answer to that question coming from the FAQ bot. You can now train the intent bot to recognize a general topic, and the FAQ bot to provide very detailed answers to very specific questions on that topic.

 

Advanced Connection with Aliases

The connection method explained above works perfectly when implemented on the current version of your Intent bot but, as soon as you create a new version / fork your bot, the API will lose the OAuth2 authentication credentials (even if you saved them in a template): this is not a bug but rather a security feature.

To avoid having to re-map the OAuth2 authentication details on each of your API calls every-time you create a new version, you can use the “Aliases” functionality, as follows:

  • In your intent Bot go to Settings -> System Aliases and click on the button “Enable System Aliases: the system will create an alias from your existing API connection (you can change the default name)
  • From Settings go into your Environment: you will now see a new section called System Alias Configuration
  • Expand the arrow on the right, and enter here
    1. your callback URL: take the callback URL copied from the FAQ Bot (https://api.cai.tools.sap/build/v1/dialog) until “v1”, so excluding the last part “/dialog”. The final URL should be “https://api.cai.tools.sap/build/v1”
    2. your Auth URL, Client ID and Client Secret copied from the FAQ Bot as explained in the section “Basic Connection” at the point 2.c. You can now save.
    3. Now go into your API call, and in the main section, close to POST, you will find a drop-down menu where you can choose whether to select the “Absolute URL” (as explained in the section Basic Connection) or the Alias you just createdIf you choose the Alias, you will notice that the Authentication section will disappear, as the Authentication is now handled by the Alias set in the environment
  1. In the URL section, you will not enter the full callback URL, but only that final “/dialog” part that we omitted in step 3.a. You can now save.
  2. You can now create a new version of your bot and the API details will be carried over.

Passing memory values

Our Digital Purchase Assistant (and many other Intent-based bots) make use of menus with buttons, to guide the users through all the available options and topics: if the user clicks on buttons, however, there is no real “typed statement” to pass with {{escape nlp.source}} to the FAQ bot, as described in the previous section.

The solution to this is in 2 steps:

  1. Configure your intent bot to recognize when the user is clicking buttons (rather than typing) and use memory values to store a specific statement that contains the question related to those buttons. For example: let’s say that the user clicks on a button called “compatibility requirements”, we can configure a memory value to store a phrase like “what are the compatibility requirements”Using this method is of course possible to create multiple memory values for intents and entities, so let’s say that the next piece of information is “for which product do you want to know the compatibility requirements?” and the answer is “product X” we can store this second piece of information in a separate memory value.
  1. Configure the API call to pass the memory values: so the API body is NOT going to be
    {"message": {"content":"{{escape nlp.source}}","type":"text"}, "conversation_id": "{{nlp.uuid}}"}​

…like we did in the first section but rather

{"message": {"content":"{{escape memory.MEMTOTHEFAQ}} {{escape memory.MEMTOTHEFAQ2}}","type":"text"}, "conversation_id": "{{nlp.uuid}}"}

With the syntax {{escape memory.MEMTOTHEFAQ}} we can pass whatever memory value name we set up. In the example below, we are building the question “compatibility requirements product X” by combining the 2 memory values and passing them as a unified statement to the FAQ bot.

 

Handling multiple answers from the FAQ bot

In case there are multiple possible “answers” to a user question, Intent-type bots have a powerful tool called Disambiguation (see explanation here), while FAQ-type bots have another powerful method: within the default FAQ skill…

…there is an action group that, when the confidence is below a certain threshold, proposes the first 3 questions from the Array in order of confidence, dynamically populating buttons to make it easy for the user to get to the right answer.

The problem, however, is that the FAQ bot answer retrieved by the API call described above is just text, without buttons. To expose to the user the multiple options from the FAQ bot we used the following method:

  1. Configure your FAQ answer block to store the first 1-n questions into memory values (in the example below we chose the first 3 questions): this can be achieved by storing into memory values the first n elements of the array of answers (“{{qna.faq.answers.0.question}}”, “{{qna.faq.answers.1.question}}”, “{{qna.faq.answers.2.question}}” etc.).
    These memory values will also be passed as part of the API answer:
  1. Configure your intent bot to
    • recognize the fact that the disambiguation skill has been triggered (by checking if the memory values are populated)
    • dynamically populate post-back buttons using the memory values retrieved by the API: in order to achieve this we use a response of type “Custom” …
  2. Create a custom code like the one described below (the syntax is explained here)
{{#if api_service_response.default.body.results.conversation.memory.faqbutn1}}

{

  "type": "buttons",

  "delay": "0",

  "content": {

    "title": "API call to test disambiguation with buttons

    {{api_service_response.default.body.results.messages[0].content}}",

    "buttons": [

      {

        "title": "{{api_service_response.default.body.results.conversation.memory.faqbutn1}}",

        "value": "{{api_service_response.default.body.results.conversation.memory.faqbutn1}}",

        "type": "postback"

      },

            {

        "title": "{{api_service_response.default.body.results.conversation.memory.faqbutn2}}",

        "value": "{{api_service_response.default.body.results.conversation.memory.faqbutn2}}",

        "type": "postback"

      },

      {

        "title": "{{api_service_response.default.body.results.conversation.memory.faqbutn3}}",

        "value": "{{api_service_response.default.body.results.conversation.memory.faqbutn3}}",

        "type": "postback"

      }

    ]

  }

}

 

{{else}}

{

 "type": "text",

  "content": "API call to test when no disambiguation: {{api_service_response.default.body.results.messages[0].content}}",

  "markdown": "true",

  "delay": "0"

}

{{/if}}

Let’s explain this custom code:

  1. {{#if api_service_response.default.body.results.conversation.memory.faqbutn1}}

This first line tests if a value exists in the memory slot “faqbutn1”: if yes it means that we are       dealing with multiple answers from the FAQ bot, {{else}} we are dealing with a direct answer

 

{

  "type": "buttons",

  "delay": "0",

  "content": {

    "title": "{{api_service_response.default.body.results.messages[0].content}}",

    "buttons": [

      {

        "title": "{{api_service_response.default.body.results.conversation.memory.faqbutn1}}",

        "value": "{{api_service_response.default.body.results.conversation.memory.faqbutn1}}",

        "type": "postback"

      },

…

    ]

  }

}

In this second block of code, as we are dealing with multiple answers from the FAQ bot, we take the memory values faqbutn1, 2 and 3 from the API response, we create an answer of type “buttons” and populate each post-back button (title and value) with the memory value, which is the actual question retrieved by the disambiguation skill.

 

{{else}}

{

 "type": "text",

  "content": "{{api_service_response.default.body.results.messages[0].content}}",

  "markdown": "true",

  "delay": "0"

}

This last block of code is the “else”, the case in which there is no value in the memory field, which means we have a direct answer: the syntax is, therefore, the one explained in the first chapter “Basic Connection”

 

Conclusion

By applying this method our Digital Purchase Assistant is now able to answer a wide array of very detailed questions on a topic, while having a more lateral and complex conversation and, most importantly, we can expand this array of questions more easily by expanding the FAQ questions list.

Randa Khaled

Randa Khaled

Author Since: November 19, 2020

0 0 votes
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x