В мой бот интегрированы QnaMaker и LUIS. Я хочу, чтобы пользователь мог задавать вопросы между разговорами. Я уже обнаружил, что проблема в том, что бот всегда сначала смотрит в luis и qna, прежде чем обрабатывать ввод пользователя.
Например, если у меня есть запрос выбора с «Начать сейчас» и «Остановить сейчас», Luis или qna прервут и обработают ввод, снова вызовут диалоговое окно, что приведет к бесконечному циклу и никогда не достигнет следующего шага.
Я думаю, что это плохой дизайн с моей стороны. есть ли способ для следующего шага сначала обработать результат? Если он не распознал результат, luis и qna должны обработать ввод.
private async Task<bool> IsTurnInterruptedDispatchToQnAMakerAsync(ITurnContext turnContext, string topDispatch, string appName, CancellationToken cancellationToken = default(CancellationToken))
{
var dc = await _dialogs.CreateContextAsync(turnContext);
const string qnaDispatchKey = "q_xxxxxxxx";
if (topDispatch.Equals(qnaDispatchKey))
{
var results = await _services.QnAServices[appName].GetAnswersAsync(turnContext);
if (results.Any())
{
await turnContext.SendActivityAsync(results.First().Answer, cancellationToken: cancellationToken);
}
if (dc.ActiveDialog != null)
{
await dc.RepromptDialogAsync();
}
return true;
}
return false;
}
return false;
}
на OnTurnAsync()
var interruptedQnaMaker = await IsTurnInterruptedDispatchToQnAMakerAsync(turnContext, topDispatch, QnaConfiguration, cancellationToken);
if (interruptedQnaMaker)
{
await _basicAccessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
await _basicAccessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken);
return;
}
У вас как бы есть два вопроса, и я отвечу на них оба. Я не знаю, есть ли "лучший" способ сделать это - это действительно зависит от вашего кода. Возможно, вам также придется сделать некоторую комбинацию двух вещей, описанных ниже.
В моем примере показано, как это сделать с помощью LUIS, но здесь вы можете довольно легко заменить QnAMaker.
Передайте свой BotServices
в свой диалог (в конструкторе MyBot.cs
):
Dialogs.Add(new MyDialog(services));
Примечание. В зависимости от того, где вы это делаете, вы можете передать свой LuisRecognizer
вместо всех своих услуг.
Используйте BotServices
в конструкторе MyDialog
:
public class MyDialog : ComponentDialog
{
private readonly BotServices _services;
public MyDialog(BotServices services) : base(nameof(QuickDialog))
{
[...]
_services = services;
}
Создайте валидатор в ChoicePrompt:
AddDialog(new ChoicePrompt(nameof(ChoicePrompt), luisValidation));
Создайте свой валидатор, который позволит вам настроить ввод пользователя и установить его как нечто другое (например, намерение LUIS):
private async Task<bool> LuisValidationAsync(PromptValidatorContext<FoundChoice> promptContext, CancellationToken cancellationToken)
{
// ...Succeeded will only be true for a ChoicePrompt if user input matches a Choice
if (!promptContext.Recognized.Succeeded)
{
// User input doesn't match a choice, so get the LUIS result
var luisResults = await _services.LuisServices["nameOfLuisServiceInBotFile"].RecognizeAsync(promptContext.Context, cancellationToken);
var topScoringIntent = luisResults?.GetTopScoringIntent();
var topIntent = topScoringIntent.Value.intent;
// Save the results and pass them onto the next waterfall step
promptContext.Recognized.Succeeded = true;
promptContext.Recognized.Value = new FoundChoice()
{
Index = 0,
Score = 1,
Value = topIntent
};
// We converted to a valid LUIS result, so return true
return true;
}
// ...Succeeded was true, so return true
return true;
}
Есть несколько разных мест, где вы можете что-то сделать с результатом, вместо того, чтобы просто изменить ввод пользователя. Например, на следующем шаге вы можете:
switch ((stepContext.Result as FoundChoice).Value)
{
case "Reply":
await stepContext.Context.SendActivityAsync("Reply");
break;
case "Cancel":
return await stepContext.EndDialogAsync("Cancel Me");
}
return await stepContext.NextAsync();
Если пользователь вызовет намерение «Отменить», это поднимется до MyBot.cs
, а dialogResult.Result
будет равно «Отменить меня».
Есть два способа пропустить распознавание LUIS:
Если вы не хотите проверять прерывания, настройте условия, при которых вы хотели бы пропустить это. Вы можете использовать что-то вроде:
var interruptedQnaMaker = false;
if (!<yourCondition>)
{
var interruptedQnaMaker = await IsTurnInterruptedDispatchToQnAMakerAsync(turnContext, topDispatch, QnaConfiguration, cancellationToken);
}
Я сделал что-то похожее в узел-бот, где полностью пропустил luisRecognizer для некоторых диалогов. Для вас это может выглядеть примерно так:
var dc = await _dialogs.CreateContextAsync(turnContext);
if (dc.ActiveDialog != null && dc.ActiveDialog.id == "SkipLuisDialog")
{
var interruptedQnaMaker = await IsTurnInterruptedDispatchToQnAMakerAsync(turnContext, topDispatch, QnaConfiguration, cancellationToken);
}
Похоже, вы настроили его так, что когда LUIS возвращает намерение (topDispatch
), которое соответствует qnaDispatchKey
, это происходит, когда вы запускаете прерывание. Если «Начать сейчас» и «Остановить сейчас» возвращаются qnaDispatchKey
как намерение, вы можете настроить приложение LUIS, чтобы предотвратить это.сильный текст
Идентификатор моего диалога, похоже, не меняется и всегда «mainDialog». Могу ли я открыть вопрос об этом? Мне нравится ваша идея пропустить диалог для LUIS, если идентификатор диалога совпадает
Это вопрос, который я открыл относительно идентификаторов. Спасибо stackoverflow.com/questions/55594028/…
Спасибо! Это дает мне несколько идей. Путем настройки приложения LUIS, например, удаления высказываний, похожих на «начать сейчас» и «остановить сейчас»?