Skip to main content

Contextual Bandits

Usage with Contextual Multi-Armed Bandits

Eppo supports contextual multi-armed bandits. You configure bandit flags in the Eppo application, where you can set up:

  • Flag keys
  • Status quo variations
  • Bandit variations
  • Targeting rules

When you query the bandit in your code, you provide the available actions to the SDK.

To leverage bandits using the Dart SDK, there are two additional steps over regular feature flags:

  1. Implement a logger that handles bandit actions
  2. Query the bandit for an action

Define a bandit assignment logger

In order for the bandit to learn an optimized policy, we need to capture and log the bandit's actions. The Dart SDK uses a single logger class that handles both assignment and bandit action logging:

/// Represents a bandit event.
@JS()
@staticInterop
@anonymous
class BanditEvent {
external factory BanditEvent();
}

/// Extension methods for BanditEvent.
extension BanditEventExtension on BanditEvent {
external String get banditKey;
external String get action;
external String get subject;
external String get timestamp;
external JSAny get subjectAttributes;
external FlagEvaluationDetails? get evaluationDetails;
}

await eppo
.init(
EppoConfig(
apiKey: '<SDK_KEY>',
banditLogger: BanditLogger(
logBanditEvent:
((JSAny event) {
final typedEvent = event as BanditEvent;
print('warehouse bandit event logged:', typedEvent.toJson());
}
).toJS,
),
),
)
.toDart;

The SDK will invoke the logBanditAction() method with a Map containing the following fields:

FieldDescriptionExample
timestamp (String)The time when the action is taken in UTC"2024-03-22T14:26:55.000Z"
flagKey (String)The key of the feature flag corresponding to the bandit"bandit-test-allocation-4"
banditKey (String)The key (unique identifier) of the bandit"ad-bandit-1"
subjectKey (String)An identifier of the subject or user assigned to the experiment variation"ed6f85019080"
subjectNumericAttributes (JSAny)Metadata about numeric attributes of the subject. Map of the name of attributes their provided values{"age": 30}
subjectCategoricalAttributes (JSAny)Metadata about non-numeric attributes of the subject. Map of the name of attributes their provided values{"loyalty_tier": "gold"}
action (String)The action assigned by the bandit"promo-20%-off"
actionNumericAttributes (JSAny)Metadata about numeric attributes of the assigned action. Map of the name of attributes their provided values{"brandAffinity": 0.2}
actionCategoricalAttributes (JSAny)Metadata about non-numeric attributes of the assigned action. Map of the name of attributes their provided values{"previouslyPurchased": false}
actionProbability (double)The weight between 0 and 1 the bandit valued the assigned action0.25
optimalityGap (double)The difference between the score of the selected action and the highest-scored action456
modelVersion (String)Unique identifier for the version (iteration) of the bandit parameters used to determine the action probability"v123"
metaData (JSAny)Any additional freeform meta data, such as the version of the SDK{ "sdkLibVersion": "3.5.1" }

Query the bandit for an action

To query the bandit for an action, use the banditAction() method. This method takes the following parameters:

  • flagKey (String): The key of the feature flag corresponding to the bandit
  • subject (Subject): The subject with its attributes
  • actions (JSAny): Map of actions (by name) to their attributes
  • defaultValue (String): The default variation to return if the flag is not successfully evaluated
final result = client.getBanditAction(
'flag-with-shoe-bandit',
'<subject-key>',
{'age': 30, 'country': 'uk', 'pricingTier': '1'}.jsify() as JSAny,
{
'nike': [
{'brand_affinity': 0.4},
{'from': 'usa'},
],
'adidas': [
{'brand_affinity': 2.0},
{'from': 'germany'}
]
}.jsify() as JSAny,
'default'
);

if (result.action != null) {
// Follow the Bandit action
renderShoeAd(result.action!);
} else {
// User was not selected for a Bandit.
// A variation is still assigned.
renderDefaultShoeAd(result.variation);
}

Bandit Result

The banditAction() method returns a BanditResult object with the following properties:

  • variation (String): The variation assigned to the subject
  • action (String?): The action selected by the bandit, or null if no bandit action was selected

Subject Attributes

The subject contains contextual information about the subject that is independent of bandit actions. For example, the subject's age or country.

Action Attributes

The action attributes contain contextual information about each action.

For simple cases where actions don't have attributes, you can also pass a list of action names:

// Simple list of action names
final actions = ['nike', 'adidas', 'puma'].asMap().map((_, name) => MapEntry(name, Attributes()));

Debugging

You may encounter a situation where a flag assignment produces a value that you did not expect. There are functions detailed here to help you understand how flags are assigned, which will allow you to take corrective action on potential configuration issues.