# Learning (/docs/strategies/learning)



Learning strategies don't score a fixed blend of signals — they **learn** which
recipient wins by trying candidates and observing outcomes. They balance
*exploration* (trying uncertain recipients to gather data) against *exploitation*
(routing to known winners). They **derive their state from durable
outcome-learning evidence*&#x2A;, and all of them live in the optional &#x2A;*`predicted`**
contrib module.

| Strategy                                | Type              | Capabilities      | Aliases  |
| --------------------------------------- | ----------------- | ----------------- | -------- |
| [Thompson sampling](#thompson-sampling) | Bayesian bandit   | explain, stateful | `bandit` |
| [LinUCB](#linucb)                       | Contextual bandit | explain, stateful | —        |

`RegisterPredicted` (`modules/strategies/predicted/register.go`) registers
`predicted_value`, `thompson_sampling`, `linucb`, and the `bandit` alias for
`thompson_sampling`; the factories themselves live in
`pkg/strategy/builtin/predicted/`.

<Callout title="Module-gated: enable it and promote a deployment" type="warn">
  These strategies are **not*&#x2A; in the built-in pack. To use them the **`predicted`
  contrib module must be enabled** and backed by a **promoted deployment**; until
  then any recipe that references them reports `missing_dependencies`. They require
  the outcome and quality repositories, and — because their learned posteriors must
  survive restarts — a `BanditStateStore`.
</Callout>

<Callout title="Learned state lives in the state plane, not the snapshot" type="info">
  A bandit's posteriors are **mutable strategy state**, not decision inputs. They
  live in the routing-managed [strategy state plane](/docs/strategies/contracts#strategy-state-plane)
  (Redis-backed via `NewRedisBanditState` for the fast path, Postgres as authority)
  — the same plane that holds SWRR weights and fair-catchup windows. That is a
  different plane from the read-only [feature snapshot](/docs/strategies/contracts#strategy-feature-snapshot)
  they read as context. Selection reads a state snapshot and emits a post-commit
  mutation intent; it never writes state inside `Select`.
</Callout>

## Thompson sampling [#thompson-sampling]

**Capabilities:** explain, stateful · &#x2A;*Aliases:** `bandit`

A classic multi-armed bandit. It treats each recipient as an "arm" with a
posterior distribution over its reward, **samples** from each posterior, and
routes to the arm with the highest sample. Arms it is uncertain about have wide
posteriors and therefore occasionally win the sample — that is exploration, and
it is automatic rather than a fixed epsilon.

**Why use it.** When you don't know in advance which recipient is best and you
want the system to find out with minimal regret. Thompson sampling is famously
sample-efficient: it converges on winners quickly while still probing the field,
and it self-corrects when a recipient's performance drifts. Ideal for lead
routing, offer selection, and any assignment where "best" is discovered, not
declared.

**How to configure it.** No product-surface sliders — behavior comes from the
per-arm posteriors it maintains. It is `stateful`: posteriors are updated from
committed outcomes and persisted through the bandit state store. It advertises
`explain`, so it can report the sampled values behind a decision.

**Where it fits.** The `Select` stage. Because it is deliberately
non-deterministic (its determinism profile is *exploratory bandit*), its
[strategy contract](/docs/strategies/contracts#strategy-contract) requires a
learning-dataset governance requirement — the platform will not certify an
exploratory strategy without one.

## LinUCB [#linucb]

**Capabilities:** explain, stateful

A **contextual** bandit. Where Thompson sampling learns one reward distribution
per recipient, LinUCB learns a *linear model* per arm over the routable's
features, plus a confidence bound. It answers a sharper question: not "who is
best overall?" but "who is best **for this kind of routable**?"

**Why use it.** When the right recipient depends on the request. A recipient who
excels at high-value enterprise leads may be mediocre on small self-serve ones;
LinUCB learns those conditional patterns from features and routes accordingly,
using its upper confidence bound to keep exploring arms it hasn't seen enough of
under the current context.

**How to configure it.** No product-surface sliders. It consumes routable
features and maintains per-arm model state (also `stateful`, also durable through
the state store). It advertises `explain`.

**Where it fits.** The `Select` stage. It reads the typed
[feature snapshot](/docs/strategies/contracts#strategy-feature-snapshot) as its
context vector, which is also what makes its decisions replayable for audit and
offline evaluation.

<Callout title="Behavior change: contextual exploration now uses features" type="info">
  LinUCB's exploration step now receives each candidate's own feature vector as its
  context. Previously exploration was handed an empty vector, so the contextual
  model could not influence which arm was explored and selection silently degraded
  to uniform random. With per-candidate features flowing through, LinUCB's
  confidence bounds now actually steer exploration — expect its exploratory picks
  to reflect the routable's features rather than being effectively random.
</Callout>

## Choosing between them [#choosing-between-them]

<Callout title="Rule of thumb" type="info">
  Use **Thompson sampling** when a recipient's quality is roughly the same across
  all requests — you just need to find the best. Use **LinUCB** when quality
  depends on the request's features and you want per-context winners. Both need
  enough traffic to learn; on a cold or tiny pool, a
  [scoring](/docs/strategies/scoring) strategy with a confidence gate is safer.
</Callout>

## Related [#related]

<Cards>
  <Card title="Predicted value" href="/docs/strategies/scoring#predicted-value">
    Supervised value prediction from the same module, without exploration.
  </Card>

  <Card title="Strategy state plane" href="/docs/strategies/contracts#strategy-state-plane">
    Where durable posteriors and per-arm models live.
  </Card>

  <Card title="Governance" href="/docs/strategies/governance">
    Shadow campaigns and experiments to validate a bandit before it takes live traffic.
  </Card>
</Cards>
