Is there any possible way to apply luis after capturing user input from Adaptive text block?

[亡魂溺海] 提交于 2019-12-11 18:11:36

问题


Within a bot,we have an adaptive card where the user has a choice to select yes or no. On selecting YES, user is prompted to enter the keywords. After the user gives input in the textblock in adaptive card, the input has to be captured and sent as input parameter to web api. However, after the input is given, we will have to apply luis since there is a possibility of having synonyms for the input text. In the below code, keywords variable refers to the input text given by user and for which LUIS has to be applied.

    private async Task CustomisePPT(IDialogContext context, IAwaitable<object> result)
    {          
        try
        {                
            var replyMessage = context.MakeMessage();
            var newMessage = context.Activity.AsMessageActivity();
            var userMessage = newMessage.Value;
            var take=userMessage.ToString().Substring(userMessage.ToString().IndexOf("GetUserInputKeywords"));
            var split = take.ToString().Substring("GetUserInputKeywords".Length+2);
            string keywords = split.Trim();
            keywords = keywords.Substring(1, keywords.Length - 5);

            using (HttpClient client = new HttpClient())
            {
                // api takes the user message as a query paramater
                string RequestURI = "https://xyz" + ***keywords***;
                HttpResponseMessage responsemMsg = await client.GetAsync(RequestURI);
                // TODO: handle fail case

                if (responsemMsg.IsSuccessStatusCode)
                {
                    var apiResponse = await responsemMsg.Content.ReadAsStringAsync();
                }
            }
        }
        catch (Exception ex)
        {

        }
        //throw new NotImplementedException();
        await Task.CompletedTask;
    }

回答1:


@Taher's answer will help you integrate LUIS. This one will help you use it with Adaptive Cards.

Adaptive Cards send their Submit results a little different than regular user text. When a user types in the chat and sends a normal message, it ends up in Context.Activity.Text. When a user fills out an input on an Adaptive Card, it ends up in Context.Activity.Value, which is an object where the key names are the id in your card and the values are the field values in the adaptive card.

For example, the json:

{
    "type": "AdaptiveCard",
    "body": [
        {
            "type": "TextBlock",
            "text": "Test Adaptive Card"
        },
        {
            "type": "ColumnSet",
            "columns": [
                {
                    "type": "Column",
                    "items": [
                        {
                            "type": "TextBlock",
                            "text": "Text:"
                        }
                    ],
                    "width": 20
                },
                {
                    "type": "Column",
                    "items": [
                        {
                            "type": "Input.Text",
                            "id": "userText",
                            "placeholder": "Enter Some Text"
                        }
                    ],
                    "width": 80
                }
            ]
        }
    ],
    "actions": [
        {
            "type": "Action.Submit",
            "title": "Submit"
        }
    ],
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.0"
}

.. creates a card that looks like:

If a user enters "Testing Testing 123" in the text box and hits Submit, Context.Activity will look something like:

{ type: 'message',
  value: { userText: 'Testing Testing 123' },
  from: { id: 'xxxxxxxx-05d4-478a-9daa-9b18c79bb66b', name: 'User' },
  locale: '',
  channelData: { postback: true },
  channelId: 'emulator',
  conversation: { id: 'xxxxxxxx-182b-11e9-be61-091ac0e3a4ac|livechat' },
  id: 'xxxxxxxx-182b-11e9-ad8e-63b45e3ebfa7',
  localTimestamp: 2019-01-14T18:39:21.000Z,
  recipient: { id: '1', name: 'Bot', role: 'bot' },
  timestamp: 2019-01-14T18:39:21.773Z,
  serviceUrl: 'http://localhost:58453' }

The user submission can be seen in Context.Activity.Value.userText.

Note that adaptive card submissions are sent as a postBack, which means that the submission data doesn't appear in the chat window as part of the conversation--it stays on the Adaptive Card.

Using Adaptive Cards with Waterfall Dialogs

Your question doesn't quite relate to this, but since you may end up attempting this, I thought it might be important to include in my answer.

Natively, Adaptive Cards don't work like prompts. With a prompt, the prompt will display and wait for user input before continuing. But with Adaptive Cards (even if it contains an input box and a submit button), there is no code in an Adaptive Card that will cause a Waterfall Dialog to wait for user input before continuing the dialog.

So, if you're using an Adaptive Card that takes user input, you generally want to handle whatever the user submits outside of the context of a Waterfall Dialog.

That being said, if you want to use an Adaptive Card as part of a Waterfall Dialog, there is a workaround. Basically, you:

  1. Display the Adaptive Card
  2. Display a Text Prompt
  3. Convert the user's Adaptive Card input into the input of a Text Prompt

In your Waterfall Dialog class (steps 1 and 2):

private async Task<DialogTurnResult> DisplayCardAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // Create the Adaptive Card
    var cardPath = Path.Combine(".", "AdaptiveCard.json");
    var cardJson = File.ReadAllText(cardPath);
    var cardAttachment = new Attachment()
    {
        ContentType = "application/vnd.microsoft.card.adaptive",
        Content = JsonConvert.DeserializeObject(cardJson),
    };

    // Create the text prompt
    var opts = new PromptOptions
    {
        Prompt = new Activity
        {
            Attachments = new List<Attachment>() { cardAttachment },
            Type = ActivityTypes.Message,
            Text = "waiting for user input...", // You can comment this out if you don't want to display any text. Still works.
        }
    };

    // Display a Text Prompt and wait for input
    return await stepContext.PromptAsync(nameof(TextPrompt), opts);
}

private async Task<DialogTurnResult> HandleResponseAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // Do something with step.result
    // Adaptive Card submissions are objects, so you likely need to JObject.Parse(step.result)
    await stepContext.Context.SendActivityAsync($"INPUT: {stepContext.Result}");
    return await stepContext.NextAsync();
}

In your main bot class (<your-bot>.cs) (step 3):

var activity = turnContext.Activity;

if (string.IsNullOrWhiteSpace(activity.Text) && activity.Value != null)
{
    activity.Text = JsonConvert.SerializeObject(activity.Value);
}

Using this to answer your question

You can handle integrating this with LUIS in either the HandleResponseAsync() (if you don't really care that the exact user input ends up in Step.Result, or you can handle it in the if (channelData.ContainsKey("postback")) block (if you want to change the user input before sending it to the next step in your dialog.




回答2:


It is really just an api call, which should be configured.

First, you should add Luis configuration to your .bot file.

{
"name": "LuisBot",
"description": "",
"services": [
    {
        "type": "endpoint",
        "name": "development",
        "endpoint": "http://localhost:3978/api/messages",
        "appId": "",
        "appPassword": "",
        "id": "166"
    },
    {
        "type": "luis",
        "name": "LuisBot",
        "appId": "<luis appid>",
        "version": "0.1",
        "authoringKey": "<luis authoring key>",
        "subscriptionKey": "<luis subscription key>",
        "region": "<luis region>",
        "id": "158"
    }
],
"padlock": "",
"version": "2.0"

}

Next, we initialize a new instance of the BotService class in BotServices.cs, which grabs the above information from your .bot file. The external service is configured using the BotConfiguration class.

public class BotServices
{
    // Initializes a new instance of the BotServices class
    public BotServices(BotConfiguration botConfiguration)
    {
        foreach (var service in botConfiguration.Services)
        {
            switch (service.Type)
            {
                case ServiceTypes.Luis:
                {
                    var luis = (LuisService)service;
                    if (luis == null)
                    {
                        throw new InvalidOperationException("The LUIS service is not configured correctly in your '.bot' file.");
                    }

                    var app = new LuisApplication(luis.AppId, luis.AuthoringKey, luis.GetEndpoint());
                    var recognizer = new LuisRecognizer(app);
                    this.LuisServices.Add(luis.Name, recognizer);
                    break;
                    }
                }
            }
        }

    // Gets the set of LUIS Services used. LuisServices is represented as a dictionary.  
    public Dictionary<string, LuisRecognizer> LuisServices { get; } = new Dictionary<string, LuisRecognizer>();
}

Next, register the LUIS app as a singleton in the Startup.cs file using the following code within ConfigureServices method.

// Initialize Bot Connected Services clients.
var connectedServices = new BotServices(botConfig);
services.AddSingleton(sp => connectedServices);
services.AddSingleton(sp => botConfig);

Inject the services, which are configured in the .bot file, in your Bot.cs Class:

public class LuisBot : IBot
{
    // Services configured from the ".bot" file.
    private readonly BotServices _services;

    // Initializes a new instance of the LuisBot class.
    public LuisBot(BotServices services)
    {
        _services = services ?? throw new System.ArgumentNullException(nameof(services));
        if (!_services.LuisServices.ContainsKey(LuisKey))
        {
            throw new System.ArgumentException($"Invalid configuration....");
        }
    }
}

The Input of the adaptivecard can be captured in the actions array of the adaptivecard as an Action.Submit like this:

"type": "Action.Submit"

NOW you can make a Luis api call, which is what you really need. This call can be done wherever you want in your class, which injects the Luis service:

 var recognizerResult = await _services.LuisServices[LuisKey].RecognizeAsync(turnContext, cancellationToken);

This Code Snippet is from the MS Docs here



来源:https://stackoverflow.com/questions/54748358/is-there-any-possible-way-to-apply-luis-after-capturing-user-input-from-adaptive

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!