从自适应文本块捕获用户输入后,是否有任何可能的方法来应用 luis?
Is there any possible way to apply luis after capturing user input from Adaptive text block?
在机器人中,我们有一张自适应卡片,用户可以在其中选择 select 是或否。在 selecting YES 上,提示用户输入关键字。用户在自适应卡的文本块中输入后,必须捕获输入并将其作为输入参数发送到 Web api。但是,在给出输入后,我们将不得不应用 luis,因为输入文本可能具有同义词。在下面的代码中,关键字变量指的是用户给出的输入文本,必须对其应用 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;
}
真的只是一个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"
}
接下来,我们在 BotServices.cs
中初始化 BotService
class 的新实例,它从您的 .bot 文件中获取上述信息。使用 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>();
}
接下来,在 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
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....");
}
}
}
adaptivecard
的输入可以作为 Action.Submit
在 adaptivecard
的动作数组中捕获,如下所示:
"type": "Action.Submit"
现在您可以拨打 Luis api 电话,这正是您真正需要的。这个调用可以在你的 class 中的任何地方完成,它会注入 Luis 服务:
var recognizerResult = await _services.LuisServices[LuisKey].RecognizeAsync(turnContext, cancellationToken);
此代码片段来自 MS 文档 here
@Taher 的回答将帮助您集成 LUIS。这将帮助您将其与自适应卡片一起使用。
Adaptive Cards 发送的提交结果与常规用户文本略有不同。当用户输入聊天内容并发送一条普通消息时,它会以 Context.Activity.Text
结尾。当用户在 Adaptive Card 上填写输入时,它以 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"
}
.. 创建一张如下所示的卡片:
如果用户在文本框中输入 "Testing Testing 123" 并点击提交,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
。
请注意,自适应卡片提交作为回发发送,这意味着提交数据不会作为对话的一部分出现在聊天中 window——它保留在自适应卡片上。
使用自适应卡片 Waterfall Dialogs
你的问题与此不太相关,但由于你最终可能会尝试这样做,我认为将其包含在我的回答中可能很重要。
自适应卡片本身不像提示那样工作。有了提示,提示将显示并等待用户输入,然后再继续。但是对于自适应卡片(即使它包含一个输入框和一个提交按钮),自适应卡片中没有代码会导致瀑布对话框在继续对话框之前等待用户输入。
因此,如果您使用的是接受用户输入的自适应卡片,您通常希望处理用户在 Waterfall Dialog 上下文之外提交的任何内容。
也就是说,如果您想将自适应卡片用作瀑布对话框的一部分,则有一个解决方法。基本上,你:
- 显示自适应卡片
- 显示文本提示
- 将用户的自适应卡片输入转换为文本提示的输入
在瀑布对话框中 class(步骤 1 和 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();
}
在您的主机器人中 class (<your-bot>.cs
)(第 3 步):
var activity = turnContext.Activity;
if (string.IsNullOrWhiteSpace(activity.Text) && activity.Value != null)
{
activity.Text = JsonConvert.SerializeObject(activity.Value);
}
用这个来回答你的问题
您可以在 HandleResponseAsync()
中处理将此与 LUIS 集成(如果您真的不关心确切的用户输入最终在 Step.Result
中,或者您可以在 if (channelData.ContainsKey("postback"))
块(如果您想在将用户输入发送到对话框的下一步之前更改用户输入。
在机器人中,我们有一张自适应卡片,用户可以在其中选择 select 是或否。在 selecting YES 上,提示用户输入关键字。用户在自适应卡的文本块中输入后,必须捕获输入并将其作为输入参数发送到 Web api。但是,在给出输入后,我们将不得不应用 luis,因为输入文本可能具有同义词。在下面的代码中,关键字变量指的是用户给出的输入文本,必须对其应用 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;
}
真的只是一个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"
}
接下来,我们在 BotServices.cs
中初始化 BotService
class 的新实例,它从您的 .bot 文件中获取上述信息。使用 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>();
}
接下来,在 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
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....");
}
}
}
adaptivecard
的输入可以作为 Action.Submit
在 adaptivecard
的动作数组中捕获,如下所示:
"type": "Action.Submit"
现在您可以拨打 Luis api 电话,这正是您真正需要的。这个调用可以在你的 class 中的任何地方完成,它会注入 Luis 服务:
var recognizerResult = await _services.LuisServices[LuisKey].RecognizeAsync(turnContext, cancellationToken);
此代码片段来自 MS 文档 here
@Taher 的回答将帮助您集成 LUIS。这将帮助您将其与自适应卡片一起使用。
Adaptive Cards 发送的提交结果与常规用户文本略有不同。当用户输入聊天内容并发送一条普通消息时,它会以 Context.Activity.Text
结尾。当用户在 Adaptive Card 上填写输入时,它以 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"
}
.. 创建一张如下所示的卡片:
如果用户在文本框中输入 "Testing Testing 123" 并点击提交,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
。
请注意,自适应卡片提交作为回发发送,这意味着提交数据不会作为对话的一部分出现在聊天中 window——它保留在自适应卡片上。
使用自适应卡片 Waterfall Dialogs
你的问题与此不太相关,但由于你最终可能会尝试这样做,我认为将其包含在我的回答中可能很重要。
自适应卡片本身不像提示那样工作。有了提示,提示将显示并等待用户输入,然后再继续。但是对于自适应卡片(即使它包含一个输入框和一个提交按钮),自适应卡片中没有代码会导致瀑布对话框在继续对话框之前等待用户输入。
因此,如果您使用的是接受用户输入的自适应卡片,您通常希望处理用户在 Waterfall Dialog 上下文之外提交的任何内容。
也就是说,如果您想将自适应卡片用作瀑布对话框的一部分,则有一个解决方法。基本上,你:
- 显示自适应卡片
- 显示文本提示
- 将用户的自适应卡片输入转换为文本提示的输入
在瀑布对话框中 class(步骤 1 和 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();
}
在您的主机器人中 class (<your-bot>.cs
)(第 3 步):
var activity = turnContext.Activity;
if (string.IsNullOrWhiteSpace(activity.Text) && activity.Value != null)
{
activity.Text = JsonConvert.SerializeObject(activity.Value);
}
用这个来回答你的问题
您可以在 HandleResponseAsync()
中处理将此与 LUIS 集成(如果您真的不关心确切的用户输入最终在 Step.Result
中,或者您可以在 if (channelData.ContainsKey("postback"))
块(如果您想在将用户输入发送到对话框的下一步之前更改用户输入。