Search code examples
c#botframeworkwit-ai

Form and intent in Bot Framework, wit.ai


I have a problem: my bot doesn't follow form, it just continuing to threat next answers as intents.

http://g.recordit.co/GultDCEndR.gif

when i type the first message "порахуй", it launches my form Exams.BuildForm, but the next my answer causes leaving the form, because the intent is different, as i got it.

What did I expected to have: on my defined intent bot starts form and keeps asking questions from it till they are answered.

It launches form at "порахуй конкурсний бал" case. And leaves when intent of message doesn't match case. Is it expected behavior? And if so, how should I change my code to make form ask questions till all of them won't be answered?

my code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using com.valgut.libs.bots.Wit;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.FormFlow;
using Microsoft.Bot.Connector;

namespace IoT_NULP_Bot.Controllers
{
    [Serializable]
    public class Exams
    {
        [Prompt("Для початку, ваш результат з Математики?")]
        public float Math { get; set; }

        [Prompt("Українська мова та Література?")]
        public float Ukrainian { get; set; }

        [Prompt("Вибіркова дисціпліна - Іноземна мова, Фізика або Історія України")]
        public float LanguagePhysics { get; set; }

        public static IForm<Exams> BuildForm()
        {
            return new FormBuilder<Exams>()
                .Message("Давайте порахуємо ваш конкурсний бал при вступі на ІоТ :) Для цього дайте мені відповіді на наступні запитання")
                .Field(nameof(Ukrainian))
                .Field(nameof(Math))
                .Field(nameof(LanguagePhysics))
                .Build();
        }
    }

    [BotAuthentication]
    public class MessagesController : ApiController
    {
        private static IoT_BotDbEntities db = new IoT_BotDbEntities();

        /// <summary>
        /// POST: api/Messages
        /// Receive a message from a user and reply to it
        /// </summary>
        public async Task<HttpResponseMessage> Post([FromBody] Activity activity)
        {
            ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
            var wit = new WitClient("RKIJ5LVF4GKLEWJ5O5NPUPXVCQMKGLJW");
            switch (activity.Type)
            {
                case ActivityTypes.Message:

                    var msg = wit.Converse(activity.From.Id, activity.Text);

                    var intent = string.Empty;
                    double conf = 0;
                    try
                    {
                        var a = msg.entities["intent"];
                        if (a != null)
                        {
                            foreach (var z in msg.entities["intent"])
                            {
                                if (z.confidence > conf)
                                {
                                    conf = z.confidence;
                                    intent = z.value.ToString();
                                }
                            }
                        }
                    }
                    catch (KeyNotFoundException)
                    {
                    }
                    Activity reply = activity.CreateReply();

                    switch (intent)
                    {
                        case "порахуй конкурсний бал":
                            await Conversation.SendAsync(activity, MakeRoot);
                            break;
                        case "статистика вступу":
                            reply.Attachments = new List<Attachment>();
                            Attachment attachment = new Attachment();
                            attachment.ContentType = "image/png";
                            attachment.ContentUrl =
                                @"http://image.prntscr.com/image/ee2502f6a68041e1813f1bcd125a8bb2.png";
                            Attachment secondAttachment = new Attachment();
                            secondAttachment.ContentType = "image/png";
                            secondAttachment.ContentUrl =
                                @"http://image.prntscr.com/image/258aa8e844d74ab7b63447a9f551ecbd.png";
                            reply.Attachments.Add(attachment);
                            reply.Attachments.Add(secondAttachment);
                            reply.Text = GetReplyFromDb(intent);
                            break;
                        case "фото":
                            var photo = GetRandomPhoto();
                            reply.Attachments = new List<Attachment>();
                            Attachment attachment1 = new Attachment();
                            attachment1.ContentType = "image/png";
                            attachment1.ContentUrl = photo.photoLink;
                            reply.Attachments.Add(attachment1);
                            reply.Text = photo.descrip;
                            break;
                        default:
                            reply.Text = GetReplyFromDb(intent);
                            break;
                    }
                    await connector.Conversations.ReplyToActivityAsync(reply);
                    break;
                default:
                    break;
            }

            var response = Request.CreateResponse(HttpStatusCode.OK);
            return response;
        }

        internal static IDialog<Exams> MakeRoot()
        {
            return Chain.From(() => FormDialog.FromForm(Exams.BuildForm));
        }

        private static Photo GetRandomPhoto()
        {
            var arrToRandomFrom = db.Photos.ToArray();
            return arrToRandomFrom[new Random().Next(arrToRandomFrom.Length)];
        }

        private static string GetReplyFromDb(string intent)
        {
            var arrToRandomFrom = db.Responses.Where(x => x.Intent.content == intent).ToArray();
            if (arrToRandomFrom.Length > 0)
                return arrToRandomFrom[new Random().Next(arrToRandomFrom.Length)].content;
            else
            {
                var noreply = db.Responses.Where(x => x.Intent.content == "noreply").ToArray();
                return noreply[new Random().Next(noreply.Length)].content;
            }
        }
    }
}

Solution

  • I think the problem is due to how you have the code. Once you detect the intent that launches the form you should not process any other intent (meaning that you shouldn't allow going trough other switch/case statements). Subsequent messages when the form was started should hit the await Conversation.SendAsync(activity, MakeRoot) for this to work properly.

    My suggestion here would be to re-do your logic. First of all, you need a root dialog, where most of your logic should reside; having it on the controller is not a very good practice. Once you do that, you can do await Conversation.SendAsync(activity, () => YourDialog) and in the MessageReceived of your dialog, you can call wit.ai, process intent, launch the form, etc.

    I think that doing these changes will be likely enough.