Uso de diálogos en una aptitudUse dialogs within a skill

se aplica a: SDK V4APPLIES TO: SDK v4

En este artículo se muestra cómo crear una aptitud que admita varias acciones.This article demonstrates how to create a skill that supports multiple actions. Admite estas acciones mediante diálogos.It supports these actions using dialogs. El diálogo principal recibe la entrada inicial del consumidor de aptitudes y, a continuación, inicia la acción adecuada.The main dialog receives the initial input from the skill consumer, and then starts the appropriate action. Para más información sobre cómo implementar el consumidor de aptitudes en el código de ejemplo asociado, vea cómo consumir una aptitud mediante diálogos.For information about implementing the skill consumer for the associated sample code, see how to consume a skill using dialogs.

En este artículo se da por supuesto que ya está familiarizado con la creación de aptitudes.This article assumes you are already familiar with creating skills. Para más información sobre cómo crear un bot de aptitudes en general, consulte cómo implementar una aptitud.For how to create a skill bot in general, see how to implement a skill.

PrerrequisitosPrerequisites

Nota

A partir de la versión 4.11, no necesita un identificador de aplicación y una contraseña para probar una aptitud localmente en el emulador.Starting with version 4.11, you do not need an app ID and password to test a skill locally in the Emulator. Todavía se requiere una suscripción de Azure para implementar la aptitud en Azure.An Azure subscription is still required to deploy your skill to Azure.

Acerca de este ejemploAbout this sample

El ejemplo skills skillDialog incluye proyectos para dos bots:The skills skillDialog sample includes projects for two bots:

  • El bot raíz del diálogo, que utiliza una clase de diálogo de aptitud para consumir una aptitud.The dialog root bot, which uses a skill dialog class to consume a skill.
  • El bot de aptitud de diálogo, que utiliza un diálogo para controlar las actividades que provienen de los consumidores de aptitudes.The dialog skill bot, which uses a dialog to handle activities coming from skill consumers. Esta aptitud es una adaptación del ejemplo de bot principal.This skill is an adaptation of the core bot sample. Para más información sobre el bot principal, consulte cómo incorporar reconocimiento de lenguaje natural a un bot.(For more about the core bot, see how to add natural language understanding to your bot.)

Este artículo se centra en cómo usar diálogos dentro de un bot de aptitudes para administrar varias acciones.This article focuses on how to use a dialogs within a skill bot to manage multiple actions.

Para más información sobre el bot de consumidor de aptitudes, consulte cómo consumir una aptitud mediante diálogos.For information about the skill consumer bot, see how to consume a skill using dialogs.

RecursosResources

Para los bots implementados, la autenticación de bot a bot requiere que cada bot participante tenga un identificador de aplicación y una contraseña válidos.For deployed bots, bot-to-bot authentication requires that each participating bot has a valid app ID and password. Sin embargo, puede probar las aptitudes y los consumidores de aptitudes localmente con el emulador sin un identificador de aplicación y una contraseña.However, you can test skills and skill consumers locally with the Emulator without an app ID and password.

Para que la aptitud esté disponible para los bots orientados al usuario, registre la aptitud en Azure.To make the skill available to user-facing bots, register the skill with Azure. Puede usar un registro de canales de bot.You can use a Bot Channels Registration. Para más información, vea cómo registrar un bot con Azure Bot Service.For more information, see how to register a bot with Azure Bot Service.

Opcionalmente, el bot de aptitudes puede utilizar un modelo LUIS de reserva de vuelos.Optionally, the skill bot can use a flight-booking LUIS model. Para utilizar este modelo, use el archivo CognitiveModels/FlightBooking.json para crear, entrenar y publicar el modelo de LUIS.To use this model, use the CognitiveModels/FlightBooking.json file to create, train, and publish the LUIS model.

Configuración de aplicacionesApplication configuration

  1. Opcionalmente, agregue el identificador de aplicación y la contraseña de la aptitud al archivo de configuración de la aptitud.Optionally, add the skill's app ID and password to the skill's configuration file. (Si el consumidor de aptitudes o aptitudes usa un identificador de aplicación y una contraseña, ambos deben hacerlo).(If either the skill or skill consumer uses an app ID and password, both must.)

  2. Si usa el modelo de LUIS, agregue el identificador de aplicación, la clave de API y el nombre de host de API de LUIS.If you are using the LUIS model, Add the LUIS app ID, API key, and API host name.

DialogSkillBot\appsettings.jsonDialogSkillBot\appsettings.json

{
  "MicrosoftAppId": "",
  "MicrosoftAppPassword": "",
  "ConnectionName": "",

  "LuisAppId": "",
  "LuisAPIKey": "",
  "LuisAPIHostName": "",

  // This is a comma separate list with the App IDs that will have access to the skill.
  // This setting is used in AllowedCallersClaimsValidator.
  // Examples: 
  //    [ "*" ] allows all callers.
  //    [ "AppId1", "AppId2" ] only allows access to parent bots with "AppId1" and "AppId2".
  "AllowedCallers": [ "*" ]
}

Lógica del enrutamiento de actividadesActivity-routing logic

La aptitud admite un par de características diferentes.The skill supports a couple different features. Puede reservar un vuelo u obtener la información del tiempo de una ciudad.It can book a flight or get the weather for a city. Además, si recibe un mensaje fuera de estos contextos, puede usar LUIS para intentar interpretar el mensaje.In addition, if it receives a message outside either of these contexts, it can use LUIS to try to interpret the message. El manifiesto de la aptitud describe estas acciones, sus parámetros de entrada y salida, y los puntos de conexión de la aptitud.The skill's manifest describes these actions, their input and output parameters, and the skill's endpoints. Cabe destacar que la aptitud puede controlar un evento "BookFlight" o "GetWeather".Of note, the skill can handle a "BookFlight" or "GetWeather" event. También puede controlar las actividades de mensajes.It can also handle message activities.

La aptitud define un diálogo de enrutamiento de actividades que se usa para seleccionar la acción que se va a iniciar, en función de la actividad de entrada inicial del consumidor de aptitudes.The skill defines an activity-routing dialog it uses to select which action to initiate, based on the initial incoming activity from the skill consumer. Si se proporciona, el modelo de LUIS puede reconocer intenciones de reserva de vuelo y de consulta del tiempo en un mensaje inicial.If provided, the LUIS model can recognize book-flight and get-weather intents in an initial message.

La acción de reserva de vuelo es un proceso que consta de varios pasos y se implementa como un diálogo independiente.The book-flight action is a multi-step process, implemented as a separate dialog. Una vez iniciada la acción, este diálogo controla las actividades entrantes.Once the action begins, incoming activities are handled by that dialog. La acción de consulta del tiempo tiene una lógica de marcador de posición que se reemplazaría en un bot totalmente implementado.The get-weather action has placeholder logic that would be replaced in a fully implemented bot.

El diálogo de enrutamiento de actividades incluye código para:The activity-routing dialog includes code to:

Los diálogos que se usan en la aptitud heredan de la clase de diálogo de componente.The dialogs used in the skill inherit from the component dialog class. Para más información sobre los diálogos de componente, consulte cómo administrar la complejidad de los diálogos.For more about component dialogs, see how to manage dialog complexity.

Inicialización del diálogoInitialize the dialog

El diálogo de enrutamiento de actividades incluye un diálogo secundario para reservar un vuelo.The activity-routing dialog includes a child dialog for booking a flight. El diálogo en cascada principal tiene un paso que iniciará una acción en función de la actividad inicial recibida.The main waterfall dialog has one step that will start an action based on the initial activity received.

También acepta un reconocedor de LUIS.It also accepts a LUIS recognizer. Si se inicializa este reconocedor, el diálogo lo utilizará para interpretar la intención de una actividad de mensaje inicial.If this recognizer is initialized, the dialog will use it to interpret the intent of an initial message activity.

DialogSkillBot\Dialogs\ActivityRouterDialog.csDialogSkillBot\Dialogs\ActivityRouterDialog.cs

private readonly DialogSkillBotRecognizer _luisRecognizer;

public ActivityRouterDialog(DialogSkillBotRecognizer luisRecognizer)
    : base(nameof(ActivityRouterDialog))
{
    _luisRecognizer = luisRecognizer;

    AddDialog(new BookingDialog());
    AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[] { ProcessActivityAsync }));

    // The initial child Dialog to run.
    InitialDialogId = nameof(WaterfallDialog);
}

Procesamiento de una actividad inicialProcess an initial activity

En el primer y único paso del diálogo de cascada principal, la aptitud comprueba el tipo de actividad entrante.In the first (and only) step of the main waterfall dialog, the skill checks the incoming activity type.

  • Las actividades de eventos se reenvían a un controlador de actividad según el evento, que inicia la acción adecuada según el nombre del evento.Event activities are forwarded to an on event activity handler that starts the appropriate action based on the name of the event.
  • Las actividades de mensajes se reenvían a un controlador de la actividad según el mensaje, que realiza un procesamiento adicional antes de decidir qué hacer.Message activities are forwarded to an on message activity handler that performs additional processing before deciding what to do.

Si la aptitud no reconoce el tipo de la actividad entrante o el nombre del evento, envía un mensaje de error y finaliza.If the skill doesn't recognize the type of the incoming activity or the name of the event, it sends an error message and ends.

DialogSkillBot\Dialogs\ActivityRouterDialog.csDialogSkillBot\Dialogs\ActivityRouterDialog.cs

private async Task<DialogTurnResult> ProcessActivityAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // A skill can send trace activities, if needed.
    await stepContext.Context.TraceActivityAsync($"{GetType().Name}.ProcessActivityAsync()", label: $"Got ActivityType: {stepContext.Context.Activity.Type}", cancellationToken: cancellationToken);

    switch (stepContext.Context.Activity.Type)
    {
        case ActivityTypes.Event:
            return await OnEventActivityAsync(stepContext, cancellationToken);

        case ActivityTypes.Message:
            return await OnMessageActivityAsync(stepContext, cancellationToken);

        default:
            // We didn't get an activity type we can handle.
            await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Unrecognized ActivityType: \"{stepContext.Context.Activity.Type}\".", inputHint: InputHints.IgnoringInput), cancellationToken);
            return new DialogTurnResult(DialogTurnStatus.Complete);
    }
}
// This method performs different tasks based on the event name.
private async Task<DialogTurnResult> OnEventActivityAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var activity = stepContext.Context.Activity;
    await stepContext.Context.TraceActivityAsync($"{GetType().Name}.OnEventActivityAsync()", label: $"Name: {activity.Name}. Value: {GetObjectAsJsonString(activity.Value)}", cancellationToken: cancellationToken);

    // Resolve what to execute based on the event name.
    switch (activity.Name)
    {
        case "BookFlight":
            return await BeginBookFlight(stepContext, cancellationToken);

        case "GetWeather":
            return await BeginGetWeather(stepContext, cancellationToken);

        default:
            // We didn't get an event name we can handle.
            await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Unrecognized EventName: \"{activity.Name}\".", inputHint: InputHints.IgnoringInput), cancellationToken);
            return new DialogTurnResult(DialogTurnStatus.Complete);
    }
}

Control de las actividades de mensajesHandle message activities

Si el reconocedor de LUIS está configurado, la aptitud llama a LUIS y, a continuación, inicia una acción basada en la intención.If the LUIS recognizer is configured, the skill calls LUIS and then starts an action based on the intent. Si el reconocedor de LUIS no está configurado o no se admite la intención, la aptitud envía un mensaje de error y finaliza.If the LUIS recognizer is not configured or the intent is not supported, the skill sends an error message and ends.

DialogSkillBot\Dialogs\ActivityRouterDialog.csDialogSkillBot\Dialogs\ActivityRouterDialog.cs

// This method just gets a message activity and runs it through LUIS. 
private async Task<DialogTurnResult> OnMessageActivityAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var activity = stepContext.Context.Activity;
    await stepContext.Context.TraceActivityAsync($"{GetType().Name}.OnMessageActivityAsync()", label: $"Text: \"{activity.Text}\". Value: {GetObjectAsJsonString(activity.Value)}", cancellationToken: cancellationToken);

    if (!_luisRecognizer.IsConfigured)
    {
        await stepContext.Context.SendActivityAsync(MessageFactory.Text("NOTE: LUIS is not configured. To enable all capabilities, add 'LuisAppId', 'LuisAPIKey' and 'LuisAPIHostName' to the appsettings.json file.", inputHint: InputHints.IgnoringInput), cancellationToken);
    }
    else
    {
        // Call LUIS with the utterance.
        var luisResult = await _luisRecognizer.RecognizeAsync<FlightBooking>(stepContext.Context, cancellationToken);

        // Create a message showing the LUIS results.
        var sb = new StringBuilder();
        sb.AppendLine($"LUIS results for \"{activity.Text}\":");
        var (intent, intentScore) = luisResult.Intents.FirstOrDefault(x => x.Value.Equals(luisResult.Intents.Values.Max()));
        sb.AppendLine($"Intent: \"{intent}\" Score: {intentScore.Score}");

        await stepContext.Context.SendActivityAsync(MessageFactory.Text(sb.ToString(), inputHint: InputHints.IgnoringInput), cancellationToken);

        // Start a dialog if we recognize the intent.
        switch (luisResult.TopIntent().intent)
        {
            case FlightBooking.Intent.BookFlight:
                return await BeginBookFlight(stepContext, cancellationToken);

            case FlightBooking.Intent.GetWeather:
                return await BeginGetWeather(stepContext, cancellationToken);

            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 new DialogTurnResult(DialogTurnStatus.Complete);
}

Inicio de una acción de varios pasosBegin a multi-step action

La acción de reserva de vuelo inicia un diálogo de varios pasos para obtener los detalles de la reserva del usuario.The book-flight action starts a multi-step dialog to get the booking details from the user.

La acción de obtención del tiempo no está implementada.The get-weather action is not implemented. Actualmente, envía un mensaje de marcador de posición y finaliza.Currently, it sends a placeholder message and then ends.

DialogSkillBot\Dialogs\ActivityRouterDialog.csDialogSkillBot\Dialogs\ActivityRouterDialog.cs

private async Task<DialogTurnResult> BeginBookFlight(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var activity = stepContext.Context.Activity;
    var bookingDetails = new BookingDetails();
    if (activity.Value != null)
    {
        bookingDetails = JsonConvert.DeserializeObject<BookingDetails>(JsonConvert.SerializeObject(activity.Value));
    }

    // Start the booking dialog.
    var bookingDialog = FindDialog(nameof(BookingDialog));
    return await stepContext.BeginDialogAsync(bookingDialog.Id, bookingDetails, cancellationToken);
}
private static async Task<DialogTurnResult> BeginGetWeather(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var activity = stepContext.Context.Activity;
    var location = new Location();
    if (activity.Value != null)
    {
        location = JsonConvert.DeserializeObject<Location>(JsonConvert.SerializeObject(activity.Value));
    }

    // We haven't implemented the GetWeatherDialog so we just display a TODO message.
    var getWeatherMessageText = $"TODO: get weather for here (lat: {location.Latitude}, long: {location.Longitude}";
    var getWeatherMessage = MessageFactory.Text(getWeatherMessageText, getWeatherMessageText, InputHints.IgnoringInput);
    await stepContext.Context.SendActivityAsync(getWeatherMessage, cancellationToken);
    return new DialogTurnResult(DialogTurnStatus.Complete);
}

Devolución de un resultadoReturn a result

La aptitud inicia un diálogo de reserva para la acción de reserva de vuelo.The skill starts a booking dialog for the book-flight action. Como el diálogo de enrutamiento de actividades tiene un solo paso, cuando el diálogo de reserva finaliza, el diálogo de enrutamiento de actividades también finaliza y el resultado del diálogo de reserva se convierte en el resultado del diálogo de enrutamiento de actividades.Since the activity-routing dialog has just one step, when the booking dialog ends, the activity-routing dialog also ends, and the dialog result from the booking dialog becomes the dialog result for the activity-routing dialog.

La acción de obtención del tiempo simplemente finaliza sin establecer un valor devuelto.The get-weather action simply ends without setting a return value.

Cancelación de una acción de varios pasosCanceling a multi-step action

El diálogo de reserva y su diálogo de resolución de fecha secundario derivan del diálogo base de cancelación y ayuda, que comprueba los mensajes del usuario.The booking dialog and its child date-resolver dialog both derive from the base cancel-and-help dialog, which checks messages from the user.

  • Cuando se elige "ayuda" o "?", se muestra un mensaje de ayuda y el flujo de conversación continúa en el siguiente turno.On "help" or "?", it displays a help message, and then continues the conversation flow on the following turn.
  • Cuando se elige "cancelar" o "salir", se cancelan todos los diálogos y la aptitud finaliza.On "cancel" or "quit", it cancels all dialogs, which ends the skill.

Para más información, consulte cómo controlar las interrupciones del usuario.For more information, see how to handle user interruptions.

Registro de serviciosService registration

Los servicios necesarios para esta aptitud son los mismos que los necesarios para un bot de aptitudes en general.The services needed for this skill are the same as those needed for a skill bot in general. Vea cómo implementar una aptitud para obtener una explicación de los servicios necesarios.See how to implement a skill for a discussion of the required services.

Manifiesto de aptitudSkill manifest

Un manifiesto de aptitud es un archivo JSON que describe las actividades que puede realizar la aptitud, sus parámetros de entrada y salida, y sus puntos de conexión.A skill manifest is a JSON file that describes the activities the skill can perform, its input and output parameters, and the skill's endpoints. El manifiesto contiene la información que necesita para tener acceso a la aptitud desde otro bot.The manifest contains the information you need to access the skill from another bot.

DialogSkillBot\wwwroot\manifest\dialogchildbot-manifest-1.0.jsonDialogSkillBot\wwwroot\manifest\dialogchildbot-manifest-1.0.json

{
  "$schema": "https://schemas.botframework.com/schemas/skills/skill-manifest-2.0.0.json",
  "$id": "DialogSkillBot",
  "name": "Skill bot with dialogs",
  "version": "1.0",
  "description": "This is a sample skill definition for multiple activity types.",
  "publisherName": "Microsoft",
  "privacyUrl": "https://dialogskillbot.contoso.com/privacy.html",
  "copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
  "license": "",
  "iconUrl": "https://dialogskillbot.contoso.com/icon.png",
  "tags": [
    "sample",
    "travel",
    "weather",
    "luis"
  ],
  "endpoints": [
    {
      "name": "default",
      "protocol": "BotFrameworkV3",
      "description": "Default endpoint for the skill.",
      "endpointUrl": "https://dialogskillbot.contoso.com/api/messages",
      "msAppId": "00000000-0000-0000-0000-000000000000"
    }
  ],
  "activities": {
    "bookFlight": {
      "description": "Books a flight (multi turn).",
      "type": "event",
      "name": "BookFlight",
      "value": {
        "$ref": "#/definitions/bookingInfo"
      },
      "resultValue": {
        "$ref": "#/definitions/bookingInfo"
      }
    },
    "getWeather": {
      "description": "Retrieves and returns the weather for the user's location.",
      "type": "event",
      "name": "GetWeather",
      "value": {
        "$ref": "#/definitions/location"
      },
      "resultValue": {
        "$ref": "#/definitions/weatherReport"
      }
    },
    "passthroughMessage": {
      "type": "message",
      "description": "Receives the user's utterance and attempts to resolve it using the skill's LUIS models.",
      "value": {
        "type": "object"
      }
    }
  },
  "definitions": {
    "bookingInfo": {
      "type": "object",
      "required": [
        "origin"
      ],
      "properties": {
        "origin": {
          "type": "string",
          "description": "This is the origin city for the flight."
        },
        "destination": {
          "type": "string",
          "description": "This is the destination city for the flight."
        },
        "travelDate": {
          "type": "string",
          "description": "The date for the flight in YYYY-MM-DD format."
        }
      }
    },
    "weatherReport": {
      "type": "array",
      "description": "Array of forecasts for the next week.",
      "items": [
        {
          "type": "string"
        }
      ]
    },
    "location": {
      "type": "object",
      "description": "Location metadata.",
      "properties": {
        "latitude": {
          "type": "number",
          "title": "Latitude"
        },
        "longitude": {
          "type": "number",
          "title": "Longitude"
        },
        "postalCode": {
          "type": "string",
          "title": "Postal code"
        }
      }
    }
  }
}

El esquema del manifiesto de aptitud es un archivo JSON que describe el esquema del manifiesto de aptitud.The skill manifest schema is a JSON file that describes the schema of the skill manifest. La versión más reciente del esquema es v2.1.The latest schema version is v2.1.

Prueba del bot de aptitudesTest the skill bot

Puede probar la aptitud en el emulador con el consumidor de aptitudes.You can test the skill in the Emulator with the skill consumer. Para ello, debe ejecutar los bots de aptitudes y de consumidor de aptitudes al mismo tiempo.To do so, you need to run both the skill and skill consumer bots at the same time. Vea cómo usar un diálogo para consumir una aptitud para obtener información sobre cómo configurar la aptitud.See how to use a dialog to consume a skill for information on how to configure the skill.

Descargue e instale la versión más reciente de Bot Framework Emulator.Download and install the latest Bot Framework Emulator.

  1. Ejecute el bot de aptitud de diálogo y el bot raíz de diálogo localmente en su máquina.Run the dialog skill bot and dialog root bot locally on your machine. Si necesita instrucciones, consulte el archivo README (LÉAME) del ejemplo de C#, JavaScript o Python.If you need instructions, refer to the README file for the C#, JavaScript or Python sample.
  2. Use el emulador para probar el bot.Use the Emulator to test the bot.
    • La primera vez que se une a la conversación, el bot muestra un mensaje de bienvenida y le pregunta a qué aptitud le gustaría llamar.When you first join the conversation, the bot displays a welcome message and asks you what skill you would like to call. El bot de aptitud de este ejemplo tiene solo una aptitud.The skill bot for this sample has just one skill.
    • Seleccione DialogSkillBot.Select DialogSkillBot.
  3. El bot siguiente le pide que elija una acción para la aptitud.The bot next asks you to choose an action for the skill. Elija "BookFlight".Choose "BookFlight".
    1. La aptitud comienza la acción de reserva de vuelo; responda a los avisos.The skill begins its book-flight action; answer the prompts.
    2. Cuando la aptitud se completa, el bot raíz muestra los detalles de la reserva antes de volver a solicitar la aptitud a la que le gustaría llamar.When the skill completes, the root bot displays the booking details before prompting again for the skill you'd like to call.
  4. Vuelva a seleccionar DialogSkillBot y "BookFlight".Select DialogSkillBot again and "BookFlight".
    1. Responda a la primera solicitud y, a continuación, escriba "Cancelar" para cancelar la acción.Answer the first prompt, then enter "cancel" to cancel the action.
    2. El bot de aptitud finaliza sin completar la acción y el consumidor solicita la aptitud a la que le gustaría llamar.The skill bot ends without completing the action, and the consumer prompts for the skill you'd like to call.

Información adicionalAdditional information