Botframework v4:confirmPrompt 中的字符串 "No" 在 luis.ai 中被识别为取消
Botframework v4: string "No" in confirmPrompt is being recognized as cancel in luis.ai
你好,当我用确认提示或是或否提示提示用户时。 Luis 将 "no" 检测为取消意图,这会取消我的整个对话。然后我从取消意图中删除了 "no" 但现在“路易斯没有检测到问候意图。问候意图中甚至没有 "no" 词。我尽可能不想禁用 luis,因为用户可以随时取消。我该如何解决这个问题?谢谢!
这是代码。
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
var dc = await _dialogs.CreateContextAsync(turnContext, cancellationToken);
var activity = turnContext.Activity;
var userstate = await _basicAccessors.BasicUserStateAccessor.GetAsync(turnContext, () => new BasicUserState(), cancellationToken);
var state = await _basicAccessors.BasicStateAccessor.GetAsync(turnContext, () => new BasicState(), cancellationToken);
if (turnContext.Activity.Type == ActivityTypes.Message)
{
turnContext.TurnState.Add("BasicAccessors", _basicAccessors);
string text = string.IsNullOrEmpty(turnContext.Activity.Text) ? string.Empty : turnContext.Activity.Text.ToLower();
var luisResults = await _services.LuisServices[LuisConfiguration].RecognizeAsync(dc.Context, cancellationToken);
var topScoringIntent = luisResults?.GetTopScoringIntent();
var topIntent = topScoringIntent.Value.intent;
string userName = string.Empty;
if (activity.From.Name != null)
{
userName = activity.From.Name;
}
userstate.Name = userName;
await _basicAccessors.BasicUserStateAccessor.SetAsync(turnContext, userstate);
await _basicAccessors.BasicStateAccessor.SetAsync(turnContext, state);
var interrupted = await IsTurnInterruptedAsync(dc, topIntent);
if (interrupted)
{
// Bypass the dialog.
// Save state before the next turn.
await _basicAccessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
await _basicAccessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken);
return;
}
// Continue the current dialog
var dialogResult = await dc.ContinueDialogAsync();
// if no one has responded,
if (!dc.Context.Responded)
{
// examine results from active dialog
switch (dialogResult.Status)
{
case DialogTurnStatus.Empty:
switch (topIntent)
{
case GreetingIntent:
await dc.BeginDialogAsync(MainDialogId);
break;
case "loan calculator":
case "loan calc":
await dc.BeginDialogAsync(MainDialogId);
break;
case NoneIntent:
default:
await dc.Context.SendActivityAsync("I didn't understand what you just said to me.");
break;
}
break;
case DialogTurnStatus.Waiting:
// The active dialog is waiting for a response from the user, so do nothing.
break;
case DialogTurnStatus.Complete:
await dc.EndDialogAsync();
break;
default:
await dc.CancelAllDialogsAsync();
break;
}
}
}
else if (activity.Type == ActivityTypes.ConversationUpdate)
{
if (activity.MembersAdded != null)
{
// Iterate over all new members added to the conversation.
foreach (var member in activity.MembersAdded)
{
// Greet anyone that was not the target (recipient) of this message.
// To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
if (member.Id != activity.Recipient.Id)
{
await SendWelcomeMessageAsync(turnContext, cancellationToken);
}
}
}
}
await _basicAccessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
await _basicAccessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken);
}
private async Task<bool> IsTurnInterruptedAsync(DialogContext dc, string topIntent)
{
// See if there are any conversation interrupts we need to handle.
if (topIntent.Equals(CancelIntent))
{
if (dc.ActiveDialog != null)
{
await dc.CancelAllDialogsAsync();
await dc.Context.SendActivityAsync("Ok. I've canceled our last activity.");
}
else
{
await dc.Context.SendActivityAsync("I don't have anything to cancel.");
}
return true; // Handled the interrupt.
}
if (topIntent.Equals(HelpIntent))
{
await dc.Context.SendActivityAsync("Let me try to provide some help.");
await dc.Context.SendActivityAsync("I understand greetings, being asked for help, or being asked to cancel what I am doing.");
if (dc.ActiveDialog != null)
{
await dc.RepromptDialogAsync();
}
return true; // Handled the interrupt.
}
这似乎是一个设计难题。我的建议是重新安排您的机器人的优先级,以便在您的对话未得到正确响应时 仅 调用 LUIS。这可以通过将 IsTurnInterruptedAsync
从 OnTurnAsync
移动到验证器方法来实现。验证器方法可以作为参数传递给提示符的构造函数:
_dialogs.Add(new ConfirmPrompt(CONFIRMPROMPT, ValidateAsync));
您需要确保可以从任何地方访问您的首要意图,以防您的提示处于不同 class。您可以通过将最高意图添加到您的回合状态来做到这一点,该状态仅持续一个回合的范围。在 OnTurnAsync
:
中包含此代码
var topScoringIntent = luisResults?.GetTopScoringIntent();
var topIntent = topScoringIntent.Value.intent;
// Include this:
turnContext.TurnState.Add("topIntent", topIntent);
您的 ValidateAsync
方法可能如下所示:
private async Task<bool> ValidateAsync(PromptValidatorContext<bool> promptContext, CancellationToken cancellationToken)
{
if (promptContext.Recognized.Succeeded)
{
return true;
}
await IsTurnInterruptedAsync(promptContext.Context);
return false;
}
由于 IsTurnInterruptedAsync
现在可能处于不同的 class,它需要通过您的转向状态访问最高意图。如果 _dialogs
超出范围,您也可以将其添加到您的转向状态,或者将其设为 public/static 属性.
private async Task<bool> IsTurnInterruptedAsync(ITurnContext turnContext)
{
var dc = await _dialogs.CreateContextAsync(turnContext);
var topIntent = turnContext.TurnState.Get<string>("topIntent");
// See if there are any conversation interrupts we need to handle.
if (topIntent.Equals(CancelIntent))
{
// . . .
这样,只有当话语对对话无效时才会发生中断。
即使话语对对话有效,您也可能希望允许中断。当然,对于文本提示,任何内容都是有效的,但是您可能仍然不想让用户键入 "What do I do?" 作为他们的名字。如果您想针对某些类型的对话框先将话语发送到 LUIS,然后针对其他类型的对话框先将话语发送到对话框,您可以像这样检查位于堆栈顶部的对话框的类型:
var activeDialog = _dialogs.Find(dc.ActiveDialog.Id);
if (activeDialog is ConfirmPrompt || activeDialog is ChoicePrompt)
你好,当我用确认提示或是或否提示提示用户时。 Luis 将 "no" 检测为取消意图,这会取消我的整个对话。然后我从取消意图中删除了 "no" 但现在“路易斯没有检测到问候意图。问候意图中甚至没有 "no" 词。我尽可能不想禁用 luis,因为用户可以随时取消。我该如何解决这个问题?谢谢!
这是代码。
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
var dc = await _dialogs.CreateContextAsync(turnContext, cancellationToken);
var activity = turnContext.Activity;
var userstate = await _basicAccessors.BasicUserStateAccessor.GetAsync(turnContext, () => new BasicUserState(), cancellationToken);
var state = await _basicAccessors.BasicStateAccessor.GetAsync(turnContext, () => new BasicState(), cancellationToken);
if (turnContext.Activity.Type == ActivityTypes.Message)
{
turnContext.TurnState.Add("BasicAccessors", _basicAccessors);
string text = string.IsNullOrEmpty(turnContext.Activity.Text) ? string.Empty : turnContext.Activity.Text.ToLower();
var luisResults = await _services.LuisServices[LuisConfiguration].RecognizeAsync(dc.Context, cancellationToken);
var topScoringIntent = luisResults?.GetTopScoringIntent();
var topIntent = topScoringIntent.Value.intent;
string userName = string.Empty;
if (activity.From.Name != null)
{
userName = activity.From.Name;
}
userstate.Name = userName;
await _basicAccessors.BasicUserStateAccessor.SetAsync(turnContext, userstate);
await _basicAccessors.BasicStateAccessor.SetAsync(turnContext, state);
var interrupted = await IsTurnInterruptedAsync(dc, topIntent);
if (interrupted)
{
// Bypass the dialog.
// Save state before the next turn.
await _basicAccessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
await _basicAccessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken);
return;
}
// Continue the current dialog
var dialogResult = await dc.ContinueDialogAsync();
// if no one has responded,
if (!dc.Context.Responded)
{
// examine results from active dialog
switch (dialogResult.Status)
{
case DialogTurnStatus.Empty:
switch (topIntent)
{
case GreetingIntent:
await dc.BeginDialogAsync(MainDialogId);
break;
case "loan calculator":
case "loan calc":
await dc.BeginDialogAsync(MainDialogId);
break;
case NoneIntent:
default:
await dc.Context.SendActivityAsync("I didn't understand what you just said to me.");
break;
}
break;
case DialogTurnStatus.Waiting:
// The active dialog is waiting for a response from the user, so do nothing.
break;
case DialogTurnStatus.Complete:
await dc.EndDialogAsync();
break;
default:
await dc.CancelAllDialogsAsync();
break;
}
}
}
else if (activity.Type == ActivityTypes.ConversationUpdate)
{
if (activity.MembersAdded != null)
{
// Iterate over all new members added to the conversation.
foreach (var member in activity.MembersAdded)
{
// Greet anyone that was not the target (recipient) of this message.
// To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
if (member.Id != activity.Recipient.Id)
{
await SendWelcomeMessageAsync(turnContext, cancellationToken);
}
}
}
}
await _basicAccessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
await _basicAccessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken);
}
private async Task<bool> IsTurnInterruptedAsync(DialogContext dc, string topIntent)
{
// See if there are any conversation interrupts we need to handle.
if (topIntent.Equals(CancelIntent))
{
if (dc.ActiveDialog != null)
{
await dc.CancelAllDialogsAsync();
await dc.Context.SendActivityAsync("Ok. I've canceled our last activity.");
}
else
{
await dc.Context.SendActivityAsync("I don't have anything to cancel.");
}
return true; // Handled the interrupt.
}
if (topIntent.Equals(HelpIntent))
{
await dc.Context.SendActivityAsync("Let me try to provide some help.");
await dc.Context.SendActivityAsync("I understand greetings, being asked for help, or being asked to cancel what I am doing.");
if (dc.ActiveDialog != null)
{
await dc.RepromptDialogAsync();
}
return true; // Handled the interrupt.
}
这似乎是一个设计难题。我的建议是重新安排您的机器人的优先级,以便在您的对话未得到正确响应时 仅 调用 LUIS。这可以通过将 IsTurnInterruptedAsync
从 OnTurnAsync
移动到验证器方法来实现。验证器方法可以作为参数传递给提示符的构造函数:
_dialogs.Add(new ConfirmPrompt(CONFIRMPROMPT, ValidateAsync));
您需要确保可以从任何地方访问您的首要意图,以防您的提示处于不同 class。您可以通过将最高意图添加到您的回合状态来做到这一点,该状态仅持续一个回合的范围。在 OnTurnAsync
:
var topScoringIntent = luisResults?.GetTopScoringIntent();
var topIntent = topScoringIntent.Value.intent;
// Include this:
turnContext.TurnState.Add("topIntent", topIntent);
您的 ValidateAsync
方法可能如下所示:
private async Task<bool> ValidateAsync(PromptValidatorContext<bool> promptContext, CancellationToken cancellationToken)
{
if (promptContext.Recognized.Succeeded)
{
return true;
}
await IsTurnInterruptedAsync(promptContext.Context);
return false;
}
由于 IsTurnInterruptedAsync
现在可能处于不同的 class,它需要通过您的转向状态访问最高意图。如果 _dialogs
超出范围,您也可以将其添加到您的转向状态,或者将其设为 public/static 属性.
private async Task<bool> IsTurnInterruptedAsync(ITurnContext turnContext)
{
var dc = await _dialogs.CreateContextAsync(turnContext);
var topIntent = turnContext.TurnState.Get<string>("topIntent");
// See if there are any conversation interrupts we need to handle.
if (topIntent.Equals(CancelIntent))
{
// . . .
这样,只有当话语对对话无效时才会发生中断。
即使话语对对话有效,您也可能希望允许中断。当然,对于文本提示,任何内容都是有效的,但是您可能仍然不想让用户键入 "What do I do?" 作为他们的名字。如果您想针对某些类型的对话框先将话语发送到 LUIS,然后针对其他类型的对话框先将话语发送到对话框,您可以像这样检查位于堆栈顶部的对话框的类型:
var activeDialog = _dialogs.Find(dc.ActiveDialog.Id);
if (activeDialog is ConfirmPrompt || activeDialog is ChoicePrompt)