Dela via


Självstudie: Använda Personanpassning i .NET-chattrobot

Viktigt!

Från och med den 20 september 2023 kommer du inte att kunna skapa nya personaliseringsresurser. Personanpassningstjänsten dras tillbaka den 1 oktober 2026.

Använd en C# .NET-chattrobot med en Personalizer-loop för att tillhandahålla rätt innehåll till en användare. Den här chattroboten föreslår en specifik kaffe eller te till en användare. Användaren kan acceptera eller avvisa det förslaget. Detta ger personanpassningsinformation för att göra nästa förslag mer lämpligt.

I den här självstudiekursen får du lära du dig att:

  • Ställa in Azure-resurser
  • Konfigurera och köra robot
  • Interagera med roboten med Hjälp av Bot Framework-emulatorn
  • Förstå var och hur roboten använder Personanpassning

Hur fungerar chattroboten?

En chattrobot är vanligtvis en fram och tillbaka-konversation med en användare. Den här specifika chattroboten använder Personanpassning för att välja den bästa åtgärden (kaffe eller te) för att erbjuda användaren. Personalizer använder förstärkningsinlärning för att göra det valet.

Chattroboten måste hantera svängar i konversationen. Chattroboten använder Bot Framework för att hantera robotarkitekturen och konversationen och använder Azure AI Language Understanding (LUIS) för att förstå avsikten med det naturliga språket från användaren.

Chattroboten är en webbplats med en specifik väg som är tillgänglig för att besvara förfrågningar, http://localhost:3978/api/messages. Du kan använda Bot Framework-emulatorn för att visuellt interagera med chattroboten som körs medan du utvecklar en robot lokalt.

Användarinteraktioner med roboten

Det här är en enkel chattrobot som gör att du kan ange textfrågor.

Användaren anger text Roboten svarar med text Beskrivning av åtgärder som roboten vidtar för att fastställa svarstext
Ingen text har angetts – roboten börjar konversationen. 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.
Roboten börjar konversationen med instruktionstext och låter dig veta vad kontexten är: Tuesday, Snowy.
Show menu Here is our menu: Coffee: Cappuccino Espresso Latte Macchiato Mocha Tea: GreenTea Rooibos Fastställ avsikten med frågan med HJÄLP av LUIS och visa sedan menyalternativ för kaffe- och teobjekt. Funktionerna i åtgärderna är
What do you suggest How about Latte? Fastställ avsikten med frågan med hjälp av LUIS, anropa sedan Rank API och visa det främsta valet som en fråga How about {response.RewardActionId}?. Visar även JSON-anrop och svar i illustrationssyfte.
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?
Fastställ avsikten med frågan med HJÄLP av LUIS och anropa sedan Reward API med belöning för 1, visar JSON-anrop och svar i illustrationssyfte.
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?
Fastställ avsikten med frågan med HJÄLP av LUIS och anropa sedan Reward API med belöning för 0, visar JSON-anrop och svar i illustrationssyfte.
Reset Returnerar instruktionstext. Fastställ avsikten med frågan med hjälp av LUIS och visa sedan instruktionstexten och återställer kontexten.

Personanpassning i den här roboten

Den här chattroboten använder Personanpassning för att välja den översta åtgärden (specifikt kaffe eller te), baserat på en lista över åtgärder (viss typ av innehåll) och kontextfunktioner.

Roboten skickar listan över åtgärder, tillsammans med kontextfunktioner, till personanpassningsloopen. Personanpassning returnerar den enskilt bästa åtgärden till din robot, som roboten visar.

I den här självstudien är åtgärderna typer av kaffe och te:

Kaffe Te
Cappuccino
Espresso
Latte
Mocha
GreenTea
Rooibos

Rank-API: För att hjälpa personanpassningen att lära sig om dina åtgärder skickar roboten följande med varje ranknings-API-begäran:

  • Åtgärder med funktioner
  • Kontextfunktioner

En funktion i modellen är information om åtgärden eller kontexten som kan aggregeras (grupperas) mellan medlemmar i chattrobotens användarbas. En funktion är inte individuellt specifik (till exempel ett användar-ID) eller mycket specifik (till exempel en exakt tid på dagen).

Funktioner används för att justera åtgärder mot den aktuella kontexten i modellen. Modellen är en representation av personanpassningens tidigare kunskaper om åtgärder, kontext och deras funktioner som gör att den kan fatta välgrundade beslut.

Modellen, inklusive funktioner, uppdateras enligt ett schema baserat på inställningen För modelluppdateringsfrekvens i Azure-portalen.

Funktioner bör väljas med samma planering och design som du skulle tillämpa på alla scheman eller modeller i din tekniska arkitektur. Funktionsvärdena kan anges med affärslogik eller tredjepartssystem.

Varning

Funktioner i det här programmet är till för demonstration och kanske inte nödvändigtvis är de bästa funktionerna att använda i en webbapp för ditt användningsfall.

Åtgärdsfunktioner

Varje åtgärd (innehållsobjekt) har funktioner som hjälper dig att särskilja kaffe- eller teobjektet.

Funktionerna konfigureras inte som en del av loopkonfigurationen i Azure-portalen. I stället skickas de som ett JSON-objekt med varje Rank API-anrop. Detta gör det möjligt för åtgärderna och deras funktioner att växa, ändra och krympa med tiden, vilket gör att Personanpassning kan följa trender.

Funktioner för kaffe och te inkluderar:

  • Ursprungsplats för kaffeböna som Kenya och Brasilien
  • Är kaffet eller teet ekologiskt?
  • Ljus eller mörk kafferost

Medan kaffet har tre funktioner i föregående lista, har te bara en. Skicka endast funktioner till Personanpassning som är meningsfulla för åtgärden. Skicka inte in ett tomt värde för en funktion om det inte gäller för åtgärden.

Kontextfunktioner

Kontextfunktioner hjälper Personanpassning att förstå miljöns kontext, till exempel visningsenheten, användaren, platsen och andra funktioner som är relevanta för ditt användningsfall.

Kontexten för den här chattroboten omfattar:

  • Typ av väder (snöigt, regnigt, soligt)
  • Dag i veckan

Val av funktioner slumpmässigt i den här chattroboten. I en riktig robot använder du verkliga data för dina kontextfunktioner.

Designöverväganden för den här roboten

Det finns några varningar att notera om den här konversationen:

  • Robotinteraktion: Konversationen är mycket enkel eftersom den visar rankning och belöning i ett enkelt användningsfall. Det visar inte alla funktioner i Bot Framework SDK eller emulatorn.
  • Personanpassning: Funktionerna väljs slumpmässigt för att simulera användning. Randomisera inte funktioner i ett scenario med anpassning av produktion.
  • Language Understanding (LUIS): De få exempelyttrandena för LUIS-modellen är endast avsedda för det här exemplet. Använd inte så få exempelyttranden i ditt LUIS-produktionsprogram.

Installera nödvändig programvara

  • Visual Studio 2019. Lagringsplatsen för nedladdningsbara exempel innehåller instruktioner om du föredrar att använda .NET Core CLI.
  • Microsoft Bot Framework Emulator är ett skrivbordsprogram som gör det möjligt för robotutvecklare att testa och felsöka sina robotar på localhost eller fjärrköra via en tunnel.

Ladda ned exempelkoden för chattroboten

Chattroboten är tillgänglig på lagringsplatsen För personanpassningsexempel. Klona eller ladda ned lagringsplatsen och öppna sedan exemplet i /samples/ChatbotExample katalogen med Visual Studio 2019.

Om du vill klona lagringsplatsen använder du följande Git-kommando i ett Bash-gränssnitt (terminal).

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

Skapa och konfigurera Personalizer- och LUIS-resurser

Skapa Azure-resurser

Om du vill använda chattroboten måste du skapa Azure-resurser för Personalizer och Language Understanding (LUIS).

  • Skapa LUIS-resurser. Skapa både en redigerings- och förutsägelseresurs.
  • Skapa Personalizer-resurs och kopiera sedan nyckeln och slutpunkten från Azure-portalen. Du måste ange dessa värden i appsettings.json filen för .NET-projektet.

Skapa LUIS-app

Om du är nybörjare på LUIS måste du logga in och omedelbart migrera ditt konto. Du behöver inte skapa nya resurser. Välj i stället de resurser som du skapade i föregående avsnitt i den här självstudien.

  1. Om du vill skapa ett nytt LUIS-program går du till LUIS-portalen och väljer din prenumeration och redigeringsresurs.
  2. På samma sida väljer du sedan + Ny app för konversation och sedan Importera som JSON.
  3. I popup-dialogrutan väljer du Välj fil och väljer /samples/ChatbotExample/CognitiveModels/coffeebot.json sedan filen. Ange namnet Personalizer Coffee bot.
  4. Välj knappen Träna i det övre högra navigeringsfältet i LUIS-portalen.
  5. Välj knappen Publicera för att publicera appen till produktionsplatsen för förutsägelsekörningen.
  6. Välj Hantera och sedan Inställningar. Kopiera värdet för app-ID :t. Du måste ange det här värdet i appsettings.json filen för .NET-projektet.
  7. Välj Azure-resurser i avsnittet Hantera. Då visas de associerade resurserna i appen.
  8. Välj Lägg till förutsägelseresurs. I popup-dialogrutan väljer du din prenumeration och förutsägelseresursen som skapades i ett tidigare avsnitt i den här självstudien och väljer sedan Klar.
  9. Kopiera värdena för primärnyckeln och slutpunkts-URL:en. Du måste ange dessa värden i appsettings.json filen för .NET-projektet.

Konfigurera robot med filen appsettings.json

  1. Öppna chattrobotens lösningsfil, ChatbotSamples.sln, med Visual Studio 2019.

  2. Öppna appsettings.json i projektets rotkatalog.

  3. Ange alla fem inställningar som kopierats i föregående avsnitt i den här självstudien.

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

Skapa och köra roboten

När du har konfigurerat appsettings.jsonär du redo att skapa och köra chattroboten. När du gör det öppnas en webbläsare på den webbplats som körs, http://localhost:3978.

Screenshot of browser displaying chat bot web site.

Håll webbplatsen igång eftersom självstudien förklarar vad roboten gör, så att du kan interagera med roboten.

Konfigurera Bot Framework-emulatorn

  1. Öppna Bot Framework-emulatorn och välj Öppna robot.

    Screenshot of Bot Framework Emulator startup screen.

  2. Konfigurera roboten med följande robot-URL och välj sedan Anslut:

    http://localhost:3978/api/messages

    Screenshot of Bot Framework Emulator open bot settings.

    Emulatorn ansluter till chattroboten och visar instruktionstexten, tillsammans med loggnings- och felsökningsinformation som är användbar för lokal utveckling.

    Screenshot of Bot Framework Emulator in first turn of conversation.

Använda roboten i Bot Framework-emulatorn

  1. Be om att få se menyn genom att ange I would like to see the menu. Chattroboten visar objekten.

  2. Låt roboten föreslå ett objekt genom att ange Please suggest a drink for me. Emulatorn visar rankningsbegäran och svar i chattfönstret, så att du kan se hela JSON. Och roboten ger ett förslag, ungefär som How about Latte?

  3. Svar som du vill att det innebär att du accepterar Personanpassningens högst rankade val, I like it. emulatorn visar Reward-begäran med belöningspoäng 1 och svar i chattfönstret, så att du kan se hela JSON. Och roboten svarar med That’s great! I’ll keep learning your preferences over time. och Would you like to get a new suggestion or reset the simulated context to a new day?

    Om du svarar med no valet skickas belöningspoängen 0 till Personanpassning.

Förstå .NET-koden med hjälp av Personanpassning

.NET-lösningen är en enkel chattrobot för bot framework. Koden som är relaterad till Personanpassning finns i följande mappar:

  • /samples/ChatbotExample/Bots
    • PersonalizerChatbot.cs fil för interaktionen mellan roboten och Personanpassning
  • /samples/ChatbotExample/ReinforcementLearning – hanterar åtgärder och funktioner för personanpassningsmodellen
  • /samples/ChatbotExample/Model – filer för personanpassningsåtgärder och funktioner samt för LUIS-avsikter

PersonalizerChatbot.cs – arbeta med Personalizer

Klassen PersonalizerChatbot härleds från Microsoft.Bot.Builder.ActivityHandler. Den har tre egenskaper och metoder för att hantera konversationsflödet.

Varning

Kopiera inte kod från den här självstudien. Använd exempelkoden på lagringsplatsen För personanpassningsexempel.

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)
}

Metoderna prefix med Send hantera konversation med roboten och LUIS. Metoderna ChooseRankAsync och RewardAsync interagera med Personanpassning.

Anropa ranknings-API och visa resultat

Metoden ChooseRankAsync bygger JSON-data som ska skickas till Api:et för personanpassningsrankning genom att samla in åtgärder med funktioner och kontextfunktioner.

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;
}

Anropa Reward API och visa resultat

Metoden RewardAsync skapar JSON-data som ska skickas till Personanpassningsbelönings-API:et genom att fastställa poäng. Poängen bestäms utifrån LUIS-avsikten som identifieras i användartexten och skickas OnTurnAsync från metoden.

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);
}

Designöverväganden för en robot

Det här exemplet är avsett att demonstrera en enkel lösning från slutpunkt till slutpunkt för Personanpassning i en robot. Ditt användningsfall kan vara mer komplext.

Om du har för avsikt att använda Personanpassning i en produktionsrobot planerar du för:

  • Realtidsåtkomst till Personanpassning varje gång du behöver ett rangordnat val. Ranknings-API:et kan inte batchlagras eller cachelagras. Belöningsanropet kan fördröjas eller avlastas till en separat process och om du inte returnerar en belöning under tidsperioden anges ett standardbelöningsvärde för händelsen.
  • Användningsfallsbaserad beräkning av belöningen: Det här exemplet visade två belöningar på noll och en utan intervall mellan och inget negativt värde för en poäng. Ditt system behöver mer detaljerad bedömning.
  • Robotkanaler: Det här exemplet använder en enda kanal, men om du tänker använda mer än en kanal, eller varianter av robotar på en enda kanal, kan det behöva betraktas som en del av kontextfunktionerna i personanpassningsmodellen.

Rensa resurser

När du är klar med den här självstudien rensar du upp följande resurser:

  • Ta bort exempelprojektkatalogen.
  • Ta bort personaliseraren och LUIS-resursen i Azure-portalen.

Nästa steg