我一直在尝试使用带有QnAMaker
API的Microsoft Cognitive和AI工具包,以创建一个简单的聊天机器人。
虽然我的普通qnaMakerAi聊天机器人工作正常,但在我尝试增强它的功能并在响应中包含机器人反馈时存在问题。
我一直在按照here引用的确切代码示例。
我遇到的问题是:
Exception: Object reference not set to an instance of an object.
[File of type 'text/plain'].
调试器在代码部分给出错误 - (在文件WebApiConfig.cs中)
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Formatting = Newtonsoft.Json.Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
};
我还在 - https://github.com/Microsoft/BotBuilder/issues/4267中提出了有关该问题的详细说明。
请检查并建议。
根据用户评论,这里是MessagesController的代码 -
using System;
using System.Threading.Tasks;
using System.Web.Http;
using Microsoft.Bot.Connector;
using Microsoft.Bot.Builder.Dialogs;
using System.Web.Http.Description;
using System.Net.Http;
using QnABot.Dialogs;
namespace Microsoft.Bot.Sample.QnABot
{
[BotAuthentication]
public class MessagesController : ApiController
{
/// <summary>
/// POST: api/Messages
/// receive a message from a user and send replies
/// </summary>
/// <param name="activity"></param>
[ResponseType(typeof(void))]
public virtual async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
// check if activity is of type message
if (activity.GetActivityType() == ActivityTypes.Message)
{
//await Conversation.SendAsync(activity, () => new RootDialog());
await Conversation.SendAsync(activity, () => new QnaDialog());
}
else
{
HandleSystemMessage(activity);
}
return new HttpResponseMessage(System.Net.HttpStatusCode.Accepted);
}
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
// Handle conversation state changes, like members being added and removed
// Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
// Not available in all channels
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}
}
}
对于QnADialog -
using Microsoft.Bot.Builder.Azure;
using Microsoft.Bot.Builder.CognitiveServices.QnAMaker;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
namespace QnABot.Dialogs
{
[Serializable]
public class QnaDialog : QnAMakerDialog
{
public QnaDialog() : base(new QnAMakerService(new QnAMakerAttribute("b372e477-0a2f-4a5a-88d5-3a664d16a4c3", "4ee02ead3xxxxxx", "Sorry, I couldn't find an answer for that", 0.5)))
{
}
protected override async Task RespondFromQnAMakerResultAsync(IDialogContext context, IMessageActivity message, QnAMakerResults result)
{
// answer is a string
var answer = result.Answers.First().Answer;
Activity reply = ((Activity)context.Activity).CreateReply();
string[] qnaAnswerData = answer.Split(';');
int dataSize = qnaAnswerData.Length;
string title = qnaAnswerData[0];
string description = qnaAnswerData[1];
string url = qnaAnswerData[2];
string imageURL = qnaAnswerData[3];
HeroCard card = new HeroCard
{
Title = title,
Subtitle = description,
};
card.Buttons = new List<CardAction>
{
new CardAction(ActionTypes.OpenUrl, "Learn More", value: url)
};
card.Images = new List<CardImage>
{
new CardImage( url = imageURL)
};
reply.Attachments.Add(card.ToAttachment());
await context.PostAsync(reply);
}
protected override async Task DefaultWaitNextMessageAsync(IDialogContext context, IMessageActivity message, QnAMakerResults result)
{
// get the URL
var answer = result.Answers.First().Answer;
string[] qnaAnswerData = answer.Split(';');
string qnaURL = qnaAnswerData[2];
// pass user's question
var userQuestion = (context.Activity as Activity).Text;
context.Call(new FeedbackDialog(qnaURL, userQuestion), ResumeAfterFeedback);
}
private async Task ResumeAfterFeedback(IDialogContext context, IAwaitable<IMessageActivity> result)
{
if (await result != null)
{
await MessageReceivedAsync(context, result);
}
else
{
context.Done<IMessageActivity>(null);
}
}
}
}
对于FeedBackDialog -
using Microsoft.ApplicationInsights;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
namespace QnABot.Dialogs
{
[Serializable]
public class FeedbackDialog : IDialog<IMessageActivity>
{
private string qnaURL;
private string userQuestion;
public FeedbackDialog(string url, string question)
{
// keep track of data associated with feedback
qnaURL = url;
userQuestion = question;
}
public async Task StartAsync(IDialogContext context)
{
var feedback = ((Activity)context.Activity).CreateReply("Did you find what you need?");
feedback.SuggestedActions = new SuggestedActions()
{
Actions = new List<CardAction>()
{
new CardAction(){ Title = "", Type=ActionTypes.PostBack, Value=$"yes-positive-feedback" },
new CardAction(){ Title = "", Type=ActionTypes.PostBack, Value=$"no-negative-feedback" }
}
};
await context.PostAsync(feedback);
context.Wait(this.MessageReceivedAsync);
}
public async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var userFeedback = await result;
if (userFeedback.Text.Contains("yes-positive-feedback") || userFeedback.Text.Contains("no-negative-feedback"))
{
// create telemetry client to post to Application Insights
TelemetryClient telemetry = new TelemetryClient();
if (userFeedback.Text.Contains("yes-positive-feedback"))
{
// post feedback to App Insights
var properties = new Dictionary<string, string>
{
{"Question", userQuestion },
{"URL", qnaURL },
{"Vote", "Yes" }
// add properties relevant to your bot
};
telemetry.TrackEvent("Yes-Vote", properties);
}
else if (userFeedback.Text.Contains("no-negative-feedback"))
{
// post feedback to App Insights
}
await context.PostAsync("Thanks for your feedback!");
context.Done<IMessageActivity>(null);
}
else
{
// no feedback, return to QnA dialog
context.Done<IMessageActivity>(userFeedback);
}
}
}
}
答案 0 :(得分:3)
好的,第一个问题是您在QnaDialog声明中反转了2个参数:
public QnaDialog() : base(new QnAMakerService(new QnAMakerAttribute("b372e477-0a2f-4a5a-88d5-3a664d16a4c3", "4ee02ead3xxxxxx", "Sorry, I couldn't find an answer for that", 0.5)))
语法为:Qn
public QnAMakerAttribute(string subscriptionKey, string knowledgebaseId, ...
在这里你颠倒了你的知识库和你的知识库。 Guid应该排在第2位,而不是第1位。
请注意,我在问题和回复中修改了您的订阅密钥,您应该注意这样分享。
您使用的示例似乎无效:
;
分隔符的字符串(比如当你输入“hi”时,答案是“你好”我添加了一些安全措施以避免在这些情况下出现错误:
protected override async Task RespondFromQnAMakerResultAsync(IDialogContext context, IMessageActivity message, QnAMakerResults result)
{
// answer is a string
var answer = result.Answers.First().Answer;
Activity reply = ((Activity)context.Activity).CreateReply();
var qnaAnswerData = answer.Split(';');
var dataSize = qnaAnswerData.Length;
if (dataSize == 3)
{
var title = qnaAnswerData[0];
var description = qnaAnswerData[1];
var url = qnaAnswerData[2];
var imageUrl = qnaAnswerData[3];
var card = new HeroCard
{
Title = title,
Subtitle = description,
Buttons = new List<CardAction>
{
new CardAction(ActionTypes.OpenUrl, "Learn More", value: url)
},
Images = new List<CardImage>
{
new CardImage(url = imageUrl)
},
};
reply.Attachments.Add(card.ToAttachment());
}
else
{
reply.Text = answer;
}
await context.PostAsync(reply);
}
protected override async Task DefaultWaitNextMessageAsync(IDialogContext context, IMessageActivity message, QnAMakerResults result)
{
if (result.Answers.Count > 0)
{
// get the URL
var answer = result.Answers.First().Answer;
var qnaAnswerData = answer.Split(';');
var dataSize = qnaAnswerData.Length;
if (dataSize == 3)
{
var qnaUrl = qnaAnswerData[2];
// pass user's question
var userQuestion = (context.Activity as Activity).Text;
context.Call(new FeedbackDialog(qnaUrl, userQuestion), ResumeAfterFeedback);
}
else
{
await ResumeAfterFeedback(context, new AwaitableFromItem<IMessageActivity>(null));
}
}
else
{
await ResumeAfterFeedback(context, new AwaitableFromItem<IMessageActivity>(null));
}
}
private async Task ResumeAfterFeedback(IDialogContext context, IAwaitable<IMessageActivity> result)
{
if (await result != null)
{
await MessageReceivedAsync(context, result);
}
else
{
context.Done<IMessageActivity>(null);
}
}