Obsługa przerw w działaniu jest ważnym aspektem niezawodnego bota. Użytkownicy nie zawsze będą postępować zgodnie ze zdefiniowanym przepływem konwersacji krok po kroku. Mogą spróbować zadać pytanie w trakcie procesu lub po prostu chcieć go anulować zamiast go ukończyć. W tym artykule opisano kilka typowych sposobów obsługi przerw w działaniu bota.
Podstawowy przykład bota używa usługi Language Understanding (LUIS) do identyfikowania intencji użytkowników; jednak identyfikowanie intencji użytkownika nie jest celem tego artykułu.
Aby uzyskać informacje na temat identyfikowania intencji użytkowników, zobacz Opis języka naturalnego i Dodawanie interpretacji języka naturalnego do bota.
Przykład używany w tym artykule modeluje bota rezerwacji lotów, który używa okien dialogowych do uzyskiwania informacji o locie od użytkownika. W dowolnym momencie podczas rozmowy z botem użytkownik może wydać pomoc lub anulować polecenia, aby spowodować przerwę. Istnieją dwa typy przerw w działaniu:
Aby użyć okien dialogowych, zainstaluj pakiet NuGet Microsoft.Bot.Builder.Dialogs .
Dialogs\CancelAndHelpDialog.cs
Zaimplementuj klasę w CancelAndHelpDialog
celu obsługi przerw w działaniu użytkowników. Okna dialogowe BookingDialog
z możliwością anulowania i DateResolverDialog
pochodzą z tej klasy.
public class CancelAndHelpDialog : ComponentDialog
CancelAndHelpDialog
W klasie metoda wywołuje metodę InterruptAsync
, aby sprawdzić, OnContinueDialogAsync
czy użytkownik przerwał normalny przepływ. Jeśli przepływ zostanie przerwany, metody klasy bazowej są wywoływane; w przeciwnym razie zwracana jest zwracana wartość InterruptAsync
zwracana.
protected override async Task<DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default)
{
var result = await InterruptAsync(innerDc, cancellationToken);
if (result != null)
{
return result;
}
return await base.OnContinueDialogAsync(innerDc, cancellationToken);
}
Jeśli użytkownik wpisze "help", metoda wysyła komunikat, InterruptAsync
a następnie wywołuje DialogTurnResult (DialogTurnStatus.Waiting)
polecenie, aby wskazać, że okno dialogowe u góry oczekuje na odpowiedź od użytkownika. W ten sposób przepływ konwersacji zostanie przerwany tylko na kolei, a następny obrót będzie kontynuowany od miejsca, w którym konwersacja została przerwana.
Jeśli użytkownik wpisze "cancel", wywołuje CancelAllDialogsAsync
wewnętrzny kontekst okna dialogowego, który czyści stos okna dialogowego i powoduje zakończenie z anulowanym stanem i bez wartości wyniku. Do (pokazano MainDialog
później), pojawi się, że okno dialogowe rezerwacji zakończyło się i zwróciło wartość null, podobnie jak wtedy, gdy użytkownik zdecyduje się nie potwierdzić swojej rezerwacji.
private async Task<DialogTurnResult> InterruptAsync(DialogContext innerDc, CancellationToken cancellationToken)
{
if (innerDc.Context.Activity.Type == ActivityTypes.Message)
{
var text = innerDc.Context.Activity.Text.ToLowerInvariant();
switch (text)
{
case "help":
case "?":
var helpMessage = MessageFactory.Text(HelpMsgText, HelpMsgText, InputHints.ExpectingInput);
await innerDc.Context.SendActivityAsync(helpMessage, cancellationToken);
return new DialogTurnResult(DialogTurnStatus.Waiting);
case "cancel":
case "quit":
var cancelMessage = MessageFactory.Text(CancelMsgText, CancelMsgText, InputHints.IgnoringInput);
await innerDc.Context.SendActivityAsync(cancelMessage, cancellationToken);
return await innerDc.CancelAllDialogsAsync(cancellationToken);
}
}
return null;
}
Aby użyć okien dialogowych, zainstaluj pakiet npm botbuilder-dialogs .
okna dialogowe/cancelAndHelpDialog.js
Zaimplementuj klasę w CancelAndHelpDialog
celu obsługi przerw w działaniu użytkowników. Okno dialogowe BookingDialog
z możliwością anulowania i DateResolverDialog
rozszerzenie tej klasy.
class CancelAndHelpDialog extends ComponentDialog {
CancelAndHelpDialog
W klasie metoda wywołuje metodę interrupt
, aby sprawdzić, onContinueDialog
czy użytkownik przerwał normalny przepływ. Jeśli przepływ zostanie przerwany, metody klasy bazowej są wywoływane; w przeciwnym razie zwracana jest zwracana wartość interrupt
zwracana.
async onContinueDialog(innerDc) {
const result = await this.interrupt(innerDc);
if (result) {
return result;
}
return await super.onContinueDialog(innerDc);
}
Jeśli użytkownik wpisze "pomoc", metoda wysyła komunikat, interrupt
a następnie zwraca { status: DialogTurnStatus.waiting }
obiekt, aby wskazać, że okno dialogowe na górze oczekuje na odpowiedź od użytkownika. W ten sposób przepływ konwersacji zostanie przerwany tylko na kolei, a następny obrót będzie kontynuowany od miejsca, w którym konwersacja została przerwana.
Jeśli użytkownik wpisze "cancel", wywołuje cancelAllDialogs
wewnętrzny kontekst okna dialogowego, który czyści stos okna dialogowego i powoduje zakończenie z anulowanym stanem i bez wartości wyniku. Do (pokazano MainDialog
później), pojawi się, że okno dialogowe rezerwacji zakończyło się i zwróciło wartość null, podobnie jak wtedy, gdy użytkownik zdecyduje się nie potwierdzić swojej rezerwacji.
async interrupt(innerDc) {
if (innerDc.context.activity.text) {
const text = innerDc.context.activity.text.toLowerCase();
switch (text) {
case 'help':
case '?': {
const helpMessageText = 'Show help here';
await innerDc.context.sendActivity(helpMessageText, helpMessageText, InputHints.ExpectingInput);
return { status: DialogTurnStatus.waiting };
}
case 'cancel':
case 'quit': {
const cancelMessageText = 'Cancelling...';
await innerDc.context.sendActivity(cancelMessageText, cancelMessageText, InputHints.IgnoringInput);
return await innerDc.cancelAllDialogs();
}
}
}
}
CancelAndHelpDialog.java
Zaimplementuj klasę w CancelAndHelpDialog
celu obsługi przerw w działaniu użytkowników. Okna dialogowe BookingDialog
z możliwością anulowania i DateResolverDialog
pochodzą z tej klasy.
public class CancelAndHelpDialog extends ComponentDialog {
CancelAndHelpDialog
W klasie metoda wywołuje metodę interrupt
, aby sprawdzić, onContinueDialog
czy użytkownik przerwał normalny przepływ. Jeśli przepływ zostanie przerwany, metody klasy bazowej są wywoływane; w przeciwnym razie zwracana jest zwracana wartość interrupt
zwracana.
@Override
protected CompletableFuture<DialogTurnResult> onContinueDialog(DialogContext innerDc) {
return interrupt(innerDc).thenCompose(result -> {
if (result != null) {
return CompletableFuture.completedFuture(result);
}
return super.onContinueDialog(innerDc);
});
}
Jeśli użytkownik wpisze "help", metoda wysyła komunikat, interrupt
a następnie wywołuje DialogTurnResult(DialogTurnStatus.WAITING)
polecenie, aby wskazać, że okno dialogowe u góry oczekuje na odpowiedź od użytkownika. W ten sposób przepływ konwersacji zostanie przerwany tylko na kolei, a następny obrót będzie kontynuowany od miejsca, w którym konwersacja została przerwana.
Jeśli użytkownik wpisze "cancel", wywołuje cancelAllDialogs
wewnętrzny kontekst okna dialogowego, który czyści stos okna dialogowego i powoduje zakończenie z anulowanym stanem i bez wartości wyniku. Do (pokazano MainDialog
później), pojawi się, że okno dialogowe rezerwacji zakończyło się i zwróciło wartość null, podobnie jak wtedy, gdy użytkownik zdecyduje się nie potwierdzić swojej rezerwacji.
private CompletableFuture<DialogTurnResult> interrupt(DialogContext innerDc) {
if (innerDc.getContext().getActivity().isType(ActivityTypes.MESSAGE)) {
String text = innerDc.getContext().getActivity().getText().toLowerCase();
switch (text) {
case "help":
case "?":
Activity helpMessage = MessageFactory
.text(helpMsgText, helpMsgText, InputHints.EXPECTING_INPUT);
return innerDc.getContext().sendActivity(helpMessage)
.thenCompose(sendResult ->
CompletableFuture
.completedFuture(new DialogTurnResult(DialogTurnStatus.WAITING)));
case "cancel":
case "quit":
Activity cancelMessage = MessageFactory
.text(cancelMsgText, cancelMsgText, InputHints.IGNORING_INPUT);
return innerDc.getContext()
.sendActivity(cancelMessage)
.thenCompose(sendResult -> innerDc.cancelAllDialogs());
default:
break;
}
}
return CompletableFuture.completedFuture(null);
}
Aby użyć okien dialogowych, zainstaluj botbuilder-dialogs
pakiet i upewnij się, że przykładowy requirements.txt
plik zawiera odpowiednie odwołanie, takie jak botbuilder-dialogs>=4.5.0
.
Aby uzyskać więcej informacji na temat instalowania pakietów, zobacz plik README repozytorium przykładów.
Uwaga
Uruchomione pip install botbuilder-dialogs
polecenie spowoduje również zainstalowanie botbuilder-core
parametrów , botbuilder-connector
i botbuilder-schema
.
okna dialogowe/cancel-and-help-dialog.py
Zaimplementuj klasę w CancelAndHelpDialog
celu obsługi przerw w działaniu użytkowników. Okna dialogowe BookingDialog
z możliwością anulowania i DateResolverDialog
pochodzą z tej klasy.
class CancelAndHelpDialog(ComponentDialog):
CancelAndHelpDialog
W klasie metoda wywołuje metodę interrupt
, aby sprawdzić, on_continue_dialog
czy użytkownik przerwał normalny przepływ. Jeśli przepływ zostanie przerwany, metody klasy bazowej są wywoływane; w przeciwnym razie zwracana jest zwracana wartość interrupt
zwracana.
async def on_continue_dialog(self, inner_dc: DialogContext) -> DialogTurnResult:
result = await self.interrupt(inner_dc)
if result is not None:
return result
return await super(CancelAndHelpDialog, self).on_continue_dialog(inner_dc)
Jeśli użytkownik wpisze ciąg "help" lub "?", metoda wysyła komunikat, interrupt
a następnie wywołuje DialogTurnResult(DialogTurnStatus.Waiting)
polecenie, aby wskazać, że okno dialogowe w górnej części stosu oczekuje na odpowiedź od użytkownika. W ten sposób przepływ konwersacji zostanie przerwany tylko na kolei, a następny obrót będzie kontynuowany od miejsca, w którym konwersacja została przerwana.
Jeśli użytkownik wpisze "cancel" lub "quit", wywołuje cancel_all_dialogs()
wewnętrzny kontekst okna dialogowego, który czyści stos okna dialogowego i powoduje zakończenie z anulowanym stanem i bez wartości wyniku. W wyświetlonym MainDialog
później oknie dialogowym rezerwacji zostanie zakończone i zwrócone wartość null, podobnie jak wtedy, gdy użytkownik zdecyduje się nie potwierdzić rezerwacji.
async def interrupt(self, inner_dc: DialogContext) -> DialogTurnResult:
if inner_dc.context.activity.type == ActivityTypes.message:
text = inner_dc.context.activity.text.lower()
help_message_text = "Show Help..."
help_message = MessageFactory.text(
help_message_text, help_message_text, InputHints.expecting_input
)
if text in ("help", "?"):
await inner_dc.context.send_activity(help_message)
return DialogTurnResult(DialogTurnStatus.Waiting)
cancel_message_text = "Cancelling"
cancel_message = MessageFactory.text(
cancel_message_text, cancel_message_text, InputHints.ignoring_input
)
if text in ("cancel", "quit"):
await inner_dc.context.send_activity(cancel_message)
return await inner_dc.cancel_all_dialogs()
return None
Po zaimplementowaniu klasy obsługi przerwań sprawdź, co się stanie, gdy ten bot otrzyma nowy komunikat od użytkownika.
Dialogs\MainDialog.cs
Po nadejściu nowego działania komunikatu bot uruchamia element MainDialog
. Monituje MainDialog
użytkownika o pomoc. Następnie uruchamia metodę BookingDialog
w metodzie MainDialog.ActStepAsync
z wywołaniem metody , jak BeginDialogAsync
pokazano poniżej.
private async Task<DialogTurnResult> ActStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if (!_luisRecognizer.IsConfigured)
{
// LUIS is not configured, we just run the BookingDialog path with an empty BookingDetailsInstance.
return await stepContext.BeginDialogAsync(nameof(BookingDialog), new BookingDetails(), cancellationToken);
}
// Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt.)
var luisResult = await _luisRecognizer.RecognizeAsync<FlightBooking>(stepContext.Context, cancellationToken);
switch (luisResult.TopIntent().intent)
{
case FlightBooking.Intent.BookFlight:
await ShowWarningForUnsupportedCities(stepContext.Context, luisResult, cancellationToken);
// Initialize BookingDetails with any entities we may have found in the response.
var bookingDetails = new BookingDetails()
{
// Get destination and origin from the composite entities arrays.
Destination = luisResult.ToEntities.Airport,
Origin = luisResult.FromEntities.Airport,
TravelDate = luisResult.TravelDate,
};
// Run the BookingDialog giving it whatever details we have from the LUIS call, it will fill out the remainder.
return await stepContext.BeginDialogAsync(nameof(BookingDialog), bookingDetails, cancellationToken);
case FlightBooking.Intent.GetWeather:
// We haven't implemented the GetWeatherDialog so we just display a TODO message.
var getWeatherMessageText = "TODO: get weather flow here";
var getWeatherMessage = MessageFactory.Text(getWeatherMessageText, getWeatherMessageText, InputHints.IgnoringInput);
await stepContext.Context.SendActivityAsync(getWeatherMessage, cancellationToken);
break;
default:
// Catch all for unhandled intents
var didntUnderstandMessageText = $"Sorry, I didn't get that. Please try asking in a different way (intent was {luisResult.TopIntent().intent})";
var didntUnderstandMessage = MessageFactory.Text(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput);
await stepContext.Context.SendActivityAsync(didntUnderstandMessage, cancellationToken);
break;
}
return await stepContext.NextAsync(null, cancellationToken);
}
Następnie w FinalStepAsync
metodzie MainDialog
klasy okno dialogowe rezerwacji zostało zakończone, a rezerwacja jest uważana za ukończoną lub anulowaną.
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
// If the child dialog ("BookingDialog") was cancelled, the user failed to confirm or if the intent wasn't BookFlight
// the Result here will be null.
if (stepContext.Result is BookingDetails result)
{
// Now we have all the booking details call the booking service.
// If the call to the booking service was successful tell the user.
var timeProperty = new TimexProperty(result.TravelDate);
var travelDateMsg = timeProperty.ToNaturalLanguage(DateTime.Now);
var messageText = $"I have you booked to {result.Destination} from {result.Origin} on {travelDateMsg}";
var message = MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput);
await stepContext.Context.SendActivityAsync(message, cancellationToken);
}
// Restart the main dialog with a different message the second time around
var promptMessage = "What else can I do for you?";
return await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage, cancellationToken);
}
Kod w BookingDialog
pliku nie jest tutaj wyświetlany, ponieważ nie jest bezpośrednio związany z obsługą przerw. Służy do monitowania użytkowników o szczegóły rezerwacji. Ten kod można znaleźć w oknie dialogowym\BookingDialogs.cs.
okna dialogowe/mainDialog.js
Po nadejściu nowego działania komunikatu bot uruchamia element MainDialog
. Monituje MainDialog
użytkownika o pomoc. Następnie uruchamia metodę bookingDialog
w metodzie MainDialog.actStep
z wywołaniem metody , jak beginDialog
pokazano poniżej.
async actStep(stepContext) {
const bookingDetails = {};
if (!this.luisRecognizer.isConfigured) {
// LUIS is not configured, we just run the BookingDialog path.
return await stepContext.beginDialog('bookingDialog', bookingDetails);
}
// Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt)
const luisResult = await this.luisRecognizer.executeLuisQuery(stepContext.context);
switch (LuisRecognizer.topIntent(luisResult)) {
case 'BookFlight': {
// Extract the values for the composite entities from the LUIS result.
const fromEntities = this.luisRecognizer.getFromEntities(luisResult);
const toEntities = this.luisRecognizer.getToEntities(luisResult);
// Show a warning for Origin and Destination if we can't resolve them.
await this.showWarningForUnsupportedCities(stepContext.context, fromEntities, toEntities);
// Initialize BookingDetails with any entities we may have found in the response.
bookingDetails.destination = toEntities.airport;
bookingDetails.origin = fromEntities.airport;
bookingDetails.travelDate = this.luisRecognizer.getTravelDate(luisResult);
console.log('LUIS extracted these booking details:', JSON.stringify(bookingDetails));
// Run the BookingDialog passing in whatever details we have from the LUIS call, it will fill out the remainder.
return await stepContext.beginDialog('bookingDialog', bookingDetails);
}
case 'GetWeather': {
// We haven't implemented the GetWeatherDialog so we just display a TODO message.
const getWeatherMessageText = 'TODO: get weather flow here';
await stepContext.context.sendActivity(getWeatherMessageText, getWeatherMessageText, InputHints.IgnoringInput);
break;
}
default: {
// Catch all for unhandled intents
const didntUnderstandMessageText = `Sorry, I didn't get that. Please try asking in a different way (intent was ${ LuisRecognizer.topIntent(luisResult) })`;
await stepContext.context.sendActivity(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput);
}
}
return await stepContext.next();
}
Następnie w finalStep
metodzie MainDialog
klasy okno dialogowe rezerwacji zostało zakończone, a rezerwacja jest uważana za ukończoną lub anulowaną.
async finalStep(stepContext) {
// If the child dialog ("bookingDialog") was cancelled or the user failed to confirm, the Result here will be null.
if (stepContext.result) {
const result = stepContext.result;
// Now we have all the booking details.
// This is where calls to the booking AOU service or database would go.
// If the call to the booking service was successful tell the user.
const timeProperty = new TimexProperty(result.travelDate);
const travelDateMsg = timeProperty.toNaturalLanguage(new Date(Date.now()));
const msg = `I have you booked to ${ result.destination } from ${ result.origin } on ${ travelDateMsg }.`;
await stepContext.context.sendActivity(msg, msg, InputHints.IgnoringInput);
}
// Restart the main dialog with a different message the second time around
return await stepContext.replaceDialog(this.initialDialogId, { restartMsg: 'What else can I do for you?' });
}
Kod w BookingDialog
pliku nie jest tutaj wyświetlany, ponieważ nie jest bezpośrednio związany z obsługą przerw. Służy do monitowania użytkowników o szczegóły rezerwacji. Ten kod można znaleźć w oknach dialogowych/bookingDialogs.js.
MainDialog.java
Po nadejściu nowego działania komunikatu bot uruchamia element MainDialog
. Monituje MainDialog
użytkownika o pomoc. Następnie uruchamia metodę BookingDialog
w metodzie MainDialog.actStep
z wywołaniem metody , jak beginDialog
pokazano poniżej.
private CompletableFuture<DialogTurnResult> actStep(WaterfallStepContext stepContext) {
if (!luisRecognizer.isConfigured()) {
// LUIS is not configured, we just run the BookingDialog path with an empty BookingDetailsInstance.
return stepContext.beginDialog("BookingDialog", new BookingDetails());
}
// Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt.)
return luisRecognizer.recognize(stepContext.getContext()).thenCompose(luisResult -> {
switch (luisResult.getTopScoringIntent().intent) {
case "BookFlight":
// Extract the values for the composite entities from the LUIS result.
ObjectNode fromEntities = luisRecognizer.getFromEntities(luisResult);
ObjectNode toEntities = luisRecognizer.getToEntities(luisResult);
// Show a warning for Origin and Destination if we can't resolve them.
return showWarningForUnsupportedCities(
stepContext.getContext(), fromEntities, toEntities)
.thenCompose(showResult -> {
// Initialize BookingDetails with any entities we may have found in the response.
BookingDetails bookingDetails = new BookingDetails();
bookingDetails.setDestination(toEntities.get("airport").asText());
bookingDetails.setOrigin(fromEntities.get("airport").asText());
bookingDetails.setTravelDate(luisRecognizer.getTravelDate(luisResult));
// Run the BookingDialog giving it whatever details we have from the LUIS call,
// it will fill out the remainder.
return stepContext.beginDialog("BookingDialog", bookingDetails);
}
);
case "GetWeather":
// We haven't implemented the GetWeatherDialog so we just display a TODO message.
String getWeatherMessageText = "TODO: get weather flow here";
Activity getWeatherMessage = MessageFactory
.text(
getWeatherMessageText, getWeatherMessageText,
InputHints.IGNORING_INPUT
);
return stepContext.getContext().sendActivity(getWeatherMessage)
.thenCompose(resourceResponse -> stepContext.next(null));
default:
// Catch all for unhandled intents
String didntUnderstandMessageText = String.format(
"Sorry, I didn't get that. Please "
+ " try asking in a different way (intent was %s)",
luisResult.getTopScoringIntent().intent
);
Activity didntUnderstandMessage = MessageFactory
.text(
didntUnderstandMessageText, didntUnderstandMessageText,
InputHints.IGNORING_INPUT
);
return stepContext.getContext().sendActivity(didntUnderstandMessage)
.thenCompose(resourceResponse -> stepContext.next(null));
}
});
}
Następnie w finalStep
metodzie MainDialog
klasy okno dialogowe rezerwacji zostało zakończone, a rezerwacja jest uważana za ukończoną lub anulowaną.
private CompletableFuture<DialogTurnResult> finalStep(WaterfallStepContext stepContext) {
CompletableFuture<Void> stepResult = CompletableFuture.completedFuture(null);
// If the child dialog ("BookingDialog") was cancelled,
// the user failed to confirm or if the intent wasn't BookFlight
// the Result here will be null.
if (stepContext.getResult() instanceof BookingDetails) {
// Now we have all the booking details call the booking service.
// If the call to the booking service was successful tell the user.
BookingDetails result = (BookingDetails) stepContext.getResult();
TimexProperty timeProperty = new TimexProperty(result.getTravelDate());
String travelDateMsg = timeProperty.toNaturalLanguage(LocalDateTime.now());
String messageText = String.format("I have you booked to %s from %s on %s",
result.getDestination(), result.getOrigin(), travelDateMsg
);
Activity message = MessageFactory
.text(messageText, messageText, InputHints.IGNORING_INPUT);
stepResult = stepContext.getContext().sendActivity(message).thenApply(sendResult -> null);
}
// Restart the main dialog with a different message the second time around
String promptMessage = "What else can I do for you?";
return stepResult
.thenCompose(result -> stepContext.replaceDialog(getInitialDialogId(), promptMessage));
}
Kod w BookingDialog
pliku nie jest tutaj wyświetlany, ponieważ nie jest bezpośrednio związany z obsługą przerw. Służy do monitowania użytkowników o szczegóły rezerwacji. Ten kod można znaleźć w BookingDialogs.java.
okna dialogowe/main_dialog.py
Po nadejściu nowego działania komunikatu bot uruchamia element MainDialog
. Monituje MainDialog
użytkownika o pomoc. Następnie uruchamia metodę bookingDialog
w metodzie act_step
z wywołaniem metody , jak begin_dialog
pokazano poniżej.
async def act_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
if not self._luis_recognizer.is_configured:
# LUIS is not configured, we just run the BookingDialog path with an empty BookingDetailsInstance.
return await step_context.begin_dialog(
self._booking_dialog_id, BookingDetails()
)
# Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt.)
intent, luis_result = await LuisHelper.execute_luis_query(
self._luis_recognizer, step_context.context
)
if intent == Intent.BOOK_FLIGHT.value and luis_result:
# Show a warning for Origin and Destination if we can't resolve them.
await MainDialog._show_warning_for_unsupported_cities(
step_context.context, luis_result
)
# Run the BookingDialog giving it whatever details we have from the LUIS call.
return await step_context.begin_dialog(self._booking_dialog_id, luis_result)
if intent == Intent.GET_WEATHER.value:
get_weather_text = "TODO: get weather flow here"
get_weather_message = MessageFactory.text(
get_weather_text, get_weather_text, InputHints.ignoring_input
)
await step_context.context.send_activity(get_weather_message)
else:
didnt_understand_text = (
"Sorry, I didn't get that. Please try asking in a different way"
)
didnt_understand_message = MessageFactory.text(
didnt_understand_text, didnt_understand_text, InputHints.ignoring_input
)
await step_context.context.send_activity(didnt_understand_message)
return await step_context.next(None)
Następnie w final_step
metodzie MainDialog
klasy okno dialogowe rezerwacji zostało zakończone, a rezerwacja jest uważana za ukończoną lub anulowaną.
async def final_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
# If the child dialog ("BookingDialog") was cancelled or the user failed to confirm,
# the Result here will be null.
if step_context.result is not None:
result = step_context.result
# Now we have all the booking details call the booking service.
# If the call to the booking service was successful tell the user.
# time_property = Timex(result.travel_date)
# travel_date_msg = time_property.to_natural_language(datetime.now())
msg_txt = f"I have you booked to {result.destination} from {result.origin} on {result.travel_date}"
message = MessageFactory.text(msg_txt, msg_txt, InputHints.ignoring_input)
await step_context.context.send_activity(message)
prompt_message = "What else can I do for you?"
return await step_context.replace_dialog(self.id, prompt_message)
Program obsługi błędów karty obsługuje wszelkie wyjątki, które nie zostały przechwycone w botze.
AdapterWithErrorHandler.cs
W przykładzie program obsługi karty OnTurnError
odbiera wszelkie wyjątki zgłaszane przez logikę kolei bota. Jeśli wystąpi wyjątek, program obsługi usunie stan konwersacji dla bieżącej konwersacji, aby zapobiec zablokowaniu bota w pętli błędów spowodowanej nieprawidłowym stanem.
{
// Log any leaked exception from the application.
// NOTE: In production environment, you should consider logging this to
// Azure Application Insights. Visit https://aka.ms/bottelemetry to see how
// to add telemetry capture to your bot.
logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");
// Send a message to the user
var errorMessageText = "The bot encountered an error or bug.";
var errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.IgnoringInput);
await turnContext.SendActivityAsync(errorMessage);
errorMessageText = "To continue to run this bot, please fix the bot source code.";
errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.ExpectingInput);
await turnContext.SendActivityAsync(errorMessage);
if (conversationState != null)
{
try
{
// Delete the conversationState for the current conversation to prevent the
// bot from getting stuck in a error-loop caused by being in a bad state.
// ConversationState should be thought of as similar to "cookie-state" in a Web pages.
await conversationState.DeleteAsync(turnContext);
}
catch (Exception e)
{
logger.LogError(e, $"Exception caught on attempting to Delete ConversationState : {e.Message}");
}
}
// Send a trace activity, which will be displayed in the Bot Framework Emulator
await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError");
};
}
index.js
W przykładzie program obsługi karty onTurnError
odbiera wszelkie wyjątki zgłaszane przez logikę kolei bota. Jeśli wystąpi wyjątek, program obsługi usunie stan konwersacji dla bieżącej konwersacji, aby zapobiec zablokowaniu bota w pętli błędów spowodowanej nieprawidłowym stanem.
// Send a trace activity, which will be displayed in Bot Framework Emulator
await context.sendTraceActivity(
'OnTurnError Trace',
`${ error }`,
'https://www.botframework.com/schemas/error',
'TurnError'
);
// Send a message to the user
let onTurnErrorMessage = 'The bot encountered an error or bug.';
await context.sendActivity(onTurnErrorMessage, onTurnErrorMessage, InputHints.ExpectingInput);
onTurnErrorMessage = 'To continue to run this bot, please fix the bot source code.';
await context.sendActivity(onTurnErrorMessage, onTurnErrorMessage, InputHints.ExpectingInput);
// Clear out state
await conversationState.delete(context);
};
// Set the onTurnError for the singleton CloudAdapter.
adapter.onTurnError = onTurnErrorHandler;
// Define a state store for your bot. See https://aka.ms/about-bot-state to learn more about using MemoryStorage.
// A bot requires a state store to persist the dialog and user state between messages.
// For local development, in-memory storage is used.
Rejestrując element AdapterWithErrorHandler
za pomocą platformy Spring w Application.java dla BotFrameworkHttpAdapter
tego przykładu, program obsługi adaptera onTurnError
odbiera wszelkie wyjątki zgłaszane przez logikę kolei bota. Jeśli wystąpi wyjątek, program obsługi usunie stan konwersacji dla bieżącej konwersacji, aby zapobiec zablokowaniu bota w pętli błędów spowodowanej nieprawidłowym stanem. W zestawie SDK języka Java element AdapterWithErrorHandler
jest implementowany jako część zestawu SDK i jest zawarty w pakiecie com.microsoft.bot.integration . Zobacz kod źródłowy zestawu Java SDK, aby uzyskać szczegółowe informacje na temat implementacji tej karty.
adapter_with_error_handler.py
W przykładzie program obsługi karty on_error
odbiera wszelkie wyjątki zgłaszane przez logikę kolei bota. Jeśli wystąpi wyjątek, program obsługi usunie stan konwersacji dla bieżącej konwersacji, aby zapobiec zablokowaniu bota w pętli błędów spowodowanej nieprawidłowym stanem.
def __init__(
self,
settings: ConfigurationBotFrameworkAuthentication,
conversation_state: ConversationState,
):
super().__init__(settings)
self._conversation_state = conversation_state
# Catch-all for errors.
async def on_error(context: TurnContext, error: Exception):
# This check writes out errors to console log
# NOTE: In production environment, you should consider logging this to Azure
# application insights.
print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr)
traceback.print_exc()
# Send a message to the user
await context.send_activity("The bot encountered an error or bug.")
await context.send_activity(
"To continue to run this bot, please fix the bot source code."
)
# Send a trace activity if we're talking to the Bot Framework Emulator
if context.activity.channel_id == "emulator":
# Create a trace activity that contains the error object
trace_activity = Activity(
label="TurnError",
name="on_turn_error Trace",
timestamp=datetime.utcnow(),
type=ActivityTypes.trace,
value=f"{error}",
value_type="https://www.botframework.com/schemas/error",
)
# Send a trace activity, which will be displayed in Bot Framework Emulator
await context.send_activity(trace_activity)
# Clear out state
nonlocal self
await self._conversation_state.delete(context)
self.on_turn_error = on_error
Startup.cs
Na koniec w Startup.cs
systemie bot jest tworzony jako przejściowy, a na każdym kroku tworzone jest nowe wystąpienie bota.
// Register the BookingDialog.
Poniżej przedstawiono definicje klas, które są używane w wywołaniu do utworzenia bota powyżej.
public class DialogAndWelcomeBot<T> : DialogBot<T>
public class DialogBot<T> : ActivityHandler
where T : Dialog
public class MainDialog : ComponentDialog
index.js
Na koniec w index.js
pliku zostanie utworzony bot.
const dialog = new MainDialog(luisRecognizer, bookingDialog);
const bot = new DialogAndWelcomeBot(conversationState, userState, dialog);
// Create HTTP server
const server = restify.createServer();
server.use(restify.plugins.bodyParser());
server.listen(process.env.port || process.env.PORT || 3978, function() {
console.log(`\n${ server.name } listening to ${ server.url }`);
console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator');
Poniżej przedstawiono definicje klas, które są używane w wywołaniu do utworzenia bota powyżej.
class MainDialog extends ComponentDialog {
class DialogAndWelcomeBot extends DialogBot {
class DialogBot extends ActivityHandler {
Application.java
Na koniec w Application.java
pliku zostanie utworzony bot.
@Bean
public Bot getBot(
Configuration configuration,
UserState userState,
ConversationState conversationState
) {
FlightBookingRecognizer recognizer = new FlightBookingRecognizer(configuration);
MainDialog dialog = new MainDialog(recognizer, new BookingDialog());
return new DialogAndWelcomeBot<>(conversationState, userState, dialog);
}
Poniżej przedstawiono definicje klas, które są używane w wywołaniu do utworzenia bota powyżej.
public class DialogAndWelcomeBot<T extends Dialog> extends DialogBot {
public class DialogBot<T extends Dialog> extends ActivityHandler {
public class MainDialog extends ComponentDialog {
app.py Na koniec w app.py
pliku zostanie utworzony bot.
# Create dialogs and Bot
RECOGNIZER = FlightBookingRecognizer(CONFIG)
BOOKING_DIALOG = BookingDialog()
DIALOG = MainDialog(RECOGNIZER, BOOKING_DIALOG)
BOT = DialogAndWelcomeBot(CONVERSATION_STATE, USER_STATE, DIALOG)
Aby uzyskać odwołanie, poniżej przedstawiono definicje klas, które są używane w wywołaniu do utworzenia bota.
class MainDialog(ComponentDialog):
class DialogAndWelcomeBot(DialogBot):
class DialogBot(ActivityHandler):