Showing posts with label Watson Assistant. Show all posts
Showing posts with label Watson Assistant. Show all posts

Monday, 4 July 2022

Automatically emailing a copy of conversation history

Many chatbots offer the option of emailing the user a copy of the conversation history. Traditionally this has been done by writing code server side, but with the latest version of the web widget associated with Watson Assistant it is really easy to implement this with a small bit of client side code.

The code to implement this can be downloaded from GitHub. What you need to do is:

  1. Place the code contained in the history_template.html file somewhere in the header part of your web site's page. 
  2. Modify the INTEGRATION_ID, REGION_ID and INSTANCE_ID variables to match your Watson Assistant instance.
  3. Create an account on elasticmail.com if you don't already have one. Then insert the SMTP_HOST, SMTP_USERNAME, SMTP_PASSWORD and FROM_ADDRESS in the javascript code.
The code is fairly easy to understand, but here are a few pointers:
  • The start and end of the code block is the same as the web widget which is provided for you by the Watson Assistant UI. In fact accessing this widget is the easiest way to get the correct values for the integrationID:, region:, and serviceInstanceID: variables.
  • The code registers a number of event handlers:
    • The histHandler() function gets called when the chat window is opened. The event passed contains the history which has been stored client side. This is used to construct the initial email contents and store it in the global variable historyText.
    • The msgSent() and msgReceived() functions are called when a message is sent by the end user and by Watson respectively. They update the global variable historyText.
    • Last, but not least the msgSent() event handler gets called whenever the chat window is closed/minimised. This calls the sendEmail() function described below.
  • The sendEmail() function implements the actual sending of the email. The documentation for this javascript library suggests that any arbitrary SMTP server can be used, but I found that it wouldn't work with any SMTP agent apart from smtp.elasticemail.com. The value of the From: variable indicates where the email will appear to be coming from - this will have to be the email address you used to create the account on ElasticMail. Be careful what address you use - this is where any replies will be sent.
If you want to customise the look and feel of the chat history emails, edit the variable baseHistoryText and/or change the watsonMsg() and userMsg() functions.

I hope you find this sample useful. As always this code is provided on a As-Is basis. No warranty or support is offered.

Friday, 8 May 2020

Understanding Intents and Entities

One of the most important things that any conversational agent needs to do is to figure out what intent(s) and entities are contained in the user's input. An intent is what the user wants to do and the entities are the things that they want to do it with or too. This can be confusing so this post tries to explain with a simple example.

To illustrate we will consider a chatbot written for a second hand electronics store. The samples of expected user input might be something like:

  1. I want to sell my old iPhone
  2. I need a new laptop
  3. I am interested in buying an new iPad
In examples 2 and 3 the user's intent is to buy, while in example statement number 1 the intent is to sell. When designing a conversational agent you define the intents by giving examples of what you expect users with this intent to say. In a real system you would need to give many more examples to give Watson a better chance of  guessing the users' intent, but this is enough to illustrate the way it works..

There are different ways to specify entities. The most common one is to give samples of the entity values. For example, if we had an entity @device and the possible values were 'phone', 'tablet' and 'laptop' - we would specify the entity as shown below by giving examples of what the user might say for each type of entity.


The second way of specifying an entity is with a regular expression. This is useful in cases where you want to catch an email address or phone number. In this cases it is not feasible to exhaustively list all possible inputs, but the rules for what an email address or phone number look like are easy to specify in a regular expression.

The third option for entities is to use one of the predefined system entities. For example, you might use the @sys-date entity a simple way to capture mentions of a date without having to go to the trouble of specifying a complex regular expression. This also has advantages such as when the user types 'tomorrow' the entity extracted is the correct date for the day after they typed it.

Many developers of chatbots don't realise that you can combine entities with intent examples to make them more powerful. If you specify your examples like this:


  1. I want to sell my old @device
  2. I need a new @device
  3. I am interested in buying a new @device
This saves you the trouble of repeating the same sentence for each type of device that the users might want to buy or sell.

Monday, 12 August 2019

Migrating your Watson Assistant workspace to WebHooks

In the last post we examined the differences between webhooks and the old way (sometimes called web actions) that IBM Watson assistant called REST functions through the use of IBM Cloud Functions. In this post we will look at a simple example of migrating from one to the other.

There is a tutorial on DeveloperWorks which guides you through all of the steps to connect your Watson Assistant skill to the Wikipedia API using the old style mechanism. In this blog post we will assume that you have already gone through the steps in the original tutorial and we will describe how you can convert your workspace to use the newly released webhooks feature.

The first step in the migration is that  you need to make sure that the Cloud Function you created to lookup Wikipedia call able to be called externally.

If the function definition is associated with a resource group, the security model will make it hard to be called, so you need to ensure that your function definition it is associated with a Cloud Foundry space. You can tell if your selected namespace is IAM based or Cloud Foundry based because of the drop-down selector at the top of the page will say (CF-Based) e.g.:



If your function is defined in a IAM resource group, the easiest way to move it is to create a new function (with the exact same code) in a Cloud Foundry space i.e. switch to the new space with the drop-down and then create the function as described in the original tutorial.

After you have saved the Action you should try it out by changing the object_of_interest to different things you might be interested in looking up and then see what the function returns.


Since WebHooks doesn't interact directly with Cloud Functions as such, you will need to ensure that your action is turned into a WebAction which can be called by any REST client. To do this, click on the EndPoints link in the left margin. This will give you the option to make your action invokable by a REST URL.


Once  you do this, you will see a curl command which can be used to invoke the web action. Initially the screen only shows API-KEY rather than the actual API key assigned to you. Click on the eye icon on the right to display the fully correct curl command.

If you have curl installed you can copy this command and execute it in your command line window, However, you will get an error because you haven't given any input parameters. To solve this add more command line options to specify the object of interest and the fact that the data you are supplying in in JSON format. e.g.:
curl -u 3a686c56-12fc-4bd9-8a08-55317fec468d:CE4A88p1qGYV69dF43iVENNn3Ok6DHdtlYz7tlrCh0yG7aNRvUgzcHHBNJxi15z9 --header "Content-Type: application/json"  --data "{\"object_of_interest\": \"love\"}" -X POST https://us-east.functions.cloud.ibm.com/api/v1/namespaces/brian_odonovan_bod-space/actions/Assistant-Functions/Wikipedia-Lookup?blocking=true
If you prefer using another tool like POSTMAN, you can easily convert this command to suit. The one thing you need to be aware of is the fact that the authorisation you supply with the -u parameter to the curl command consists of two parts - the part before the colon is effectively a username and the part after the colon is the password.

Once you have verified that you WebAction is working correctly, you next need to change the Watson Assistant skill to use webhooks when calling Wikipedia. You do this by clicking on the options tab when editing the dialog and then selecting the Webhooks option on the left (it should be selected by default) and then entering details of the URL you want to call, what credentials to use and any other headers you want to pass to the function.




You can use the URL and credentials from the curl command that you got from the web actions page described above. You might be slightly worried that there is a single URL assigned to a  skill because in some cases where you might need to access services from different sites. There are ways of getting around this limitation, but I won't describe them here since our use case doesn't need to connect to multiple services. The next blog post in this series will describe in detail how you can connect to multiple REST services from a single WA workspace..

The Dialog node which implements the interface to Wikipedia through Webhooks will be significantly different from the old one, therefore I suggest that you either delete or disable the old node. For example you could rename the node to Old Wikipedia and disable it my changing the match condition to false as illustrated below.



Now you have to create a new Dialog for calling the webhook. You should call the node Wikipedia or something similar and make sure it is activated whenever the #tell_me_about intent is detected. Next click on the 'customize' icon and this will give you an option to turn on Webhooks for this node.

As soon as you close the customize dialog you will see that you see additional UI elements which parameters you would like to pass to the REST call and what context variable you would like to use to store the response.

You will also see that the node has been converted into a multi condition response node and it pre-configures two output slots for what to say when your REST call succeeded (i.e. when the context variable has been set) or when the variable wasn't set (which probably indicates a network error or something similar).


You can use the same responses as in the in the original tutorial since the format of the response won't have changed. You can now test your application and see that it behaves more or less as before.

There are two things that you should note about the way that Watson Assistant Webhooks work:

  1. We specified that you add a parameter named object_of interest and set its value to the contents of the @object_of_interest entity (lets assume that you asked "what is love" so the value will be "love" ).

    Normally when people say that they are adding parameters to a POST call they mean that they are adding a header with the value "object_of interest: love". However, this is not what Watson Assistant does. Instead it sends a JSON body with each of the parameter values e.g. {"object_of_interest": "love"}.

    This is actually a better thing to do, but make sure you don't get confused by the terminology in the documentation.
  2. Watson Assistant tells you that it stores the response from the REST call in the context variable you specify, but this is not exactly what it does. While the Webhooks functionality is not totally tied to the Cloud Functions, it does make certain assumptions based upon the way Cloud Functions operate.

    The response from a call to a Cloud Function will contain lots of information about the call other than just the response from the REST service called. It look something like: 
{
   "activationId": "xxx",
   "end": 
<time_stamp>,
   "start": 
<time_stamp>,
   "response": {
      "result": { ...},
      "status": "success",
      "success": true
   },
   ...
}


When you IBM Cloud Function returns, the data returned by the REST service is contained in the response.result part of the JSON structure retuned. If you are not using Cloud Functions, make sure you follow this convention because Watson Assistant will be expecting it. Similarly, you should also set the response.sucsess variable to the value true because otherwise Watson Assistant will assume that the call has failed.

Thursday, 8 August 2019

What is the benefit of the new webhooks feature in Watson Assistant

When building an AI chatbot it is impossible to incorporate all knowledge directly in your skill. As a result developers often find themselves wanting to call external functions to answer certain queries. IBM has responded to this requirement by supporting the calling of cloud functions from within a Dialog node in Watson Assistant.

While developers have found this useful, they have also complained that it is inflexible and not so easy to use. To answer these complaints,  IBM has recently released a new feature called webhooks. This feature was available in limited Beta for several months, but has just been released generally so now is a good time to look at it.

This table summarises the differences between the two mechanisms:

Aspect webhooks old way
URL Flexibility With webhooks you can call any arbitrary URL. This means that you are not tied to using IBM Cloud Functions as an intermediate layer, Of course Watson Assistant will always make a POST call to your URL and supply the parameters in JSON. If the REST API you want to call does not accept this, you will need some mechanism to transform the call. However, you are free to choose any transformation tool that you want. With the old mechanism, you could only call a Cloud Function which is defined in the same environment as the Watson Assistant instance which is doing the calling. This was quite restrictive and although it worked OK with Cloud Foundry based authentication, it was not really compatible with the new IAM style resource group authentication currently used in the IBM cloud.
UI Assistance There is a UI to guide you in defining the authorisation and other parameters for your call to a webhook. This makes it quite user friendly. The way that you specified that a REST call should be made is by editing the JSON response from a node to include an action parameter. Apart from the documentation there was no assistance to developer to define this correctly.


Now that we have compared the two mechanisms, our next blog post will look at a simple example of migrating from one to the other.


Tuesday, 12 February 2019

Matching Only on the Number of Digits you Want

Frequently in Watson you will have two entities that both involve numbers. Say a birth month is two digits long and a birth year four. A problem can arise where the shorter number is found in the longer number because there are two digits inside the four digits.

Month Entity

Year Entity

But when Year is given Month is found.

A way around this is to use the \b word boundary regular expression to say I only want numbers if there are spaces or words around the digits.

and now it works. The \b is not included in the captured entity just the number which is handy

I have tested this in Chinese where they do not use spaces and it also works. Which is great.

Wednesday, 28 November 2018

Matching patterns and getting their values in Watson Assistant/Conversation

When IBM Watson Assistant (formerly known as Watson Conversation) is deciding how to respond to a user's utterance it is vital that it correctly identifies the intent (what the user wants to do) and the entities (what are the things involved in the intent). For example, if the user says "I want to buy a book" - the intent would be #MakePurchase and the entity @ItemOfInterest would have a value of "book".

In earlier releases of Watson Assistant, the only way to specify possible entity values was either by manually specifying a list of possible values or else by selecting one of the predefined system entities such as @sys-date. Sometimes this works quite well, but other times (e.g. when you are expecting an email address or an account number) is not feasible to list all of the possible values that people might enter.

Luckily, the latest version of the Watson Assistant service allows you to specify allowable entity values with a regular expression. Unfortunately, people sometimes find it hard to retrieve the matched value from a pattern match. If you are not careful you will be told that an email address was specified and not what exact email address was given. Therefore this blog post works through a very simple conversational design to explain what you need to do.

First off, you define an intent. We will call our intent #sendMessage and we give Watson a few examples of what the user might say when they want to send a message.


Then we create a @contact_info entity which we expect users to specify when they are sending a message. To complete this entity, the user types a message indicating that they want to send a message. We expect that the message will also contain details of where to send the message, either an email address or a phone number (the phone number can be specified in US style or in the e164 standard common in other parts of the world).

This picture shows how the entity definition will look. Don't worry if you can't read the regular expressions in the screenshots, you can download the workspace design.



Now you need to insert a dialog node to handle requests to send messages. We create a node in our dialog flow which is triggered when Watson detects that the user's intention is to send a message. We know that it is necessary to have contact information to send a message, so if the user didn't supply this we will prompt them.



Then we need sub-nodes which deal with sending either emails or phone messages. We select which to activate depending the value of the @contact_info variable, which will be either email, us_phone_num or e164_phone_num.

When sending a message, it is not enough to know that the user gave us an email address - we need to know the exact email address given.  To do that, we  need to define a variable whose value will be specified as  "<? @contact_info.literal ?>".  The screen shot below shows the dialog node for sending a phone message.




This is the end of our very simple BOT. If you want to see this in action, download the design file here and  import it into your own Watson Assistant instance. Here is a screenshot of what I saw when I clicked on the "Try it out" button to see the bot in action.


In summary, regular expression entities can be really useful, so long as you remember to use the @entity_name.literal syntax to get the actual content that was matched rather than simply which rule was fire.

Thursday, 5 April 2018

Naming Intents

How should you name intents? Heres one way and an explanation as to why.

In this post we described clustering a topic into intents. The naming scheme I used was TopicIntent.

When you go to improve accuracy you will merge and split intents. You tend not to do this outside Topics. I find that if you have the topic name in the intent when you do these changes it is easier to keep your brain in one context.

Cluster Topics

"Happy families are all alike; every unhappy family is unhappy in its own way." the Anna Karenina principle

Some Topics cover loads but you don't really care about the individual intents inside. For example if you have a Complaints topic that could cover all sorts of things people moan about.

No one wants a message back saying "This robot cares we have lost your bags". A complaint question will have to be passed onto a person. If we can tell that person that we have a complaint they can then decide what to do next. If you do not break down complaint topic into intents though all sorts of questions will be in one intent. It will deal with damage, delays, queues, lost items, dirty conditions etc. This giant varied intent will suck in other questions damaging your overall system accuracy.

With a varied topic like complaints that your chatbot cannot handle by itself. If you make one giant intent it will damage your overall accuracy. But because complaints tend to be about a few things at once, 'The food was terrible and the portions were small', there is often not one solid intent anyway. By labelling all complaints ComplaintIntent it is possible to ignore the intent part as getting the topic right is good enough.

In our accuracy tests we can strip the intent part off and say that if we land in Complaint that is good enough. But not create on giant intent that covers too much and that will suck in all other questions.

This issue of big topic particularly happens with Off Topic topics where questions are out of scope, silly or just cover large areas that you can't really answer.

There are other ways to label intents. This TopicIntent method is what I use. If you have a different way please mention it in the comments.

Wednesday, 4 April 2018

Clustering Questions Part 2: Intentions

Once you have divided your questions into Topics the next step is to divide them into Intents. This is how I would find the intents inside a topic

An Intent is a purpose or goal expressed by a user’s input such as finding contact information or booking a trip.

Imagine you had an airline booking chatbot. And you had these questions in the Topic booking

There is a dataset of travel questions here I will take some questions from there and invent some myself

A booking topic could have

Question Intent
I'd like to book a trip to Atlantis from Caprica on May 13 BookTicket
I'd like to book a trip from Chicago to San Diego between Aug 26th and Sept 5th BookTicket
i wanna go to Kobe whats available? BookTicket
Can I get information for a trip from Toluca to Paris on August 25th? BookTicket
I'd like to book a trip to Tel Aviv from Tijuana. I was wondering if there are any packages from August 23rd to 26th BookTicket
I want to know how far in advance I can book a flight BookFuture
When do bookings open for 6 months time BookFuture
I want to get a ticket for my christmas flight home BookFuture
Can I check my booking? BookCheck
can i check my booking status BookCheck
can i check the status of my booking BookCheck
how do i check the status of my booking BookCheck
i need to check my booking status BookCheck
Let me know the status of my Booking BookCheck
Can I book now pay later BookPay
How can I pay for a booking? BookPay

On this the verbs Check and Pay each seem to form an intent. There is one intent on When bookings can happen. And a few unknowns that might make more sense when we have more questions later.

At this stage realise you are going to make mistakes and have to go back over your intents as you learn by doing. Fixing your intents once you have had a first cut at defining I will come back to.

One could way to find intents in a topic is to look for verbs. Unrelated actions tend to have different verbs. In this case the topic Booking is already a verb and a noun. This is common enough. Dual meanings like this can be a nightmare with Entities but that is another blogpost.

In this topic something like 'cancel a booking' is likely to be an intention. Here Cancel is the verb and booking as the object of the sentence.

Other clues to the intention are the Lexical Answer Type, the subject and the object The LAT is the type of question. Who questions have different types of answers to When questions. In practise I don't find you commonly use the LAT to define intentions.

One possible exception to this is definitional questions where users ask "What is a..." for a domain term to be explained. If more than 5% of your questions are definitional you may not have collected representitive questions as manufactured questions by non real users or real users forced to ask questions tend to be definitional. When someone runs out of real questions they will ask 'What is a booking'.

The Subject of the sentence is also rarely useful. Sometimes who is doing an action changes the answer but usually there is a set scheme to buy, book, cancel etc and who is doing it doesn't matter.

The Object of the sentence is more often useful. Frequently an intention is a combination of the verb and what it is being done to. Whichever one isn't the Topic is usually the intent. Booking might be a topic and various things you do with a booking would be intents.

In summary go through each topic. If there are verbs shared across questions they might go together in an intent. But you have to use the domain experts knowledge of what questions have the same intention this step cannot be automated.

Tuesday, 3 April 2018

Clustering Questions into Topics

IBM Watson used to claim it took 15 minutes to match up a question with an intent. The technique described here halves that time. Context switching is mentally draining and wastes a lot of time. Concentrating on one part of a job until it is done is much more efficient than switching between tasks.

In a similar way once we have our questions collected the next task is to divide them into topics. Then these topics will be looked at individually.

A topic is a category of types of questions people will ask your chatbot.

In an Airline these might be Checkin, Booking, Airmiles

In an Insurance company Renewal, Claim, Coverage

Before looking at the questions try think of 5 topics that might occur in customer questions to your business.

How many topics?

Roughly 20. You might have ten or 30. A rule of thumb used in K Nearest Neighbour classification is if you have N documents you expect to have Sqrt(N) clusters. This works out as 44 for 2000 clusters. You won't have 2000 questions at this stage more likely under 1000.

Can you automate discovering topics

Yes you can using a KNN algorithm with the number of clusters given above. No you really should not. You learn a hell of a lot clustering 500 questions. You will have to read all these questions eventually anyway so you might as well learn this stuff now.

Process of Marking up topics

Say you have 500 questions in a spreadsheet. What we are trying to do here is mark up a new column 'Topic' that puts each of these questions in a topic.

Go through your 500 questions. Looking for the 5 topics you listed in question above. You may find that actually what you thought was one cluster is two. Or that a topic you expected is missing If you are looking for the clothesReturn topic I would search for the key words 'return' and 'bring back'. I would look for the obvious words in each of the topics I expect.

Once I had marked up the obvious keywords from my list of 500 that were clothesReturn if I found a new question in that topic I would look for the word it had that showed me it was that topic but was not in my original search list

Can I exchange a jumper I bought yesterday for a new one

I would then search for other uses of 'exchange'. It is a word likely to be used in clothesReturn but one I missed earlier.

If you know the domain roughly half of the questions will be classified by your obvious keywords.

I would read through the remaining questions with my 5 expected topics in my head. If I see something that is obviously a new topic I add that to the topic list.

Feel free with marking 5-10% of questions with unknown. these might make more sense when you have more questions or might be part of the long tail, out of scope or off topic that your chatbot will not handle.

What Next

Once you have a spreadsheet with a column marked up with the Topic of each question the next step is to find the intent of each question. But now you are reviewing a series of questions in one topic which makes it much easier to concentrate and work in a batch mode.

I will describe this step of marking up intents in a later blogpost