使用 C# 在一个机器人中使用多个 QnA 服务

Multiple QnA Service in one Bot using C#

我有 3 个 QnA 服务。我希望它们同时在单个 BOT 中使用。这如何使用 C# 实现。我最初的想法是将 KB ID 和 Sub Key 放入一个数组中(如何实现或者数组可以工作?)。我在 Node.JS 中看到了一些代码,但我不知道如何转换代码在 C# 中。

public class QnaDialog : QnAMakerDialog
{
    public QnaDialog() : base(
        new QnAMakerService(new QnAMakerAttribute(ConfigurationManager.AppSettings["QnaSubscriptionKey1"],
        ConfigurationManager.AppSettings["QnaKnowledgebaseId1"], "Hmm, I wasn't able to find an article about that. Can you try asking in a different way?", 0.5)),

        new QnAMakerService(new QnAMakerAttribute(ConfigurationManager.AppSettings["QnaSubscriptionKey2"],
        ConfigurationManager.AppSettings["QnaKnowledgebaseId2"], "Hmm, I wasn't able to find an article about that. Can you try asking in a different way?", 0.5)),

        new QnAMakerService(new QnAMakerAttribute(ConfigurationManager.AppSettings["QnaSubscriptionKey3"],
        ConfigurationManager.AppSettings["QnaKnowledgebaseId4"], "Hmm, I wasn't able to find an article about that. Can you try asking in a different way?", 0.5))
        )
    {
    }
}

通过在属性中提供多种服务,您可以在单个机器人中使用多个 QnAMaker 知识库。

使用来自 Nuget 包 BotBuilder.CognitiveServicesQnAMakerDialog 的基本实现是:

[Serializable]
[QnAMaker("QnaSubscriptionKey1", "QnaKnowledgebaseId1", "Hmm, I wasn't able to find an article about that. Can you try asking in a different way?", 0.50, 3)]
[QnAMaker("QnaSubscriptionKey2", "QnaKnowledgebaseId2", "Hmm, I wasn't able to find an article about that. Can you try asking in a different way?", 0.5, 3)]
[QnAMaker("QnaSubscriptionKey3", "QnaKnowledgebaseId3", "Hmm, I wasn't able to find an article about that. Can you try asking in a different way?", 0.5, 3)]
public class RootDialog : QnAMakerDialog
{
}

BUT(是的,有一个 "but")在某些情况下,您可能会在处理消息时遇到异常。由于 QnAMakerDialog 是开源的(来源是 here),您可以很容易地发现问题出在 return 服务调用的实现中,在 MessageReceivedAsync 中:

var sendDefaultMessageAndWait = true;
qnaMakerResults = tasks.FirstOrDefault(x => x.Result.ServiceCfg != null)?.Result;
if (tasks.Count(x => x.Result.Answers?.Count > 0) > 0)
{
    var maxValue = tasks.Max(x => x.Result.Answers[0].Score);
    qnaMakerResults = tasks.First(x => x.Result.Answers[0].Score == maxValue).Result;

    if (qnaMakerResults != null && qnaMakerResults.Answers != null && qnaMakerResults.Answers.Count > 0)
    {
        if (this.IsConfidentAnswer(qnaMakerResults))
        {
            await this.RespondFromQnAMakerResultAsync(context, message, qnaMakerResults);
            await this.DefaultWaitNextMessageAsync(context, message, qnaMakerResults);
        }
        else
        {
            feedbackRecord = new FeedbackRecord { UserId = message.From.Id, UserQuestion = message.Text };
            await this.QnAFeedbackStepAsync(context, qnaMakerResults);
        }

        sendDefaultMessageAndWait = false;
    }
}

if (sendDefaultMessageAndWait)
{
    await context.PostAsync(qnaMakerResults.ServiceCfg.DefaultMessage);
    await this.DefaultWaitNextMessageAsync(context, message, qnaMakerResults);
}

在此代码中,如果您的所有服务都无法回答您的问题(即:如果您的至少一个 QnAMaker KB 没有回答您的问题,则这行代码将会中断问题)

tasks.Max(x => x.Result.Answers[0].Score);

解决方法:您可以通过获取源代码并修复方法来实现自己的 QnAMakerDialog,例如:

public async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
    var message = await argument;

    if (message != null && !string.IsNullOrEmpty(message.Text))
    {
        var tasks = this.services.Select(s => s.QueryServiceAsync(message.Text)).ToArray();
        await Task.WhenAll(tasks);

        if (tasks.Any())
        {
            var sendDefaultMessageAndWait = true;
            qnaMakerResults = tasks.FirstOrDefault(x => x.Result.ServiceCfg != null)?.Result;

            var qnaMakerFoundResults = tasks.Where(x => x.Result.Answers.Any()).ToList();
            if (qnaMakerFoundResults.Any())
            {
                var maxValue = qnaMakerFoundResults.Max(x => x.Result.Answers[0].Score);
                qnaMakerResults = qnaMakerFoundResults.First(x => x.Result.Answers[0].Score == maxValue).Result;

                if (qnaMakerResults?.Answers != null && qnaMakerResults.Answers.Count > 0)
                {
                    if (this.IsConfidentAnswer(qnaMakerResults))
                    {
                        await this.RespondFromQnAMakerResultAsync(context, message, qnaMakerResults);
                        await this.DefaultWaitNextMessageAsync(context, message, qnaMakerResults);
                    }
                    else
                    {
                        feedbackRecord = new FeedbackRecord { UserId = message.From.Id, UserQuestion = message.Text };
                        await this.QnAFeedbackStepAsync(context, qnaMakerResults);
                    }

                    sendDefaultMessageAndWait = false;
                }
            }

            if (sendDefaultMessageAndWait)
            {
                await context.PostAsync(qnaMakerResults.ServiceCfg.DefaultMessage);
                await this.DefaultWaitNextMessageAsync(context, message, qnaMakerResults);
            }
        }
    }
}