Samouczek: korzystanie z usługi Personalizacja w czatbotze platformy .NET
Ważne
Od 20 września 2023 r. nie będzie można tworzyć nowych zasobów usługi Personalizacja. Usługa Personalizacja jest wycofywana 1 października 2026 r.
Użyj czatbota platformy .NET w języku C# z pętlą Personalizacja, aby udostępnić użytkownikowi poprawną zawartość. Ten czatbot sugeruje użytkownikowi konkretną kawę lub herbatę. Użytkownik może zaakceptować lub odrzucić tę sugestię. Dzięki temu można uzyskać informacje dotyczące usługi Personalizacja, aby ułatwić bardziej odpowiednie wprowadzenie następnej sugestii.
Ten samouczek zawiera informacje na temat wykonywania następujących czynności:
- Konfigurowanie zasobów platformy Azure
- Konfigurowanie i uruchamianie bota
- Interakcja z botem przy użyciu bota Bot Framework Emulator
- Dowiedz się, gdzie i jak bot używa usługi Personalizacja
Jak działa czatbot?
Czatbot jest zazwyczaj konwersacją w tle i z użytkownikiem. Ten konkretny czatbot używa usługi Personalizacja, aby wybrać najlepszą akcję (kawę lub herbatę) w celu zaoferowania użytkownikowi. Personalizacja korzysta z uczenia wzmacniania w celu dokonania tego wyboru.
Czatbot musi zarządzać kolei w konwersacji. Czatbot używa platformy Bot Framework do zarządzania architekturą i konwersacją bota oraz używa usługi Azure AI Language Understanding (LUIS), aby zrozumieć intencję języka naturalnego od użytkownika.
Czatbot to witryna internetowa z określoną trasą dostępną do odpowiadania na żądania. http://localhost:3978/api/messages
Program Bot Framework Emulator umożliwia wizualną interakcję z uruchomionym czatbotem podczas lokalnego opracowywania bota.
Interakcje użytkownika z botem
Jest to prosty czatbot, który umożliwia wprowadzanie zapytań tekstowych.
Użytkownik wprowadza tekst | Bot odpowiada tekstem | Opis działania, który wykonuje bot w celu określenia tekstu odpowiedzi |
---|---|---|
Nie wprowadzono tekstu — bot rozpoczyna konwersację. | 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. |
Bot rozpoczyna konwersację z tekstem instruktażowym i informuje o tym, jaki jest kontekst: Tuesday , Snowy . |
Show menu |
Here is our menu: Coffee: Cappuccino Espresso Latte Macchiato Mocha Tea: GreenTea Rooibos |
Określ intencję zapytania przy użyciu usługi LUIS, a następnie wyświetl opcje menu kawy i herbaty. Funkcje akcji to |
What do you suggest |
How about Latte? |
Określenie intencji zapytania przy użyciu usługi LUIS, a następnie wywołanie interfejsu API rangi i wyświetlenie najlepszego wyboru jako pytania How about {response.RewardActionId}? . Wyświetla również wywołanie JSON i odpowiedź na potrzeby ilustracji. |
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? |
Określ intencję zapytania przy użyciu usługi LUIS, a następnie wywołaj interfejs API programu Reward z nagrodą 1 , wyświetla wywołanie JSON i odpowiedź na potrzeby ilustracji. |
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? |
Określ intencję zapytania przy użyciu usługi LUIS, a następnie wywołaj interfejs API programu Reward z nagrodą 0 , wyświetla wywołanie JSON i odpowiedź na potrzeby ilustracji. |
Reset |
Zwraca tekst instrukcji. | Określ intencję zapytania przy użyciu usługi LUIS, a następnie wyświetla tekst instrukcji i resetuje kontekst. |
Personalizacja w tym botze
Ten czatbot używa usługi Personalizacja do wybierania akcji (określonej kawy lub herbaty) na podstawie listy akcji (pewnego typu zawartości) i funkcji kontekstowych.
Bot wysyła listę akcji wraz z funkcjami kontekstu do pętli Personalizacja. Usługa Personalizacja zwraca pojedynczą najlepszą akcję do bota, która jest wyświetlana przez bota.
W tym samouczku akcje są rodzajami kawy i herbaty:
Kawa | Herbata |
---|---|
Cappuccino Espresso Latte Mocha |
GreenTea Rooibos |
Interfejs API rangi: Aby ułatwić personalizacji poznanie akcji, bot wysyła następujące żądania interfejsu API rangi:
- Akcje z funkcjami
- Funkcje kontekstowe
Funkcja modelu to informacje o akcji lub kontekście, które można agregować (pogrupować) między członkami bazy użytkowników czatbota. Funkcja nie jest specyficzna indywidualnie (na przykład identyfikator użytkownika) ani bardzo specyficzna (na przykład dokładna pora dnia).
Funkcje służą do dopasowywania akcji do bieżącego kontekstu w modelu. Model jest reprezentacją wcześniejszej wiedzy personalizacji na temat akcji, kontekstu i ich funkcji, które umożliwiają podejmowanie wykształconych decyzji.
Model, w tym funkcje, jest aktualizowany zgodnie z harmonogramem na podstawie ustawienia Częstotliwość aktualizacji modelu w witrynie Azure Portal.
Należy wybrać funkcje z tym samym planowaniem i projektem, które mają być stosowane do dowolnego schematu lub modelu w architekturze technicznej. Wartości funkcji można ustawiać za pomocą logiki biznesowej lub systemów innych firm.
Uwaga
Funkcje w tej aplikacji są przeznaczone do pokazu i mogą nie być najlepszymi funkcjami używanymi w aplikacji internetowej w twoim przypadku użycia.
Funkcje akcji
Każda akcja (element zawartości) ma funkcje ułatwiające odróżnienie kawy lub herbaty.
Funkcje nie są konfigurowane jako część konfiguracji pętli w witrynie Azure Portal. Zamiast tego są one wysyłane jako obiekt JSON z każdym wywołaniem interfejsu API rangi. Pozwala to na elastyczność akcji i ich funkcji zwiększania, zmieniania i zmniejszania w czasie, co umożliwia personalizacji obserwowanie trendów.
Funkcje kawy i herbaty obejmują:
- Lokalizacja pochodzenia fasoli kawowej, takiej jak Kenia i Brazylia
- Czy kawa lub herbata są organiczne?
- Jasne lub ciemne pieczenie kawy
Podczas gdy kawa ma trzy funkcje na poprzedniej liście, herbata ma tylko jeden. Przekaż tylko funkcje do usługi Personalizacja, które mają sens dla akcji. Nie przekazuj pustej wartości dla funkcji, jeśli nie ma ona zastosowania do akcji.
Funkcje kontekstowe
Funkcje kontekstowe ułatwiają personalizacji zrozumienie kontekstu środowiska, takiego jak urządzenie wyświetlania, użytkownik, lokalizacja i inne funkcje, które są istotne dla danego przypadku użycia.
Kontekst tego czatbota obejmuje:
- Rodzaj pogody (śnieżna, deszczowa, słoneczna)
- Dzień tygodnia
Wybór funkcji jest losowy w tym czatbotze. W prawdziwym botze użyj rzeczywistych danych dla funkcji kontekstu.
Zagadnienia dotyczące projektowania dla tego bota
Istnieje kilka przestrog, które należy pamiętać o tej rozmowie:
- Interakcja z botem: konwersacja jest bardzo prosta, ponieważ demonstruje rangę i nagrodę w prostym przypadku użycia. Nie demonstruje pełnej funkcjonalności zestawu Bot Framework SDK lub emulatora.
- Personalizacja: funkcje są wybierane losowo do symulowania użycia. Nie należy losować funkcji w produkcyjnym scenariuszu personalizacji.
- Language Understanding (LUIS): kilka przykładowych wypowiedzi modelu usługi LUIS jest przeznaczonych tylko dla tego przykładu. Nie używaj tak kilku przykładowych wypowiedzi w produkcyjnej aplikacji usługi LUIS.
Instalowanie wymaganego oprogramowania
- Visual Studio 2019. Repozytorium przykładów do pobrania zawiera instrukcje, jeśli wolisz używać interfejsu wiersza polecenia platformy .NET Core.
- Microsoft Bot Framework Emulator to aplikacja klasyczna, która umożliwia deweloperom botów testowanie i debugowanie botów na hoście lokalnym lub zdalne uruchamianie za pośrednictwem tunelu.
Pobieranie przykładowego kodu czatbota
Czatbot jest dostępny w repozytorium przykładów usługi Personalizacja. Sklonuj lub pobierz repozytorium, a następnie otwórz przykład w katalogu za /samples/ChatbotExample
pomocą programu Visual Studio 2019.
Aby sklonować repozytorium, użyj następującego polecenia Git w powłoce Bash (terminalu).
git clone https://github.com/Azure-Samples/cognitive-services-personalizer-samples.git
Tworzenie i konfigurowanie zasobów usługi Personalizacja i usługi LUIS
Tworzenie zasobów platformy Azure
Aby użyć tego czatbota, należy utworzyć zasoby platformy Azure dla usługi Personalizacja i Language Understanding (LUIS).
- Tworzenie zasobów usługi LUIS. Utwórz zarówno zasób tworzenia, jak i przewidywania.
- Utwórz zasób usługi Personalizacja, a następnie skopiuj klucz i punkt końcowy z witryny Azure Portal. Należy ustawić te wartości w
appsettings.json
pliku projektu .NET.
Tworzenie aplikacji usługi LUIS
Jeśli dopiero zaczynasz korzystać z usługi LUIS, musisz zalogować się i natychmiast przeprowadzić migrację konta. Nie musisz tworzyć nowych zasobów, zamiast tego wybierać zasoby utworzone w poprzedniej sekcji tego samouczka.
- Aby utworzyć nową aplikację usługi LUIS, w portalu usługi LUIS wybierz swoją subskrypcję i zasób tworzenia.
- Następnie na tej samej stronie wybierz pozycję + Nowa aplikacja do konwersacji, a następnie zaimportuj jako kod JSON.
- W oknie podręcznym wybierz pozycję Wybierz plik , a następnie wybierz
/samples/ChatbotExample/CognitiveModels/coffeebot.json
plik. Wprowadź nazwęPersonalizer Coffee bot
. - Wybierz przycisk Train (Trenuj) w prawym górnym rogu portalu usługi LUIS.
- Wybierz przycisk Publikuj, aby opublikować aplikację w miejscu produkcyjnym dla środowiska uruchomieniowego przewidywania.
- Wybierz pozycję Zarządzaj, a następnie pozycję Ustawienia. Skopiuj wartość identyfikatora aplikacji. Tę wartość należy ustawić w
appsettings.json
pliku projektu .NET. - Nadal w sekcji Zarządzanie wybierz pozycję Zasoby platformy Azure. Spowoduje to wyświetlenie skojarzonych zasobów w aplikacji.
- Wybierz pozycję Dodaj zasób przewidywania. W oknie podręcznym wybierz subskrypcję i zasób przewidywania utworzony w poprzedniej sekcji tego samouczka, a następnie wybierz pozycję Gotowe.
- Skopiuj wartości klucza podstawowego i adresu URL punktu końcowego. Należy ustawić te wartości w
appsettings.json
pliku projektu .NET.
Konfigurowanie bota przy użyciu pliku appsettings.json
Otwórz plik rozwiązania czatbota z
ChatbotSamples.sln
programem Visual Studio 2019.Otwórz
appsettings.json
plik w katalogu głównym projektu.Ustaw wszystkie pięć ustawień skopiowanych w poprzedniej sekcji tego samouczka.
{ "PersonalizerChatbot": { "LuisAppId": "", "LuisAPIKey": "", "LuisServiceEndpoint": "", "PersonalizerServiceEndpoint": "", "PersonalizerAPIKey": "" } }
Kompilowanie i uruchamianie bota
Po skonfigurowaniu elementu appsettings.json
możesz przystąpić do kompilowania i uruchamiania czatbota. Gdy to zrobisz, zostanie otwarta przeglądarka uruchomiona w witrynie internetowej . http://localhost:3978
Zachowaj uruchomioną witrynę internetową, ponieważ w samouczku wyjaśniono, co robi bot, dzięki czemu możesz wchodzić w interakcję z botem.
Konfigurowanie emulatora platformy Bot Framework
Otwórz program Bot Framework Emulator i wybierz pozycję Otwórz bota.
Skonfiguruj bota przy użyciu następującego adresu URL bota, a następnie wybierz pozycję Połącz:
http://localhost:3978/api/messages
Emulator łączy się z czatbotem i wyświetla tekst instrukcji wraz z informacjami dotyczącymi rejestrowania i debugowania przydatnymi dla programowania lokalnego.
Używanie bota w emulatorze platformy Bot Framework
Poproś o wyświetlenie menu, wprowadzając polecenie
I would like to see the menu
. Czatbot wyświetla elementy.Pozwól botowi zasugerować element, wprowadzając
Please suggest a drink for me.
pozycję Emulator wyświetla żądanie rangi i odpowiedź w oknie czatu, aby zobaczyć pełny kod JSON. Bot udostępnia sugestię podobną do następującej:How about Latte?
Odpowiedź, która oznacza, że akceptujesz najwyżej sklasyfikowany wybór usługi Personalizacja.
I like it.
Emulator wyświetla żądanie nagrody z wynikiem nagrody 1 i odpowiedzią w oknie czatu, aby zobaczyć pełny kod JSON. Bot odpowiada za pomocąThat’s great! I’ll keep learning your preferences over time.
polecenia iWould you like to get a new suggestion or reset the simulated context to a new day?
Jeśli odpowiesz
no
na wybór, wynik nagrody 0 zostanie wysłany do usługi Personalizacja.
Omówienie kodu platformy .NET przy użyciu usługi Personalizacja
Rozwiązanie platformy .NET to prosty czatbot platformy botów. Kod związany z usługą Personalizacja znajduje się w następujących folderach:
/samples/ChatbotExample/Bots
PersonalizerChatbot.cs
plik interakcji między botem a usługą Personalizacja
/samples/ChatbotExample/ReinforcementLearning
— zarządza akcjami i funkcjami modelu personalizacji/samples/ChatbotExample/Model
— pliki dla akcji i funkcji usługi Personalizacja oraz intencji usługi LUIS
PersonalizerChatbot.cs — praca z usługą Personalizacja
Klasa PersonalizerChatbot
pochodzi z klasy Microsoft.Bot.Builder.ActivityHandler
. Ma trzy właściwości i metody do zarządzania przepływem konwersacji.
Uwaga
Nie kopiuj kodu z tego samouczka. Użyj przykładowego kodu w repozytorium przykładów usługi Personalizacja.
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)
}
Metody poprzedzone zarządzaniem konwersacją z Send
botem i usługą LUIS. Metody ChooseRankAsync
i RewardAsync
interakcja z usługą Personalizacja.
Wywoływanie interfejsu API rangi i wyświetlanie wyników
Metoda ChooseRankAsync
kompiluje dane JSON, które mają być wysyłane do interfejsu API rangi usługi Personalizacja, zbierając akcje z funkcjami i funkcjami kontekstu.
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;
}
Wywoływanie interfejsu API programu Reward i wyświetlanie wyników
Metoda RewardAsync
kompiluje dane JSON do wysyłania do interfejsu API programu Reward usługi Personalizacja, określając wynik. Wynik jest określany na podstawie intencji usługi LUIS zidentyfikowanej w tekście użytkownika i wysłanej OnTurnAsync
z metody .
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);
}
Zagadnienia dotyczące projektowania bota
Ten przykład ma na celu zademonstrowanie prostego kompleksowego rozwiązania usługi Personalizacja w botze. Twój przypadek użycia może być bardziej złożony.
Jeśli zamierzasz używać usługi Personalizacja w bocie produkcyjnym, zaplanuj:
- Dostęp w czasie rzeczywistym do usługi Personalizacja za każdym razem, gdy potrzebny jest wybór w rankingu. Interfejs API rangi nie może być wsadowy ani buforowany. Połączenie nagrody może zostać opóźnione lub odciążone do oddzielnego procesu, a jeśli nie zwrócisz nagrody w określonym przedziale czasu, domyślna wartość nagrody zostanie ustawiona dla wydarzenia.
- Obliczenie nagrody na podstawie przypadku użycia: w tym przykładzie pokazano dwie nagrody zerowe i jedną bez zakresu między i bez wartości ujemnej dla wyniku. System wykonany z potrzeby bardziej szczegółowego oceniania.
- Kanały botów: w tym przykładzie jest używany jeden kanał, ale jeśli zamierzasz używać więcej niż jednego kanału lub odmian botów w jednym kanale, może to być konieczne w ramach funkcji kontekstowych modelu personalizacji.
Czyszczenie zasobów
Po zakończeniu pracy z tym samouczkiem wyczyść następujące zasoby:
- Usuń przykładowy katalog projektu.
- Usuń zasób personalizacji i usługi LUIS w witrynie Azure Portal.
Następne kroki
- Jak działa usługa Personalizacja
- Funkcje: poznaj pojęcia dotyczące funkcji przy użyciu akcji i kontekstu
- Nagrody: dowiedz się więcej o obliczaniu nagród