TAI BUI
Back to curriculum
LearnPython

Long-Context Evaluation — NIAH, RULER, LongBench, MRCR

Gemini 3 Pro advertises 10M tokens of context. At 1M tokens, 8-needle MRCR accuracy drops to 26.3%. Advertised isn't usable. Long-context evaluation tells you the real capacity of the model you're actually shipping on.

TIME: ~60 minutesPREREQUISITES: Phase 5 · 13 (Question Answering), Phase 5 · 23 (Chunking Strategies)

Learning Objectives

  • Name the major long-context benchmarks (NIAH, RULER, LongBench v2, MRCR, NoLiMa, HELMET, BABILong) and state what each one specifically measures.
  • Explain why a model's advertised context window diverges from its effective usable length, including the lost-in-the-middle depth bias.
  • Build a custom NIAH depth-by-length sweep, extend it to multi-needle and multi-hop variable-tracing tests, and run a model against LongBench v2.
  • Report the two numbers that belong on a long-context spec sheet: effective retrieval length and effective reasoning length, instead of trusting an advertised maximum or a vendor's self-reported score.

The Problem

You have a 200-page contract. The model claims a 1M-token context. You paste the contract in and ask: "What is the termination clause?" The model answers confidently and wrong: it pulls from the cover page, because the real clause sits 120k tokens deep, well past where the model still attends.

This is the 2026 context-capacity gap. Spec sheets say 1M or 10M. Reality says 60-70% of that is usable, and "usable" depends on the task.

  • Retrieval (single needle in haystack): near-perfect up to the advertised max on frontier models.
  • Multi-hop / aggregation: degrades sharply past ~128k on most models.
  • Reasoning over dispersed facts: the first task to fail.

Long-context evaluation measures these three axes. This lesson names the benchmarks, states what each one actually measures, and shows how to build a custom needle test for your own domain.

Warm-up0/3
1. A friend hides one specific sentence somewhere inside a 300-page document and asks you to find it by reading through. In plain terms, what kind of task is that?Warm-up
2. A student gets every question right on a quiz that only asks them to look up one stated fact. What can you safely conclude?Warm-up
3. Language models read text as tokens. Roughly, what is a token?Warm-up
Check: The Problem0/2
1. Why is the advertised context window often very different from the usable context?Explain
2. A model advertises 1M tokens. You paste in a contract and ask for the termination clause, which sits 120k tokens deep. It answers confidently from the cover page. What is the most likely explanation?Analyze

The Concept

NIAH baseline, RULER multi-task, LongBench holistic

Needle-in-a-Haystack (NIAH, 2023). Plant a fact ("the magic word is pineapple") at a controlled depth in a long context and ask the model to retrieve it, sweeping depth × length. It's the original long-context benchmark. Frontier models now saturate it, which makes it necessary but not sufficient.

RULER (Nvidia, 2024). 13 task types across 4 categories: retrieval (single / multi-key / multi-value), multi-hop tracing (variable tracking), aggregation (common word frequency), QA, at a configurable context length (4k to 128k+). It catches models that saturate NIAH but fail on multi-hop. In the 2024 release, only half of the 17 models claiming 32k+ context actually held quality at 32k.

LongBench v2 (2024). 503 multiple-choice questions spanning 8k-2M word contexts across six task categories: single-doc QA, multi-doc QA, long in-context learning, long dialogue, code repo, long structured data. This is the production benchmark for real-world long-context behavior.

NIAH, RULER, and LongBench v2 cover most production needs. Four more benchmarks push on narrower failure modes:

MRCR (Multi-Round Coreference Resolution). Multi-turn coreference at scale, with 8-needle, 24-needle, and 100-needle variants. It exposes how many facts a model can actually juggle before attention degrades.

NoLiMa. Short for "non-lexical needle." The needle and the query share no literal overlap, so retrieval requires one step of semantic reasoning. Harder than NIAH.

HELMET. Concatenates many documents and asks a question about any one of them, testing selective attention.

BABILong. Embeds bAbI reasoning chains inside irrelevant haystacks, testing reasoning-in-a-haystack rather than plain retrieval.

What to actually report

  • Advertised context window. The spec-sheet number.
  • Effective retrieval length. NIAH pass at some threshold (e.g., 90%).
  • Effective reasoning length. Multi-hop or aggregation pass at that threshold.
  • Degradation curve. Accuracy vs context length, plotted per task type.

Two numbers belong on your spec sheet: retrieval-effective and reasoning-effective. The reasoning-effective number usually lands at 25-50% of the advertised window.

Check: The Concept0/14
1. What does RULER add over NIAH?Define
2. What is the lost-in-the-middle effect?Define
3. What does NoLiMa stress?Define
4. What does MRCR test, and what needle-count variants does it use?Define
5. What does the HELMET benchmark test?Define
6. What does BABILong test that plain NIAH does not?Define
7. Which benchmark is described as the production benchmark for real-world long-context behavior, with multiple-choice questions over six task categories?Define
8. What is NIAH?Define
9. What does effective context mean?Define
10. What is a multi-needle test?Define
11. RULER's 2024 release found that only half of 17 models claiming 32k+ context held quality at 32k. What does that show?Explain
12. Frontier models now saturate NIAH. Why does the lesson still call it necessary but not sufficient?Explain
13. What two numbers should a long-context spec sheet report?Explain
14. On most models, which of these usually fails first as context grows?Explain

Build It

Step 1: a custom NIAH for your domain

See code/main.py. The skeleton:

python
def build_haystack(filler_text, needle, depth_ratio, total_tokens):
    if not (0.0 <= depth_ratio <= 1.0):
        raise ValueError(f"depth_ratio must be in [0, 1], got {depth_ratio}")
    if total_tokens <= 0:
        raise ValueError(f"total_tokens must be positive, got {total_tokens}")
 
    filler_tokens = tokenize(filler_text)
    needle_tokens = tokenize(needle)
    if not filler_tokens:
        raise ValueError("filler_text produced no tokens")
 
    # Repeat filler until long enough to fill the haystack body.
    body_len = max(total_tokens - len(needle_tokens), 0)
    while len(filler_tokens) < body_len:
        filler_tokens = filler_tokens + filler_tokens
    filler_tokens = filler_tokens[:body_len]
 
    insert_at = min(int(body_len * depth_ratio), body_len)
    haystack = filler_tokens[:insert_at] + needle_tokens + filler_tokens[insert_at:]
    return " ".join(haystack)
 
 
def score_niah(model, haystack, question, expected):
    answer = model.complete(f"Context: {haystack}\nQ: {question}\nA:", max_tokens=50)
    return 1 if expected.lower() in answer.lower() else 0

Sweep depth_ratio ∈ {0, 0.25, 0.5, 0.75, 1.0} × total_tokens ∈ {1k, 4k, 16k, 64k} and plot the heatmap. That's the NIAH card for your target model.

Step 2: a multi-needle variant

python
def build_multi_needle(filler, needles, total_tokens):
    depths = [0.1, 0.4, 0.7]
    chunks = [filler[:int(total_tokens * 0.1)]]
    for depth, needle in zip(depths, needles):
        chunks.append(needle)
        next_chunk = filler[int(total_tokens * depth): int(total_tokens * (depth + 0.3))]
        chunks.append(next_chunk)
    return " ".join(chunks)

Questions like "What are the three magic words?" require retrieving all three at once. Single-needle success does not predict multi-needle success.

Step 3: multi-hop variable tracing (RULER-style)

python
haystack = """X1 = 42. ... (filler) ... X2 = X1 + 10. ... (filler) ... X3 = X2 * 2."""
question = "What is X3?"

Answering it correctly means chaining three assignments in order. Frontier models at 128k often drop to 50-70% accuracy on exactly this kind of task.

Step 4: LongBench v2 on your stack

python
from datasets import load_dataset
longbench = load_dataset("THUDM/LongBench-v2")
 
def eval_model_on_longbench(model, subset="single-doc-qa"):
    tasks = [x for x in longbench["test"] if x["task"] == subset]
    correct = 0
    for x in tasks:
        answer = model.complete(x["context"] + "\n\nQ: " + x["question"], max_tokens=20)
        if normalize(answer) == normalize(x["answer"]):
            correct += 1
    return correct / len(tasks)

Report per-category accuracy. Aggregate scores hide big task-level differences.

Check: Build It0/7
1. Why does the lesson say single-needle success does not predict multi-needle success?Explain
2. Why report LongBench v2 accuracy per category instead of one aggregate score?Explain
3. Assume tokenize splits on whitespace. build_haystack('a b', 'N', 0.5, 5) is called. What does it return?Apply
4. Assume tokenize splits on whitespace. build_haystack('a b', 'N X Y', 0.5, 2) is called. The needle has more tokens than total_tokens allows. What does it return?Apply
5. The expected answer is 'pineapple', and the model replies 'It is PINEAPPLE, I think.' What does score_niah return?Apply
6. In the variable-tracing haystack, X1 = 42, X2 = X1 + 10 and X3 = X2 * 2, all buried in filler. What is the correct answer to 'What is X3?'Apply
7. You are building a NIAH for medical notes and worry the model could pass by keyword matching. Which design best addresses that?Design

Pitfalls

  • NIAH-only evaluation. Passing NIAH at 1M tokens says nothing about multi-hop. Always run RULER or a custom multi-hop test alongside it.
  • Uniform depth sampling. Many implementations only test depth=0.5. Test depth=0, 0.25, 0.5, 0.75, 1.0: the "lost in the middle" effect is real.
  • Lexical overlap with filler. If the needle shares keywords with the filler, retrieval becomes trivial. Use NoLiMa-style non-overlapping needles instead.
  • Ignoring latency. 1M-token prompts take 30-120 seconds to prefill: the pass over the whole prompt before the model emits its first token. Measure time-to-first-token alongside accuracy.
  • Vendor-self-reported numbers. OpenAI, Google, and Anthropic all publish their own scores. Always re-run independently on your own use case.
Check: Pitfalls0/7
1. Why should a depth sweep test several depths such as 0, 0.25, 0.5, 0.75 and 1.0 rather than only 0.5?Explain
2. Why does the lesson tell you to use needles that share no keywords with the filler?Explain
3. Why measure time-to-first-token at long context lengths?Explain
4. Why does the lesson warn against trusting vendor-published long-context benchmark scores?Explain
5. Your model scores 98% on a single-needle NIAH sweep at 200k tokens but only 40% on a 3-hop variable-tracing test at the same length. What should you conclude?Analyze
6. A team tests its model only at depth 0.5, sees a pass, and ships a feature. Users later report misses on facts at other positions. What was wrong with the evaluation?Analyze
7. A long-context feature scores well on accuracy at 1M tokens, yet users complain that nothing appears for a minute after they submit. Which measurement was missing?Analyze

Use It

The 2026 stack:

SituationBenchmark
Quick sanity checkCustom NIAH at 3 depths × 3 lengths
Model selection for productionRULER (13 tasks) at your target length
Real-world QA qualityLongBench v2 single-doc-QA subset
Multi-hop reasoningBABILong or custom variable-tracing
Conversational / dialogueMRCR 8-needle at your target length
Model upgrade regressionFixed in-house NIAH + RULER harness, run on every new model

Rule of thumb for production: never trust a context window until you've run NIAH plus at least one reasoning task at your intended length.

Check: Use It0/3
1. You are evaluating a customer-support assistant whose conversations run long, and you need a benchmark that fits that setting. Which does the lesson's table suggest?Apply
2. You must pick a model for a tool that follows cross-references between clauses across about 100k tokens of contracts. Which evaluation plan fits?Design
3. You want a gate that catches long-context regressions whenever your team upgrades the underlying model. Which design fits?Design

Ship It

Save as outputs/skill-long-context-eval.md:

markdown
---
name: long-context-eval
description: Design a long-context evaluation battery for a given model and use case.
version: 1.0.0
phase: 5
lesson: 28
tags: [nlp, long-context, evaluation]
---
 
Given a target model, target context length, and use case, output:
 
1. Tests. NIAH depth × length grid; RULER multi-hop; custom domain task.
2. Sampling. Depths 0, 0.25, 0.5, 0.75, 1.0 at each length.
3. Metrics. Retrieval pass rate; reasoning pass rate; time-to-first-token; cost-per-query.
4. Cutoff. Effective retrieval length (90% pass) and effective reasoning length (70% pass). Report both.
5. Regression. Fixed harness, rerun on every model upgrade, surface deltas.
 
Refuse to trust a context window from the model card alone. Refuse NIAH-only evaluation for any multi-hop workload. Refuse vendor self-reported long-context scores as independent evidence.
Author's Note

The number that changed how I ship long-context features isn't the advertised window. It's the RULER finding: half of 17 models claiming 32k+ context couldn't hold quality at 32k. I stopped taking any context-length claim at face value, including whatever the model I'm using claims about itself.

Before a feature gets to depend on "the model can see the whole document," I run a NIAH sweep and at least one multi-hop test at the length I actually need, not the length on the spec sheet. It costs an afternoon, and it has saved me more than once from shipping retrieval that quietly falls apart on document forty of fifty.

Exercises

  1. Easy. Build a NIAH with 3 depths (0.25, 0.5, 0.75) × 3 lengths (1k, 4k, 16k). Run on any model. Plot pass rate as a 3×3 heatmap.
  2. Medium. Add a 3-needle variant. Measure retrieval of all 3 at each length. Compare to single-needle pass rate at the same length.
  3. Hard. Construct a variable-tracing task (X1 → X2 → X3, with 3 hops) embedded in 64k of filler. Measure accuracy across 3 frontier models. Report effective reasoning length per model.

Key Terms

TermWhat people sayWhat it actually means
NIAHNeedle in haystackPlant a fact in filler, ask the model to retrieve it.
RULERNIAH on steroids13 task types across retrieval / multi-hop / aggregation / QA.
Effective contextThe real capacityLength at which accuracy still holds above threshold.
Lost in the middleDepth biasModels under-attend to content in the middle of long inputs.
Multi-needleMany facts at onceMultiple plants; tests attention juggling, not retrieval alone.
MRCRMulti-round coref8, 24, or 100-needle coreference; exposes attention saturation.
NoLiMaNon-lexical needleNeedle and query share no literal tokens; requires reasoning.

Further Reading

Code

Ships With