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

# Predictive routing: the trained agent-match model

> How the per-tenant agent-match model predicts which agent pairs best with each caller — hierarchical shrinkage over agent and customer-context features, blended into predictive routing only when you train it and give it weight.

# Predictive routing: the trained agent-match model

Predictive routing ranks the agents eligible for an inbound work item and
picks the one with the best predicted outcome. The marginal signal — each
agent's own recent CSAT, handle time, and load headroom — scores an agent in
isolation. Two agents with identical CSAT histories look interchangeable to
it, even if one consistently delights enterprise billing callers while the
other shines with first-time SMB users. The **match model** closes that gap:
a per-tenant trained model over the interaction between who is calling, why
they are calling, and which agent handles them.

This page explains what the model learns, which features it sees, how the
trained artifact moves from the offline trainer to the live router, how the
match score blends into the final pick, and how to enable and verify it.

## Model overview — three levels of learned means

A trained match model is a compact JSON artifact holding learned outcome
means at three levels of a hierarchy:

1. **The tenant mean** — the average outcome across every labelled
   interaction in the training window (the global prior).
2. **The agent mean** — each agent's own average outcome.
3. **The agent-by-feature mean** — each agent's average outcome on
   interactions carrying a specific context feature, such as
   `segment:enterprise`, `intent:billing`, or `value:high`.

Outcomes are normalised to \[0, 1] — a CSAT survey score of 1–5 maps to
`(score − 1) / 4`, so 0 is the worst outcome and 1 the best. Training is
**target encoding with hierarchical shrinkage**: the predicted outcome for
an agent on a given interaction is the agent-by-feature means shrunk toward
the agent mean, which is itself shrunk toward the tenant mean, each in
proportion to how much history backs it:

```text theme={null}
featureEst = (n·cellMean + k·agentEst) / (n + k)
agentEst   = (n·agentMean + k·tenantMean) / (n + k)
```

Here `n` is the labelled sample count for that level and `k` is a shrinkage
pseudo-count (default 10) — a cell reaches roughly half trust at `n = k`.
This is partial pooling: thin evidence leans on the level above it, strong
evidence speaks for itself. The prediction for one interaction is the mean
of the feature estimates the agent has cells for, or the agent estimate when
no feature cell exists.

## Feature vocabulary — what the model sees

Context features come from two sources, combined into one normalised,
de-duplicated set per interaction:

* **CDP context tags.** The contact's attribute tags (`segment:enterprise`,
  `intent:billing`, `value:high`, `style:analytical`, and any other tag your
  CDP writes) each become one feature key.
* **The routing queue as an intent proxy.** The queue the interaction routed
  through becomes one `queue:<id>` feature, so the model learns per-queue
  affinities even before contacts carry rich tags.

Every feature key is trimmed, lowercased, and length-capped, so
`Segment:Enterprise` and `segment:enterprise` are the same feature. The
queue feature is placed first so it survives the per-sample cap, and its
`queue:` prefix keeps it from colliding with a raw tag of the same text.
The same extraction runs at training time and at serve time, so the two
always agree on feature keys.

## Train/serve parity — one module, one artifact

The training maths and the scoring maths live in a single pure, no-I/O
module shared by two consumers:

* **The offline trainer**, a scheduled job that reads your tenant's recent
  completed calls joined to CSAT survey responses and the caller's contact
  tags, trains the model, and writes the artifact.
* **The runtime scorer**, the predictive arm of the work-item router, which
  reads the artifact and scores each candidate agent for the inbound
  interaction's context.

Because both sides import the same module, train/serve drift is impossible —
a trainer change cannot shift scoring semantics for the runtime. The
artifact itself lives in your organization's settings as JSON
(`acd_predictive_routing.match_model`), with no schema migration; the same
settings pattern the other predictive-routing knobs use. Scheduled training
runs weekly; each run replaces the artifact atomically.

### Tuning knobs

| Knob                       | Purpose                                                                                  | Default |
| -------------------------- | ---------------------------------------------------------------------------------------- | ------- |
| Shrinkage pseudo-count `k` | How fast cells earn trust; a cell is \~50% trusted at `n = k`.                           | 10      |
| Minimum labelled samples   | Below this, no model is trained and the previous artifact stands.                        | 50      |
| Lookback window            | Days of completed-call history the trainer reads (your `lookback_days` predictive knob). | 30      |
| Artifact bounds            | Up to 1,000 agents and 64 feature cells per agent, kept by sample size.                  | —       |

## The serving blend — match score meets aggregate ranking

At dispatch time, the predictive arm first computes the marginal score — the
weighted blend of normalised CSAT, handle time, and headroom (`csat_weight`,
`handle_weight`, `headroom_weight`, renormalised to sum to 1). The trained
match score then interpolates in only when a model exists **and** you give
it a positive `match_weight`:

```text theme={null}
finalScore = (1 − matchWeight)·marginal + matchWeight·match
```

With no trained model, or with `match_weight` at 0, the predictive arm is
exactly the pre-model marginal predictor — the knob is inert until you
train. Setting `match_weight` decides how much of the final pick the
customer-context signal owns.

### Cold-start fallbacks

The model never punishes thin evidence:

* **Unseen agent** → scores the tenant mean, not zero.
* **Agent with no cell for the interaction's features** → falls back to that
  agent's own mean.
* **No labelled samples at all** → no model is trained, and routing stays on
  the marginal predictor.

An unseen customer segment is only ever *informed* by the model when
evidence exists — it is never penalised for being new.

## Pairing with attribute and affinity scorers

Orbit evaluates routing rules at two different moments, and the match model
occupies the second one:

* **Pre-queue (attribute and affinity rules).** Skill requirements,
  attribute rules, and behavioral-affinity profiles decide which agents are
  *eligible* when a conversation enters or re-enters a queue. Affinity
  pairing scores the Jaccard overlap between contact tags and each agent's
  affinity profile, blended with headroom, on its own opt-in arm.
* **At dispatch (the predictive arm).** Within the eligible pool, the
  predictive arm ranks candidates by expected outcome — marginal signals by
  default, plus the trained match score once you train a model. The match
  model *adjusts the ranking among eligible agents*; it never widens or
  narrows eligibility.

Use skill and affinity rules to gate membership; use the match model to
break ties the aggregate signals cannot see.

## Operator posture — enable, train, verify

The feature is **tenant-owned throughout**:

* **Opt-in by two explicit switches.** Training runs only when
  `acd_predictive_routing` is enabled **and** `training_enabled` is set. The
  platform never trains or applies a model without both; enabling predictive
  routing alone keeps the marginal predictor unchanged.
* **Config lives in your organization's settings** (`acd_predictive_routing`
  knobs), so no migration applies and no platform default changes your
  routing.
* **Model quality is observable in your own data.** The model trains on your
  CSAT-answered calls, so its predicted outcomes are checkable against your
  CSAT analytics; scheduled training refreshes the artifact weekly.
* **No A/A vendor claims.** The match model is a deterministic,
  interpretable target-encoding model over your own historical outcomes —
  not a black-box vendor classifier. Every score decomposes to means you can
  audit.

To adopt it: enable predictive routing, turn on training, let the scheduled
job build the artifact, then set `match_weight` above 0 and compare the
predictive arm against the baseline using `treatment_percent` as an A/B
dial.

## See also

* [ACD queue model](/concepts/acd-queue-model) — the queue machinery the
  predictive arm dispatches within
* [Omnichannel queue routing](/guides/omnichannel-queue-routing) — the
  operator loop for queues, attribute rules, and affinity policies across
  digital channels
* [CDP event model](/concepts/cdp-event-model) — where the contact's context
  tags come from
* [Recommendations](/concepts/recommendations) — adjacent analytics surfaces
  for queue performance
* [Tenant isolation](/concepts/tenant-isolation) — why each tenant's model
  trains and serves in isolation
