> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Adaptive Learning

> Per-offer self-learning propensity models that improve automatically with every customer interaction.

## Overview

KaireonAI's adaptive learning system learns from every customer interaction to predict which offers each customer is most likely to engage with. Unlike batch-only ML systems, KaireonAI updates propensity estimates in **real time** — every impression, click, conversion, and dismissal immediately improves future recommendations.

The system uses a **hierarchical architecture** that shares learning across offers, categories, and channels while maintaining per-offer specialization.

## How It Works

```
Customer Outcome Recorded (via Respond API)
    │
    ▼
┌──────────────────────────────────────┐
│  Atomic Adaptation Updates           │
│                                      │
│  offer:Auto Renewal   → evidence +1  │
│  category:Retention   → evidence +1  │
│  channel:Email        → evidence +1  │
│  direction:outbound   → evidence +1  │
│  global               → evidence +1  │
└──────────────────────────────────────┘
    │
    ▼
Next Recommend API Call
    │
    ▼
┌──────────────────────────────────────┐
│  Hierarchical Propensity Lookup      │
│  (most-specific scope wins)          │
│                                      │
│  1. Offer      (evidence >= 50)      │
│  2. Offer+blend (evidence > 0)       │
│  3. Channel    (evidence >= 15)      │
│  4. Category   (evidence >= 20)      │
│  5. Direction  (evidence >= 10)      │
│  6. Global     (evidence >= 10)      │
│  7. Model score fallback             │
│  8. Default: 0.5                     │
└──────────────────────────────────────┘
```

### Default Propensity

New offers start with a **default propensity of 0.5** — a neutral score that neither favors nor penalizes the offer. As evidence accumulates, the learned propensity replaces the default.

### Evidence Blending

When an offer has some evidence but below the maturity threshold (50 interactions), the system **blends** offer-level data with its strongest available broader-scope prior (the first of channel → direction → category → global that has evidence):

```
propensity = (offerRate × offerEvidence + fallbackRate × smoothingWeight)
             / (offerEvidence + smoothingWeight)
```

`smoothingWeight` defaults to **10**. This gives new offers a **warm start** from the closest broader cell's average performance, rather than starting cold.

### Maturity Levels

| Evidence | Status     | Behavior                              |
| -------- | ---------- | ------------------------------------- |
| 0        | Cold start | Uses category prior or 0.5 default    |
| 1-49     | Immature   | Blends offer data with category prior |
| 50-199   | Maturing   | Uses offer-level propensity directly  |
| 200+     | Mature     | Stable, reliable predictions          |

## PRIE Scoring Formula

KaireonAI uses a **weighted geometric mean** of four factors to produce a final priority score:

```
Score = P^wp × R^wr × I^wi × E^we
```

| Factor | Name       | Range | Source                          | Default Weight |
| ------ | ---------- | ----- | ------------------------------- | -------------- |
| **P**  | Propensity | 0–1   | Adaptive learning or ML model   | 0.4            |
| **R**  | Relevance  | 0–1   | Channel match, recency, segment | 0.2            |
| **I**  | Impact     | 0–1   | Business value, margin, revenue | 0.3            |
| **E**  | Emphasis   | 0–1   | Offer priority (marketer lever) | 0.1            |

**Weights must sum to 1.0.** The geometric mean ensures:

* A **zero in any dimension eliminates the candidate** (0^x = 0)
* Default propensity (0.5) produces a baseline score of \~0.5
* Each factor contributes proportionally to its weight

### Weight Profiles

Configure PRIE weights on the Score node or via a Strategy Profile:

```json theme={null}
{
  "method": "formula",
  "formula": {
    "propensityWeight": 0.4,
    "relevanceWeight": 0.2,
    "impactWeight": 0.3,
    "emphasisWeight": 0.1
  }
}
```

**Propensity-heavy** (P=0.8, R=0.05, I=0.1, E=0.05): Model-driven — offers the AI predicts will perform best dominate.

**Emphasis-heavy** (P=0.1, R=0.1, I=0.1, E=0.7): Marketer-driven — offer priority determines ranking.

**Impact-heavy** (P=0.1, R=0.1, I=0.7, E=0.1): Revenue-driven — highest business value offers surface first.

### Ranking Profile Weight Mapping

When using a [Ranking Profile](/api-reference/ranking-profiles), the `weights` JSON maps to PRIE as follows:

| `RankingProfile.weights` key | PRIE factor    | Default |
| ---------------------------- | -------------- | ------- |
| `conversion`                 | P (Propensity) | 0.4     |
| `recency`                    | R (Relevance)  | 0.2     |
| `margin`                     | I (Impact)     | 0.3     |
| `fairness`                   | E (Emphasis)   | 0.1     |

## Model Adaptation Table

Per-offer learning is stored in the `model_adaptations` table — not as a JSON blob, but as independent rows that support atomic concurrent updates:

| Field           | Type    | Description                                                                                                              |
| --------------- | ------- | ------------------------------------------------------------------------------------------------------------------------ |
| `scope`         | string  | `global`, `category`, `offer`, `channel`, `direction`                                                                    |
| `scopeId`       | string  | Entity ID (offerId, categoryId, channelId), or `"inbound"`/`"outbound"` for direction — `""` (empty sentinel) for global |
| `positives`     | int     | Count of positive outcomes                                                                                               |
| `negatives`     | int     | Count of negative outcomes                                                                                               |
| `evidence`      | int     | Total interactions tracked                                                                                               |
| `positiveRate`  | float   | Computed: positives / evidence                                                                                           |
| `paused`        | boolean | When true, learning is frozen                                                                                            |
| `predictorAucs` | JSON    | Per-predictor univariate AUC scores                                                                                      |

Each `(modelId, scope, scopeId)` combination gets its own row, updated atomically via `INSERT ON CONFLICT UPDATE`.

### Model isolation — adaptations credit the model that decided

Adaptation rows are written for **exactly one model per outcome: the model that produced the decision**. When `/respond` records an outcome, the target model is resolved from the delivery row's recorded `modelId` (stamped by `/recommend` when a model scored the candidate). When no delivery row for the `(customer, offer)` carries a model attribution, **no adaptation rows are written** — the outcome is recorded but no model's propensity cells move. The outcome is never fanned out to every active model, so one model's outcome stream cannot flatten another model's cold-start scoring through shared global/direction cells. The decision-time read is symmetric: scoring reads adaptations for the flow's primary model only.

(This isolation applies to the adaptation rows in this table. Per-outcome *incremental model-state* updates for `bayesian` / `thompson_bandit` / `epsilon_greedy` / `online_learner` models are a separate mechanism and apply to every active incremental-type model.)

## Cold-Start Prior Seeding

New offers no longer start from a flat prior. When an offer is created with a category, KaireonAI seeds an offer-scope Model Adaptation per model from **same-category neighbor offers**:

* Neighbors must have real offer-scope evidence (≥ 20 outcomes)
* The seeded rate is the evidence-weighted mean of neighbor positive rates
* Seeding writes **10 pseudo-observations** — enough to nudge early scoring toward category reality, small enough to wash out quickly as real `/respond` outcomes arrive
* Existing adaptations with real evidence are never overwritten
* Seeding is fire-and-forget: a failure never blocks offer creation
* The maturity ramp is unaffected — exposure gating still keys off real interaction counts

## Evidence Decay

To prevent stale historical patterns from dominating, the system applies **exponential evidence decay** daily:

* **Decay rate**: 0.5% per day (evidence halves in \~139 days)
* **Applied by**: `GET /api/v1/cron/scheduled-retrains` (cron job)
* **Effect**: Recent interactions matter more than old ones

## Predictor Auto-Activation

During batch training, each predictor's univariate AUC is computed:

| AUC     | Status   | Meaning                                       |
| ------- | -------- | --------------------------------------------- |
| \< 0.52 | Inactive | No better than random — excluded from scoring |
| ≥ 0.52  | Active   | Informative — contributes to propensity       |

Predictor AUCs are stored in the global adaptation row and surfaced in the model detail API.

## Reset & Pause

### Reset Offer Learning

When an offer was misconfigured (wrong QR rules, wrong audience), reset its learned state:

```bash theme={null}
POST /api/v1/algorithm-models/{id}/reset-offer
{
  "offerId": "offer-auto-renewal",
  "resetTo": "category_prior",
  "reason": "Fixed decisioning gates"
}
```

**Options for `resetTo`:**

* `category_prior` — Fall back to category average (recommended)
* `global_prior` — Fall back to tenant-wide average
* `zero` — Full cold start (0.5 default)

### Pause Learning

Freeze learning for an offer while investigating:

```bash theme={null}
POST /api/v1/algorithm-models/{id}/reset-offer
{
  "action": "pause",
  "offerId": "offer-auto-renewal",
  "reason": "Investigating data quality"
}
```

Resume with `"action": "resume"`.

### Reset Category

Reset all offers in a category:

```bash theme={null}
POST /api/v1/algorithm-models/{id}/reset-offer
{
  "scope": "category",
  "categoryId": "cat-retention",
  "reason": "Category restructure"
}
```

## Scheduled Retraining

The cron endpoint `GET /api/v1/cron/scheduled-retrains` handles:

1. **Schedule-based retraining**: Models with `learnSchedule` (e.g., "1h", "24h", "7d") are retrained when the interval elapses
2. **Evidence-based retraining**: Models are retrained when 100+ new outcomes accumulate, regardless of schedule
3. **Evidence decay**: Applied daily to all adaptation rows

Configure per model:

```json theme={null}
{
  "autoLearn": true,
  "learnMode": "both",
  "learnSchedule": "1h"
}
```

| learnMode     | Behavior                                          |
| ------------- | ------------------------------------------------- |
| `none`        | No automatic learning                             |
| `incremental` | Online updates on every outcome (via Respond API) |
| `scheduled`   | Batch retraining on schedule (via cron)           |
| `both`        | Incremental + scheduled (recommended)             |

## Attribution-Aware Learning

When a conversion outcome has attribution data, the system looks up the attribution credit for the specific offer. This enables weighted learning — an offer that contributed 33% to a conversion gets proportional credit, not full credit.

This prevents **feedback inversion** where offers that appear frequently (high impression count) get disproportionate positive signal from conversions they didn't actually cause.

## Next Steps

<CardGroup cols={2}>
  <Card title="Decision Flows" icon="sitemap" href="/decisioning/decision-flows">
    Configure the Score node with PRIE weights and model selection.
  </Card>

  <Card title="Algorithm Models" icon="brain" href="/api-reference/algorithm-models">
    Create and manage ML models for propensity scoring.
  </Card>
</CardGroup>
