Hev meets Jev
Building your own SOTA reranker is now highly achievable.
I was given early access to Jev, TypeSafe’s new structured-output model, and my first thought was a reranker. If you don’t live in search, here is the two-minute version.
What a reranker does
Say you have a couple of ways to search a set of documents. Keyword search produces one list of candidates, vector search produces another, and you need to sort those N lists into one final top 10 (top-K) for the person on the other end. Those last two steps are ranking and reranking. Ranking is mostly algorithmic: reciprocal rank fusion merges the lists. Reranking is mostly inference: a model looks at the query and each candidate and scores how well they match. It runs at query time, so it has to be fast, and there is a boutique industry of models that do exactly this and nothing else.
Why Jev
Jev does not generate text. You send it a state and a set of typed questions, and it answers every question in parallel with a calibrated probability. It is designed for structured output at high throughput and low latency, which sounded to me like exactly the shape a reranker needs.
The question type I used is a Noul, Jev’s true-or-false type: a statement plus criteria
for true and for false, answered as a probability from 0 to 1. The whole prompt is one
question per candidate document:
question: "Document `documents.{id}` is relevant to `query`: it contains information that answers or directly addresses it."
criteria:
"true": "The document contains information that answers the query or directly addresses what it asks about."
"false": "The document is only loosely related, on a similar topic, or does not address what the query asks."
The state is the query plus the thirty candidates, keyed D00 through D29:
{"query": "...", "documents": {"D00": {...}, ..., "D29": {...}}}
Thirty Nouls, one per key, and the rank is the sort by probability. That is the batch shape: one call per query. The single shape asks the same question one (query, document) pair per call. The phrasing is generic on purpose. I tuned it on SciFact’s train split only, and a version written for that corpus did no better, which is what you want if the reranker is going to sit inside a search engine that does not know the corpus.
The experiment
Three BEIR datasets with labeled queries: SciFact (300 scientific claims), NFCorpus (323 medical queries), and FiQA (300 financial questions). BM25 pulls a top-30 shortlist per query as the baseline, and every reranker permutes that same list. I scored nDCG@10 against the labels, averaged it over the three datasets, and compared Jev with Voyage, Cohere, and Mixedbread on quality, price, and latency. Differences are paired bootstrap over queries. Language models got their own run, further down.
The results
Added 2026-09-19. The original version of this post benchmarked Mixedbread’s
mxbai-rerank-large-v2and missedmxbai-rerank-v3.1-listwise, their newer listwise model, which is not on Hugging Face and is served only from their API. I have since run it on the same shortlists, and it is in every table and chart below. It is the strongest reranker in the set: it beats Jev on NFCorpus and FiQA by margins whose confidence intervals exclude zero, and it beats the model I did test by 0.018 to 0.044 nDCG@10 per corpus while running faster. Mixedbread’s published rate does not split by model, so it carries the same $3.50 per 1,000 queries.
Amazingly, Jev lands alongside the purpose-built models on all three axes. That is with no tuning, and none of the query-intent data you could capture in production.
| Reranker | nDCG@10, mean | p50 / p95 | $ per 1k queries |
|---|---|---|---|
| Jev reranker, batch | 0.501 | 223 ms / 1.4 s | $0.54 |
| Jev reranker, single | 0.502 | 131 ms / 256 ms per call | $0.87 |
| Mixedbread mxbai-rerank-v3.1-listwise, hosted | 0.511 | 217 ms / 262 ms | $3.50 |
| Voyage rerank-3 | 0.504 | 185 ms / 287 ms | $0.50 |
| Cohere rerank-v3.5 | 0.486 | 192 ms / 456 ms | $2.00 |
| Mixedbread mxbai-rerank-large-v2, hosted | 0.476 | 361 ms / 429 ms | $3.50 |
| MiniLM-L6 cross-encoder, local | 0.447 | ||
| BM25 order, no rerank | 0.404 |
Per-corpus nDCG@10
| Reranker | SciFact | NFCorpus | FiQA |
|---|---|---|---|
| Jev reranker, batch | 0.768 | 0.358 | 0.376 |
| Jev reranker, single | 0.772 | 0.358 | 0.376 |
| Mixedbread mxbai-rerank-v3.1-listwise, hosted | 0.767 | 0.367 | 0.398 |
| Voyage rerank-3 | 0.755 | 0.357 | 0.402 |
| Cohere rerank-v3.5 | 0.745 | 0.340 | 0.374 |
| Mixedbread mxbai-rerank-large-v2, hosted | 0.749 | 0.324 | 0.354 |
| MiniLM-L6 cross-encoder, local | 0.682 | 0.336 | 0.323 |
| BM25 order, no rerank | 0.667 | 0.310 | 0.234 |
| gpt-5.6-luna, reasoning off, listwise | 0.747 | 0.355 | 0.363 |
| Claude Haiku 4.5, listwise | 0.723 | ||
| Claude Opus 5, low effort, listwise | 0.756 |
Costs are each run’s actual usage at list price, normalized to 1,000 queries and averaged
over the three datasets. Voyage’s number comes from its own billed usage.total_tokens, and
Cohere and Mixedbread charge per search.
Quality against price
mean nDCG@10 · $ per 1,000 queries
Scroll sideways for the whole chart.
Quality against latency
mean nDCG@10 · p50 per query, whisker to p95
Scroll sideways for the whole chart.
The honest read: Voyage rerank-3 is a hair better and a hair cheaper, Mixedbread’s
v3.1-listwise is better than either at six and a half times Jev’s price, and the tail on
Jev’s batch calls (p95 near 1.4 s) is worse than the specialized rerankers’. The single
shape fixes the tail at the cost of thirty times the requests. Paired per query, the batch
shape is at or above Cohere on all three datasets, above mxbai-rerank-large-v2 on all
three, and trades with Voyage: an edge on SciFact, a tie on NFCorpus, a loss on FiQA.
Against v3.1-listwise it ties on SciFact and loses on NFCorpus and FiQA.
What Jev returns that none of them do is a calibrated probability per document. On SciFact, documents Jev scored above 0.9 were judged relevant 76% of the time, and documents it scored below 0.1, half a percent of the time. A cross-encoder gives you a logit you have to threshold per corpus. A probability lets you prune an overfetched pool, compare scores across shards and retrieval legs, and gate a downstream step, with one number you did not have to calibrate yourself. One call over the pool is a rerank and a prune in the same round trip.
Do you even need a reranker?
With agentic search, you can legitimately ask whether you need a reranker at all, or whether a small LLM could do the job. It can. I was surprised to see gpt-5.6-luna land at nearly the same quality and cost as the purpose-built models, and Opus 5 tie Voyage outright. The catch is latency, and at the top end, price: seconds per query instead of a fifth of a second, and Opus costs nearly two hundred times what Voyage does. On SciFact, the one dataset all of them ran on, with the same Jev run for comparison:
| Reranker | nDCG@10, SciFact | p50 / p95 | $ per 1k queries |
|---|---|---|---|
| Jev reranker, batch | 0.768 | 224 ms / 1.8 s | $0.60 |
| Claude Opus 5, low effort, listwise | 0.756 | 5.0 s / 7.5 s | $94.68 |
| gpt-5.6-luna, reasoning off, listwise | 0.747 | 3.2 s / 5.8 s | $2.36 |
| Claude Haiku 4.5, listwise | 0.723 | 2.9 s / 11.6 s | $13.93 |
Opus also refused 12 of the 300 scientific claims outright.
What I checked
- Determinism. Re-issuing 30 of the batch calls moved scores by 0.004 on average, 0.14 at worst, and changed zero top-1 results.
- Position bias. Reversing the candidate order kept per-document scores at Spearman 0.83 and nDCG@10 within 0.005.
- A floor. On SciFact, a random permutation of the same shortlist scores 0.12; BM25’s own order scores 0.667.
- Question shape. A single
Choiceover the documents (“which one answers this?”) is slightly better when one document is relevant and much worse when many are. OneNoulper document is the shape to ship.
Caveats
These are public benchmarks, so training-set contamination is possible for every model in the table. Rerank depth was 30. The hosted rerankers accept much deeper lists per call, while Jev’s 32k-token request budget holds about 50 passages. Latency was measured from a laptop with network included. And Jev is hosted only, so the documents leave your environment.
Conclusion
If you are on a team that tests ranking models already, you owe it to yourself to at least
look at this. I am opening up my extremely simple ranking approach at
github.com/hev/reranker: the prompt, the request
schema, a 90-line wrapper with chunking and a prune threshold, and the results with
confidence intervals. It is on PyPI as hev-rerank. I am excited to see what others build
that is similar.
pip install hev-rerank
from hev_rerank import rerank
hits = rerank(query, shortlist, top_n=10, threshold=0.1)