Uso de un diálogo para consumir una aptitud

SE APLICA A: SDK v4

En este artículo se muestra cómo usar un diálogo de aptitud dentro de un consumidor de aptitudes. El diálogo de capacidad expone las actividades del bot primario al bot de capacidad y devuelve las respuestas de la capacidad al usuario. El bot de aptitud al que tiene acceso este consumidor puede controlar tanto las actividades de mensaje como las actividades de evento. Para ver un ejemplo de un manifiesto de aptitud e información sobre cómo implementar la aptitud, consulte cómo usar diálogos dentro de una aptitud.

Para obtener información sobre el uso de un bot de aptitud fuera de los diálogos, vea cómo implementar un consumidor de aptitudes.

Nota:

Los SDK de JavaScript, C# y Python de Bot Framework seguirán siendo compatibles, pero el SDK de Java se va a retirar con la compatibilidad final a largo plazo que finaliza en noviembre de 2023.

Los bots existentes creados con el SDK de Java seguirán funcionando.

Para la creación de nuevos bots, considera el uso de Power Virtual Agents y lee sobre cómo elegir la solución de bot de chat adecuada.

Para obtener más información, consulta El futuro de la creación de bots.

Requisitos previos

Acerca de este ejemplo

El ejemplo skills skillDialog incluye proyectos para dos bots:

  • El bot raíz del diálogo, que utiliza una clase de diálogo de aptitud para consumir una aptitud.
  • El bot de aptitud de diálogo, que utiliza un diálogo para controlar las actividades que provienen de los consumidores de aptitudes.

Este artículo se centra en el uso de una clase de diálogo de aptitud en un bot raíz para administrar la aptitud, enviar actividades de mensajes y eventos y cancelar la aptitud.

Para obtener información sobre otros aspectos de la creación de un consumidor de aptitudes, consulte cómo implementar un consumidor de aptitudes.

Para obtener información sobre el bot de aptitudes de diálogo, consulte cómo usar diálogos dentro de una aptitud.

Recursos

Para los bots implementados, la autenticación entre bots requiere que cada bot participante tenga una identidad válida. Sin embargo, puede probar las aptitudes y los consumidores de aptitudes localmente con Bot Framework Emulator sin información de identidad.

Configuración de aplicaciones

  1. Opcionalmente, agregue la información de identidad del bot raíz al archivo de configuración.
  2. Agregue el punto de conexión del host de aptitudes (el servicio o la URL de devolución de llamada) al que las habilidades deben responder al consumidor de aptitudes.
  3. Agregue una entrada para cada aptitud que usará el consumidor de aptitudes. Cada entrada incluye:
    • Un identificador que el consumidor de aptitudes usará para identificar cada aptitud.
    • Opcionalmente, la aplicación o el identificador de cliente del bot de aptitudes.
    • El punto de conexión de mensajería de la aptitud.

Nota:

Si la aptitud o el consumidor de aptitudes especifican una identidad, ambos deben especificarla.

DialogRootBot\appsettings.json

Opcionalmente, agregue la información de identidad del bot raíz y agregue la aplicación o el identificador de cliente para el bot de aptitud de eco a la matriz BotFrameworkSkills.

{
  "MicrosoftAppType": "",
  "MicrosoftAppId": "",
  "MicrosoftAppPassword": "",
  "MicrosoftAppTenantId": "",

  "SkillHostEndpoint": "http://localhost:3978/api/skills/",
  "BotFrameworkSkills": [
    {
      "Id": "DialogSkillBot",
      "AppId": "",
      "SkillEndpoint": "http://localhost:39783/api/messages"
    }
  ]
}

Lógica del diálogo

El diálogo principal del bot incluye un diálogo de aptitud para cada aptitud que este bot consume. El diálogo de aptitud administra la aptitud a través de los distintos objetos relacionados con las aptitudes, como el cliente de aptitudes y los objetos del generador de identificadores de conversación de aptitudes. En el diálogo principal también se muestra cómo cancelar la aptitud (mediante el diálogo de aptitudes) en función de los datos proporcionados por el usuario.

La aptitud que este bot usa es compatible con un par de características diferentes. Puede reservar un vuelo o conocer el tiempo de una ciudad. Además, si recibe un mensaje fuera de estos contextos y se configura un reconocedor de LUIS, intenta interpretar la intención del usuario.

Nota:

Reconocimiento del lenguaje (LUIS) se retirará el 1 de octubre de 2025. A partir del 1 de abril de 2023, no podrás crear nuevos recursos de LUIS. Hay disponible una versión más reciente de las funcionalidades de reconocimiento del lenguaje como parte del Lenguaje de Azure AI.

Reconocimiento del lenguaje conversacional (CLU), una característica del lenguaje de Azure AI, es la versión actualizada de LUIS. Para obtener más información sobre la compatibilidad con reconocimiento del lenguaje en el SDK de Bot Framework, consulte Reconocimiento del lenguaje natural.

El manifiesto de aptitud (C#, JavaScript, Java, Python) describe las acciones que puede realizar la aptitud, sus parámetros de entrada y salida y sus puntos de conexión. Cabe destacar que la aptitud puede controlar un evento "BookFlight" o "GetWeather". También puede controlar los mensajes.

El diálogo principal incluye código para:

El diálogo principal hereda de la clase del diálogo de componente. Para más información sobre los diálogos de componente, consulte cómo administrar la complejidad de los diálogos.

Inicializar el diálogo principal

El diálogo principal incluye diálogos (para administrar el flujo de conversación fuera de la aptitud) y un diálogo de aptitud (para administrar las aptitudes). La cascada incluye los pasos siguientes, que se describen con más detalle en las secciones que figuran a continuación.

  1. Pida al usuario que seleccione la aptitud que se va a usar. (El bot raíz consume una aptitud).
  2. Pida al usuario que seleccione la acción que se va a usar para esa aptitud. (El bot de aptitud define tres acciones).
  3. Inicie la aptitud elegida con una actividad inicial en función de la acción elegida.
  4. Una vez completada la aptitud, muestre los resultados, si los hay. A continuación, reinicie la aptitud.

DialogRootBot\Dialogs\MainDialog.cs

La clase MainDialog deriva de ComponentDialog. Además del estado de la conversación, el diálogo necesita la identidad del bot raíz y las referencias al generador de identificadores de conversación de aptitudes, el cliente HTTP de aptitud y los objetos de configuración de aptitud.

El constructor del diálogo comprueba sus parámetros de entrada, agrega diálogos de aptitud, agrega una solicitud y diálogos en cascada para administrar el flujo de conversación fuera de la aptitud y crea un descriptor de acceso de propiedad para realizar el seguimiento de la aptitud activa, si existe.

El constructor llama a AddSkillDialogs, un método auxiliar, para crear un SkillDialog para cada aptitud que se incluye en el archivo de configuración, como se lee de este en un objeto SkillsConfiguration.

// Helper method that creates and adds SkillDialog instances for the configured skills.
private void AddSkillDialogs(ConversationState conversationState, SkillConversationIdFactoryBase conversationIdFactory, SkillsConfiguration skillsConfig, string botId)
{
    foreach (var skillInfo in _skillsConfig.Skills.Values)
    {
        // Create the dialog options.
        var skillDialogOptions = new SkillDialogOptions
        {
            BotId = botId,
            ConversationIdFactory = conversationIdFactory,
            SkillClient = _auth.CreateBotFrameworkClient(),
            SkillHostEndpoint = skillsConfig.SkillHostEndpoint,
            ConversationState = conversationState,
            Skill = skillInfo
        };

        // Add a SkillDialog for the selected skill.
        AddDialog(new SkillDialog(skillDialogOptions, skillInfo.Id));
    }
}

Selección de una aptitud

En el primer paso, el diálogo principal solicita al usuario a qué aptitud le gustaría llamar y utiliza el mensaje de opción "SkillPrompt" para obtener la respuesta. (Este bot solo define una aptitud).

DialogRootBot\Dialogs\MainDialog.cs

// Render a prompt to select the skill to call.
private async Task<DialogTurnResult> SelectSkillStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // Create the PromptOptions from the skill configuration which contain the list of configured skills.
    var messageText = stepContext.Options?.ToString() ?? "What skill would you like to call?";
    var repromptMessageText = "That was not a valid choice, please select a valid skill.";
    var options = new PromptOptions
    {
        Prompt = MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput),
        RetryPrompt = MessageFactory.Text(repromptMessageText, repromptMessageText, InputHints.ExpectingInput),
        Choices = _skillsConfig.Skills.Select(skill => new Choice(skill.Value.Id)).ToList()
    };

    // Prompt the user to select a skill.
    return await stepContext.PromptAsync("SkillPrompt", options, cancellationToken);
}

Selección de una acción de aptitud

En el paso siguiente, el diálogo principal:

  1. Guarda la información sobre la aptitud que el usuario seleccionó.
  2. Solicita al usuario qué acción de aptitud le gustaría usar y utiliza el mensaje de opción "SkillActionPrompt" para obtener la respuesta.
    • Utiliza un método auxiliar para obtener una lista de acciones entre las que elegir.
    • El validador de solicitud asociado a esta solicitud enviará de forma predeterminada un mensaje a la aptitud si la entrada del usuario no coincide con una de las opciones.

Las opciones incluidas en este bot ayudan a probar las acciones definidas para esta aptitud. Lo más habitual es que lea las opciones del manifiesto de la aptitud y presente opciones al usuario en función de esa lista.

DialogRootBot\Dialogs\MainDialog.cs

// Render a prompt to select the action for the skill.
private async Task<DialogTurnResult> SelectSkillActionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // Get the skill info based on the selected skill.
    var selectedSkillId = ((FoundChoice)stepContext.Result).Value;
    var selectedSkill = _skillsConfig.Skills.FirstOrDefault(s => s.Value.Id == selectedSkillId).Value;

    // Remember the skill selected by the user.
    stepContext.Values[_selectedSkillKey] = selectedSkill;

    // Create the PromptOptions with the actions supported by the selected skill.
    var messageText = $"Select an action # to send to **{selectedSkill.Id}** or just type in a message and it will be forwarded to the skill";
    var options = new PromptOptions
    {
        Prompt = MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput),
        Choices = GetSkillActions(selectedSkill)
    };

    // Prompt the user to select a skill action.
    return await stepContext.PromptAsync("SkillActionPrompt", options, cancellationToken);
}
// Helper method to create Choice elements for the actions supported by the skill.
private IList<Choice> GetSkillActions(BotFrameworkSkill skill)
{
    // Note: the bot would probably render this by reading the skill manifest.
    // We are just using hardcoded skill actions here for simplicity.

    var choices = new List<Choice>();
    switch (skill.Id)
    {
        case "DialogSkillBot":
            choices.Add(new Choice(SkillActionBookFlight));
            choices.Add(new Choice(SkillActionBookFlightWithInputParameters));
            choices.Add(new Choice(SkillActionGetWeather));
            break;
    }

    return choices;
}
// This validator defaults to Message if the user doesn't select an existing option.
private Task<bool> SkillActionPromptValidator(PromptValidatorContext<FoundChoice> promptContext, CancellationToken cancellationToken)
{
    if (!promptContext.Recognized.Succeeded)
    {
        // Assume the user wants to send a message if an item in the list is not selected.
        promptContext.Recognized.Value = new FoundChoice { Value = SkillActionMessage };
    }

    return Task.FromResult(true);
}

Inicio de una aptitud

En el paso siguiente, el diálogo principal:

  1. Recupera información sobre la aptitud y la actividad de aptitud que el usuario ha seleccionado.
  2. Utiliza un método auxiliar para crear la actividad que se va a enviar inicialmente a la aptitud.
  3. Crea las opciones de diálogo con las que se inicia el diálogo de aptitud. Esto incluye la actividad inicial que se va a enviar.
  4. Guarda el estado antes de llamar a la aptitud. (Esto es necesario, ya que la respuesta de la aptitud puede llegar a una instancia diferente del consumidor de aptitud).
  5. Comienza el diálogo de aptitud, pasando el identificador de aptitud para llamar y las opciones con las que se llama.

DialogRootBot\Dialogs\MainDialog.cs

// Starts the SkillDialog based on the user's selections.
private async Task<DialogTurnResult> CallSkillActionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var selectedSkill = (BotFrameworkSkill)stepContext.Values[_selectedSkillKey];

    Activity skillActivity;
    switch (selectedSkill.Id)
    {
        case "DialogSkillBot":
            skillActivity = CreateDialogSkillBotActivity(((FoundChoice)stepContext.Result).Value, stepContext.Context);
            break;

        // We can add other case statements here if we support more than one skill.
        default:
            throw new Exception($"Unknown target skill id: {selectedSkill.Id}.");
    }

    // Create the BeginSkillDialogOptions and assign the activity to send.
    var skillDialogArgs = new BeginSkillDialogOptions { Activity = skillActivity };

    // Save active skill in state.
    await _activeSkillProperty.SetAsync(stepContext.Context, selectedSkill, cancellationToken);

    // Start the skillDialog instance with the arguments. 
    return await stepContext.BeginDialogAsync(selectedSkill.Id, skillDialogArgs, cancellationToken);
}

Resumen del resultado de la aptitud

En el último paso, el diálogo principal:

  1. Si la aptitud devolvió un valor, muestra el resultado al usuario.
  2. Borra la aptitud activa del estado del diálogo.
  3. Quita la propiedad de la aptitud activa del estado de conversación.
  4. Se reinicia (el diálogo principal).

DialogRootBot\Dialogs\MainDialog.cs

// The SkillDialog has ended, render the results (if any) and restart MainDialog.
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var activeSkill = await _activeSkillProperty.GetAsync(stepContext.Context, () => null, cancellationToken);

    // Check if the skill returned any results and display them.
    if (stepContext.Result != null)
    {
        var message = $"Skill \"{activeSkill.Id}\" invocation complete.";
        message += $" Result: {JsonConvert.SerializeObject(stepContext.Result)}";
        await stepContext.Context.SendActivityAsync(MessageFactory.Text(message, message, inputHint: InputHints.IgnoringInput), cancellationToken: cancellationToken);
    }

    // Clear the skill selected by the user.
    stepContext.Values[_selectedSkillKey] = null;

    // Clear active skill in state.
    await _activeSkillProperty.DeleteAsync(stepContext.Context, cancellationToken);

    // Restart the main dialog with a different message the second time around.
    return await stepContext.ReplaceDialogAsync(InitialDialogId, $"Done with \"{activeSkill.Id}\". \n\n What skill would you like to call?", cancellationToken);
}

Permiso para que el usuario cancele la aptitud

El diálogo principal invalida el comportamiento predeterminado del método on continue dialog para permitir al usuario cancelar la aptitud actual, si existe. Dentro del método:

  • Si hay una aptitud activa y el usuario envía un mensaje de anulación, cancela todos los diálogos y pone en cola el diálogo principal para reiniciar desde el principio.
  • A continuación, llama a la implementación base del método on continue dialog para continuar procesando el turno actual.

DialogRootBot\Dialogs\MainDialog.cs

protected override async Task<DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default)
{
    // This is an example on how to cancel a SkillDialog that is currently in progress from the parent bot.
    var activeSkill = await _activeSkillProperty.GetAsync(innerDc.Context, () => null, cancellationToken);
    var activity = innerDc.Context.Activity;
    if (activeSkill != null && activity.Type == ActivityTypes.Message && activity.Text.Equals("abort", StringComparison.OrdinalIgnoreCase))
    {
        // Cancel all dialogs when the user says abort.
        // The SkillDialog automatically sends an EndOfConversation message to the skill to let the
        // skill know that it needs to end its current dialogs, too.
        await innerDc.CancelAllDialogsAsync(cancellationToken);
        return await innerDc.ReplaceDialogAsync(InitialDialogId, "Canceled! \n\n What skill would you like to call?", cancellationToken);
    }

    return await base.OnContinueDialogAsync(innerDc, cancellationToken);
}

Lógica del controlador de actividades

Dado que un diálogo principal controla la lógica de aptitudes para cada turno, el controlador de actividades se parece mucho a otros ejemplos de diálogo.

DialogRootBot\Bots\RootBot.cs

public class RootBot<T> : ActivityHandler
    where T : Dialog
private readonly ConversationState _conversationState;
private readonly Dialog _mainDialog;

public RootBot(ConversationState conversationState, T mainDialog)
{
    _conversationState = conversationState;
    _mainDialog = mainDialog;
}
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
{
    if (turnContext.Activity.Type != ActivityTypes.ConversationUpdate)
    {
        // Run the Dialog with the Activity.
        await _mainDialog.RunAsync(turnContext, _conversationState.CreateProperty<DialogState>("DialogState"), cancellationToken);
    }
    else
    {
        // Let the base class handle the activity.
        await base.OnTurnAsync(turnContext, cancellationToken);
    }

    // Save any state changes that might have occurred during the turn.
    await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
}

Registro de servicios

Los servicios necesarios para usar un diálogo de aptitud son los mismos que los que se necesitan para un consumidor de aptitudes en general. Vea cómo implementar un consumidor de aptitudes para obtener una explicación de los servicios necesarios.

Prueba del bot raíz

Puede probar el consumidor de aptitudes en el emulador como si fuera un bot normal; sin embargo, tiene que ejecutar los bots de aptitud y del consumidor de aptitudes al mismo tiempo. Vea cómo usar diálogos dentro de una aptitud para obtener más información sobre cómo configurar la aptitud.

Descargue e instale la versión más reciente de 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. Si necesita instrucciones, consulte los ejemplos README de C#, JavaScript, Java y Python.
  2. Use el emulador para probar el 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. El bot de aptitud de este ejemplo tiene solo una aptitud.
    • Seleccione DialogSkillBot.
  3. A continuación, el bot le pide que elija una acción para la aptitud. Elija "BookFlight".
    1. Responda a las solicitudes.
    2. La aptitud se completa y el bot raíz muestra los detalles de la reserva antes de volver a solicitar la aptitud a la que le gustaría llamar.
  4. Vuelva a seleccionar DialogSkillBot y "BookFlight".
    1. Responda a la primera solicitud y escriba "abort" para interrumpir la aptitud.
    2. El bot raíz cancela la aptitud y solicita la aptitud a la que le gustaría llamar.

Más sobre la depuración

Dado que se autentica el tráfico entre aptitudes y consumidores de aptitudes, hay pasos adicionales al depurar estos bots.

  • El consumidor de aptitudes y todas las aptitudes que consume, directa o indirectamente, deben ejecutarse.
  • Si los bots se ejecutan localmente y si alguno de los bots tiene un identificador de aplicación y una contraseña, todos los bots deben tener identificadores y contraseñas válidos.
  • Si todos los bots están implementados, consulte cómo depurar un bot desde cualquier canal mediante ngrok.
  • Si algunos de los bots se ejecutan localmente y algunos se implementan, consulte cómo depurar una aptitud o consumidor de aptitudes.

De lo contrario, puede depurar un consumidor de aptitudes o una aptitud como depurar otros bots. Para obtener más información, consulte Depuración de un bot y Depuración con Bot Framework Emulator.

Información adicional

Consulte cómo implementar un consumidor de aptitudes para saber cómo implementar un consumidor de aptitudes en general.