Multi-Lingual chat bot with cloud functions.

Bonus post for being away for so long! 🙂 Let’s talk about how to do a multi-lingual Chatbot (MLC).

Each skill is trained on its own individual language. You can mix languages into a single skill, however depending on the language selected the other is treated as either keywords or default language words. This can be handy if only certain language words are commonly used across languages.

For languages like say Korean or Arabic, it gives a limited ability versus say English. For something like English with Spanish, it simply does not work.

There are a number of options to work around this.

Landing point solution

This is where the entry point into your chatbot defines the language to return. By far the easiest solution. In this case you would have for example an English + Spanish website. If the user enters the Spanish website, they get the Spanish skill selected. Likewise with English.

Slightly up from this is having the user select the language at the start of the bot executing. This can help where the anonymous user is forced to one language website, but can’t easily find how to switch.

The downside for this solution is where end users mix language. Somewhat common in certain languages. For example Arabic. They may type in the other language only to get a confused response from the bot.

Preparation work

To show the demo, I first need to create two skills. One in English and one in Spanish. I select the same intents from the catalog to save time.

I also need to create Dialog nodes.. but that is so slow to do by hand! 🙁 No problem. I created a small python script to read the intents and write my dialog nodes for me like so:

Here is the sample script for this demo. Totally unsupported, and unlikely to work with large number of intent. But should get you started if you want to make your own.

Cloud functions to the rescue!

With the two workspaces created for each language we can now create a cloud function to handle the switching. This post won’t go into details on creating a cloud function. I recommend the built in tutorials.

First in the welcome node we will add the following fields.

Field Name Value Details
$host “us-south.functions.cloud.ibm.com” Set to where you created your cloud function
$action “workspaceLanguageSwitch” This is the name of the action we will create.
$language “es” The language of the other skill.
$credentials {“user”:”USERNAME”,”password”:”PASSWORD”} The cloud function username + password.
$namespace “ORG_SPACE/actions” The name of your ORG and SPACE.
$workspace_id “…” The workspace ID of the other skill.

Next we create a node directly after the welcome one with a conditional of “!$language_call” (more on that later). We also add action code as follows.

The action code allows us to call to the cloud function that we will create.

The child nodes of this node will either skip if no message comes back, or display the results of the cloud function.

On to the cloud function. We give it the name “workspaceLanguageSwitch”.

This cloud function does the following.

  • Checks that a question was sent in. If not it sends a “” message back.
  • Checks that the language of the question is set to what was requested. For example: In the English skill we check for Spanish (es).
  • If the language is matched, then it sends the question as-is to the other workspace specified. It also sets “$language_call” to true to prevent a loop.
  • Returns the result with confidence and intent.

Once this is created, we can test in the “Try it out” screen for both Spanish and English.

Here is all the sample code to try and recreate yourself.

It’s not all a bed of roses.

This is just a demo. There are a number of issues you need to be aware of you take this route.

  • The demo does a one time shot. So it will only work with Q&A responses. If you plan to use slots or process flows, then you need to add logic to store and maintain the context.
  • Your “Try it out” is free to use. Now that you have a cloud function it will cost money to run tests (all be it very cheap).
  • There is no fault control in the cloud function. So all that would need to be added.
  • Cloud functions must complete execution within 5 seconds. So if your other skill calls out to integration, it can potentially break the call.

Simple Intent Tricks

As usual it’s been a while since I’ve updated the blog, so as to keep it alive here is a quick update. For this post I’m going to show two simple techniques using the intents[] object over the #Intents reference.

As always I recommend that you only implement these if there is evidence it is needed.

Repeating Questions

A common issue with end users is that they don’t always understand the answer they get. It is very common for the user to gloss over what has been shown to them, and they can miss the actual answer they wanted.

More often than not, the user just doesn’t read the answer. 🙂

By default the user asks the question in a slightly different way, gets the same intent and asks again. One of the ways to detect how to work on answers is to look for this pattern in the logs.

Another option is to change up the conversation like a human would. To this end, we will detect if they got the same answer. First we create a $last_intent context variable, and check that like so:

Now if this node doesn’t trigger it’s important to clear the $last_intent

But this node needs to jump back to the branch to continue on. You will notice that I created a dummy node to jump to. This is a good coding convention. You put any further nodes below the jump node. This prevents the jump logic breaking if you add nodes.

Another thing to notice is the Answer General Questions condition logic:

     intents[0]["intent"].startsWith("General")

This allows you to group similar context intents into a single node. Normally most people will just pick the related intents for the condition block. So you end up with something like this:

The problem with above is that you will be more prone to making mistakes. While some of you might have spotted the “and” mistake, what is less noticeable is the missing intent #General_Ending. You can spend ages wondering why your message isn’t being displayed despite being in the node. With the one line code earlier, you don’t have to worry about any of this.

Here is the sample skill to play with.

Compound Questions

Next up is compound questions. I discussed this before, but from a code perspective. This example we are going to try doing it from within the skill itself.

IMPORTANT NOTES!

  • Watson Assistant Plus already has this feature.
  • Due to how skills work, this example cannot exceed 50 intents. Watson Assistant will disable the dialog logic if it hits the same node 50 times. This is to prevent a possible endless loop.
  • This will likely not work if you have any slots.

If you need it for more than 50 nodes then you will need to do it with code, or WA Plus (which is much easier).

First we start by creating a multi-response node (main condition is anything_else). The first response you should edit in the advanced tab and set as follows:

The condition block is just a way to see if the first intent and second intent are close. There are a number of ways to do this, for example K-Means, or difference in percent rather than scaled. While K-Means is not easily done in WA, this method works but tends to be a bit more sensitive. So play around and see which you prefer.

Once the condition is hit we set a $answer_counter to 1 and $compound_found to true.

If it’s not hit we just set $compound_found to false. We don’t need to worry about the $answer_counter as it will always be 0 when in this position.

For the intent matching in the dialog nodes you cannot use the #Intent shortcuts. Instead you do the following:

    intent[$answer_counter].intent  == "General_Greetings"

If the counter is set to 1, it will respond with the second intent first. Then we decrement that counter and loop through all intents again. You end up with something like this:

Again, here is a sample skill: