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

# Adaptive query pipeline

> Tell Mixpeek what you care about, such as cost, speed or quality, and it picks how each query runs

A retriever is a pipeline of stages: searches, filters, fusion, reranking. The right pipeline depends on the query. A product SKU lookup wants a fast keyword search. A vague question wants several embedding searches fused and reranked. A photo search wants the image model. One fixed pipeline is either slow for the easy queries or weak for the hard ones.

An adaptive query pipeline closes that gap. You state what you care about, and Mixpeek decides how each query runs.

> [!WARNING]
>
> **Part of this page describes a preview.** Per-query pipeline selection (everything under [Per-query pipeline selection](#per-query-pipeline-selection)) is in development and not yet available. Field names may change before release. The parts under [What adapts today](#what-adapts-today) are live.

## What adapts today

Every retriever already adapts in three ways, with no configuration:

> [card] [The query optimizer](https://docs.mixpeek.com/docs/retrieval/query-optimization.md)
>
> Before each run, Mixpeek rewrites your stage list: it pushes filters into the vector store, fuses compatible stages and runs independent work in parallel. Explain shows what it did.

> [card] [Filtered search strategy](https://docs.mixpeek.com/docs/retrieval/filters.md)
>
> Each filtered vector search counts the documents that match the filter, then either scans exactly those documents or searches a wider part of the index, whichever suits that query.

> [card] [Auto-Tune](https://docs.mixpeek.com/docs/retrieval/auto-tune.md)
>
> With learned fusion, the weights that blend your searches adapt to each user from clicks, purchases and feedback.

These change how a pipeline runs. They do not change which pipeline runs. That is the next step.

## Per-query pipeline selection

> [!NOTE]
>
> Preview. Not yet available.

You save two or three versions of a retriever that make different trade-offs, for example a fast keyword version, your current hybrid version and a thorough version with reranking. For each query, Mixpeek picks the version expected to best meet your objective, from how that kind of query performed on your own evaluation data.

```mermaid theme={null}
flowchart LR
  Q[Query] --> S[Classify the query<br/>into a shape]
  S --> P{Confirmed better<br/>version for this shape<br/>and objective?}
  P -- yes --> V[Run that saved version]
  P -- no --> D[Run your current version]
  V --> R[Results + plan block]
  D --> R
```

### How it works

**1. You save candidate versions**

Each candidate is an ordinary saved revision of your retriever, with its own stages and parameters. You can read, test and call each one directly. Your current version is always the default.

**2. You pick a goal and an evaluation dataset**

Set `optimize_for` (`quality`, `balanced`, `speed` or `cost`) and point the planner at a judged evaluation dataset for this retriever.

**3. Mixpeek evaluates the candidates**

Evaluation runs score every candidate on your judged queries, recording quality, latency and credits per query. They run when the dataset, a candidate or your current version changes, at most once a day.

**4. It confirms a version per kind of query**

Queries are grouped into shapes by cheap text statistics (length, rare terms, digits and so on). A shape switches away from your current version only when a candidate wins on one half of the judged queries for that shape and the win holds on the other half.

**5. Each query runs the confirmed version**

A query of that shape runs the confirmed version. Every response says which version ran and why, with forecasts for the alternatives.

### What you control

| Setting | Where | What it does |
| - | - | - |
| `optimize_for` | retriever, or per request | The trade-off: `quality`, `balanced` (default), `speed` or `cost` |
| `budget_limits` | retriever (exists today) | Hard limits on credits and time per query. A version forecast to exceed them is never chosen. A request can tighten them, never raise them |
| `candidates` | retriever | Up to two saved revisions the planner may choose besides your current version |
| `evaluation_dataset` | retriever | The judged dataset that candidates are confirmed on |
| `pins` | retriever | Send one user, or one verified group, to a specific version |
| `adaptation` | retriever | `on` to route and serve, `shadow` to decide and log while serving your current version, `off` to run as saved |
| pagination | request (exists today) | How deep you read. Shallow reads favour precision and deep reads favour recall, with no extra setting |

### Configure it

> [tab] Retriever
>
> ```json theme={null}
> {
>   "retriever_name": "support_search",
>   "stages": ["...your current stages..."],
>   "budget_limits": { "max_credits": 1.5, "max_time_ms": 400 },
>   "planner": {
>     "adaptation": "shadow",
>     "optimize_for": "balanced",
>     "candidates": [7, 2],
>     "evaluation_dataset": "eds_support_judged",
>     "pins": [{ "match": { "user_id": "usr_41" }, "version": 2 }]
>   }
> }
> ```
>
> Start in `shadow`: the planner decides and logs what it would serve while your current version keeps serving. Switch to `on` when the logged decisions look right.

> [tab] Request
>
> ```json theme={null}
> {
>   "inputs": { "query": "refund policy for annual plans" },
>   "objective": { "optimize_for": "speed", "budget": { "max_time_ms": 150 } }
> }
> ```
>
> `objective` overrides the retriever's setting for this call. Its budget can only tighten the retriever's limits.

> [tab] Response
>
> ```json theme={null}
> {
>   "plan": {
>     "plan_id": "pln_3f2a...",
>     "revision": 7,
>     "source": "learned",
>     "shape_id": "shp_07",
>     "forecast": { "latency_ms_p90": 48, "credits": 0.4, "quality": 0.71, "n": 64 },
>     "alternatives": [
>       { "plan_id": "pln_77b1...", "revision": 2, "latency_ms_p90": 210, "credits": 1.1, "quality": 0.76, "n": 64 }
>     ],
>     "reason_codes": []
>   }
> }
> ```
>
> `source` says why this version ran: `learned`, `pinned`, `request`, `default` or `fallback`. `n` is the number of judged queries behind each forecast. To take an alternative, send its `plan_id` as `plan` on the next call.

### What it promises

> [accordion] It never experiments on your live traffic
>
> Candidates are compared only by evaluation runs on your judged dataset. Live queries are never used to test versions against each other, and your users' queries are never re-run.

> [accordion] It leaves your current version only on evidence
>
> A shape switches only when a candidate's win is confirmed on judged queries it was not chosen on, and its worst-case queries are no worse than your current version's beyond a set margin. A retriever with no judged dataset never switches. Editing your current version sends every shape back to it until the next evaluation run.

> [accordion] Budgets are limits
>
> A version forecast to exceed your credit or time limit is never chosen. If none fits, the fastest version runs and the response says so.

> [accordion] Billing does not change
>
> A query is billed for the version that ran, as today, and never above your retriever's credit limit. Evaluation runs are billed as evaluation runs are today.

> [accordion] Your settings win
>
> Pins, a version named in the request, and `adaptation: off` always take precedence over what the planner learned.

> [accordion] Your data stays yours
>
> Decisions and statistics are stored as documents in your own namespace. Nothing learned from your evaluations is used for anyone else. Marketplace and public retrievers cannot turn the planner on, so consumers' queries never train a publisher's routing.

### What we have measured so far

These numbers come from our own simulator and test namespaces, not from production traffic.

| Question | Result |
| - | - |
| Do a few saved versions capture most of the gain? | Routing among 3 saved versions came within 0.006 nDCG\@10 of routing among all 63 pipelines we could build from the same namespace |
| Does it gain without regressing? | Across 11 test namespaces, 6 objectives and 3 traffic shifts (198 cases), utility rose by 0.077 on average over each namespace's default, and no case fell more than 0.0013 below it |
| Where does it help most? | Mixed media. On a photo namespace, picking the image model for the queries that want it raised nDCG\@10 by about 0.08 |
| Does it hold budgets? | 0 budget violations under simulated cold starts and slowdowns |
| Does it make AI agents better at finding answers? | No measurable change at a generous budget, across 420 paired agent tasks. Agents read the whole first page and rephrase when they miss, so a better order inside the page matters less to them. For agents, the value is in staying inside cost and latency limits |

> [!NOTE]
>
> We also tested routing each query individually with a trained model, and re-bucketing query shapes automatically. Held to the same safety rules, neither beat shapes from simple text statistics, so the preview uses those.

## Related

> [card] [Query optimization and explain](https://docs.mixpeek.com/docs/retrieval/query-optimization.md)
>
> See the plan the optimizer built for a query, with per-stage estimates.

> [card] [Auto-Tune](https://docs.mixpeek.com/docs/retrieval/auto-tune.md)
>
> Learned fusion weights per user, from interactions.

> [card] [Evaluations](https://docs.mixpeek.com/docs/retrieval/evaluations.md)
>
> Build the judged dataset the planner confirms versions on.

> [card] [Filters](https://docs.mixpeek.com/docs/retrieval/filters.md)
>
> How filters run inside the vector store.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.