Bot Framework 中的形式和意图,wit.ai
Form and intent in Bot Framework, wit.ai
我有一个问题:我的机器人不遵循形式,它只是继续威胁下一个答案作为意图。
http://g.recordit.co/GultDCEndR.gif
当我输入第一条消息“порахуй”时,它会启动我的表单 Exams.BuildForm,但我的下一个回答导致离开表单,因为意图不同,正如我得到的那样。
我期望得到什么:根据我定义的意图,机器人启动表单并不断向其提问直到得到回答。
它在“порахуй конкурсний бал”案例中启动表格。并在消息的意图与大小写不匹配时离开。这是预期的行为吗?如果是这样,我应该如何更改我的代码以使表单提问直到所有问题都没有得到回答?
我的代码:
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;
}
}
}
}
我认为问题出在您的代码方式上。一旦检测到启动表单的意图,您就不应该处理任何其他意图(这意味着您不应该允许通过其他 switch/case 语句)。表单启动时的后续消息应点击 await Conversation.SendAsync(activity, MakeRoot)
才能正常工作。
我的建议是重做你的逻辑。首先,您需要一个根对话框,您的大部分逻辑都应该驻留在其中;将它放在控制器上并不是一个很好的做法。执行此操作后,您可以等待 Conversation.SendAsync(activity, () => YourDialog)
并在对话框的 MessageReceived
中调用 wit.ai、处理意图、启动表单等。
我认为进行这些更改可能就足够了。
我有一个问题:我的机器人不遵循形式,它只是继续威胁下一个答案作为意图。
http://g.recordit.co/GultDCEndR.gif
当我输入第一条消息“порахуй”时,它会启动我的表单 Exams.BuildForm,但我的下一个回答导致离开表单,因为意图不同,正如我得到的那样。
我期望得到什么:根据我定义的意图,机器人启动表单并不断向其提问直到得到回答。
它在“порахуй конкурсний бал”案例中启动表格。并在消息的意图与大小写不匹配时离开。这是预期的行为吗?如果是这样,我应该如何更改我的代码以使表单提问直到所有问题都没有得到回答?
我的代码:
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;
}
}
}
}
我认为问题出在您的代码方式上。一旦检测到启动表单的意图,您就不应该处理任何其他意图(这意味着您不应该允许通过其他 switch/case 语句)。表单启动时的后续消息应点击 await Conversation.SendAsync(activity, MakeRoot)
才能正常工作。
我的建议是重做你的逻辑。首先,您需要一个根对话框,您的大部分逻辑都应该驻留在其中;将它放在控制器上并不是一个很好的做法。执行此操作后,您可以等待 Conversation.SendAsync(activity, () => YourDialog)
并在对话框的 MessageReceived
中调用 wit.ai、处理意图、启动表单等。
我认为进行这些更改可能就足够了。