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

# Migrating from an LLM classification pipeline

> Move an existing classifier onto judgments: measure accuracy first, then cut over in a week.

export const productName = "Vainona";

If you call an LLM on every ticket, message, document or event to classify it, you are running the pipeline {productName} replaces. That pipeline includes a queue, workers, retries, a cache, a schema, versioning, cron jobs and evaluation. This guide moves one workload over. It assumes you have labelled examples of what the classifier should say.

## 1. Measure accuracy on your own data first

Decision models are not LLMs, and yours is the only data that decides whether they are good enough. Before you migrate anything, compare the engine against your current classifier on your labelled examples. We run this with you: send us the labelled set and your current classifier's outputs, and you get back accuracy and cost for each engine next to your incumbent. If the engine loses on your data, stop there.

You can also measure it yourself: write the labelled documents to a namespace, post the labels as [outcomes](/guides/measure-improve-tune#post-labelled-examples), and read the judgment's calibration report.

## 2. Turn each prompt into a judgment

Most classification prompts map onto one of three types:

| Your prompt asks...                   | Judgment type                                           |
| ------------------------------------- | ------------------------------------------------------- |
| Yes or no ("Does this need a human?") | `bool`, with a threshold instead of a hard-coded cutoff |
| Which of these categories?            | `choice`, with each category's description in `options` |
| How much, on a scale?                 | `score`, with ordered `levels`                          |

Move the prompt's instructions into `question` and `criteria`, and the parts of the record it reads into a [context recipe](/guides/context-recipes). Prompts that sent the same record for five separate classifications become five judgments that share one recipe, which go to the engine as one request.

The engines cannot abstain. A `choice` judgment always gets a `none_of_the_above` escape option, and its probability comes back as `escape_p`. Use it where your prompt said "otherwise, answer other".

## 3. Mirror your writes

Send every create, update and delete from your system of record to the write API. `patch` and `append` let you send only what changed, and writes are idempotent, so your sync can retry freely. Documents stay yours: {productName} is not your system of record. To load the records you already have, see [import existing data](/guides/import-existing-data).

Choose each judgment's [freshness policy](/guides/freshness-policies). A judgment your pipeline ran on every change, whose results you filter on, is `on_change`. One you only show on a detail page can be `on_read`, which costs nothing until someone looks.

## 4. Backfill with the estimate in front of you

Existing records have no answers yet. Ask for a backfill estimate first:

```python theme={null}
ns.judgments.backfill("needs_escalation", confirm=False)
# {"estimate": {"documents": 1204332, "tokens": 2408664000, "judgment_units": 2408664, "cost_usd": 240.87, "duration_s": 107529}}
```

The estimate shows the duration next to the cost, because engine rate limits can make large backfills take days. Set a namespace budget, then confirm.

## 5. Compare, then cut over

Run both systems side by side on live traffic. Query where they disagree, and open a few of those documents in the dashboard to see the exact context the engine saw and its raw output. Tune thresholds on the answers you now have, not on a new prompt. With 100 or more outcomes, the [threshold recommender](/guides/measure-improve-tune#pick-thresholds-with-the-recommender) picks one for a precision or recall target.

To cut over, read answers from queries, or wait for them on the write that needs them:

```python theme={null}
ns.write(upsert=[ticket], wait_for=["needs_escalation"], wait_timeout_ms=5000)
```

Then turn off the old pipeline. Every answer from then on is versioned and kept with the context that produced it.
