我正在使用 Microsoft Bot Framework(用于 .NET)、QnA Maker 和 QnAMakerDialog(https://github.com/garypretty/botframework/tree/master/QnAMakerDialog)开发一个聊天机器人。托管聊天框控件的机器人和 Web 项目部署在 Azure 中。我使用 Direct Line 作为渠道。
对话流程非常简单。用户从主分支开始。根据用户输入,对话继续使用 QnAMakerDialog 或用于反馈的自定义对话框。
问题如下:
用户从主分支开始。只要用户不键入“结束”,我就会将对话转发到 QnA 对话框并尝试回答他的问题。在某些时候,用户键入“结束”。所以,我启动了反馈对话框。用户键入反馈。现在,应该感谢他的反馈并将其送回 QnA 对话。取而代之的是,他得到的答复是在 QnA 知识数据库中没有找到好的答案。这意味着,不知何故,他发现自己在错误的分支上!机器人认为他在 QnA 分支,但实际上他应该在反馈分支......
按照相同的步骤,无法始终重现此错误。它随机发生,没有规律。甚至更多——它只发生在某些环境中。它从来没有发生在我的开发机器上,它很少发生在一个环境中,它经常发生在第三个环境中。(这两个环境的配置几乎相同,问题不会从那里出现)。此外,问题不能来自 QnAMakerDialog - 我使用自定义 QnADialog 进行了测试,它始终返回静态消息而不是来自 QnAMaker 的答案,并且问题仍然存在。
这是代码。任何想法都非常受欢迎。
[BotAuthentication]
public class MessagesController : ApiController
{
private readonly ILog log;
public MessagesController(ILog log)
{
this.log = log;
}
internal static IDialog<object> MakeRoot()
{
return Chain.From(() => new HomeDialog());
}
public virtual async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
var client = new ConnectorClient(new Uri(activity.ServiceUrl));
try
{
switch (activity.GetActivityType())
{
case ActivityTypes.Message:
var typingReply = activity.CreateReply();
typingReply.Type = ActivityTypes.Typing;
await client.Conversations.ReplyToActivityAsync(typingReply);
await Conversation.SendAsync(activity, MakeRoot);
break;
default:
HandleSystemMessage(activity);
break;
}
}
catch (Exception ex)
{
var errorReply = activity.CreateReply();
errorReply.Type = ActivityTypes.Message;
errorReply.Text ="I'm sorry, I'm having issues understanding you. Let's try again.";
await client.Conversations.ReplyToActivityAsync(errorReply);
log.Error("Issue in the bot.", ex);
}
return new HttpResponseMessage(System.Net.HttpStatusCode.Accepted);
}
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
}
else if (message.Type == ActivityTypes.Typing)
{
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}
}
[Serializable]
public class HomeDialog : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
await RedirectToQnaDialog(context);
}
private async Task RedirectToQnaDialog(IDialogContext context)
{
await context.Forward(new QnaDialog(), QnaDialogResumeAfter, context.Activity, CancellationToken.None);
}
private async Task QnaDialogResumeAfter(IDialogContext context, IAwaitable<object> result)
{
var message = await result;
PromptDialog.Text(context,
ResumeAfterQuestionTyped,
"Type your question or 'end' to end this conversation.",
"Please retry", 3);
}
private async Task ResumeAfterQuestionTyped(IDialogContext context, IAwaitable<string> inputFromUser)
{
var question = await inputFromUser;
if (question.ToLower().Equals("end"))
{
await context.PostAsync("You would really help me out by giving feedback. " +
"What subjects should we include to provide answers for your questions?");
context.Call(new FeedbackDialog(), FeedbackDialogResumeAfter);
}
else
{
await context.Forward(new QnaDialog(), QnaDialogResumeAfter, context.Activity, CancellationToken.None);
}
}
private async Task FeedbackDialogResumeAfter(IDialogContext context, IAwaitable<object> result)
{
await context.PostAsync("Thank you for your feedback. You can now continue to ask me more questions.");
context.Wait(MessageReceivedAsync);
}
[Serializable]
public class QnaDialog : QnAMakerDialog
{
public QnaDialog() : base(new QnAMakerService
(new QnAMakerAttribute(ConfigurationManager.AppSettings["QnaSubscriptionKey"],
ConfigurationManager.AppSettings["QnaKnownledgeBaseKey"],
ConfigurationManager.AppSettings["QnaNotFoundReply"],
Convert.ToDouble(ConfigurationManager.AppSettings["QnaPrecentageMatch"]), 5)))
{
}
protected override async Task RespondFromQnAMakerResultAsync(IDialogContext context, IMessageActivity message,
QnAMakerResults results)
{
if (results.Answers.Count > 0)
{
var response = results.Answers.First().Answer;
await context.PostAsync(response);
}
}
protected override async Task DefaultWaitNextMessageAsync(IDialogContext context, IMessageActivity message,
QnAMakerResults result)
{
context.Done<IMessageActivity>(null);
}
[Serializable]
public class FeedbackDialog : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
context.Done(message);
}
}
}