Creating a Real-Time Decisions Project : Day 7

Yesterday, I enhanced my Oracle Real-Time Decisions project to start providing cross-sell offers to customers who had ordered through the SOA Suite Order Bookings business process. Today, I'm going to take the acceptances, or rejections, from customers and feed then back into the model, so that future offers can take into account the preferences of customers over time.

The first step in this process is to define two choice events that are associated with the cross-sell group I defined yesterday. These two events represent the two events that are associated with presenting a cross-sell offer - what offer we made, and whether the offer was accepted. In a moment we'll build a second model to help us predict the latter more accurately.

So, now to create the associated Choice Event Model. I call the model "Offer Acceptance Predictor", associate it with the set of cross-selling choices, give it a window of a week to base it's predictions on (i.e. disregarding choices made more than a week ago), and tie it into the presented and accepted choice events I defined a moment ago.

I then go back to the session entity definition and add an attribute to hold the name of the offer we've presented to the customer, and then return to the Get Cross Sell Offer and amend the advisor logic to record the offer within this new session attribute, using this Java code:

logInfo("Integration Point - Get Cross Sell Offer");
logInfo("  Customer status = " + session().getCustomer().getStatus() );// 'choices' is array returned by the 'Select Offer' decision
if (choices.size() > 0) {
  //Get the first offer from array
  Choice offer = choices.get(0);
  //For the selected offer, record that it has been 'presented'
  //Set the session attribute 'OfferExtended' with the offer's ID.
  logInfo("  Offer presented: '" + offer.getSDOLabel() + "'");

The advisor that presents the offer to the customer now records the offer selected in the session variable. Next I want to create another informant, this time to record the response from the customer.

I call this new informant "Offer Feedback", give it the customer ID key, set the external system name and informant order, and create an input variable which will hold the customer's response.

I then switch to the Logic tab and enter the following Java code to record positive responses.
logInfo("Integration Point - Offer Feedback");
//"yes" or "no" to accept offer.
String	positive = request.getPositive();
positive = positive.toLowerCase();

//Get the offer id from session attribute 'OfferExtended'
String extendedOfferID = session().getOfferExtended();
if (extendedOfferID != null) {
//Get the offer from choice group 'Cross Selling Offer'
Choice offer = CrossSellingOffer.getChoice(extendedOfferID);
if (offer != null){
String offerId = offer.getSDOId();
//If response is "yes", then record the offer as accepted.
if (positive.equals("yes")) {
offer.recordEvent ("accepted");
logInfo(" Offer '" + offer.getSDOLabel() + "' accepted");

I then save and then deploy the project, to test out this new informant. Running the load generator again, and giving a 30%/70% weighting between offer acceptance and rejection, I can see that the offer feedback is being recorded correctly, and that at the moment, only extended warranties are being offered as they score the lowest in terms of cost.

If I want to, I can adjust the cost scoring so that, for example, customers who have the "Gold" status on their account have the cost metric adjusted up so that it's less likely to be recommended, for these customers we'd rather recommend something else.

To do this, I define a scoring rule that implements a condition that sets cost to 100 if the customer status is Gold, otherwise it sets it to 30, like this:

I then assign this rule to the Cost scoring setting for the Extended Warranty choice.

Now, if I run the load generator again, I can see the offers being split between extended warranties and gift wrapping.

So far so good. The final step now is to use the offer acceptance prediction model to influence the cross-sell offer choice, so that offers that are more likely to be accepted have a higher chance of being picked by RTD to present to the customer. To show this working, I'm going to create a new metric, "Revenue", which I'm going to use in conjunction with the prediction model.

I add Revenue as a choice group attribute, alongside URL, description and agent script, and set values for this for all of the cross-sell choices. This will be the baseline revenue figure for each choice.

I then define an additional performance goal, "Expected Revenue", which I add to the existing "Minimize Cost" performance goal.

Then, I go back to the score setting for the Cross-Sell Offer choice group and add in this performance goal, and set the scoring algorithm to multiply the base revenue for each choice by the chance of each choice actually being accepted, which is a value between 0 and 1 (0% and 100%)

Finally, I go back to the decision I created earlier and factor revenue into the decision, weighting the decision 50/50 between cost and revenue:

Now, when I run the load generator again, the choices are far more spread amongst the five options, as we now factor expected revenue into the choice which itself is adjusted over time as certain offers get accepted or rejected.

So there we have it; we've created a feedback loop where customer acceptances and rejections over time are fed back into the predictive model and influence the cross-sell offers being presented to customers. Next week, I'll take a look back at what we've built using Real-Time Decisions, and think again about how the ideas used in this particular real-time marketing project can be applied to our original scenario, deciding which supplier to use to fufill a customer order.