Compartir a través de


Introducción al empleo de varias ranuras con el Personalizador de Azure AI

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.

Con Personalización de varias ranuras (versión preliminar) puede mostrar contenido en diseños web, carruseles y listas donde se muestre más de una acción (por ejemplo, un producto o un fragmento de contenido) a los usuarios. Con las API de varias ranuras de Personalizer puede conseguir que los modelos de IA de Personalizer aprendan qué contextos y productos de usuario desencadenan determinados comportamientos; para ello, tienen en cuenta la selección de ubicación en la interfaz de usuario y aprenden de ella. Por ejemplo, Personalizer puede aprender que determinados productos o contenido desencadenan más clics como barra lateral o pie de página que como resaltado principal en una página.

En esta guía va a aprender a usar las API de varias ranuras de Personalizer.

Documentación de referencia | Código fuente de la biblioteca | Paquete (NuGet) | Concepto de varias ranuras | Ejemplos

Requisitos previos

  • Una suscripción a Azure: cree una cuenta gratuita
  • La versión actual de .NET Core.
  • Cuando tenga la suscripción de Azure, cree un recurso de Personalizer en Azure Portal para obtener la clave y el punto de conexión. Tras su implementación, seleccione Ir al recurso.
    • Necesitará la clave y el punto de conexión del recurso que cree para conectar la aplicación a Personalizer API. Más adelante en este inicio rápido, debe pegar la clave y el punto de conexión en el código que se incluye a continuación.
    • Puede usar el plan de tarifa gratis (F0) para probar el servicio y actualizarlo más adelante a un plan de pago para producción.

Instalación

Actualización de instancias de Personalizer para que tenga varias ranuras

Nota

La personalización de varias ranuras (versión preliminar) afecta a otras funcionalidades del servicio Personalizer. Este cambio no se puede deshacer. Antes de habilitar la personalización de varias ranuras, consulte Personalización de varias ranuras (versión preliminar).

  1. Deshabilitación de la optimización automática En Azure Portal, en el recurso Personalizer, en la página Configuración de modelo y aprendizaje de Administración de recursos, desactive la optimización automática y guarde.

Nota

La personalización de varias ranuras no funcionará a menos que deshabilite la optimización automática. En el futuro se admitirá la optimización automática para la personalización de varias ranuras.

  1. Actualización de Personalizer para que tenga varias ranuras. En Azure Portal, en el recurso Personalizer, en la página Configuración de modelo y aprendizaje de Administración de recursos, seleccione Exportar configuración de aprendizaje. El campo arguments del archivo JSON descargado comenzará por --cb_explore_adf. Cambie esto por --ccb_explore_adf y guarde el archivo. CB (bandidos contextuales) y CCB (bandidos contextuales condicionales) son los algoritmos que usa Personalizer para la personalización de una y varias ranuras, respectivamente. ADF (características dependientes de la acción) significa que las acciones se expresan o identifican con características.

Configuración de aprendizaje antes del cambio

Configuración de aprendizaje después del cambio

En la misma pestaña del portal, en importar configuración de aprendizaje, busque el archivo JSON modificado recientemente y ábralo. De este modo, su instancia de Personalizer se actualizará para ser de varias ranuras y permitir en lo sucesivo llamadas Rank y Reward de este tipo.

Cambio de la frecuencia de actualización del modelo

En el Azure Portal, vaya a la página Configuración del recurso Personalizer y cambie el valor de Frecuencia de actualización del modelo a 30 segundos. Con esta duración breve, el modelo se entrenará rápidamente, lo que permite ver cómo cambia la acción recomendada en cada iteración.

Cambiar la frecuencia de actualización del modelo

Cambio del tiempo de espera de recompensa

En el Azure Portal, vaya a la página Configuración del recurso de Personalizer y cambie el valor de Tiempo de espera de recompensa a 10 minutos. Esto determina cuánto tiempo esperará el modelo después de enviar una recomendación para recibir los comentarios de recompensa de esa recomendación. El entrenamiento no se producirá hasta que haya transcurrido el tiempo de espera de las recompensas.

Cambio del tiempo de espera de recompensa

Creación de una aplicación de C#

Cree una nueva aplicación de consola de .NET Core en el IDE o editor que prefiera.

En una ventana de consola (por ejemplo, cmd, PowerShell o Bash), use el comando new dotnet para crear una nueva aplicación de consola con el nombre personalizer-quickstart. Este comando crea un sencillo proyecto "Hola mundo" de C# con un solo archivo de origen: Program.cs.

dotnet new console -n personalizer-quickstart

Cambie el directorio a la carpeta de aplicaciones recién creada. Para compilar la aplicación:

dotnet build

La salida de la compilación no debe contener advertencias ni errores.

...
Build succeeded.
 0 Warning(s)
 0 Error(s)
...

Instalación de la biblioteca cliente

Dentro del directorio de aplicaciones, instale la biblioteca cliente de Personalizer para .NET con el siguiente comando:

dotnet add package Azure.AI.Personalizer --version 2.0.0-beta.2

En el directorio del proyecto, abra el archivo Program.cs en el entorno de desarrollo integrado o en el editor que prefiera. Agregue lo siguiente mediante directivas:

using System;
using Azure;
using Azure.AI.Personalizer;
using System.Collections.Generic;
using System.Linq;

Modelo de objetos

El cliente de Personalizer es un objeto PersonalizerClient que se autentica en Azure mediante Azure.AzureKeyCredential, que contiene la clave.

Para solicitar el mejor elemento del contenido de cada ranura, cree un objeto PersonalizerRankMultiSlotOptions y, después, páselo a PersonalizerClient.RankMultiSlot. El método RankMultiSlot devuelve un PersonalizerMultiSlotRankResult.

Para enviar una puntuación de recompensa a Personalizer, cree un PersonalizerRewardMultiSlotOptions y páselo al método PersonalizerClient.RewardMultiSlot junto con el identificador de evento correspondiente.

La puntuación de recompensa en este inicio rápido carece de importancia. En un sistema de producción, la determinación de lo que afecta a la puntuación de recompensa y cuánto le afecta, puede ser un proceso complejo que decida cambiar con el tiempo. Esta debe ser una de las decisiones de diseño principales en la arquitectura de Personalizer.

Ejemplos de código

Estos fragmentos de código muestran cómo realizar las siguientes tareas con la biblioteca cliente de Personalizer para .NET:

Autenticar el cliente

En esta sección, hará dos cosas:

  • Especificar la clave y el punto de conexión
  • Creación de un cliente de Personalizer

Para comenzar, agregue las líneas siguientes a la clase Program. Asegúrese de agregar la clave y el punto de conexión del recurso de Personalizer.

Importante

Vaya a Azure Portal. Si el recurso de Personalizer que ha creado en la sección Requisitos previos se ha implementado correctamente, haga clic en el botón Ir al recurso en Pasos siguientes. Puede encontrar su clave y punto de conexión en la página de clave y punto de conexión del recurso, en Administración de recursos.

Recuerde quitar la clave del código cuando haya terminado y no hacerla nunca pública. En el caso de producción, considere la posibilidad de usar alguna forma segura de almacenar las credenciales, y acceder a ellas. Por ejemplo, Azure Key Vault.

private const string ServiceEndpoint  = "https://REPLACE-WITH-YOUR-PERSONALIZER-RESOURCE-NAME.cognitiveservices.azure.com";
private const string ResourceKey = "<REPLACE-WITH-YOUR-PERSONALIZER-KEY>";

A continuación, cree las direcciones URL Rank y Reward.

static PersonalizerClient InitializePersonalizerClient(Uri url)
{
    return new PersonalizerClient(url, new AzureKeyCredential(ResourceKey));
}

Obtención de opciones de contenido representadas como acciones

Las acciones representan las opciones de contenido entre las que quiere que Personalizer seleccione el mejor elemento de contenido. Agregue los métodos siguientes a la clase Program para representar el conjunto de acciones y sus características.

private static IList<PersonalizerRankableAction> GetActions()
{
    IList<PersonalizerRankableAction> actions = new List<PersonalizerRankableAction>
    {
        new PersonalizerRankableAction(
            id: "Red-Polo-Shirt-432",
            features:
            new List<object>() { new { onSale = "true", price = "20", category = "Clothing" } }
        ),

        new PersonalizerRankableAction(
            id: "Tennis-Racket-133",
            features:
            new List<object>() { new { onSale = "false", price = "70", category = "Sports" } }
        ),

        new PersonalizerRankableAction(
            id: "31-Inch-Monitor-771",
            features:
            new List<object>() { new { onSale = "true", price = "200", category = "Electronics" } }
        ),

        new PersonalizerRankableAction(
            id: "XBox-Series X-117",
            features:
            new List<object>() { new { onSale = "false", price = "499", category = "Electronics" } }
        )
    };

    return actions;
}

Obtención de ranuras

Las ranuras componen la página con la que va a interactuar el usuario. Personalizer decidirá qué acción mostrar en cada una de las ranuras definidas. Es posible excluir acciones de ranuras específicas, que se muestran como ExcludeActions. BaselineAction es la acción predeterminada para la ranura que se habría mostrado si no se hubiera usado Personalizer.

Esta guía de inicio rápido tiene características de ranura sencillas. En sistemas de producción, determinar y evaluar características puede ser una tarea compleja.

private static IList<PersonalizerSlotOptions> GetSlots()
{
    IList<PersonalizerSlotOptions> slots = new List<PersonalizerSlotOptions>
    {
        new PersonalizerSlotOptions(
            id: "BigHeroPosition",
            features: new List<object>() { new { size = "large", position = "left" } },
            excludedActions: new List<string>() { "31-Inch-Monitor-771" },
            baselineAction: "Red-Polo-Shirt-432"

        ),

        new PersonalizerSlotOptions(
            id: "SmallSidebar",
            features: new List<object>() { new { size = "small", position = "right" } },
            excludedActions: new List<string>() { "Tennis-Racket-133" },
            baselineAction: "XBox-Series X-117"
        ),
    };

    return slots;
}

Obtención de las preferencias del usuario para el contexto

Agregue los siguientes métodos a la clase Program para obtener la entrada de un usuario desde la línea de comandos con relación a la hora del día y el tipo de dispositivo en el que se encuentra el usuario. Estos métodos se usarán como características de contexto.

static string GetTimeOfDayForContext()
{
    string[] timeOfDayFeatures = new string[] { "morning", "afternoon", "evening", "night" };

    Console.WriteLine("\nWhat time of day is it (enter number)? 1. morning 2. afternoon 3. evening 4. night");
    if (!int.TryParse(GetKey(), out int timeIndex) || timeIndex < 1 || timeIndex > timeOfDayFeatures.Length)
    {
        Console.WriteLine("\nEntered value is invalid. Setting feature value to " + timeOfDayFeatures[0] + ".");
        timeIndex = 1;
    }

    return timeOfDayFeatures[timeIndex - 1];
}
static string GetDeviceForContext()
{
    string[] deviceFeatures = new string[] { "mobile", "tablet", "desktop" };

    Console.WriteLine("\nWhat is the device type (enter number)? 1. Mobile 2. Tablet 3. Desktop");
    if (!int.TryParse(GetKey(), out int deviceIndex) || deviceIndex < 1 || deviceIndex > deviceFeatures.Length)
    {
        Console.WriteLine("\nEntered value is invalid. Setting feature value to " + deviceFeatures[0] + ".");
        deviceIndex = 1;
    }

    return deviceFeatures[deviceIndex - 1];
}

Ambos métodos usan el método GetKey para leer la selección del usuario desde la línea de comandos.

private static string GetKey()
{
    return Console.ReadKey().Key.ToString().Last().ToString().ToUpper();
}
private static IList<object> GetContext(string time, string device)
{
    return new List<object>()
    {
        new { time = time },
        new { device = device }
    };
}

Creación del bucle de aprendizaje

El bucle de aprendizaje de Personalizer es un ciclo de llamadas RankMultiSlot y RewardMultiSlot. En este inicio rápido, cada llamada Rank para personalizar el contenido, va seguida de una llamada Reward para indicar a Personalizer cómo es de eficaz el funcionamiento del servicio.

El siguiente código pasa por un ciclo en el que se pregunta al usuario sus preferencias en la línea de comandos, se envía esa información a Personalizer para que seleccione la mejor acción para cada ranura, se presenta la selección al cliente para que elija entre las opciones de la lista y, después, se envía una puntuación de recompensa a Personalizer en la que se señala el grado de acierto que ha tenido el servicio en su selección.

static void Main(string[] args)
{
    Console.WriteLine($"Welcome to this Personalizer Quickstart!\n" +
    $"This code will help you understand how to use the Personalizer APIs (multislot rank and multislot reward).\n" +
    $"Each iteration represents a user interaction and will demonstrate how context, actions, slots, and rewards work.\n" +
    $"Note: Personalizer AI models learn from a large number of user interactions:\n" +
    $"You won't be able to tell the difference in what Personalizer returns by simulating a few events by hand.\n" +
    $"If you want a sample that focuses on seeing how Personalizer learns, see the Python Notebook sample.");

    int iteration = 1;
    bool runLoop = true;

    IList<PersonalizerRankableAction> actions = GetActions();
    IList<PersonalizerSlotOptions> slots = GetSlots();
    PersonalizerClient client = InitializePersonalizerClient(new Uri(ServiceEndpoint));

    do
    {
        Console.WriteLine("\nIteration: " + iteration++);

        string timeOfDayFeature = GetTimeOfDayForContext();
        string deviceFeature = GetDeviceForContext();

        IList<object> currentContext = GetContext(timeOfDayFeature, deviceFeature);

        string eventId = Guid.NewGuid().ToString();

        var multiSlotRankOptions = new PersonalizerRankMultiSlotOptions(actions, slots, currentContext, eventId);
        PersonalizerMultiSlotRankResult multiSlotRankResult = client.RankMultiSlot(multiSlotRankOptions);

        for (int i = 0; i < multiSlotRankResult.Slots.Count(); ++i)
        {
            string slotId = multiSlotRankResult.Slots[i].SlotId;
            Console.WriteLine($"\nPersonalizer service decided you should display: { multiSlotRankResult.Slots[i].RewardActionId} in slot {slotId}. Is this correct? (y/n)");

            string answer = GetKey();

            if (answer == "Y")
            {
                client.RewardMultiSlot(eventId, slotId, 1f);
                Console.WriteLine("\nGreat! The application will send Personalizer a reward of 1 so it learns from this choice of action for this slot.");
            }
            else if (answer == "N")
            {
                client.RewardMultiSlot(eventId, slotId, 0f);
                Console.WriteLine("\nYou didn't like the recommended item. The application will send Personalizer a reward of 0 for this choice of action for this slot.");
            }
            else
            {
                client.RewardMultiSlot(eventId, slotId, 0f);
                Console.WriteLine("\nEntered choice is invalid. Service assumes that you didn't like the recommended item.");
            }
        }

        Console.WriteLine("\nPress q to break, any other key to continue:");
        runLoop = !(GetKey() == "Q");

    } while (runLoop);
}

Observe detalladamente las llamadas de clasificación y recompensa en las secciones siguientes. Agregue los siguientes métodos, que obtienen las opciones de contenido seleccionadas, obtienen las ranuras y envían solicitudes Rank y Reward de varias ranuras antes de ejecutar el archivo de código:

  • GetActions
  • GetSlots
  • GetTimeOfDayForContext
  • GetDeviceForContext
  • GetKey
  • GetContext

Solicitud de la mejor acción

Para completar la solicitud Rank, el programa solicita las preferencias del usuario para crear un elemento Context de las opciones de contenido. La solicitud contiene el contexto, las acciones y las ranuras con las características respectivas, y un id. de evento único, con el fin de recibir una respuesta.

Esta guía de inicio rápido tiene características de contexto sencillas de hora del día y dispositivo del usuario. En los sistemas de producción, la determinación y evaluación de acciones y características no es una cuestión trivial.

string timeOfDayFeature = GetTimeOfDayForContext();
string deviceFeature = GetDeviceForContext();

IList<object> currentContext = GetContext(timeOfDayFeature, deviceFeature);

string eventId = Guid.NewGuid().ToString();

var multiSlotRankOptions = new PersonalizerRankMultiSlotOptions(actions, slots, currentContext, eventId);
PersonalizerMultiSlotRankResult multiSlotRankResult = client.RankMultiSlot(multiSlotRankOptions);

Envío de una recompensa

Para obtener la puntuación de recompensa para la solicitud Reward, el programa obtiene la selección del usuario mediante la línea de comandos, asigna un valor numérico (puntuación de recompensa) a la selección y, después, envía el identificador de evento único y la puntuación de recompensa para cada ranura como valor numérico a la API Reward. No es necesario definir una recompensa para cada ranura.

En este inicio rápido se asigna un número simple como puntuación de recompensa: 0 o 1. En sistemas de producción, determinar cuándo y qué enviar a la llamada Reward puede ser más complicado, en función de las necesidades específicas.

for (int i = 0; i < multiSlotRankResult.Slots.Count(); ++i)
{
    string slotId = multiSlotRankResult.Slots[i].SlotId;
    Console.WriteLine($"\nPersonalizer service decided you should display: { multiSlotRankResult.Slots[i].RewardActionId} in slot {slotId}. Is this correct? (y/n)");

    string answer = GetKey();

    if (answer == "Y")
    {
        client.RewardMultiSlot(eventId, slotId, 1f);
        Console.WriteLine("\nGreat! The application will send Personalizer a reward of 1 so it learns from this choice of action for this slot.");
    }
    else if (answer == "N")
    {
        client.RewardMultiSlot(eventId, slotId, 0f);
        Console.WriteLine("\nYou didn't like the recommended item. The application will send Personalizer a reward of 0 for this choice of action for this slot.");
    }
    else
    {
        client.RewardMultiSlot(eventId, slotId, 0f);
        Console.WriteLine("\nEntered choice is invalid. Service assumes that you didn't like the recommended item.");
    }
}

Ejecución del programa

Ejecute la aplicación con el comando run dotnet desde el directorio de aplicaciones.

dotnet run

El programa del inicio rápido realiza dos preguntas para recopilar las preferencias del usuario, conocidas como características, y, después, proporciona la acción principal.

El código fuente de este inicio rápido está disponible.

Documentación de referencia | Personalización conceptual de varias ranuras | Ejemplos

Requisitos previos

  • Una suscripción a Azure: cree una cuenta gratuita
  • Instale Node.js y NPM (se ha comprobado con Node.js v14.16.0 y NPM 6.14.11).
  • Cuando tenga la suscripción de Azure, cree un recurso de Personalizer en Azure Portal para obtener la clave y el punto de conexión. Tras su implementación, seleccione Ir al recurso.
    • Necesitará la clave y el punto de conexión del recurso que cree para conectar la aplicación a API Personalizer. Más adelante en este inicio rápido, debe pegar la clave y el punto de conexión en el código que se incluye a continuación.
    • Puede usar el plan de tarifa gratis (F0) para probar el servicio y actualizarlo más adelante a un plan de pago para producción.

Instalación

Actualización de instancias de Personalizer para que tenga varias ranuras

Nota

La personalización de varias ranuras (versión preliminar) afecta a otras funcionalidades del servicio Personalizer. Este cambio no se puede deshacer. Antes de habilitar la personalización de varias ranuras, consulte Personalización de varias ranuras (versión preliminar).

  1. Deshabilitación de la optimización automática En Azure Portal, en el recurso Personalizer, en la página Configuración de modelo y aprendizaje de Administración de recursos, desactive la optimización automática y guarde.

Nota

La personalización de varias ranuras no funcionará a menos que deshabilite la optimización automática. En el futuro se admitirá la optimización automática para la personalización de varias ranuras.

  1. Actualización de Personalizer para que tenga varias ranuras. En Azure Portal, en el recurso Personalizer, en la página Configuración de modelo y aprendizaje de Administración de recursos, seleccione Exportar configuración de aprendizaje. El campo arguments del archivo JSON descargado comenzará por --cb_explore_adf. Cambie esto por --ccb_explore_adf y guarde el archivo. CB (bandidos contextuales) y CCB (bandidos contextuales condicionales) son los algoritmos que usa Personalizer para la personalización de una y varias ranuras, respectivamente. ADF (características dependientes de la acción) significa que las acciones se expresan o identifican con características.

Configuración de aprendizaje antes del cambio

Configuración de aprendizaje después del cambio

En la misma pestaña del portal, en importar configuración de aprendizaje, busque el archivo JSON modificado recientemente y ábralo. De este modo, su instancia de Personalizer se actualizará para ser de varias ranuras y permitir en lo sucesivo llamadas Rank y Reward de este tipo.

Cambio de la frecuencia de actualización del modelo

En el Azure Portal, vaya a la página Configuración del recurso Personalizer y cambie el valor de Frecuencia de actualización del modelo a 30 segundos. Con esta duración breve, el modelo se entrenará rápidamente, lo que permite ver cómo cambia la acción recomendada en cada iteración.

Cambiar la frecuencia de actualización del modelo

Cambio del tiempo de espera de recompensa

En el Azure Portal, vaya a la página Configuración del recurso de Personalizer y cambie el valor de Tiempo de espera de recompensa a 10 minutos. Esto determina cuánto tiempo esperará el modelo después de enviar una recomendación para recibir los comentarios de recompensa de esa recomendación. El entrenamiento no se producirá hasta que haya transcurrido el tiempo de espera de las recompensas.

Cambio del tiempo de espera de recompensa

Creación de una aplicación Node.js

En una ventana de la consola (como cmd, PowerShell o Bash), cree un directorio para la aplicación y vaya a él.

mkdir myapp && cd myapp

Ejecute el comando npm init -y para crear un archivo package.json.

npm init -y

Cree una aplicación de Node.js en el editor o IDE preferidos denominada sample.js y cree variables para el punto de conexión y la clave de suscripción del recurso.

Importante

Vaya a Azure Portal. Si el recurso de Personalizer que ha creado en la sección Requisitos previos se ha implementado correctamente, haga clic en el botón Ir al recurso en Pasos siguientes. Puede encontrar su clave y punto de conexión en la página de clave y punto de conexión del recurso, en Administración de recursos.

Recuerde quitar la clave del código cuando haya terminado y no hacerla nunca pública. En el caso de producción, considere la posibilidad de usar alguna forma segura de almacenar las credenciales, y acceder a ellas. Por ejemplo, Azure Key Vault.

const axios = require('axios');
const { v4: uuidv4 } = require('uuid');
const readline = require('readline-sync');
// The endpoint specific to your personalization service instance; 
// e.g. https://<your-resource-name>.cognitiveservices.azure.com
const PersonalizationBaseUrl = '<REPLACE-WITH-YOUR-PERSONALIZER-ENDPOINT>';
// The key specific to your personalization service instance; e.g. "0123456789abcdef0123456789ABCDEF"
const ResourceKey = '<REPLACE-WITH-YOUR-PERSONALIZER-KEY>';

Instalación de los paquetes NPM para realizar un inicio rápido

npm install readline-sync uuid axios --save

Modelo de objetos

Con el fin de solicitar el mejor elemento de contenido para cada ranura, cree una solicitud rankRequest y, después, envíe una solicitud POST a multislot/rank. Después, la respuesta se analiza en un elemento rankResponse.

Para enviar una puntuación de recompensa a Personalizer, cree un objeto rewards y, después, envíe una solicitud POST a multislot/events/{eventId}/reward.

La determinación de la puntuación de recompensa en este inicio rápido no es importante. En un sistema de producción, la determinación de lo que afecta a la puntuación de recompensa y cuánto le afecta, puede ser un proceso complejo que decida cambiar con el tiempo. Esta debe ser una de las decisiones de diseño principales en la arquitectura de Personalizer.

Ejemplos de código

Estos fragmentos de código muestran cómo realizar las siguientes tareas enviando solicitudes HTTP para NodeJS:

Creación de direcciones URL base

En esta sección creará las direcciones URL Rank o Reward mediante la dirección URL base y los encabezados de solicitud con la clave de recurso.

const MultiSlotRankUrl = PersonalizationBaseUrl.concat('personalizer/v1.1-preview.1/multislot/rank');
const MultiSlotRewardUrlBase = PersonalizationBaseUrl.concat('personalizer/v1.1-preview.1/multislot/events/');
const Headers = {
    'ocp-apim-subscription-key': ResourceKey,
    'Content-Type': 'application/json'
};

Obtención de opciones de contenido representadas como acciones

Las acciones representan las opciones de contenido entre las que quiere que Personalizer seleccione el mejor elemento de contenido. Agregue los siguientes métodos al script para representar el conjunto de acciones y sus características.

function getActions() {
    return [
        {
            'id': 'Red-Polo-Shirt-432',
            'features': [
                {
                    'onSale': 'true',
                    'price': 20,
                    'category': 'Clothing'
                }
            ]
        },
        {
            'id': 'Tennis-Racket-133',
            'features': [
                {
                    'onSale': 'false',
                    'price': 70,
                    'category': 'Sports'
                }
            ]
        },
        {
            'id': '31-Inch-Monitor-771',
            'features': [
                {
                    'onSale': 'true',
                    'price': 200,
                    'category': 'Electronics'
                }
            ]
        },
        {
            'id': 'XBox-Series X-117',
            'features': [
                {
                    'onSale': 'false',
                    'price': 499,
                    'category': 'Electronics'
                }
            ]
        }
    ];
}

Obtención de las preferencias del usuario para el contexto

Agregue los siguientes métodos al script para obtener la entrada de un usuario desde la línea de comandos con relación a la hora del día y el tipo de dispositivo en el que se encuentra el usuario. Se usarán como características de contexto.

function getContextFeatures() {
    const timeOfDayFeatures = ['morning', 'afternoon', 'evening', 'night'];
    const deviceFeatures = ['mobile', 'tablet', 'desktop'];

    let answer = readline.question('\nWhat time of day is it (enter number)? 1. morning 2. afternoon 3. evening 4. night\n');
    let selection = parseInt(answer);
    const timeOfDay = selection >= 1 && selection <= 4 ? timeOfDayFeatures[selection - 1] : timeOfDayFeatures[0];

    answer = readline.question('\nWhat type of device is the user on (enter number)? 1. mobile 2. tablet 3. desktop\n');
    selection = parseInt(answer);
    const device = selection >= 1 && selection <= 3 ? deviceFeatures[selection - 1] : deviceFeatures[0];

    console.log('Selected features:\n');
    console.log('Time of day: ' + timeOfDay + '\n');
    console.log('Device: ' + device + '\n');

    return [
        {
            'time': timeOfDay
        },
        {
            'device': device
        }
    ];
}

Obtención de ranuras

Las ranuras componen la página con la que va a interactuar el usuario. Personalizer decidirá qué acción mostrar en cada una de las ranuras definidas. Es posible excluir acciones de ranuras específicas, que se muestran como ExcludeActions. BaselineAction es la acción predeterminada para la ranura que se habría mostrado si no se hubiera usado Personalizer.

Esta guía de inicio rápido tiene características de ranura sencillas. En sistemas de producción, determinar y evaluar características puede ser una tarea compleja.

function getSlots() {
    return [
        {
            'id': 'BigHeroPosition',
            'features': [
                {
                    'size': 'large',
                    'position': 'left',
                }
            ],
            'excludedActions': ['31-Inch-Monitor-771'],
            'baselineAction': 'Red-Polo-Shirt-432'
        },
        {
            'id': 'SmallSidebar',
            'features': [
                {
                    'size': 'small',
                    'position': 'right',
                }
            ],
            'excludedActions': ['Tennis-Racket-133'],
            'baselineAction': 'XBox-Series X-117'
        }
    ];
}

Realización de solicitudes HTTP

Agregue estas funciones para enviar solicitudes POST al punto de conexión de Personalizer a fin de realizar llamadas de clasificación y recompensa de varias ranuras.

async function sendMultiSlotRank(rankRequest) {
    try {
        let response = await axios.post(MultiSlotRankUrl, rankRequest, { headers: Headers })
        return response.data;
    }
    catch (err) {
        if(err.response)
        {
            throw err.response.data
        }
        console.log(err)
        throw err;
    }
}
async function sendMultiSlotReward(rewardRequest, eventId) {
    try {
        let rewardUrl = MultiSlotRewardUrlBase.concat(eventId, '/reward');
        let response = await axios.post(rewardUrl, rewardRequest, { headers: Headers })
    }
    catch (err) {
        console.log(err);
        throw err;
    }
}

Obtención de comentarios sobre decisiones de Personalizer

Agregue el siguiente método al script. Indicará si Personalizer ha tomado una decisión correcta con relación a cada ranura mediante un símbolo de la línea de comandos.

function getRewardForSlot() {
    let answer = readline.question('\nIs this correct? (y/n)\n').toUpperCase();
    if (answer === 'Y') {
        console.log('\nGreat! The application will send Personalizer a reward of 1 so it learns from this choice of action for this slot.\n');
        return 1;
    }
    else if (answer === 'N') {
        console.log('\nYou didn\'t like the recommended item.The application will send Personalizer a reward of 0 for this choice of action for this slot.\n');
        return 0;
    }
    console.log('\nEntered choice is invalid. Service assumes that you didn\'t like the recommended item.\n');
    return 0;
}

Creación del bucle de aprendizaje

El bucle de aprendizaje de Personalizer es un ciclo de llamadas Rank y Reward. En este inicio rápido, cada llamada Rank para personalizar el contenido, va seguida de una llamada Reward para indicar a Personalizer cómo es de eficaz el funcionamiento del servicio.

El siguiente código pasa por un ciclo en el que se pregunta al usuario sus preferencias en la línea de comandos, se envía esa información a Personalizer para que seleccione la mejor acción para cada ranura, se presenta la selección al cliente para que elija entre las opciones de la lista y, después, se envía una puntuación de recompensa a Personalizer en la que se señala el grado de acierto que ha tenido el servicio en su selección.

let runLoop = true;

(async () => {
    do {

        let multiSlotRankRequest = {};

        // Generate an ID to associate with the request.
        multiSlotRankRequest.eventId = uuidv4();

        // Get context information from the user.
        multiSlotRankRequest.contextFeatures = getContextFeatures();

        // Get the actions list to choose from personalization with their features.
        multiSlotRankRequest.actions = getActions();

        // Get the list of slots for which Personalizer will pick the best action.
        multiSlotRankRequest.slots = getSlots();

        multiSlotRankRequest.deferActivation = false;

        try {
            //Rank the actions for each slot
            let multiSlotRankResponse = await sendMultiSlotRank(multiSlotRankRequest);
            let multiSlotrewards = {};
            multiSlotrewards.reward = [];
    
            for (let i = 0; i < multiSlotRankResponse.slots.length; i++) {
                console.log('\nPersonalizer service decided you should display: '.concat(multiSlotRankResponse.slots[i].rewardActionId, ' in slot ', multiSlotRankResponse.slots[i].id, '\n'));
    
                let slotReward = {};
                slotReward.slotId = multiSlotRankResponse.slots[i].id;
                // User agrees or disagrees with Personalizer decision for slot
                slotReward.value = getRewardForSlot();
                multiSlotrewards.reward.push(slotReward);
            }
    
            // Send the rewards for the event
            await sendMultiSlotReward(multiSlotrewards, multiSlotRankResponse.eventId);
    
            let answer = readline.question('\nPress q to break, any other key to continue:\n').toUpperCase();
            if (answer === 'Q') {
                runLoop = false;
            }
        }
        catch (err) {
            console.log(err);
            throw err;
        }



    } while (runLoop);
})()

Observe detalladamente las llamadas de clasificación y recompensa en las secciones siguientes.

Agregue los siguientes métodos, que obtienen las opciones de contenido seleccionadas, obtienen las preferencias del usuario con respecto al contexto, obtienen las ranuras, realizan solicitudes HTTP y obtienen la recompensa de cada ranura antes de ejecutar el archivo de código:

  • getActions
  • getContextFeatures
  • getSlots
  • sendRank
  • sendReward
  • getRewardForSlot

Solicitud de la mejor acción

Para completar la solicitud Rank, el programa solicita las preferencias del usuario para crear opciones de contenido. El cuerpo de la solicitud contiene el contexto, las acciones y las ranuras con sus respectivas características. El método sendMultiSlotRank toma un objeto rankRequest y ejecuta la solicitud de clasificación de varias ranuras.

Esta guía de inicio rápido tiene características de contexto sencillas de hora del día y dispositivo del usuario. En los sistemas de producción, la determinación y evaluación de acciones y características no es una cuestión trivial.

let multiSlotRankRequest = {};

// Generate an ID to associate with the request.
multiSlotRankRequest.eventId = uuidv4();

// Get context information from the user.
multiSlotRankRequest.contextFeatures = getContextFeatures();

// Get the actions list to choose from personalization with their features.
multiSlotRankRequest.actions = getActions();

// Get the list of slots for which Personalizer will pick the best action.
multiSlotRankRequest.slots = getSlots();

multiSlotRankRequest.deferActivation = false;

//Rank the actions for each slot
try {
    let multiSlotRankResponse = await sendMultiSlotRank(multiSlotRankRequest);
}
catch (err) {
    console.log(err);
    throw err;
}

Envío de una recompensa

Para obtener la puntuación de recompensa de la solicitud Reward, el programa obtiene la selección del usuario en cada ranura mediante la línea de comandos, asigna un valor numérico (puntuación de recompensa) a la selección y, después, envía el identificador de evento único, el identificador de ranura y la puntuación de recompensa para cada ranura al método sendMultiSlotReward. No es necesario definir una recompensa para cada ranura.

En este inicio rápido se asigna un número simple como puntuación de recompensa: 0 o 1. En sistemas de producción, determinar cuándo y qué enviar a la llamada Reward puede ser más complicado, en función de las necesidades específicas.

let multiSlotrewards = {};
multiSlotrewards.reward = [];

for (i = 0; i < multiSlotRankResponse.slots.length; i++) {
    console.log('\nPersonalizer service decided you should display: '.concat(multiSlotRankResponse.slots[i].rewardActionId, ' in slot ', multiSlotRankResponse.slots[i].id, '\n'));

    let slotReward = {};
    slotReward.slotId = multiSlotRankResponse.slots[i].id;
    // User agrees or disagrees with Personalizer decision for slot
    slotReward.value = getRewardForSlot();
    multiSlotrewards.reward.push(slotReward);
}

// Send the rewards for the event
await sendMultiSlotReward(multiSlotrewards, multiSlotRankResponse.eventId);

Ejecución del programa

Ejecute la aplicación con el comando de Node.js desde el directorio de aplicación.

node sample.js

El programa del inicio rápido realiza dos preguntas para recopilar las preferencias del usuario, conocidas como características, y, después, proporciona la acción principal.

El código fuente de este inicio rápido está disponible.

Personalización conceptual de varias ranuras | Ejemplos

Requisitos previos

  • Una suscripción a Azure: cree una cuenta gratuita
  • Python 3.x
  • Cuando tenga la suscripción de Azure, cree un recurso de Personalizer en Azure Portal para obtener la clave y el punto de conexión. Tras su implementación, seleccione Ir al recurso.
    • Necesitará la clave y el punto de conexión del recurso que cree para conectar la aplicación a API Personalizer. Más adelante en este inicio rápido, debe pegar la clave y el punto de conexión en el código que se incluye a continuación.
    • Puede usar el plan de tarifa gratis (F0) para probar el servicio y actualizarlo más adelante a un plan de pago para producción.

Instalación

Actualización de instancias de Personalizer para que tenga varias ranuras

Nota

La personalización de varias ranuras (versión preliminar) afecta a otras funcionalidades del servicio Personalizer. Este cambio no se puede deshacer. Antes de habilitar la personalización de varias ranuras, consulte Personalización de varias ranuras (versión preliminar).

  1. Deshabilitación de la optimización automática En Azure Portal, en el recurso Personalizer, en la página Configuración de modelo y aprendizaje de Administración de recursos, desactive la optimización automática y guarde.

Nota

La personalización de varias ranuras no funcionará a menos que deshabilite la optimización automática. En el futuro se admitirá la optimización automática para la personalización de varias ranuras.

  1. Actualización de Personalizer para que tenga varias ranuras. En Azure Portal, en el recurso Personalizer, en la página Configuración de modelo y aprendizaje de Administración de recursos, seleccione Exportar configuración de aprendizaje. El campo arguments del archivo JSON descargado comenzará por --cb_explore_adf. Cambie esto por --ccb_explore_adf y guarde el archivo. CB (bandidos contextuales) y CCB (bandidos contextuales condicionales) son los algoritmos que usa Personalizer para la personalización de una y varias ranuras, respectivamente. ADF (características dependientes de la acción) significa que las acciones se expresan o identifican con características.

Configuración de aprendizaje antes del cambio

Configuración de aprendizaje después del cambio

En la misma pestaña del portal, en importar configuración de aprendizaje, busque el archivo JSON modificado recientemente y ábralo. De este modo, su instancia de Personalizer se actualizará para ser de varias ranuras y permitir en lo sucesivo llamadas Rank y Reward de este tipo.

Cambio de la frecuencia de actualización del modelo

En el Azure Portal, vaya a la página Configuración del recurso Personalizer y cambie el valor de Frecuencia de actualización del modelo a 30 segundos. Con esta duración breve, el modelo se entrenará rápidamente, lo que permite ver cómo cambia la acción recomendada en cada iteración.

Cambiar la frecuencia de actualización del modelo

Cambio del tiempo de espera de recompensa

En el Azure Portal, vaya a la página Configuración del recurso de Personalizer y cambie el valor de Tiempo de espera de recompensa a 10 minutos. Esto determina cuánto tiempo esperará el modelo después de enviar una recomendación para recibir los comentarios de recompensa de esa recomendación. El entrenamiento no se producirá hasta que haya transcurrido el tiempo de espera de las recompensas.

Cambio del tiempo de espera de recompensa

Creación de una nueva aplicación de Python

Cree un archivo de Python y variables para el punto de conexión y la clave de suscripción del recurso.

Importante

Vaya a Azure Portal. Si el recurso de Personalizer que ha creado en la sección Requisitos previos se ha implementado correctamente, haga clic en el botón Ir al recurso en Pasos siguientes. Puede encontrar su clave y punto de conexión en la página de clave y punto de conexión del recurso, en Administración de recursos.

Recuerde quitar la clave del código cuando haya terminado y no hacerla nunca pública. En el caso de producción, considere la posibilidad de usar alguna forma segura de almacenar las credenciales, y acceder a ellas. Por ejemplo, Azure Key Vault.

import json, uuid, requests

# The endpoint specific to your personalization service instance; 
# e.g. https://<your-resource-name>.cognitiveservices.azure.com
PERSONALIZATION_BASE_URL = "<REPLACE-WITH-YOUR-PERSONALIZER-ENDPOINT>"
# The key specific to your personalization service instance; e.g. "0123456789abcdef0123456789ABCDEF"
RESOURCE_KEY = "<REPLACE-WITH-YOUR-PERSONALIZER-KEY>"

Modelo de objetos

Con el fin de solicitar el mejor elemento de contenido para cada ranura, cree una solicitud rank_request y, después, envíe una solicitud POST a multislot/rank. Luego la respuesta se analiza en rank_response.

Para enviar una puntuación de recompensa a Personalizer, cree una llamada rewards y, a continuación, envíe una solicitud POST a multislot/events/{eventId}/reward.

En este artículo de inicio rápido, determinar la puntuación de recompensa no es importante. En un sistema de producción, la determinación de lo que afecta a la puntuación de recompensa, y cuánto le afecta, puede ser un proceso complejo que decida cambiar con el tiempo. Esta debe ser una de las decisiones de diseño principales en la arquitectura de Personalizer.

Ejemplos de código

Estos fragmentos de código muestran cómo realizar las siguientes tareas enviando solicitudes HTTP para Python:

Creación de direcciones URL base

En esta sección creará las direcciones URL Rank o Reward mediante la dirección URL base y los encabezados de solicitud con la clave de recurso.

MULTI_SLOT_RANK_URL = '{0}personalizer/v1.1-preview.1/multislot/rank'.format(PERSONALIZATION_BASE_URL)
MULTI_SLOT_REWARD_URL_BASE = '{0}personalizer/v1.1-preview.1/multislot/events/'.format(PERSONALIZATION_BASE_URL)
HEADERS = {
    'ocp-apim-subscription-key': RESOURCE_KEY,
    'Content-Type': 'application/json'
}

Obtención de opciones de contenido representadas como acciones

Las acciones representan las opciones de contenido entre las que quiere que Personalizer seleccione el mejor elemento de contenido. Agregue los siguientes métodos al script para representar el conjunto de acciones y sus características.

def get_actions():
    return [
        {
            "id": "Red-Polo-Shirt-432",
            "features": [
                {
                    "onSale": "true",
                    "price": 20,
                    "category": "Clothing"
                }
            ]
        },
        {
            "id": "Tennis-Racket-133",
            "features": [
                {
                    "onSale": "false",
                    "price": 70,
                    "category": "Sports"
                }
            ]
        },
        {
            "id": "31-Inch-Monitor-771",
            "features": [
                {
                    "onSale": "true",
                    "price": 200,
                    "category": "Electronics"
                }
            ]
        },
        {
            "id": "XBox-Series X-117",
            "features": [
                {
                    "onSale": "false",
                    "price": 499,
                    "category": "Electronics"
                }
            ]
        }
    ]

Obtención de las preferencias del usuario para el contexto

Agregue los siguientes métodos al script para obtener la entrada de un usuario desde la línea de comandos con relación a la hora del día y el tipo de dispositivo en el que se encuentra el usuario. Se usarán como características de contexto.

def get_context_features():
    time_features = ["morning", "afternoon", "evening", "night"]
    time_pref = input("What time of day is it (enter number)? 1. morning 2. afternoon 3. evening 4. night\n")
    try:
        parsed_time = int(time_pref)
        if(parsed_time <=0 or parsed_time > len(time_features)):
            raise IndexError
        time_of_day = time_features[parsed_time-1]
    except (ValueError, IndexError):
        print("Entered value is invalid. Setting feature value to", time_features[0] + ".")
        time_of_day = time_features[0]

    device_features = ['mobile', 'tablet', 'desktop']
    device_pref = input("What type of device is the user on (enter number)? 1. mobile 2. tablet 3. desktop\n")
    try:
        parsed_device = int(device_pref)
        if(parsed_device <=0 or parsed_device > len(device_features)):
            raise IndexError
        device = device_features[parsed_device-1]
    except (ValueError, IndexError):
        print("Entered value is invalid. Setting feature value to", device_features[0]+ ".")
        device = device_features[0]

    return [
        {'time': time_of_day},
        {'device': device}
        ]

Obtención de ranuras

Las ranuras componen la página con la que va a interactuar el usuario. Personalizer decidirá qué acción mostrar en cada una de las ranuras definidas. Es posible excluir acciones de ranuras específicas, que se muestran como ExcludeActions. BaselineAction es la acción predeterminada para la ranura que se habría mostrado si no se hubiera usado Personalizer.

Esta guía de inicio rápido tiene características de ranura sencillas. En sistemas de producción, determinar y evaluar características puede ser una tarea compleja.

def get_slots():
    return [
        {
            "id": "BigHeroPosition",
            "features": [
                {
                    "size": "large",
                    "position": "left",
                }
            ],
            "excludedActions": ["31-Inch-Monitor-771"],
            "baselineAction": "Red-Polo-Shirt-432"
        },
        {
            "id": "SmallSidebar",
            "features": [
                {
                    "size": "small",
                    "position": "right",
                }
            ],
            "excludedActions": ["Tennis-Racket-133"],
            "baselineAction": "XBox-Series X-117"
        }
    ]

Realización de solicitudes HTTP

Agregue estas funciones para enviar solicitudes POST al punto de conexión de Personalizer a fin de realizar llamadas de clasificación y recompensa de varias ranuras.

def send_multi_slot_rank(rank_request):
multi_slot_response = requests.post(MULTI_SLOT_RANK_URL, data=json.dumps(rank_request), headers=HEADERS)
if multi_slot_response.status_code != 201:
    raise Exception(multi_slot_response.text)
return json.loads(multi_slot_response.text)
def send_multi_slot_reward(reward_request, event_id):
    reward_url = '{0}{1}/reward'.format(MULTI_SLOT_REWARD_URL_BASE, event_id)
    requests.post(reward_url, data=json.dumps(reward_request), headers=HEADERS)

Obtención de comentarios sobre decisiones de Personalizer

Agregue el siguiente método al script. Indicará si Personalizer ha tomado una decisión correcta con relación a cada ranura mediante un símbolo de la línea de comandos.

def get_reward_for_slot():
    answer = input('\nIs this correct? (y/n)\n').upper()
    if (answer == 'Y'):
        print('\nGreat! The application will send Personalizer a reward of 1 so it learns from this choice of action for this slot.\n')
        return 1
    elif (answer == 'N'):
        print('\nYou didn\'t like the recommended item.The application will send Personalizer a reward of 0 for this choice of action for this slot.\n')
        return 0
    print('\nEntered choice is invalid. Service assumes that you didn\'t like the recommended item.\n')
    return 0

Creación del bucle de aprendizaje

El bucle de aprendizaje de Personalizer es un ciclo de llamadas Rank y Reward. En este inicio rápido, cada llamada Rank para personalizar el contenido, va seguida de una llamada Reward para indicar a Personalizer cómo es de eficaz el funcionamiento del servicio.

El siguiente código pasa por un ciclo en el que se pregunta al usuario sus preferencias en la línea de comandos, se envía esa información a Personalizer para que seleccione la mejor acción para cada ranura, se presenta la selección al cliente para que elija entre las opciones de la lista y, después, se envía una puntuación de recompensa a Personalizer en la que se señala el grado de acierto que ha tenido el servicio en su selección.

run_loop = True

while run_loop:

    eventId = str(uuid.uuid4())
    context = get_context_features()
    actions = get_actions()
    slots = get_slots()

    rank_request = {
        "eventId": eventId,
        "contextFeatures": context,
        "actions": actions,
        "slots": slots,
        "deferActivation": False
      }

    #Rank the actions for each slot
    multi_slot_rank_response = send_multi_slot_rank(rank_request)
    multi_slot_rewards = {"reward": []}

    for i in range(len(multi_slot_rank_response['slots'])):
        print('\nPersonalizer service decided you should display: {0} in slot {1}\n'.format(multi_slot_rank_response['slots'][i]['rewardActionId'], multi_slot_rank_response['slots'][i]['id']))

        slot_reward = {'slotId': multi_slot_rank_response['slots'][i]['id']}
        # User agrees or disagrees with Personalizer decision for slot
        slot_reward['value'] = get_reward_for_slot()
        multi_slot_rewards['reward'].append(slot_reward)

    # Send the rewards for the event
    send_multi_slot_reward(multi_slot_rewards, multi_slot_rank_response['eventId'])

    answer = input('\nPress q to break, any other key to continue:\n').upper()
    if (answer == 'Q'):
        run_loop = False

Observe detalladamente las llamadas de clasificación y recompensa en las secciones siguientes.

Agregue los siguientes métodos, que obtienen las opciones de contenido seleccionadas, obtienen las preferencias del usuario con respecto al contexto, obtienen las ranuras, realizan solicitudes HTTP y obtienen la recompensa de cada ranura antes de ejecutar el archivo de código:

  • get_actions
  • get_context_features
  • get_slots
  • send_rank
  • send_reward
  • get_reward_for_dsot

Solicitud de la mejor acción

Para completar la solicitud Rank, el programa solicita las preferencias del usuario para crear opciones de contenido. El cuerpo de la solicitud contiene el contexto, las acciones y las ranuras con sus respectivas características. El método send_multi_slot_rank toma un objeto rankRequest y ejecuta la solicitud de clasificación de varias ranuras.

Esta guía de inicio rápido tiene características de contexto sencillas de hora del día y dispositivo del usuario. En los sistemas de producción, la determinación y evaluación de acciones y características no es una cuestión trivial.

eventId = str(uuid.uuid4())
context = get_context_features()
actions = get_actions()
slots = get_slots()

rank_request = {
    "eventId": eventId,
    "contextFeatures": context,
    "actions": actions,
    "slots": slots,
    "deferActivation": False
    }

#Rank the actions for each slot
multi_slot_rank_response = send_multi_slot_rank(rank_request)

Envío de una recompensa

Para obtener la puntuación de recompensa de la solicitud Reward, el programa obtiene la selección del usuario en cada ranura mediante la línea de comandos, asigna un valor numérico (puntuación de recompensa) a la selección y, después, envía el identificador de evento único, el identificador de ranura y la puntuación de recompensa para cada ranura al método send_multi_slot_reward. No es necesario definir una recompensa para cada ranura.

En este inicio rápido se asigna un número simple como puntuación de recompensa: 0 o 1. En sistemas de producción, determinar cuándo y qué enviar a la llamada Reward puede ser más complicado, en función de las necesidades específicas.

multi_slot_rewards = {"reward": []}

for i in range(len(multi_slot_rank_response['slots'])):
    print('\nPersonalizer service decided you should display: {0} in slot {1}\n'.format(multi_slot_rank_response['slots'][i]['rewardActionId'], multi_slot_rank_response['slots'][i]['id']))

    slot_reward = {'slotId': multi_slot_rank_response['slots'][i]['id']}
    # User agrees or disagrees with Personalizer decision for slot
    slot_reward['value'] = get_reward_for_slot()
    multi_slot_rewards['reward'].append(slot_reward)

# Send the rewards for the event
send_multi_slot_reward(multi_slot_rewards, multi_slot_rank_response['eventId'])

Ejecución del programa

Ejecute la aplicación con el comando de Python desde el directorio de aplicación.

python sample.py

El programa del inicio rápido realiza dos preguntas para recopilar las preferencias del usuario, conocidas como características, y, después, proporciona la acción principal.

El código fuente de este inicio rápido está disponible.

Pasos siguientes