6/1/2016 Webmaster

Create An Intelligent Web Application Conversation Using LUIS And Dialogs


image

You can create web applications that can carry on an intelligent back and forth conversation using the Microsoft Language Understanding Intelligent Service (LUIS).

image

This article builds on the application covered in the article: Creating Intelligent Web Applications With LUIS.

To become familiar with Microsoft LUIS, you will want to read that article.

With that application, if the LUIS service recognized the Intent and the values for all required Entities entered by the end-user, all was well. However, if all the values for the required Entities was not recognized (or they were not supplied) the end-user was simply stuck.

With the LUIS application covered in this article, the end-user will be prompted with a series of questions to supply any missing data in a natural conversational format.

Walk-Thru

image

When we run the application and include the values for all the required Entities (First Name, Last Name, Class, and Period), LUIS is able to parse them and show them.

image

However, when LUIS detects that a value for a Entity is missing, it will ask a question (using a dialog).

image

The end-user needs only to answer the question[s] (there can be more than one question, delivered one question at a time), and LUIS will use the response to complete its extraction of Entity values.

Set-Up LUIS

The first step is to log into the LUIS interface at:

https://www.luis.ai/

image

… and edit the Enroll Student application from: Creating Intelligent Web Applications With LUIS.

image

Note: At the time of the writing of this article, the features described are in preview.

If you do not see the features described, switch to the preview mode by clicking Go to Preview.

image

Click on the Enroll Intent.

image

When the Intent opens, click Add Action.

image

Click Add Parameter.

image

Add the following parameters:

Name: First Name

Type: Student Name::First Name

Prompt: What is the First Name?

--

Name: Last Name

Type: Student Name::Last Name

Prompt: What is the Last Name?

--

Name: Period

Type: Period

Prompt: What is the period?

--

Name: Class

Type: Class

Prompt: What is the class?

 

Make them all required.

When you are done, click the Save button.

image

Click the Publish button.

image

Click the Update published application button to update the application.

Note the URL (if you are in preview it is different than the normal URL), the App Id and the Subscription Key.

image

Open the MVC C# application (from Creating Intelligent Web Applications With LUIS) in Visual Studio, and open the Web.config file.

Update the settings with the new values.

image

The Creating Intelligent Web Applications With LUIS article describes the process of creating the C# classes from the JSON output using http://jsonutils.com/ (or Json Class Generator).

When we do that for this version of the LUIS application, we end up with the following result that you will use for the LUIS.cs file:

 

namespace JSONUtils
{
    public class Value
    {
        public string entity { get; set; }
        public string type { get; set; }
    }
    public class Parameter
    {
        public string name { get; set; }
        public bool required { get; set; }
        public IList value { get; set; }
    }
    public class Action
    {
        public bool triggered { get; set; }
        public string name { get; set; }
        public IList parameters { get; set; }
    }
    public class TopScoringIntent
    {
        public string intent { get; set; }
        public double score { get; set; }
        public IList actions { get; set; }
    }
    public class Entity
    {
        public string entity { get; set; }
        public string type { get; set; }
        public int startIndex { get; set; }
        public int endIndex { get; set; }
        public double score { get; set; }
    }
    public class Dialog
    {
        public string prompt { get; set; }
        public string parameterName { get; set; }
        public string contextId { get; set; }
        public string status { get; set; }
    }
    public class LUIS
    {
        public string query { get; set; }
        public TopScoringIntent topScoringIntent { get; set; }
        public IList entities { get; set; }
        public Dialog dialog { get; set; }
    }
    public class Query
    {
        public string Question { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Class { get; set; }
        public string Period { get; set; }
    }
}

(Note: The Query class is a custom class that did not come from the JSON Class generator and it is used to display the results)

The Dialog Property

The primary difference between this example and the previous one is the use of the dialog property.

When LUIS has a question, it places it in the dialog property along with a ContextId that is used to track the exchange.

In the code, we save the ContextId in TempData:

 

        if (objLUISResult.dialog.prompt != null)
        {
            // If there is a question ask it
            Return.Question = objLUISResult.dialog.prompt;
            // Set the ContextID
            TempData["contextId"] = objLUISResult.dialog.contextId;
        }

 

After the question is answered, we retrieve the ContextId from TempData.

This ContextId is passed to LUIS along with the reply to the question:

 

            // Get the ContextId that tracks conversations
            string strContextId = "";
            if (TempData.ContainsKey("contextId"))
            {
                // If we have a ContextId saved in TempData
                // retrieve it
                strContextId = TempData["contextId"].ToString();
            }
            // Query the LUIS service, passing the query the user typed in
            // and any ContextId if one exists
            LUIS objLUISResult = await QueryLUIS(searchString, strContextId);

 

We append the ContextId to the query sent to LUIS:

 

        // Send Query to LUIS and get response
        string RequestURI = String.Format("{0}?id={1}&subscription-key={2}&q={3}&contextId={4}",
            LUIS_Url, LUIS_Id, LUIS_Subscription_Key, LUISQuery, contextId);
        System.Net.Http.HttpResponseMessage msg = await client.GetAsync(RequestURI);

 

Complete the Application

image

Replace all the contents of the /Home/Index.cshtml file with the following markup:

@model JSONUtils.Query
@{
    ViewBag.Title = "Index";
}
<br />
<div class="form-horizontal">
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    @using (Html.BeginForm())
    {
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                @Html.TextBox("SearchString", "",
               new { style = "width: 500px;" }) <input type="submit" value="Search" />
            div>
        div>
    }
    <div class="form-group">
        @Html.LabelFor(model => model.Question,
       htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DisplayFor(model => model.Question,
           new { htmlAttributes = new { @class = "form-control" } })
        div>
    div>
    <div class="form-group">
        @Html.LabelFor(model => model.FirstName,
       htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DisplayFor(model => model.FirstName,
           new { htmlAttributes = new { @class = "form-control" } })
        div>
    div>
    <div class="form-group">
        @Html.LabelFor(model => model.LastName,
       htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DisplayFor(model => model.LastName,
           new { htmlAttributes = new { @class = "form-control" } })
        div>
    div>
    <div class="form-group">
        @Html.LabelFor(model => model.Class,
       htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DisplayFor(model => model.Class,
           new { htmlAttributes = new { @class = "form-control" } })
        div>
    div>
    <div class="form-group">
        @Html.LabelFor(model => model.Period,
       htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DisplayFor(model => model.Period,
           new { htmlAttributes = new { @class = "form-control" } })
        div>
    div>
div>

image

Replace all the contents of the /Controllers/HomeControler.cs file with the following markup:

using JSONUtils;
using System;
using Newtonsoft.Json;
using System.Threading.Tasks;
using System.Web.Mvc;
using System.Web.Configuration;
namespace LUISEnrollStudent.Controllers
{
    public class HomeController : Controller
    {
        #region public async Task Index(string searchString)
        public async Task Index(string searchString)
        {
            Query Return = new Query();
            try
            {
                if (searchString != null)
                {
                    // Get the ContextId that tracks conversations
                    string strContextId = "";
                    if (TempData.ContainsKey("contextId"))
                    {
                        // If we have a ContextId saved in TempData
                        // retrieve it
                        strContextId = TempData["contextId"].ToString();
                    }
                    // Query the LUIS service, passing the query the user typed in
                    // and any ContextId if one exists
                    LUIS objLUISResult = await QueryLUIS(searchString, strContextId);
                    // First check to see if we have a dialog prompt
                    // These means there are questions being asked
                    // All question must be handled first
                    if (objLUISResult.dialog.prompt != null)
                    {
                        // If there is a question ask it
                        Return.Question = objLUISResult.dialog.prompt;
                        // Set the ContextID
                        TempData["contextId"] = objLUISResult.dialog.contextId;
                    }
                    else
                    {
                        // We dio not have any dialog prompts
                        // Simplly show the results
                        // First, clear any contextId
                        TempData["contextId"] = "";
                        // Loop through the results stored in the 
                        // topScoringIntent node of the JSON response
                        // Loop through the actions
                        foreach (var item in objLUISResult.topScoringIntent.actions)
                        {
                            // Loop through the parameters
                            foreach (var parameter in item.parameters)
                            {
                                // Set the values in the "Return" model based 
                                // on the values from the parameters
                                if (parameter.value[0].type == "Student Name::First Name")
                                {
                                    Return.FirstName = parameter.value[0].entity;
                                }
                                if (parameter.value[0].type == "Student Name::Last Name")
                                {
                                    Return.LastName = parameter.value[0].entity;
                                }
                                if (parameter.value[0].type == "Class")
                                {
                                    Return.Class = parameter.value[0].entity;
                                }
                                if (parameter.value[0].type == "Period")
                                {
                                    Return.Period = parameter.value[0].entity;
                                }
                            }
                        }
                    }
                }
                return View(Return);
            }
            catch (Exception ex)
            {
                ModelState.AddModelError(string.Empty, "Error: " + ex);
                return View(Return);
            }
        }
        #endregion
        // Utility
        #region private static async Task QueryLUIS(string Query, string contextId)
        private static async Task QueryLUIS(string Query, string contextId)
        {
            // Create a new LUIS class
            LUIS LUISResult = new LUIS();
            using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient())
            {
                // Get key values from the web.config
                string LUIS_Url =
                    WebConfigurationManager.AppSettings["LUIS_Url"];
                string LUIS_Id =
                    WebConfigurationManager.AppSettings["LUIS_Id"];
                string LUIS_Subscription_Key =
                    WebConfigurationManager.AppSettings["LUIS_Subscription_Key"];
                // Get the text of the query entered by the user
                var LUISQuery = Uri.EscapeDataString(Query);
                // Send Query to LUIS and get response
                string RequestURI = String.Format("{0}?id={1}&subscription-key={2}&q={3}&contextId={4}",
                    LUIS_Url, LUIS_Id, LUIS_Subscription_Key, LUISQuery, contextId);
                System.Net.Http.HttpResponseMessage msg = await client.GetAsync(RequestURI);
                if (msg.IsSuccessStatusCode)
                {
                    var JsonDataResponse = await msg.Content.ReadAsStringAsync();
                    LUISResult = JsonConvert.DeserializeObject(JsonDataResponse);
                }
            }
            return LUISResult;
        }
        #endregion
    }
}

 


Links

Introduction to LUIS (Video)

Language Understanding Intelligent Service (LUIS)

LUIS: Language Understanding Intelligent Service (beta)

Microsoft Cognitive Services

LUIS Forum

Cognitive User Voice


Download

You can download the code from the Download page

An error has occurred. This application may no longer respond until reloaded. Reload 🗙