Если вы хотите, чтобы вещи масштабировались, вам будет лучше, если вы создадите собственную службу 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
, который возвращает 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;
}
}
Это IntentRedetectorDialog
: сын BaseDialog
, единственное задание которого состоит в том, чтобы определить намерение и перейти к соответствующему диалогу. Чтобы сделать вещи более масштабируемыми, вы можете реализовать 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, вы можете просто использовать оба и установить свою стратегию на основе более уверенного результата!