从自适应文本块捕获用户输入后,是否有任何可能的方法可以应用luis?

时间:2019-02-18 13:24:59

标签: asp.net-web-api botframework luis

在机器人内部,我们有一个自适应卡,用户可以选择是或否。在选择是时,提示用户输入关键字。用户在自适应卡中的文本块中输入输入后,必须捕获输入并将其作为输入参数发送到Web api。但是,在给出输入后,由于可能存在输入文本的同义词,因此我们将不得不应用luis。在下面的代码中,关键字variable指的是用户给定的输入文本,必须对其应用LUIS。

    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;
    }

2 个答案:

答案 0 :(得分:1)

这实际上只是一个api调用,应该对其进行配置。

首先,您应该将Luis配置添加到.bot文件中。

{
"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"

}

接下来,我们在BotService中初始化BotServices.cs类的新实例,该实例从您的.bot文件中获取上述信息。外部服务使用BotConfiguration类进行配置。

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>();
}

接下来,使用Startup.cs方法中的以下代码在ConfigureServices文件中将LUIS应用注册为单例。

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

.bot类中插入Bot.cs文件中配置的服务:

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....");
        }
    }
}

adaptivecard的输入可以作为adaptivecard捕获在Action.Submit的动作数组中,如下所示:

"type": "Action.Submit"

现在,您可以真正进行Luis api调用了。可以在班级中任何需要的地方进行此调用,这会注入Luis服务:

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

此代码段摘自MS Docs here

答案 1 :(得分:1)

@Taher的答案将帮助您集成LUIS。这将帮助您将其与自适应卡一起使用。

自适应卡发送的“提交”结果与常规用户文本略有不同。当用户键入聊天并发送普通消息时,它最终以Context.Activity.Text结尾。当用户填写自适应卡上的输入时,它会以Context.Activity.Value结尾,该对象是键名是卡中的id,而值是自适应卡中的字段值卡。

例如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"
}

..创建一张看起来像这样的卡片:

Test Adaptive Card

如果用户在文本框中输入“ Testing Testing 123”,然后单击Submit,则Context.Activity将类似于:

{ 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' }

可以在Context.Activity.Value.userText中看到用户提交的内容。

请注意,自适应卡的提交是作为postBack发送的(在Context.Activity.ChannelData中可见),这意味着提交数据不会在对话的一部分中显示在聊天窗口中,而是保留在自适应卡上

将自适应卡与Waterfall Dialogs 一起使用

您的问题并不完全与此相关,但是由于您可能最终尝试这样做,因此我认为将其包含在答案中可能很重要。

从本质上讲,自适应卡不能像提示一样工作。带有提示,提示将显示并等待用户输入,然后继续。但是对于自适应卡(即使其中包含一个输入框和一个提交按钮),自适应卡中也没有代码,该代码将导致瀑布对话框在继续对话框之前先等待用户输入。

因此,如果您使用的是接受用户输入的自适应卡,则通常要处理用户在“瀑布对话框”上下文之外提交的所有内容。

话虽如此,如果您想在瀑布对话框中使用自适应卡,则有一种解决方法。基本上,您:

  1. 显示自适应卡
  2. 显示文字提示
  3. 将用户的自适应卡输入转换为文本提示输入

在“瀑布对话框”类中(步骤1和2):

    private async Task<DialogTurnResult> DisplayCardAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        var adaptiveCardJson = File.ReadAllText(Path.Combine(".", "AdaptiveCard.json"));
        var cardAttachment = new Attachment()
        {
            ContentType = "application/vnd.microsoft.card.adaptive",
            Content = JsonConvert.DeserializeObject(adaptiveCardJson),
        };
        var cardReply = stepContext.Context.Activity.CreateReply();
        cardReply.Attachments = new List<Attachment>() { cardAttachment };

        // Display the Adaptive Card
        await stepContext.Context.SendActivityAsync(cardReply);

        var opts = new PromptOptions
        {
            Prompt = new Activity
            {
                Type = ActivityTypes.Message,
                Text = "waiting for user input...",
            }
        };

        // Display a Text Prompt and wait for input
        return await stepContext.PromptAsync(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();
    }

在您的主要机器人课程(<your-bot>.cs)中(步骤3):

        if (activity.Type == ActivityTypes.Message)
        {
            // Ensure that message is a postBack (like a submission from Adaptive Cards
            var channelData = JObject.Parse(dc.Context.Activity.ChannelData.ToString());
            if (channelData != null && channelData.ContainsKey("postback"))
            {
                var postbackActivity = dc.Context.Activity;
                // Convert the user's Adaptive Card input into the input of a Text Prompt
                // Must be sent as a string
                postbackActivity.Text = postbackActivity.Value.ToString();
                await dc.Context.SendActivityAsync(postbackActivity);
            }
        }

使用它来回答您的问题

您可以在HandleResponseAsync()中将其与LUIS集成(如果您不太在乎确切的用户输入是否以Step.Result结尾,也可以在{{1 }}块(如果要在将用户输入发送到对话框的下一步之前更改用户输入。