jev-rag-benchmark

September 20, 2026 · View on GitHub

Reproducible, vendor-neutral experiments for measuring whether TypeSafe Jev improves a small RAG system. “Jev wins” is not an assumption: quality, latency, and cost can improve, stay flat, or get worse.

Benchmark status — XQuAD-TR (2026-09-20)

The locked full benchmark contains 1,044 unique Turkish XQuAD questions and a corpus of 240 passages. Published model and embedding calls use OpenRouter; no local LLM or local embedding model is used. The first stage fuses BM25 with baai/bge-m3, exposes the same 20 candidates to every reranker, and gives the answer model the best five passages. The Jev request was typesafe/jev-1.13; OpenRouter resolved it to typesafe/jev-1.13-20260917.

1. Candidate retrieval ceiling

This table answers the first prerequisite question: was the gold passage available to the reranker at all? A reranker cannot recover a passage outside its candidate pool.

Candidate sourceCandidate depthGold passage foundCandidate recall
BM25top-5979 / 1,04493.774%
BM25top-201,009 / 1,04496.648%
BM25top-501,020 / 1,04497.701%
BM25top-1001,028 / 1,04498.467%
BM25all 2401,044 / 1,044100.000%
Hybrid BM25 + BGE-M3top-51,019 / 1,04497.605%
Hybrid BM25 + BGE-M3top-201,039 / 1,04499.521%
Hybrid BM25 + BGE-M3top-501,044 / 1,044100.000%

Full-depth hybrid fusion raises the top-20 ceiling by 2.873 percentage points over BM25 alone, from 1,009 to 1,039 answer-bearing candidate sets.

2. Full reranker benchmark

Generation is disabled here, so the table isolates passage selection. All three rows use the same 1,044 questions and the same frozen hybrid top-20 candidates.

Reranking methodRecall@5Gold in top-5nDCG@10MRR@10Rerank p50Total rerank costStatus
No reranker; hybrid order97.605%1,019 / 1,04494.314%93.194%0.0 ms$0.000000Full
Jev 1.13 batch Noul99.425%1,038 / 1,04498.097%97.637%532.0 ms$0.410866Full
Cohere Rerank 3.599.425%1,038 / 1,04498.626%98.348%466.1 ms$1.044000Full

Jev and Cohere recover exactly the same number of gold passages into top-5. Jev costs 60.6% less, while Cohere leads Jev by 0.529 nDCG points and 65.9 ms at the median. Jev adds 19 top-5 recoveries and 3.783 nDCG points over the raw hybrid order, with zero API fallbacks.

Raw rows: results/xquad-tr-hybrid-fullfusion20-a-d-o.jsonl · Report: reports/generated/xquad-tr-hybrid-fullfusion20-a-d-o/

3. Frozen-context answer generation

Both generators receive the exact same frozen Jev top-5 context IDs, so retrieval and reranking variation cannot influence this comparison. “Successful answer” means token F1

= 0.5 after citation markers are removed.

MetricJev + Gemini 3.8 FlashJev + DeepSeek V4.1 Flash
Evaluation statusFull, 1,044 / 1,044Full, 1,044 / 1,044
Resolved modelgoogle/gemini-3.8-flashdeepseek/deepseek-v4.1-flash
Mean answer F127.274%29.191%
Exact match0.096%0.862%
Successful answers163 / 1,044 (15.61%)194 / 1,044 (18.58%)
Valid citation IDs93.87%95.69%
Abstention rate6.13%4.21%
Wrong-answer flag78.26%77.20%
Generation p50 / p953.128 s / 13.114 s7.641 s / 46.537 s
Generation-only cost$2.457358$0.659245
Jev + generation total$2.868223$1.070110
Cost per successful answer$0.017596$0.005516

On paired questions, DeepSeek improves mean F1 by 1.91 points (95% bootstrap CI +1.03 to +2.86) and success rate by 2.97 points (95% CI +0.96 to +5.08). It produces 31 more successful answers, lowers end-to-end cost by 62.7%, and lowers generation-only cost by 73.2%. The trade-off is latency: its observed median generation time is 2.44x Gemini's and its p95 is 3.55x Gemini's.

Paired report: reports/generated/gemini-vs-deepseek.md

4. Jev strategy ablations

These experiments test whether a more elaborate use of Jev improves the default batch reranker. Sample results are explicitly marked and are not presented as full-set scores.

ExperimentScopeRecall@5nDCG@10Main effectDecision
Batch Noul reference200 questions99.00%97.893%529.2 ms p50; $0.078964Default
Pointwise Jev200 questions99.00%97.008%No recovery; +80.8% latency; +56.5% costDo not use by default
3-order permutation ensemble200 questions99.00%98.131%+0.238 nDCG points; +10.9% latency; 3x costOptional stability mode
Multi-signal evidence router200 questions93.00%92.381%Over-filtered evidence; 10 fallbacks; about 2x latency/costSecurity mode; recalibrate first
Full-corpus hierarchical Jev100 questions76.00%61.21%Worse than BM25 top-5 at 97% Recall; $0.005061/queryRejected as retriever replacement
Jev citation verification100 questions99.00%97.693%All final cited claims verified; one regeneration; +3.7% total costOptional high-assurance mode

The candidate-order audit used 50 questions and five permutations. Mean Spearman rank correlation was 0.262, mean top-5 Jaccard was 0.288, and gold top-5 membership changed for 3/50 questions. The ensemble reduces this sensitivity, but the small quality gain does not justify tripling default reranking cost on this dataset.

5. Current recommendation

  • Best measured quality/cost: hybrid full-depth top-20 -> Jev batch top-5 -> DeepSeek V4.1 Flash.
  • Lower interactive latency: the same retrieval/Jev path -> Gemini 3.8 Flash.
  • High-assurance output: add Jev claim/source citation verification after generation.
  • Do not use Jev as the first-stage retriever: BM25 + BGE-M3 should create a strong, bounded candidate pool first.
  • Do not enable evidence filtering globally: calibrate its thresholds on the target corpus, especially for no-answer and adversarial documents.

Recall/nDCG and answer F1 measure different stages: 99.425% Recall@5 does not mean 99.425% correct generated answers. XQuAD references are short extractive answers, while the generator can produce two sourced sentences; exact match is therefore deliberately harsh. The wrong-answer flag is an automatic non-abstaining F1 < 0.5 proxy, not human factuality adjudication. Latency is observed OpenRouter route latency, not a universal TPS guarantee.

6. Visual benchmark

The charts below visualize the same locked results reported in the tables above. Their SVG sources are committed next to the PNG files so labels, scales, and values remain auditable.

Retrieval errors across the classic BM25, hybrid, and hybrid plus Jev pipelines

How to read it: the y-axis lists the retrieval pipeline, while the x-axis counts questions whose gold passage is missing from the final top five. Lower is better. The labels below each pipeline also report Recall@5, where higher is better.

Recall, ranking quality, and cost for the raw hybrid order, Jev, and Cohere

How to read it: the first two x-axes show percentages and are explicitly zoomed to 90–100%; higher is better. The third x-axis shows total reranking cost in USD for all 1,044 questions; lower is better. The y-axis in every panel lists the reranking method. All methods receive the same frozen hybrid top-20 candidates.

Successful answers, total cost, and median latency for Gemini and DeepSeek

How to read it: the first x-axis counts answers with token F1 >= 0.5, where higher is better. The next two x-axes show total pipeline cost and median generation latency, where lower is better. Both generators receive the exact same frozen Jev top-five contexts, so this chart isolates the answer-model trade-off.

The complete methodology, interpretation limits, confidence intervals, and artifact map are also preserved in docs/benchmark-2026-09-20.md.

The project is based on LlamaIndex's MIT-licensed local FastAPI/Ollama RAG example at commit f475afd8a9bbda84f252567e045d89d07b5701b3; see docs/architecture.md and NOTICE. The capped three-candidate comparison is in docs/candidate-review.md.

Install

Requirements: macOS/Linux, Python 3.11–3.13, uv, and an OpenRouter API key.

uv sync --extra dev
export OPENROUTER_API_KEY="..."
uv run jev-rag doctor

The optional cross-encoder extra is only for branch B and was not used in the published OpenRouter comparison. .env is ignored by Git; no credential file is committed.

Optional FastAPI surface, retained from the selected upstream app shape:

uv run uvicorn jev_rag_benchmark.app:app --host 127.0.0.1 --port 8000

Jev is called through OpenRouter's Decisions endpoint with OPENROUTER_API_KEY. Credentials remain server-side/environment-only. The CLI reports whether a key exists but never prints it.

Reproduce

# Download SciFact and XQuAD EN/TR, then normalize without committing raw data
uv run jev-rag data prepare

# Validate/materialize the deterministic index
uv run jev-rag index --dataset scifact

# One sourced baseline question through the capped OpenRouter config
uv run jev-rag ask "Türkiye'nin başkenti neresidir?" --dataset xquad-tr --branch A \
  --config-path configs/openrouter-smoke.yaml

# 25-query infrastructure smoke. Jev fixture is visibly marked; no paid calls.
uv run jev-rag benchmark smoke --dataset scifact --branches A,B,D --fixture-jev --skip-generation

# Preflight cost from the real top-20 candidate texts; makes zero API calls.
uv run jev-rag estimate --dataset scifact --jev-branches 1

# QA smoke with OpenRouter answer generation
uv run jev-rag benchmark full --dataset xquad-tr --branches A,D --limit 5 \
  --no-fixture-jev --no-skip-generation --config-path configs/openrouter-smoke.yaml

# Audit the first-stage ceiling, then run the locked full A/Jev/Cohere comparison.
uv run jev-rag retrieval-audit --dataset xquad-tr --candidate-ks 5,20,50,100,240 \
  --config-path configs/openrouter-hybrid-fullfusion-20.yaml
uv run jev-rag benchmark full --dataset xquad-tr --branches A,D,O \
  --no-fixture-jev --skip-generation \
  --config-path configs/openrouter-hybrid-fullfusion-20.yaml
uv run jev-rag report results/xquad-tr-a-d-o.jsonl \
  --output-dir reports/generated/xquad-tr-a-d-o

# Replay a generator over frozen Jev contexts. The command checkpoints every ten rows,
# resumes successful rows, retries empty OpenRouter generations, and supports sharding.
uv run python scripts/replay_generator.py \
  results/xquad-tr-hybrid-fullfusion20-a-d-o.jsonl \
  data/processed/xquad-tr/documents.jsonl \
  configs/openrouter-gemini-3.8-hybrid.yaml results/gemini-replay.jsonl \
  --source-branch D --target-branch G --concurrency 4

# Optional dev-only threshold calibration and candidate-order stability audit.
uv run jev-rag calibrate-jev --dataset xquad-tr --minimum-recall 0.95
uv run jev-rag jev-stability --dataset xquad-tr --limit 50 --permutations 5 \
  --config-path configs/openrouter-hybrid-fullfusion-20.yaml

# Real 25-query Jev smoke through OpenRouter, capped at \$0.02.
uv run jev-rag benchmark smoke --dataset scifact --branches A,B,D \
  --no-fixture-jev --skip-generation --config-path configs/openrouter-smoke.yaml

# Larger real Jev run: first set an explicit positive max_budget_usd in a copied config.
# The default of 0 blocks every paid call.
cp configs/paid.example.yaml configs/paid.yaml
uv run jev-rag benchmark full --dataset scifact --branches A,B,D --config-path configs/paid.yaml

# Generate CSV/JSONL-derived Turkish report and SVG
uv run jev-rag report results/scifact-a-b-d.jsonl

benchmark full uses every selected test query unless --limit is passed. A/B/D/O use the same frozen top-20 candidates and exactly five context documents. E applies the dev-selected threshold. P is pointwise Jev; R is the multi-signal evidence router; S is the permutation ensemble; H is full-corpus hierarchical Jev; V is batch Jev plus citation verification. Every generator prompt has the same 6,000-character maximum context budget.

Tests

uv run pytest
uv run ruff check .

Tests cover retrieval metrics, hybrid retrieval, EM/F1 citation handling, paired bootstrap reproducibility, Jev Noul/Choice response parsing, resolved-model logging, evidence routing, hierarchical and permutation reranking, citation verification, sharding, concurrent budget accounting, empty-generation retries, fallback behavior, and the zero-budget safety gate.

Results and costs

Each run writes JSONL, a downloadable flat CSV, and a manifest containing commit, dependency versions, dataset hashes, requested models, price date, seed, prompt versions, cache mode, and concurrency. Reports separate retrieval, reranking, generation, and end-to-end latency; Jev, generator, retry/fallback, and local compute are not conflated. Generator model, reasoning effort, token cap, measured usage, finish reason, and resolved OpenRouter model are recorded per row or manifest. Frozen-context replay prevents a fresh retrieval draw from contaminating generator comparisons.

Mock/fixture output has run_kind=fixture and must never be cited as a real benchmark. Local models have zero API price but non-zero measured runtime; the report calls this out. SciFact qrels measure retrieval only. XQuAD EN/TR answer references measure end-to-end EM/F1, and languages are reported separately.

Data licenses

Downloaded data is ignored by Git and regenerated by scripts:

  • SciFact claims/evidence annotations: CC BY 4.0; abstracts: ODC-By 1.0.
  • XQuAD: CC BY-SA 4.0.

The project source is MIT. Upstream attributions are in NOTICE.

Current limitations

  • Jev 1.13 is strongest in English; these Turkish results must not be generalized to other languages or domains without evaluation.
  • Candidate order affects Jev scores. The measured ensemble reduces that sensitivity but is not the default because it triples cost for a small nDCG gain.
  • Passage instructions are treated as untrusted data. The evidence router can label likely injection/contradiction, but this is not a complete security boundary.
  • Automatic XQuAD F1, wrong-answer flags, and Jev citation decisions are proxies, not human factuality adjudication.