Smart Queries: ask Pepper about monsters and get accurate replies

Other types of questions

Now that we can filter our monster list based on what the user asked, let’s look at different types of questions people could ask about monsters.

Let’s decompose them more specifically:

Question

What to answer

What to show on tablet

Show me red monsters

“Here they are”

All red monsters

Show me a red monster

“Here is one”

Only one red monster

How many red monsters are there?

“There are three of them”

All red monsters

Are there any red monsters?

“Yes, there are”

All red monsters

Compared to what we already have, this requires two changes:

  • We need to set the number of monsters in a QiChat variable, so that the dialogue can depend on it
  • Have a new executor that only shows one monster

Counting the Monsters

First, create a new QiChatVariable object in the MonsterDialogue class:

   private val countVariable = qiChatbot.variable("count")

And then, update the ShowMonsters executor so that it sets this variable

   inner class ShowMonstersExecutor(context: QiContext?) : BaseQiChatExecutor(context) {
       override fun runWith(params: MutableList<String>) {
           val queryMonsters = criteriaManager.filterMonsters(allMonsters)
           showMonsterList(queryMonsters)
           countVariable.value = queryMonsters.size.toString()
           criteriaManager.clearVariables()
       }

       override fun stop() {}
   }

Now, each time this executor is used, the “count” variable will be updated. Add a new command in your QiChat, that uses ^first to decide what to say:

u:([
       "How many ~monsters are there?"
       "How many ~monsters have ~attribute"
       "How many ~monsters have ~attribute and ~attribute"
       "How many ~monsters are ~adjective"
])
^execute(clearScreen)
Let me see,
^execute(showMonsters)
^first[
       "$count==0 There are not any corresponding monsters."
       "$count==1 There is one corresponding monster."
       "There are $count corresponding monsters."
       "I don't know how many monsters there are."
]

Of interest:

  • We distinguish count=1 from count>1 so that the plural works properly.
  • The last sentence, “I don’t know how many monsters there are” should never happen - but it is safer to include it anyway (and more generally, anytime you use ^first, leaving a “default” option is a good habit, even if you believe it can never happen).

Only showing one monster

Some questions only involve showing one monster; create an executor that does that (while also updating the count monster).


   inner class ShowMonsterExecutor(context: QiContext?) : BaseQiChatExecutor(context) {
       override fun runWith(params: MutableList<String>) {
           val queryMonsters = criteriaManager.filterMonsters(allMonsters)
           // Show one random monster, or an empty list if there are none
           showMonsterList(listOfNotNull(queryMonsters.randomOrNull()))
           countVariable.value = queryMonsters.size.toString()
           criteriaManager.clearVariables()
       }

       override fun stop() {}
   }

… and add it to the table of executors:

executors = hashMapOf<String, QiChatExecutor>(
   "showMonster" to ShowMonsterExecutor(qiContext),
   "showMonsters" to ShowMonstersExecutor(qiContext),
   "clearScreen" to ClearScreenExecutor(qiContext),
)

You can now add the ~monster concept and this command to the topic:

concept:(monster)[
   "~adjectives monster ~withAttributes"
]

u:(Show me a ~monster)
^execute(clearScreen)
Let me see,
^execute(showMonster)
^first[
    "$count==0 There is no such monster."
    "$count==1 This is the only one."
    "Here is one!"
]

Notice how we use the count variable again to give a slightly different answer depending on whether there was only one monster anyway.

Handling all the other questions

With this new executor, and the count variable, we can now handle all questions. Note that there can be minor semantic variations on these questions (“can I see red monsters?” “I’d like to see the monsters that are red?” etc.).

concept:(monsters)[
   "~adjectives monsters ~withAttributes"
]

concept:(monster)[
   "~adjectives monster ~withAttributes"
]

concept:(show_me)[
    "Show me"
    "Can I see"
    "Can you show me"
    "Could I see"
    "Could you show me"
    "Do you have"
    "I want to see"
    "I'd like to see"
]

concept:(are_there)[
   "are there"
   "do you have"
   "do you know"
]

concept:(all_the)[
   "{"all" "every one of"} [your the]"
   "all"
   "every"
   "each"
]

u:([
       "How many ~monsters are there?"
       "How many ~monsters have ~attribute"
       "How many ~monsters have ~attribute and ~attribute"
       "How many ~monsters are ~adjective"
])
^execute(clearScreen)
Let me see,
^execute(showMonsters)
^first[
       "$count==0 There are not any corresponding monsters."
       "$count==1 There is one corresponding monster."
       "There are $count corresponding monsters."
       "I don't know how many monsters there are."
]

u:(~show_me a ~monster)
^execute(clearScreen)
Let me see,
^execute(showMonster)
^first[
    "$count==0 There is no such monster."
    "$count==1 This is the only one."
    "Here is one!"
]

u:(~show_me ~all_the ~monsters)
^execute(clearScreen)
Okay,
^execute(showMonsters)
^first[
   "$count==0 There are no such monsters."
   "$count==1 Here it the only one."
   "Here they are!"
]

u:(~are_there {any} ~monsters ?)
^execute(clearScreen)
Let me see,
^execute(showMonsters)
^first[
   "$count==0 No, there aren't any."
   "$count==1 Yes, there is one."
   "Yes, there are $count ."
   "There seems to be a bug in this application."
]

You can now test the full dialogue on Pepper, who will now be able to answer any question you might have on monsters!