首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >BOT框架扰乱对话框状态

BOT框架扰乱对话框状态

提问于 2018-06-04 04:25:06
回答 2关注 0查看 452

我目前正在使用微软的BotFramework制作一个聊天机器人。在我的流程中,我有一个最后的对话框,让用户知道,他们正在参与竞争。对于未知的输入也有一个错误处理方法。这两种方法如下所示:

代码语言:txt
复制
[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执行情况:

代码语言:txt
复制
[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);
}

调用链从此对话框开始:

代码语言:txt
复制
[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代码:

代码语言:txt
复制
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();
});
相关文章

相似问题

相关问答用户
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档