Bot Framework 搞乱了对话框状态
Bot Framework messes up dialog state
我目前正在使用 Microsoft 的 Bot Framework 制作聊天机器人。在我的流程中,我有一个最后的对话框,让用户知道他们正在参加比赛。还有一种针对未知输入的错误处理方法。两种方法见这里:
[Serializable]
public class ConcertCityDialog : AbstractBasicDialog<DialogResult>
{
private static FacebookService FacebookService => new FacebookService(new FacebookClient());
[LuisIntent("ConcertCity")]
public async Task ConcertCityIntent(IDialogContext context, LuisResult result)
{
var fbAccount = await FacebookService.GetAccountAsync(context.Activity.From.Id);
var selectedCityName = result.Entities.FirstOrDefault()?.Entity;
concert_city selectedCity;
using (var concertCityService = new ConcertCityService())
{
selectedCity = concertCityService.FindConcertCity(selectedCityName);
}
if (selectedCity == null)
{
await NoneIntent(context, result);
return;
}
user_interaction latestInteraction;
using (var userService = new MessengerUserService())
{
var user = userService.FindByFacebookIdIncludeInteractions(context.Activity.From.Id);
latestInteraction = user.user_interaction.MaxBy(e => e.created_at);
}
latestInteraction.preferred_city_id = selectedCity.id;
latestInteraction.gif_created = true;
using (var userInteractionService = new UserInteractionService())
{
userInteractionService.UpdateUserInteraction(latestInteraction);
}
var shareIntroReply = context.MakeMessage();
shareIntroReply.Text = "Great choice! You are now participating in the competition. If you dare then pass your message \uD83D\uDE0E";
await context.PostAsync(shareIntroReply);
var reply = await MessageUtility.MakeShareMessageCard(context, fbAccount, latestInteraction, false);
await context.PostAsync(reply);
context.Done(DialogResult.Done);
}
[LuisIntent("")]
[LuisIntent("None")]
public async Task NoneIntent(IDialogContext context, LuisResult result)
{
messenger_user user;
using (var userService = new MessengerUserService())
{
user = userService.FindByFacebookId(context.Activity.From.Id);
}
var phrase = CreateMisunderstoodPhrase(user, result.Query);
using (var misunderstoodPhraseService = new MisunderstoodPhraseService())
{
misunderstoodPhraseService.CreatePhrase(phrase);
}
List<concert_city> concertCities;
using (var concertCityService = new ConcertCityService())
{
concertCities = concertCityService.GetUpcomingConcertCities().ToList();
}
// Prompt city
var reply = context.MakeMessage();
reply.Text = "I'm not sure what you mean \uD83E\uDD14<br/>Which Grøn Koncert would you like to attend?";
reply.SuggestedActions = new SuggestedActions
{
Actions = concertCities.Select(e => MessageUtility.MakeQuickAnswer(e.name)).ToList()
};
await context.PostAsync(reply);
context.Wait(MessageReceived);
}
protected override void OnDeserializedCustom(StreamingContext context)
{
}
}
这里是 AbstractBasicDialog
实现:
[Serializable]
public abstract class AbstractBasicDialog<T> : LuisDialog<T>
{
protected AbstractBasicDialog() : base(new LuisService(new LuisModelAttribute(
ConfigurationManager.AppSettings["LuisAppId"],
ConfigurationManager.AppSettings["LuisAPIKey"],
domain: ConfigurationManager.AppSettings["LuisAPIHostName"])))
{
}
[LuisIntent("Cancel")]
public virtual async Task CancelIntent(IDialogContext context, LuisResult result)
{
var randomQuotes = new List<string>
{
"If you say so, I'll leave you alone for now",
"alright then, I'll leave you alone",
"Okay then, I won't bother you anymore"
};
await context.PostAsync(MessageUtility.RandAnswer(randomQuotes));
context.Done(DialogResult.Cancel);
}
[LuisIntent("Start")]
public virtual async Task StartIntent(IDialogContext context, LuisResult result)
{
context.Done(DialogResult.Restart);
}
[LuisIntent("CustomerSupport")]
public async Task CustomerSupportIntent(IDialogContext context, LuisResult result)
{
using (var userService = new MessengerUserService())
{
var user = userService.FindByFacebookId(context.Activity.From.Id);
if (user != null)
{
user.receiving_support = true;
userService.UpdateUser(user);
}
}
await context.PostAsync("I'll let customer service know, that you want to talk to them. They will get back to you within 24 hours.<br/>If at any time you want to return to me, and start passing a message, just type \"Stop customer support\".");
context.Call(new CustomerSupportDialog(), ResumeAfterCustomerSupport);
}
private async Task ResumeAfterCustomerSupport(IDialogContext context, IAwaitable<DialogResult> result)
{
context.Done(await result);
}
protected misunderstood_phrase CreateMisunderstoodPhrase(messenger_user user, string phrase)
{
return new misunderstood_phrase
{
phrase = phrase,
dialog = GetType().Name,
messenger_user_id = user.id
};
}
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
OnDeserializedCustom(context);
}
protected abstract void OnDeserializedCustom(StreamingContext context);
}
调用链从这个对话框开始:
[Serializable]
public class BasicLuisDialog : LuisDialog<DialogResult>
{
private static FacebookService FacebookService => new FacebookService(new FacebookClient());
public BasicLuisDialog() : base(new LuisService(new LuisModelAttribute(
ConfigurationManager.AppSettings["LuisAppId"],
ConfigurationManager.AppSettings["LuisAPIKey"],
domain: ConfigurationManager.AppSettings["LuisAPIHostName"])))
{
}
[LuisIntent("")]
[LuisIntent("None")]
public async Task NoneIntent(IDialogContext context, LuisResult result)
{
var facebookAccount = await FacebookService.GetAccountAsync(context.Activity.From.Id);
RegisterUser(facebookAccount, null, out var user);
var phrase = CreateMisunderstoodPhrase(user, result.Query);
using (var misunderstoodPhraseService = new MisunderstoodPhraseService())
{
misunderstoodPhraseService.CreatePhrase(phrase);
}
var reply = context.MakeMessage();
reply.SuggestedActions = new SuggestedActions
{
Actions = new List<CardAction>
{
new CardAction { Title = "Get started", Type = ActionTypes.ImBack, Value = "Get started" },
new CardAction { Title = "Customer support", Type = ActionTypes.ImBack, Value = "Customer support" }
}
};
var name = string.IsNullOrEmpty(facebookAccount.FirstName) ? "" : $"{facebookAccount.FirstName} ";
reply.Text = $"Hm, I'm not sure what you mean {name} \uD83E\uDD14 Here are some ways you can interact with me:";
await context.PostAsync(reply);
context.Wait(MessageReceived);
}
[LuisIntent("Greeting")]
[LuisIntent("Positive")]
[LuisIntent("Start")]
public async Task GreetingIntent(IDialogContext context, LuisResult result)
{
var rnd = new Random();
var facebookAccount = await FacebookService.GetAccountAsync(context.Activity.From.Id);
// Initial Greeting
var greetings = new List<string>
{
"Well hello there",
"Hi there"
};
if (!string.IsNullOrEmpty(facebookAccount.FirstName))
{
greetings.Add("Hi {0}");
greetings.Add("Hello {0}");
greetings.Add("Welcome {0}");
}
if (facebookAccount.Gender == "male")
greetings.Add("Hey handsome");
else if (facebookAccount.Gender == "female")
greetings.Add("Hi gorgeous");
var randIndex = rnd.Next(greetings.Count);
var greeting = string.Format(greetings[randIndex], facebookAccount.FirstName);
await context.PostAsync(greeting);
await MessageUtility.StartTyping(context, 300);
country country;
using (var countryService = new CountryService())
{
country = countryService.FindCountry(facebookAccount.Locale);
}
var userHasCountry = RegisterUser(facebookAccount, country, out var user);
// If user contry not found prompt for answer
if (!userHasCountry)
{
var countryReply = context.MakeMessage();
countryReply.Text = "You are hard to keep track of - where are you from?";
countryReply.SuggestedActions = new SuggestedActions
{
Actions = new List<CardAction>
{
MessageUtility.MakeQuickAnswer("Denmark"),
MessageUtility.MakeQuickAnswer("Norway"),
MessageUtility.MakeQuickAnswer("Sweden"),
MessageUtility.MakeQuickAnswer("Other")
}
};
await context.PostAsync(countryReply);
context.Call(new CountryDialog(), AfterCountryDialog);
}
else
{
await FunPrompt(context, country);
}
}
private async Task AfterCountryDialog(IDialogContext countryContext, IAwaitable<country> countryAwaitable)
{
var country = await countryAwaitable;
var facebookAccount = await FacebookService.GetAccountAsync(countryContext.Activity.From.Id);
using (var userService = new MessengerUserService())
{
var user = userService.FindByFacebookId(facebookAccount.Id);
user.country = country;
userService.UpdateUser(user);
}
var reply = countryContext.MakeMessage();
reply.Text = "That's cool \uD83D\uDE0E";
await countryContext.PostAsync(reply);
await MessageUtility.StartTyping(countryContext, 350);
await FunPrompt(countryContext, country);
}
private async Task FunPrompt(IDialogContext context, country country)
{
if (country?.name == "norway" && DateTime.Now < new DateTime(2018, 8, 13))
{
var reply = context.MakeMessage();
reply.Text = "Unfortunately the competition isn't open in Norway yet. You can still talk to customer support if you want to";
reply.SuggestedActions = new SuggestedActions
{
Actions = new List<CardAction>
{
MessageUtility.MakeQuickAnswer("Customer support")
}
};
await context.PostAsync(reply);
context.Wait(MessageReceived);
}
else if ((country?.name == "denmark" && DateTime.Now >= new DateTime(2018, 7, 29)) ||
(country?.name == "norway" && DateTime.Now >= new DateTime(2018, 10, 21)))
{
var reply = context.MakeMessage();
reply.Text = "The competition has ended. You can still talk to customer support if you want to";
reply.SuggestedActions = new SuggestedActions
{
Actions = new List<CardAction>
{
MessageUtility.MakeQuickAnswer("Customer support")
}
};
await context.PostAsync(reply);
context.Wait(MessageReceived);
}
else
{
await context.PostAsync("Are you up for some fun?");
context.Call(new IntroductionDialog(), ResumeAfterDialog);
}
}
[LuisIntent("CustomerSupport")]
public async Task CustomerSupportIntent(IDialogContext context, LuisResult result)
{
using (var userService = new MessengerUserService())
{
var user = userService.FindByFacebookId(context.Activity.From.Id);
if (user != null)
{
user.receiving_support = true;
userService.UpdateUser(user);
}
}
await context.PostAsync("I'll let customer support know, that you want to talk to them. They should be messaging you shortly.<br/>You can end your conversation with customer support at any time by typing \"Stop customer support\".");
context.Call(new CustomerSupportDialog(), ResumeAfterDialog);
}
private async Task ResumeAfterDialog(IDialogContext context, IAwaitable<DialogResult> result)
{
var resultState = await result;
if (resultState == DialogResult.Restart)
await GreetingIntent(context, null);
else if (resultState == DialogResult.CustomerSupport)
await ResumeAfterCustomerSupport(context);
else if (resultState == DialogResult.Done || resultState == DialogResult.Cancel)
context.Done(resultState);
else
context.Wait(MessageReceived);
}
private async Task ResumeAfterCustomerSupport(IDialogContext context)
{
using (var userService = new MessengerUserService())
{
var user = userService.FindByFacebookId(context.Activity.From.Id);
if (user != null)
{
user.receiving_support = false;
userService.UpdateUser(user);
}
}
await context.PostAsync("I hope you got the help you needed. Would you like to pass a message to a friend?");
context.Call(new IntroductionDialog(), ResumeAfterDialog);
}
private bool RegisterUser(FacebookAccount fbAccount, country country, out messenger_user user)
{
if (string.IsNullOrEmpty(fbAccount?.Id))
{
user = null;
return false;
}
using (var userService = new MessengerUserService())
{
user = userService.FindByFacebookId(fbAccount.Id);
if (user != null)
return user.country != null;
user = new messenger_user
{
id = fbAccount.Id,
country = country
};
userService.CreateUser(user);
return user.country != null;
}
}
protected misunderstood_phrase CreateMisunderstoodPhrase(messenger_user user, string phrase)
{
return new misunderstood_phrase
{
phrase = phrase,
dialog = GetType().Name,
messenger_user_id = user.id
};
}
}
这在大多数情况下都有效。用户被告知他们的注册是成功的,流程通过 context.Done()
调用退出。然而,有时聊天机器人不会将对话注册为退出,如下所示:
如您所见,即使我调用了 Done()
方法,聊天机器人仍在同一个对话框中。这是我的聊天机器人中的一个普遍问题,因为它有时会在我所有的对话中发生。
您对可能出现的问题有任何意见吗?
编辑:
调试时,我在每次调用 context.Call
时都添加了断点。当我的问题出现时,它会停止击中这些断点。这可能是某些 DI 或其他东西的副作用吗?这是我的 DI 代码:
Conversation.UpdateContainer(builder =>
{
builder.RegisterModule(new DialogModule());
builder.RegisterModule(new ReflectionSurrogateModule());
builder.RegisterModule(new DialogModule_MakeRoot());
builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));
var store = new TableBotDataStore(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString);
builder.Register(c => store)
.Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
.AsSelf()
.SingleInstance();
builder.Register(c => new CachingBotDataStore(store,
CachingBotDataStoreConsistencyPolicy
.ETagBasedConsistency))
.As<IBotDataStore<BotData>>()
.AsSelf()
.InstancePerLifetimeScope();
builder.RegisterType<BasicLuisDialog>().As<LuisDialog<DialogResult>>().InstancePerDependency();
});
因为在 [LuisIntent("ConcertCity")] 中您使用 context.Done() 所以当前对话框从堆栈中退出。这就是为什么下一条消息由上一个对话框或调用 'None' 意图的消息控制器处理并且您得到此响应的原因
reply.Text = "I'm not sure what you mean \uD83E\uDD14<br/>Which Grøn Koncert would you like to attend?";
你不应该在每个地方都做 context.Done(),只有当你必须转到堆栈上的上一个对话框时才应该调用它。
我想我终于找到了问题所在。在我的代码中,我在静态 class 中实现了一个辅助方法,它会发送输入响应并等待一定时间。看到上下文被传递到这个静态方法中,这似乎导致了一些问题。
将方法更改为 LuisDialog
的扩展方法后,我不再有此问题。
如果有人能详细说明为什么这可能是个问题,我将不胜感激。
编辑:有问题的方法:
public static async Task StartTyping(IDialogContext context, int sleep)
{
var typingMsg = context.MakeMessage();
typingMsg.Type = ActivityTypes.Typing;
await context.PostAsync(typingMsg);
await Task.Delay(sleep);
}
我遇到了一个非常相似的问题,在将打字发送从静态助手 class 转移到基础 class 时,因为 Frederik 确实帮助大大减少了问题发生的次数,最终解决方案是:https://github.com/Microsoft/BotBuilder/issues/4477
简而言之,我不得不将与机器人相关的 NuGet 包(Microsoft.Bot.Builder、Microsoft.Bot.Builder.History、Microsoft.Bot.Connector)降级到 3.13.1,问题就消失了。
我目前正在使用 Microsoft 的 Bot Framework 制作聊天机器人。在我的流程中,我有一个最后的对话框,让用户知道他们正在参加比赛。还有一种针对未知输入的错误处理方法。两种方法见这里:
[Serializable]
public class ConcertCityDialog : AbstractBasicDialog<DialogResult>
{
private static FacebookService FacebookService => new FacebookService(new FacebookClient());
[LuisIntent("ConcertCity")]
public async Task ConcertCityIntent(IDialogContext context, LuisResult result)
{
var fbAccount = await FacebookService.GetAccountAsync(context.Activity.From.Id);
var selectedCityName = result.Entities.FirstOrDefault()?.Entity;
concert_city selectedCity;
using (var concertCityService = new ConcertCityService())
{
selectedCity = concertCityService.FindConcertCity(selectedCityName);
}
if (selectedCity == null)
{
await NoneIntent(context, result);
return;
}
user_interaction latestInteraction;
using (var userService = new MessengerUserService())
{
var user = userService.FindByFacebookIdIncludeInteractions(context.Activity.From.Id);
latestInteraction = user.user_interaction.MaxBy(e => e.created_at);
}
latestInteraction.preferred_city_id = selectedCity.id;
latestInteraction.gif_created = true;
using (var userInteractionService = new UserInteractionService())
{
userInteractionService.UpdateUserInteraction(latestInteraction);
}
var shareIntroReply = context.MakeMessage();
shareIntroReply.Text = "Great choice! You are now participating in the competition. If you dare then pass your message \uD83D\uDE0E";
await context.PostAsync(shareIntroReply);
var reply = await MessageUtility.MakeShareMessageCard(context, fbAccount, latestInteraction, false);
await context.PostAsync(reply);
context.Done(DialogResult.Done);
}
[LuisIntent("")]
[LuisIntent("None")]
public async Task NoneIntent(IDialogContext context, LuisResult result)
{
messenger_user user;
using (var userService = new MessengerUserService())
{
user = userService.FindByFacebookId(context.Activity.From.Id);
}
var phrase = CreateMisunderstoodPhrase(user, result.Query);
using (var misunderstoodPhraseService = new MisunderstoodPhraseService())
{
misunderstoodPhraseService.CreatePhrase(phrase);
}
List<concert_city> concertCities;
using (var concertCityService = new ConcertCityService())
{
concertCities = concertCityService.GetUpcomingConcertCities().ToList();
}
// Prompt city
var reply = context.MakeMessage();
reply.Text = "I'm not sure what you mean \uD83E\uDD14<br/>Which Grøn Koncert would you like to attend?";
reply.SuggestedActions = new SuggestedActions
{
Actions = concertCities.Select(e => MessageUtility.MakeQuickAnswer(e.name)).ToList()
};
await context.PostAsync(reply);
context.Wait(MessageReceived);
}
protected override void OnDeserializedCustom(StreamingContext context)
{
}
}
这里是 AbstractBasicDialog
实现:
[Serializable]
public abstract class AbstractBasicDialog<T> : LuisDialog<T>
{
protected AbstractBasicDialog() : base(new LuisService(new LuisModelAttribute(
ConfigurationManager.AppSettings["LuisAppId"],
ConfigurationManager.AppSettings["LuisAPIKey"],
domain: ConfigurationManager.AppSettings["LuisAPIHostName"])))
{
}
[LuisIntent("Cancel")]
public virtual async Task CancelIntent(IDialogContext context, LuisResult result)
{
var randomQuotes = new List<string>
{
"If you say so, I'll leave you alone for now",
"alright then, I'll leave you alone",
"Okay then, I won't bother you anymore"
};
await context.PostAsync(MessageUtility.RandAnswer(randomQuotes));
context.Done(DialogResult.Cancel);
}
[LuisIntent("Start")]
public virtual async Task StartIntent(IDialogContext context, LuisResult result)
{
context.Done(DialogResult.Restart);
}
[LuisIntent("CustomerSupport")]
public async Task CustomerSupportIntent(IDialogContext context, LuisResult result)
{
using (var userService = new MessengerUserService())
{
var user = userService.FindByFacebookId(context.Activity.From.Id);
if (user != null)
{
user.receiving_support = true;
userService.UpdateUser(user);
}
}
await context.PostAsync("I'll let customer service know, that you want to talk to them. They will get back to you within 24 hours.<br/>If at any time you want to return to me, and start passing a message, just type \"Stop customer support\".");
context.Call(new CustomerSupportDialog(), ResumeAfterCustomerSupport);
}
private async Task ResumeAfterCustomerSupport(IDialogContext context, IAwaitable<DialogResult> result)
{
context.Done(await result);
}
protected misunderstood_phrase CreateMisunderstoodPhrase(messenger_user user, string phrase)
{
return new misunderstood_phrase
{
phrase = phrase,
dialog = GetType().Name,
messenger_user_id = user.id
};
}
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
OnDeserializedCustom(context);
}
protected abstract void OnDeserializedCustom(StreamingContext context);
}
调用链从这个对话框开始:
[Serializable]
public class BasicLuisDialog : LuisDialog<DialogResult>
{
private static FacebookService FacebookService => new FacebookService(new FacebookClient());
public BasicLuisDialog() : base(new LuisService(new LuisModelAttribute(
ConfigurationManager.AppSettings["LuisAppId"],
ConfigurationManager.AppSettings["LuisAPIKey"],
domain: ConfigurationManager.AppSettings["LuisAPIHostName"])))
{
}
[LuisIntent("")]
[LuisIntent("None")]
public async Task NoneIntent(IDialogContext context, LuisResult result)
{
var facebookAccount = await FacebookService.GetAccountAsync(context.Activity.From.Id);
RegisterUser(facebookAccount, null, out var user);
var phrase = CreateMisunderstoodPhrase(user, result.Query);
using (var misunderstoodPhraseService = new MisunderstoodPhraseService())
{
misunderstoodPhraseService.CreatePhrase(phrase);
}
var reply = context.MakeMessage();
reply.SuggestedActions = new SuggestedActions
{
Actions = new List<CardAction>
{
new CardAction { Title = "Get started", Type = ActionTypes.ImBack, Value = "Get started" },
new CardAction { Title = "Customer support", Type = ActionTypes.ImBack, Value = "Customer support" }
}
};
var name = string.IsNullOrEmpty(facebookAccount.FirstName) ? "" : $"{facebookAccount.FirstName} ";
reply.Text = $"Hm, I'm not sure what you mean {name} \uD83E\uDD14 Here are some ways you can interact with me:";
await context.PostAsync(reply);
context.Wait(MessageReceived);
}
[LuisIntent("Greeting")]
[LuisIntent("Positive")]
[LuisIntent("Start")]
public async Task GreetingIntent(IDialogContext context, LuisResult result)
{
var rnd = new Random();
var facebookAccount = await FacebookService.GetAccountAsync(context.Activity.From.Id);
// Initial Greeting
var greetings = new List<string>
{
"Well hello there",
"Hi there"
};
if (!string.IsNullOrEmpty(facebookAccount.FirstName))
{
greetings.Add("Hi {0}");
greetings.Add("Hello {0}");
greetings.Add("Welcome {0}");
}
if (facebookAccount.Gender == "male")
greetings.Add("Hey handsome");
else if (facebookAccount.Gender == "female")
greetings.Add("Hi gorgeous");
var randIndex = rnd.Next(greetings.Count);
var greeting = string.Format(greetings[randIndex], facebookAccount.FirstName);
await context.PostAsync(greeting);
await MessageUtility.StartTyping(context, 300);
country country;
using (var countryService = new CountryService())
{
country = countryService.FindCountry(facebookAccount.Locale);
}
var userHasCountry = RegisterUser(facebookAccount, country, out var user);
// If user contry not found prompt for answer
if (!userHasCountry)
{
var countryReply = context.MakeMessage();
countryReply.Text = "You are hard to keep track of - where are you from?";
countryReply.SuggestedActions = new SuggestedActions
{
Actions = new List<CardAction>
{
MessageUtility.MakeQuickAnswer("Denmark"),
MessageUtility.MakeQuickAnswer("Norway"),
MessageUtility.MakeQuickAnswer("Sweden"),
MessageUtility.MakeQuickAnswer("Other")
}
};
await context.PostAsync(countryReply);
context.Call(new CountryDialog(), AfterCountryDialog);
}
else
{
await FunPrompt(context, country);
}
}
private async Task AfterCountryDialog(IDialogContext countryContext, IAwaitable<country> countryAwaitable)
{
var country = await countryAwaitable;
var facebookAccount = await FacebookService.GetAccountAsync(countryContext.Activity.From.Id);
using (var userService = new MessengerUserService())
{
var user = userService.FindByFacebookId(facebookAccount.Id);
user.country = country;
userService.UpdateUser(user);
}
var reply = countryContext.MakeMessage();
reply.Text = "That's cool \uD83D\uDE0E";
await countryContext.PostAsync(reply);
await MessageUtility.StartTyping(countryContext, 350);
await FunPrompt(countryContext, country);
}
private async Task FunPrompt(IDialogContext context, country country)
{
if (country?.name == "norway" && DateTime.Now < new DateTime(2018, 8, 13))
{
var reply = context.MakeMessage();
reply.Text = "Unfortunately the competition isn't open in Norway yet. You can still talk to customer support if you want to";
reply.SuggestedActions = new SuggestedActions
{
Actions = new List<CardAction>
{
MessageUtility.MakeQuickAnswer("Customer support")
}
};
await context.PostAsync(reply);
context.Wait(MessageReceived);
}
else if ((country?.name == "denmark" && DateTime.Now >= new DateTime(2018, 7, 29)) ||
(country?.name == "norway" && DateTime.Now >= new DateTime(2018, 10, 21)))
{
var reply = context.MakeMessage();
reply.Text = "The competition has ended. You can still talk to customer support if you want to";
reply.SuggestedActions = new SuggestedActions
{
Actions = new List<CardAction>
{
MessageUtility.MakeQuickAnswer("Customer support")
}
};
await context.PostAsync(reply);
context.Wait(MessageReceived);
}
else
{
await context.PostAsync("Are you up for some fun?");
context.Call(new IntroductionDialog(), ResumeAfterDialog);
}
}
[LuisIntent("CustomerSupport")]
public async Task CustomerSupportIntent(IDialogContext context, LuisResult result)
{
using (var userService = new MessengerUserService())
{
var user = userService.FindByFacebookId(context.Activity.From.Id);
if (user != null)
{
user.receiving_support = true;
userService.UpdateUser(user);
}
}
await context.PostAsync("I'll let customer support know, that you want to talk to them. They should be messaging you shortly.<br/>You can end your conversation with customer support at any time by typing \"Stop customer support\".");
context.Call(new CustomerSupportDialog(), ResumeAfterDialog);
}
private async Task ResumeAfterDialog(IDialogContext context, IAwaitable<DialogResult> result)
{
var resultState = await result;
if (resultState == DialogResult.Restart)
await GreetingIntent(context, null);
else if (resultState == DialogResult.CustomerSupport)
await ResumeAfterCustomerSupport(context);
else if (resultState == DialogResult.Done || resultState == DialogResult.Cancel)
context.Done(resultState);
else
context.Wait(MessageReceived);
}
private async Task ResumeAfterCustomerSupport(IDialogContext context)
{
using (var userService = new MessengerUserService())
{
var user = userService.FindByFacebookId(context.Activity.From.Id);
if (user != null)
{
user.receiving_support = false;
userService.UpdateUser(user);
}
}
await context.PostAsync("I hope you got the help you needed. Would you like to pass a message to a friend?");
context.Call(new IntroductionDialog(), ResumeAfterDialog);
}
private bool RegisterUser(FacebookAccount fbAccount, country country, out messenger_user user)
{
if (string.IsNullOrEmpty(fbAccount?.Id))
{
user = null;
return false;
}
using (var userService = new MessengerUserService())
{
user = userService.FindByFacebookId(fbAccount.Id);
if (user != null)
return user.country != null;
user = new messenger_user
{
id = fbAccount.Id,
country = country
};
userService.CreateUser(user);
return user.country != null;
}
}
protected misunderstood_phrase CreateMisunderstoodPhrase(messenger_user user, string phrase)
{
return new misunderstood_phrase
{
phrase = phrase,
dialog = GetType().Name,
messenger_user_id = user.id
};
}
}
这在大多数情况下都有效。用户被告知他们的注册是成功的,流程通过 context.Done()
调用退出。然而,有时聊天机器人不会将对话注册为退出,如下所示:
如您所见,即使我调用了 Done()
方法,聊天机器人仍在同一个对话框中。这是我的聊天机器人中的一个普遍问题,因为它有时会在我所有的对话中发生。
您对可能出现的问题有任何意见吗?
编辑:
调试时,我在每次调用 context.Call
时都添加了断点。当我的问题出现时,它会停止击中这些断点。这可能是某些 DI 或其他东西的副作用吗?这是我的 DI 代码:
Conversation.UpdateContainer(builder =>
{
builder.RegisterModule(new DialogModule());
builder.RegisterModule(new ReflectionSurrogateModule());
builder.RegisterModule(new DialogModule_MakeRoot());
builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));
var store = new TableBotDataStore(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString);
builder.Register(c => store)
.Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
.AsSelf()
.SingleInstance();
builder.Register(c => new CachingBotDataStore(store,
CachingBotDataStoreConsistencyPolicy
.ETagBasedConsistency))
.As<IBotDataStore<BotData>>()
.AsSelf()
.InstancePerLifetimeScope();
builder.RegisterType<BasicLuisDialog>().As<LuisDialog<DialogResult>>().InstancePerDependency();
});
因为在 [LuisIntent("ConcertCity")] 中您使用 context.Done() 所以当前对话框从堆栈中退出。这就是为什么下一条消息由上一个对话框或调用 'None' 意图的消息控制器处理并且您得到此响应的原因
reply.Text = "I'm not sure what you mean \uD83E\uDD14<br/>Which Grøn Koncert would you like to attend?";
你不应该在每个地方都做 context.Done(),只有当你必须转到堆栈上的上一个对话框时才应该调用它。
我想我终于找到了问题所在。在我的代码中,我在静态 class 中实现了一个辅助方法,它会发送输入响应并等待一定时间。看到上下文被传递到这个静态方法中,这似乎导致了一些问题。
将方法更改为 LuisDialog
的扩展方法后,我不再有此问题。
如果有人能详细说明为什么这可能是个问题,我将不胜感激。
编辑:有问题的方法:
public static async Task StartTyping(IDialogContext context, int sleep)
{
var typingMsg = context.MakeMessage();
typingMsg.Type = ActivityTypes.Typing;
await context.PostAsync(typingMsg);
await Task.Delay(sleep);
}
我遇到了一个非常相似的问题,在将打字发送从静态助手 class 转移到基础 class 时,因为 Frederik 确实帮助大大减少了问题发生的次数,最终解决方案是:https://github.com/Microsoft/BotBuilder/issues/4477
简而言之,我不得不将与机器人相关的 NuGet 包(Microsoft.Bot.Builder、Microsoft.Bot.Builder.History、Microsoft.Bot.Connector)降级到 3.13.1,问题就消失了。