C# Microsoft Bot Framework with luis 结果指向 QNA Maker 和图表 api

C# Microsoft Bot Framework with luis result directing to QNA Maker and graph api

我使用 Microsoft bot 框架制作了一个 Bot,并使用 Luis 来匹配意图。一些意图将其指向 QNA,而另一些意图将其指向图形 api。

我的问题是确定是否应该转到 qna 以搜索 qna 中的相关意图或是否应该转到图形的最佳方法是什么api用于获取结果。

到目前为止,我使用多个 Luis Intents 来匹配正确的意图,然后根据所需的意图功能将其重定向(无论是将其定向到 qna 对话框还是图形 api 对话框)。

` [LuisModel("model id", "key")]

[Serializable]
public class RootDialog : DispatchDialog
{

    //this intent directs to graph api dialog
    [LuisIntent(DialogMatches.GraphApiIntent)]
    public async Task RunGraphapiIntent(IDialogContext context, IActivity activity)
    {
            UserMessage = (activity as IMessageActivity).Text;
            await context.Forward(new GraphApiDailog(), EndDialog, context.Activity, CancellationToken.None);
    }

      //This intent directs to qna dialog
      [LuisIntent(DialogMatches.BlogMatch)]
      public async Task RunBlogDialogQna(IDialogContext context, LuisResult result)
      {
        var userQuestion = (context.Activity as Activity).Text;
        (context.Activity as Activity).Text = DialogMatches.BlogMatch;
        await context.Forward(new BasicQnAMakerDialog(), this.EndDialog, context.Activity, CancellationToken.None);
      }
      `

但是这种方法要求我使用 [LuisIntent("intentstring")] 来匹配每个意图。因为我可以有 50 或 100 个意图,所以为 50 个意图编写 50 个函数是不切实际的。

我找到了一种方法来调用 api 以从 Quickstart: Analyze text using C#

中的话语中获取意图

它利用“https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/df67dcdb-c37d-46af-88e1-8b97951ca1c2?subscription-key=&q=打开卧室灯” api 用于获取意图。

同样,我的问题是我如何知道我是否应该将它重定向到 QnaDialog 或 Graph Api 对话框以使用我得到的意图结果获取数据?

提前致谢

如果您想要扩展,您最好编写自己的 Nlp 服务,调用 Luis API 来检测意图。我认为按意图处理对话框重定向的最佳方法是制作类似于 IntentDetectorDialog 的东西,它的唯一工作是分析用户话语并转发到与检测到的意图相对应的对话框。

这是我使用了一段时间的巧妙方法:

public abstract class BaseDialog : IDialog<BaseResult>
{
    public bool DialogForwarded { get; protected set; }

    public async Task StartAsync(IDialogContext context)
    {
        context.Wait(OnMessageReceivedAsync);
    }

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

        var dialogFinished = await HandleMessageAsync(context, message);

        if (DialogForwarded) return;

        if (!dialogFinished)
        {
            context.Wait(OnMessageReceivedAsync);
        }
        else
        {
            context.Done(new DefaultDialogResult());
        }
    }

    protected abstract Task<bool> HandleMessageAsync(IDialogContext context, IMessageActivity message);

    protected async Task ForwardToDialog(IDialogContext context, 
        IMessageActivity message, BaseDialog dialog)
    {
        DialogForwarded = true;
        await context.Forward(dialog, (dialogContext, result) =>
        {
            // Dialog resume callback
            // this method gets called when the child dialog calls context.Done()
            DialogForwarded = false;
            return Task.CompletedTask;
        }, message);
    }
}

基础对话框,所有其他对话框的父对话框,将处理对话框的一般流程。如果对话尚未完成,它将通过调用 context.Wait 通知机器人框架,否则它将以 context.Done 结束对话。它还将强制所有子对话框实现 HandleMessageAsync 方法,其中 returns 和 bool 指示对话框是否已完成。并且还公开了一个可重用的方法 ForwardToDialog,我们的 IntentDetectorDialog 将使用该方法来处理意图重定向。

public class IntentDetectorDialog : BaseDialog
{
    private readonly INlpService _nlpService;

    public IntentDetectorDialog(INlpService nlpService)
    {
        _nlpService = nlpService;
    }

    protected override async Task<bool> HandleMessageAsync(IDialogContext context, IMessageActivity message)
    {
        var intentName = await _nlpService.AnalyzeAsync(message.Text);

        switch (intentName)
        {
            case "GoToQnaDialog":
                await ForwardToDialog(context, message, new QnaDialog());
                break;
            case "GoToGraphDialog":
                await ForwardToDialog(context, message, new GraphDialog());
                break;
        }

        return false;
    }
}

那是 IntentRedetectorDialogBaseDialog 的儿子,他唯一的工作就是检测意图并转发到相应的对话。为了使事情更具可扩展性,您可以实现一个 IntentDialogFactory,它可以根据检测到的意图构建对话框。

public class QnaDialog : BaseDialog
{
    protected override async Task<bool> HandleMessageAsync(IDialogContext context, IMessageActivity message)
    {
        if (message.Text == "My name is Javier")
        {
            await context.PostAsync("What a cool name!");

            // question was answered -> end the dialog
            return true;
        }
        else
        {
            await context.PostAsync("What is your name?");

            // wait for the user response
            return false;
        }
    }
}

最后我们有了 QnaDialog:也是 BaseDialog 的儿子,他唯一的工作就是询问用户名并等待响应。

编辑

根据您的评论,在您的 NlpService 中您可以拥有:

public class NlpServiceDispatcher : INlpService
{
    public async Task<NlpResult> AnalyzeAsync(string utterance)
    {
        var qnaResult = await _qnaMakerService.AnalyzeAsync(utterance);

        var luisResult = await _luisService.AnalyzeAsync(utterance);

        if (qnaResult.ConfidenceThreshold > luisResult.ConfidenceThreshold)
        {
            return qnaResult;
        }
        else
        {
            return luisResult;
        }
    }
}

然后将IntentDetectorDialog改为:

public class UtteranceAnalyzerDialog : BaseDialog
{
    private readonly INlpService _nlpService;

    public UtteranceAnalyzerDialog(INlpService nlpService)
    {
        _nlpService = nlpService;
    }

    protected override async Task<bool> HandleMessageAsync(IDialogContext context, IMessageActivity message)
    {
        var nlpResult = await _nlpService.AnalyzeAsync(message.Text);

        switch (nlpResult)
        {
            case QnaMakerResult qnaResult:
                await context.PostAsync(qnaResult.Answer);
                return true;

            case LuisResult luisResult:
                var dialog = _dialogFactory.BuildDialogByIntentName(luisResult.IntentName);
                await ForwardToDialog(context, message, dialog);
                break;
        }

        return false;
    }
}

给你!您无需在 Luis 和 QnaMaker 中重复发声,您可以同时使用两者并根据更有把握的结果来设置您的策略!