When to use adaptive policies
Use adaptive policies when:- You want to minimise regret — the cost of showing the inferior variant longer than necessary.
- The best variant may change over time (seasonality, mix shifts).
- You want continuous optimization rather than a one-time decision.
- You want personalized selection based on user context (contextual bandits).
- You need a clean point-in-time decision with an unambiguous winner for stakeholders.
- The cost of the variant is large enough that you want a deliberate ramp afterwards (use rollouts).
- Your goal metric is delayed or noisy (the engine will still work, but a static test gives you a more interpretable result).
Algorithms
Not sure which to pick — or how the goal event shapes what the optimizer learns? See Choosing an optimization algorithm.
Setting up an adaptive policy
-
Define the goal event in
.traffical/config.yamlor in the dashboard: -
Create the policy in the dashboard:
- Kind:
adaptive - Algorithm:
thompson_bernoulli - Goal event:
purchase - Goal type:
conversion_rate - Min exposures before shift:
100 - Window size:
60minutes
- Kind:
-
Define allocations. Initial ranges can be uniform; the engine will adjust them.
-
Implement in code — same as a static policy:
Tuning knobs
Optimizing with your warehouse
The optimizer’s reward source is the policy’s primary metric — its warehouse aggregates feed the bandit on every tick. On projects with defined events, the goal event you pick simply resolves to that metric for you — and the goal step also lets you switch to picking a metric directly, so you can optimize any fact-backed metric even when native events exist. On BYO-warehouse projects with no event definitions at all, the metric is the only way to declare the goal:- Define a fact over your own tables (SQL in your warehouse’s dialect) — one row per goal action, with the entity key, a timestamp, and an optional value column.
- Create a metric on that fact — conversion, count, or sum. Ratio metrics can’t be optimization goals (the reward would use only the numerator).
- Create the adaptive policy and pick that metric in the goal step. The goal type follows the metric’s type, and the metric is attached as the policy’s primary — the optimizer and measurement judge the same quantity by construction.
- The primary metric is authoritative. Re-pointing the policy’s primary metric re-points the optimizer on its next tick. A policy without a resolvable primary metric can’t start, and a running one reports a “no goal metric” health failure instead of silently optimizing nothing.
- Metrics that should go down work too. A goal metric with a decrease desired direction (error rate, time-to-complete, support contacts) makes the bandit minimize: traffic shifts toward the variant with the lowest reward.
Contextual bandits
Thelinear_contextual algorithm personalizes selection: different users get different variants based on context features. The training pipeline learns coefficients per allocation per feature; those coefficients ship in the config bundle; the SDK uses them to score each allocation for the current user at resolution time.
What it looks like in code
The code is identical to any other adaptive policy — you just pass more context:Scoring
For each allocation, the SDK computes:gamma to convert scores into selection probabilities, and enforces a minimum probability per allocation (actionProbabilityFloor) so the model keeps exploring.
Context logging allowlist
To train the model, the platform needs to see the context fields with each exposure event. To protect PII, only fields you explicitly allow-list are logged:Typing your context fields
A plain string entry lets the pipeline infer the field’s type from the data. You can also declare the type, which makes encoding predictable and guards against the classic foot-guns (a raw ID or timestamp entering the model unbounded). An allow-list entry can be an object:
Declared fields are encoded deterministically regardless of the traffic window, and editing a declaration retrains the model from scratch on its next training run — the change doesn’t wait for a scheduled rebuild. Fields you leave as plain strings keep the inferred behaviour.
When to use contextual bandits
When the optimal variant differs per user, and you can describe “who they are” with a handful of features:- Homepage layout by engagement level and device type
- CTA copy by referral source and purchase history
- Recommendation style by user segment
Per-entity bandits
For optimization at the entity level — each product, each merchant, each category learns independently — use a per-entity adaptive policy:Resolution modes
bundle is the right choice for high-traffic entities (product pages, search results). edge is for low-traffic, high-stakes entities where you need every decision to use the freshest possible weights.
Dynamic allocations
When each entity has a different number of options (e.g. each product has a different image count), usedynamicAllocations:
context.imageCount = 5, the SDK creates five allocations on the fly and selects from them based on learned weights. The selected index is reported as the allocation name in the decision metadata.
Next steps
Per-entity adaptive pattern
Full walkthrough with metric setup.
Contextual bandit pattern
Personalized layouts and CTAs.
Warehouse-native
Training contextual bandits from warehouse data.