Share via


Tutorial: Uso de Personalizer en un bot de chat de .NET

Importante

A partir del 20 de septiembre de 2023, no podrá crear nuevos recursos de Personalizer. El servicio Personalizer se va a retirar el 1 de octubre de 2026.

Utilice un bot de chat de .NET en C# con un bucle de Personalizer para proporcionar el contenido correcto a un usuario. Este bot de chat sugiere un café o un té determinado a un usuario. El usuario puede aceptar o rechazar la sugerencia. Esto proporciona información a Personalizer para ayudar a que la sugerencia siguiente sea más adecuada.

En este tutorial, aprenderá a:

  • Configuración de los recursos de Azure
  • Configurar y ejecutar un bot
  • Interactuar con el bot mediante Bot Framework Emulator
  • Comprender dónde y cómo usa el bot Personalizer

¿Cómo funciona el bot de chat?

Un bot de chat es generalmente una aplicación que mantiene un diálogo con un usuario. Este bot de chat en concreto usa Personalizer para seleccionar la mejor acción (café o té) que ofrecer al usuario. Personalizer usa el aprendizaje de refuerzo para realizar esa selección.

El bot de chat debe administrar los turnos de la conversación. El bot de chat utiliza Bot Framework para administrar la arquitectura del bot y la conversación y utiliza Azure AI Language Understanding (LUIS), para entender la intención del lenguaje natural del usuario.

El bot de chat es un sitio web con una ruta específica disponible para responder solicitudes, http://localhost:3978/api/messages. Puede usar Bot Framework Emulator para interactuar visualmente con el bot de chat en ejecución mientras desarrolla un bot en su entorno local.

Interacciones del usuario con el bot

Este es un bot de chat sencillo que permite introducir consultas de texto.

El usuario escribe texto El bot responde con texto Descripción de la acción que realiza el bot para determinar el texto de la respuesta
No se escribe texto: el bot inicia la conversación. This is a simple chatbot example that illustrates how to use Personalizer. The bot learns what coffee or tea order is preferred by customers given some context information (such as weather, temperature, and day of the week) and information about the user.
To use the bot, just follow the prompts. To try out a new imaginary context, type “Reset” and a new one will be randomly generated.
Welcome to the coffee bot, please tell me if you want to see the menu or get a coffee or tea suggestion for today. Once I’ve given you a suggestion, you can reply with ‘like’ or ‘don’t like’. It’s Tuesday today and the weather is Snowy.
El bot inicia la conversación con un texto informativo y describe el contexto: Tuesday, Snowy.
Show menu Here is our menu: Coffee: Cappuccino Espresso Latte Macchiato Mocha Tea: GreenTea Rooibos Determina la intención de la consulta mediante LUIS y, a continuación, muestra las opciones de café y té del menú. Las características de las acciones son
What do you suggest How about Latte? Determina la intención de la consulta mediante LUIS y, a continuación, llama a la API Rank y muestra la opción superior como una pregunta How about {response.RewardActionId}?. También muestra la llamada y respuesta JSON con fines ilustrativos.
I like it That’s great! I’ll keep learning your preferences over time.
Would you like to get a new suggestion or reset the simulated context to a new day?
Determina la intención de la consulta mediante LUIS y, a continuación, llama a la API Reward con la recompensa de 1 y muestra la llamada y la respuesta JSON con fines ilustrativos.
I don't like it Oh well, maybe I’ll guess better next time.
Would you like to get a new suggestion or reset the simulated context to a new day?
Determina la intención de la consulta mediante LUIS y, a continuación, llama a la API Reward con la recompensa de 0 y muestra la llamada y la respuesta JSON con fines ilustrativos.
Reset Devuelve texto informativo. Determina la intención de la consulta mediante LUIS y, a continuación, muestra el texto informativo y restablece el contexto.

Personalizer en este bot

Este bot de chat utiliza Personalizer para seleccionar la acción principal (café o té específico) en función de una lista de acciones (cierto tipo de contenido) y características de contexto.

El bot envía la lista de acciones, junto con las características de contexto, al bucle de Personalizer. Personalizer devuelve la acción más adecuada al bot que, a su vez, la mostrará.

En este tutorial, las acciones son tipos de café y té:

Café
Cappuccino
Espresso
Latte
Mocca
Té verde
Rooibos

API Rank: Para ayudar a Personalizer a obtener información sobre las acciones, el bot envía lo siguiente con cada solicitud de la API Rank:

  • Acciones con características
  • Características de contexto

Una característica del modelo es la información sobre la acción o el contexto que se puede agregar (agrupar) entre los miembros de la base de usuarios del bot de chat. Una característica no específica de forma individual (como un identificador de usuario) o muy específica (por ejemplo, una hora exacta del día).

Las características se usan para alinear acciones con el contexto actual del modelo. El modelo es una representación del conocimiento anterior de Personalizer sobre las acciones, el contexto y sus características que le permiten tomar decisiones fundadas.

El modelo, incluidas las características, se actualiza según una programación basada en la configuración de la frecuencia de actualización del modelo en Azure Portal.

Las características deben seleccionarse con la misma planeación y diseño que se aplicarían a cualquier esquema o modelo de la arquitectura técnica. Los valores de las características se pueden establecer con lógica de negocios o con sistemas de terceros.

Precaución

Las características de esta aplicación son para demostración y puede que no sean necesariamente las más adecuadas para su aplicación web en su caso de uso.

Características de acción

Cada acción (elemento de contenido) tiene características que ayudan a distinguir el elemento de café o té.

Las características no se configuran como parte de la configuración del bucle en Azure Portal. En vez de eso, se envían como un objeto JSON con cada llamada de la API Rank. Esto proporciona cierta flexibilidad para que las acciones y sus características crezcan, cambien o se reduzcan con el tiempo, lo que permite a Personalizer seguir las tendencias.

Las características de café y té incluyen:

  • Lugar de origen del grano de café, como Kenia y Brasil
  • ¿Es el café o el té de origen ecológico?
  • Tueste claro u oscuro del café

Aunque el café tiene tres características en la lista anterior, el té solo tiene una. Pase solo características a Personalizer que tengan sentido para la acción. No pase un valor vacío para una característica si no es aplicable a la acción.

Características de contexto

Las características de contexto ayudan a Personalizer a comprender el contexto del entorno, como el dispositivo de visualización, el usuario, la ubicación y otras características que son pertinentes para su caso de uso.

El contexto de este bot de chat incluye:

  • Condiciones meteorológicas (nieve, lluvia, soleado)
  • Día de la semana

La selección de características se aleatoriza en este bot de chat. En un bot real, use datos reales para las características de contexto.

Consideraciones de diseño para este bot

Hay algunas precauciones que se deben tener en cuenta sobre esta conversación:

  • Interacción del bot: la conversación es muy sencilla porque demuestra el rango y la recompensa en un caso de uso simple. No demuestra la funcionalidad completa del SDK de Bot Framework o del emulador.
  • Personalizer: las características se seleccionan de forma aleatoria para simular el uso. No aleatorice características en un escenario de Personalizer de producción.
  • Language Understanding (LUIS) : las pocas expresiones de ejemplo del modelo de LUIS están pensadas para este ejemplo únicamente. No utilice tan pocas expresiones de ejemplo en su aplicación de LUIS de producción.

Instalación del software necesario

  • Visual Studio 2019. El repositorio de ejemplos descargables incluye instrucciones si prefiere usar la CLI de .NET Core.
  • Microsoft Bot Framework Emulator es una aplicación de escritorio que permite que los desarrolladores de bots prueben y depuren sus bots en localhost o mediante la ejecución remota mediante un túnel.

Descargue el código de ejemplo del bot de chat.

El bot de chat está disponible en el repositorio de ejemplos de Personalizer. Clone o descargue el repositorio y, a continuación, abra el ejemplo del directorio /samples/ChatbotExample con Visual Studio 2019.

Para clonar el repositorio, use el siguiente comando de Git en un shell de Bash (terminal).

git clone https://github.com/Azure-Samples/cognitive-services-personalizer-samples.git

Creación y configuración de recursos de Personalizer y LUIS

Creación de recursos de Azure

Para usar este bot de chat, debe crear recursos de Azure para Personalizer y Language Understanding (LUIS).

  • Cree recursos de LUIS. Cree un recurso de creación y predicción.
  • Cree el recurso de Personalizer y, a continuación, copie la clave y el punto de conexión de Azure Portal. Deberá establecer estos valores en el archivo appsettings.json del proyecto de .NET.

Creación de una aplicación de LUIS

Si no está familiarizado con LUIS, debe iniciar sesión y migrar inmediatamente su cuenta. No es necesario crear nuevos recursos. En su lugar, seleccione los recursos que creó en la sección anterior de este tutorial.

  1. Para crear una nueva aplicación de LUIS, en el portal de LUIS, seleccione su suscripción y recurso de creación.
  2. A continuación, en la misma página, seleccione + Nueva aplicación para la conversación y, después, seleccione Importar como JSON.
  3. En el cuadro de diálogo emergente, seleccione Elegir archivo y, a continuación, seleccione el archivo /samples/ChatbotExample/CognitiveModels/coffeebot.json. Escriba el nombre Personalizer Coffee bot.
  4. En la barra de navegación superior derecha del portal de LUIS, seleccione el botón Entrenar.
  5. Seleccione el botón Publicar para publicar la aplicación en el Espacio de producción para el tiempo de ejecución de predicción.
  6. Seleccione Administrar y, después, Configuración. Copie el valor de Id. de aplicación. Deberá establecer este valor en el archivo appsettings.json del proyecto de .NET.
  7. En la misma sección Administrar, seleccione Recursos de Azure. Esto muestra los recursos asociados en la aplicación.
  8. Seleccione Agregar recurso de predicción. En el cuadro de diálogo emergente, seleccione su suscripción y el recurso de predicción creado en una sección anterior de este tutorial y, después, seleccione Listo.
  9. Copie los valores de la Clave principal y de la Dirección URL de punto de conexión. Deberá establecer estos valores en el archivo appsettings.json del proyecto de .NET.

Configuración del bot con el archivo appsettings.json

  1. Abra el archivo de la solución del bot de chat, ChatbotSamples.sln, con Visual Studio 2019.

  2. Abra appsettings.json en el directorio raíz del proyecto.

  3. Establezca las cinco configuraciones copiadas en la sección anterior de este tutorial.

    {
      "PersonalizerChatbot": {
        "LuisAppId": "",
        "LuisAPIKey": "",
        "LuisServiceEndpoint": "",
        "PersonalizerServiceEndpoint": "",
        "PersonalizerAPIKey": ""
      }
    }
    

Compilación y ejecución del bot

Una vez configurado el archivo appsettings.json, estará listo para compilar y ejecutar el bot de chat. Al hacerlo, en un explorador se abrirá el sitio web en ejecución: http://localhost:3978.

Screenshot of browser displaying chat bot web site.

Deje el sitio web en ejecución, ya que en el tutorial se explica qué está haciendo el bot, de modo que pueda interactuar con él.

Configuración de Bot Framework Emulator

  1. Abra Bot Framework Emulator y seleccione Open Bot (Abrir bot).

    Screenshot of Bot Framework Emulator startup screen.

  2. Configure el bot con la siguiente dirección URL del bot y seleccione Connect (Conectar):

    http://localhost:3978/api/messages

    Screenshot of Bot Framework Emulator open bot settings.

    El emulador se conecta al bot de chat y muestra el texto informativo, junto con información de registro y depuración que resulta útil para el desarrollo local.

    Screenshot of Bot Framework Emulator in first turn of conversation.

Uso del bot en Bot Framework Emulator

  1. Escriba I would like to see the menu para solicitar ver el menú. El bot de chat muestra los elementos.

  2. Escriba Please suggest a drink for me. para que el bot sugiera un elemento. El emulador muestra la solicitud y la respuesta de clasificación en la ventana de chat, por lo que puede ver el JSON completo. El bot realizará una sugerencia, como How about Latte?.

  3. Responda que esa sugerencia le parece bien, es decir, que acepta la selección mejor clasificada de Personalizer: I like it.. El emulador muestra la solicitud de recompensa con la puntuación de recompensa 1 y la respuesta en la ventana de chat, por lo que puede ver el JSON completo. El bot responde con That’s great! I’ll keep learning your preferences over time. y Would you like to get a new suggestion or reset the simulated context to a new day?:

    Si responde con no a la selección, se envía a Personalizer la puntuación de recompensa de 0.

Descripción del código de .NET con Personalizer

La solución de .NET es un bot de chat de Bot Framework sencillo. El código relacionado con Personalizer se encuentra en las siguientes carpetas:

  • /samples/ChatbotExample/Bots
    • Archivo PersonalizerChatbot.cs para la interacción entre el bot y Personalizer.
  • /samples/ChatbotExample/ReinforcementLearning: administra las acciones y características del modelo de Personalizer.
  • /samples/ChatbotExample/Model: archivos para las acciones y características de Personalizer, así como para las intenciones de LUIS

PersonalizerChatbot.cs: trabajar con Personalizer

La clase PersonalizerChatbot se deriva de Microsoft.Bot.Builder.ActivityHandler. Tiene tres propiedades y métodos para administrar el flujo de conversación.

Precaución

No copie el código de este tutorial. Utilice el código de ejemplo del repositorio de ejemplos de Personalizer.

public class PersonalizerChatbot : ActivityHandler
{

    private readonly LuisRecognizer _luisRecognizer;
    private readonly PersonalizerClient _personalizerClient;

    private readonly RLContextManager _rlFeaturesManager;

    public PersonalizerChatbot(LuisRecognizer luisRecognizer, RLContextManager rlContextManager, PersonalizerClient personalizerClient)
            {
                _luisRecognizer = luisRecognizer;
                _rlFeaturesManager = rlContextManager;
                _personalizerClient = personalizerClient;
            }
    }

    public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
    {
        await base.OnTurnAsync(turnContext, cancellationToken);

        if (turnContext.Activity.Type == ActivityTypes.Message)
        {
            // Check LUIS model
            var recognizerResult = await _luisRecognizer.RecognizeAsync(turnContext, cancellationToken);
            var topIntent = recognizerResult?.GetTopScoringIntent();
            if (topIntent != null && topIntent.HasValue && topIntent.Value.intent != "None")
            {
                Intents intent = (Intents)Enum.Parse(typeof(Intents), topIntent.Value.intent);
                switch (intent)
                {
                    case Intents.ShowMenu:
                        await turnContext.SendActivityAsync($"Here is our menu: \n Coffee: {CoffeesMethods.DisplayCoffees()}\n Tea: {TeaMethods.DisplayTeas()}", cancellationToken: cancellationToken);
                        break;
                    case Intents.ChooseRank:
                        // Here we generate the event ID for this Rank.
                        var response = await ChooseRankAsync(turnContext, _rlFeaturesManager.GenerateEventId(), cancellationToken);
                        _rlFeaturesManager.CurrentPreference = response.Ranking;
                        await turnContext.SendActivityAsync($"How about {response.RewardActionId}?", cancellationToken: cancellationToken);
                        break;
                    case Intents.RewardLike:
                        if (!string.IsNullOrEmpty(_rlFeaturesManager.CurrentEventId))
                        {
                            await RewardAsync(turnContext, _rlFeaturesManager.CurrentEventId, 1, cancellationToken);
                            await turnContext.SendActivityAsync($"That's great! I'll keep learning your preferences over time.", cancellationToken: cancellationToken);
                            await SendByebyeMessageAsync(turnContext, cancellationToken);
                        }
                        else
                        {
                            await turnContext.SendActivityAsync($"Not sure what you like. Did you ask for a suggestion?", cancellationToken: cancellationToken);
                        }

                        break;
                    case Intents.RewardDislike:
                        if (!string.IsNullOrEmpty(_rlFeaturesManager.CurrentEventId))
                        {
                            await RewardAsync(turnContext, _rlFeaturesManager.CurrentEventId, 0, cancellationToken);
                            await turnContext.SendActivityAsync($"Oh well, maybe I'll guess better next time.", cancellationToken: cancellationToken);
                            await SendByebyeMessageAsync(turnContext, cancellationToken);
                        }
                        else
                        {
                            await turnContext.SendActivityAsync($"Not sure what you dislike. Did you ask for a suggestion?", cancellationToken: cancellationToken);
                        }

                        break;
                    case Intents.Reset:
                        _rlFeaturesManager.GenerateRLFeatures();
                        await SendResetMessageAsync(turnContext, cancellationToken);
                        break;
                    default:
                        break;
                }
            }
            else
            {
                var msg = @"Could not match your message with any of the following LUIS intents:
                        'ShowMenu'
                        'ChooseRank'
                        'RewardLike'
                        'RewardDislike'.
                        Try typing 'Show me the menu','What do you suggest','I like it','I don't like it'.";
                await turnContext.SendActivityAsync(msg);
            }
        }
        else if (turnContext.Activity.Type == ActivityTypes.ConversationUpdate)
        {
            // Generate a new weekday and weather condition
            // These will act as the context features when we call rank with Personalizer
            _rlFeaturesManager.GenerateRLFeatures();

            // Send a welcome message to the user and tell them what actions they may perform to use this bot
            await SendWelcomeMessageAsync(turnContext, cancellationToken);
        }
        else
        {
            await turnContext.SendActivityAsync($"{turnContext.Activity.Type} event detected", cancellationToken: cancellationToken);
        }
    }

    // code removed for brevity, full sample code available for download
    private async Task SendWelcomeMessageAsync(ITurnContext turnContext, CancellationToken cancellationToken)
    private async Task SendResetMessageAsync(ITurnContext turnContext, CancellationToken cancellationToken)
    private async Task SendByebyeMessageAsync(ITurnContext turnContext, CancellationToken cancellationToken)
    private async Task<RankResponse> ChooseRankAsync(ITurnContext turnContext, string eventId, CancellationToken cancellationToken)
    private async Task RewardAsync(ITurnContext turnContext, string eventId, double reward, CancellationToken cancellationToken)
}

Los métodos precedidos por Send administran la conversación con el bot y LUIS. Los métodos ChooseRankAsync y RewardAsync interactúan con Personalizer.

Llamada a la API Rank y visualización de los resultados

El método ChooseRankAsync compila los datos JSON que se van a enviar a la API Rank de Personalizer mediante la recopilación de las acciones con características y las características de contexto.

private async Task<RankResponse> ChooseRankAsync(ITurnContext turnContext, string eventId, CancellationToken cancellationToken)
{
    IList<object> contextFeature = new List<object>
    {
        new { weather = _rlFeaturesManager.RLFeatures.Weather.ToString() },
        new { dayofweek = _rlFeaturesManager.RLFeatures.DayOfWeek.ToString() },
    };

    Random rand = new Random(DateTime.UtcNow.Millisecond);
    IList<RankableAction> actions = new List<RankableAction>();
    var coffees = Enum.GetValues(typeof(Coffees));
    var beansOrigin = Enum.GetValues(typeof(CoffeeBeansOrigin));
    var organic = Enum.GetValues(typeof(Organic));
    var roast = Enum.GetValues(typeof(CoffeeRoast));
    var teas = Enum.GetValues(typeof(Teas));

    foreach (var coffee in coffees)
    {
        actions.Add(new RankableAction
        {
            Id = coffee.ToString(),
            Features =
            new List<object>()
            {
                new { BeansOrigin = beansOrigin.GetValue(rand.Next(0, beansOrigin.Length)).ToString() },
                new { Organic = organic.GetValue(rand.Next(0, organic.Length)).ToString() },
                new { Roast = roast.GetValue(rand.Next(0, roast.Length)).ToString() },
            },
        });
    }

    foreach (var tea in teas)
    {
        actions.Add(new RankableAction
        {
            Id = tea.ToString(),
            Features =
            new List<object>()
            {
                new { Organic = organic.GetValue(rand.Next(0, organic.Length)).ToString() },
            },
        });
    }

    // Sending a rank request to Personalizer
    // Here we are asking Personalizer to decide which drink the user is most likely to want
    // based on the current context features (weather, day of the week generated in RLContextManager)
    // and the features of the drinks themselves
    var request = new RankRequest(actions, contextFeature, null, eventId);
    await turnContext.SendActivityAsync(
        "===== DEBUG MESSAGE CALL TO RANK =====\n" +
        "This is what is getting sent to Rank:\n" +
        $"{JsonConvert.SerializeObject(request, Formatting.Indented)}\n",
        cancellationToken: cancellationToken);
    var response = await _personalizerClient.RankAsync(request, cancellationToken);
    await turnContext.SendActivityAsync(
        $"===== DEBUG MESSAGE RETURN FROM RANK =====\n" +
        "This is what Rank returned:\n" +
        $"{JsonConvert.SerializeObject(response, Formatting.Indented)}\n",
        cancellationToken: cancellationToken);
    return response;
}

Llamada a la API Reward y visualización de los resultados

El método RewardAsync compila los datos JSON que se van a enviar a la API Reward de Personalizer mediante la determinación de la puntuación La puntuación se determina a partir de la intención de LUIS identificada en el texto del usuario y enviada desde el método OnTurnAsync.

private async Task RewardAsync(ITurnContext turnContext, string eventId, double reward, CancellationToken cancellationToken)
{
    await turnContext.SendActivityAsync(
        "===== DEBUG MESSAGE CALL REWARD =====\n" +
        "Calling Reward:\n" +
        $"eventId = {eventId}, reward = {reward}\n",
        cancellationToken: cancellationToken);

    // Sending a reward request to Personalizer
    // Here we are responding to the drink ranking Personalizer provided us
    // If the user liked the highest ranked drink, we give a high reward (1)
    // If they did not, we give a low reward (0)
    await _personalizerClient.RewardAsync(eventId, new RewardRequest(reward), cancellationToken);
}

Consideraciones de diseño de un bot

Este ejemplo está pensado para mostrar una solución sencilla de un extremo a otro de Personalizer en un bot. Es posible que su caso de uso sea más complejo.

Si piensa usar Personalizer en un bot de producción, planee lo siguiente:

  • Acceso en tiempo real a Personalizer cada vez que necesite una selección clasificada. La API Rank no se puede procesar por lotes ni almacenar en caché. La llamada de recompensa se puede retrasar o descargar en un proceso independiente y, si no se devuelve una recompensa en el período de tiempo determinado, se establece un valor de recompensa predeterminado para el evento.
  • Cálculo de la recompensa basado en casos de uso: en este ejemplo se han mostrado dos recompensas de cero y uno sin ningún intervalo entre ellas y sin valores negativos para una puntuación. Es posible que su sistema necesite una puntuación más pormenorizada.
  • Canales de bot: En este ejemplo se usa un solo canal, pero si piensa usar más de un canal o variaciones de bots en un solo canal, es posible que tengan que considerarse como parte de las características de contexto del modelo de Personalizer.

Limpieza de recursos

Cuando haya terminado este tutorial, elimine los recursos siguientes:

  • Elimine el directorio del proyecto de ejemplo.
  • Elimine el recurso de Personalizer y LUIS en Azure Portal.

Pasos siguientes