In the last blog, we discussed Integrating the Optimizely CMS website with Azure AI search. Now let’s take a bit more advanced topic to serve Personalization experience with Azure AI search with Azure personalizer. Together, they enable you to serve dynamic customized content and search results across user behaviour, preferences, and context.
What is Azure Personalizer?
Azure Personalizer Cognitive Service for Real-time Association using Reinforcement Learning. It gives you the ability to serve content or experiences that are most relevant to a user — informed by past behaviour and current context.
Benefits of AI Personalizer:
- So it can study and evolve as people engage with it.
- Amazingly helpful for ranking search results.
- Can customize direct calls to action, highlighted articles, or goods.
How It Works with Azure AI Search and Optimizely
- The user performs a search on your Optimizely site.
- Azure AI Search simply gives a list of matching documents
- These documents are sent to Azure Personalizer as “rankable actions.”
- The personalized orders results using the context of the user.
- Your app serves personalized results and the user’s feedback helps Personalizer to learn & evolve further.
Set Up Azure Personalizer
- Navigate to Azure Portal → Personalizer resource creation
- Save your endpoint and API key.
- In step 3, specify the Content that you want to be ranked (i.e., search results)
Integration Code
Model for Rankable Action
public class RankableDocument { public string Id { get; set; } public string Title { get; set; } public string Summary { get; set; } public string Category { get; set; } }
Send Info to Personalizer with Context:
private object GetUserContext(HttpRequestBase request) { return new { timeOfDay = DateTime.Now.Hour, device = request.Browser.IsMobileDevice ? "mobile" : "desktop", userAgent = request.UserAgent, language = request.UserLanguages?.FirstOrDefault() ?? "en" }; } public async Task<List<RankableDocument>> GetPersonalizedResultsAsync(List<RankableDocument> documents, string userId) { var contextFeatures = new[] { GetUserContext(Request) }; var actions = documents.Select(doc => new { id = doc.Id, features = new[] { new { category = doc.Category }, new { title = doc.Title } } }); _eventId = Guid.NewGuid().ToString(); var request = new { contextFeatures = contextFeatures, actions = actions, excludedActions = new string[] {}, eventId = _eventId, deferActivation = false }; var client = new HttpClient(); client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "--YOUR API KEY ---"); var response = await client.PostAsync("--Endpoint--/personalizer/v1.0/rank", new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json")); var result = JsonDocument.Parse(await response.Content.ReadAsStringAsync()); var topActionId = result.RootElement.GetProperty("rewardActionId").GetString(); return documents.OrderByDescending(d => d.Id == topActionId).ToList(); }
Now let’s consider our previous example of search page controller & view and extend it
Search Controller
public class AzureSearchPageController : PageController<AzureSearchPage> { private static string _eventId; public async Task<ActionResult> Index(AzureSearchPage currentPage, string q = "") { var results = new List<RankableDocument>(); if (!string.IsNullOrEmpty(q)) { var url = $"https://<search-service>.search.windows.net/indexes/<index-name>/docs?api-version=2021-04-30-Preview&search={q}"; using var client = new HttpClient(); client.DefaultRequestHeaders.Add("api-key", "<your-query-key>"); var response = await client.GetStringAsync(url); var doc = JsonDocument.Parse(response); results = doc.RootElement.GetProperty("value") .EnumerateArray() .Select(x => new RankableDocument { Id = x.GetProperty("id").GetString(), Title = x.GetProperty("name").GetString(), Category = x.GetProperty("type").GetString(), Summary = x.GetProperty("content").GetString() }).ToList(); results = await GetPersonalizedResultsAsync(results, "user123"); } ViewBag.Results = results; ViewBag.Query = q; ViewBag.EventId = _eventId; return View(currentPage); } [HttpPost] public async Task<ActionResult> Reward(string eventId, double rewardScore) { using var client = new HttpClient(); client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "<your-api-key>"); var rewardUrl = $"<your-endpoint>/personalizer/v1.0/events/{eventId}/reward"; var result = await client.PostAsync(rewardUrl, new StringContent(rewardScore.ToString(), Encoding.UTF8, "application/json")); return Json(new { success = result.IsSuccessStatusCode }); } }
Search Page View
@model AzureSearchPage <h1>Personalized Search Results</h1> <form method="get"> <input type="text" name="q" value="@ViewBag.Query" placeholder="Search..." /> <button type="submit">Search</button> </form> <ul> @foreach (var result in ViewBag.Results as List<RankableDocument>) { <li> <h4>@result.Title</h4> <p>@result.Summary</p> <button onclick="sendReward('@ViewBag.EventId', 1.0)">Like</button> <button onclick="sendReward('@ViewBag.EventId', 0.0)">Not Relevant</button> </li> } </ul> <script> function sendReward(eventId, score) { fetch('/AzureSearchPage/Reward', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ eventId: eventId, rewardScore: score }) }).then(r => { if (r.ok) alert("Thanks! Your feedback was recorded."); }); } </script>
With Azure AI Search delivering relevant results and Azure Personalizer re-ranking them based on real-time context, your Optimizely site becomes an intelligent experience engine.
This blog has also been published here.
Source: Read MoreÂ