BlogLLM do hloubky
Umělá inteligence

U-křivka v LLM: proč se modely ztrácejí uprostřed kontextu

5. 5. 2026 · cca 15 minut čtení

Kontextová okna dnešních modelů měří statisíce až miliony tokenů, a tak je lákavé do promptu „nasypat všechno“. Jenže modely nečtou kontext rovnoměrně. Když vynesete úspěšnost modelu proti pozici klíčové informace v promptu, dostanete charakteristickou U-křivku: začátek a konec fungují skvěle, prostředek propadá. Tomuto jevu se říká lost in the middle a má přímé důsledky pro každého, kdo staví RAG, píše delší prompty nebo provozuje agenty s dlouhými běhy.

Graf s propadem uprostřed na displeji tabletu

Foto: Burak The Weekender, Pexels

Krátká verze

Dlouhé kontextové okno není dlouhá pracovní paměť. Model sice dostane všechny tokeny, ale při odpovědi nejvíc využívá začátek a konec; proto musíte kritické informace v promptu a RAG výsledcích řadit podle pozice, ne jen podle relevance.

Co přesně experimenty ukázaly

Jev poprvé systematicky změřili Liu et al. ve studii Lost in the Middle: How Language Models Use Long Contexts (2023). Použili dvě úlohy. V první — multi-document QA — dostal model otázku a 10 až 30 dokumentů z Wikipedie, z nichž právě jeden obsahoval odpověď; zbytek byly tematicky podobné, ale nerelevantní „distraktory“. Výzkumníci pak jen posouvali pozici relevantního dokumentu a měřili přesnost. Druhá úloha — key-value retrieval — byla ještě syrovější: model dostal JSON se stovkami náhodných dvojic klíč–hodnota a měl vrátit hodnotu pro zadaný klíč. Žádná sémantika, čisté vyhledání v kontextu.

Nejdůležitější výsledky se dají shrnout do čtyř bodů:

  • Přesnost byla nejvyšší, když relevantní dokument stál úplně na začátku (tzv. primacy bias) nebo úplně na konci kontextu (recency bias).
  • S dokumentem uprostřed propadla přesnost GPT-3.5-Turbo o více než 20 procentních bodů — z ~75 % na ~54 %.
  • V nejhorším případě byl model s 20–30 dokumenty v kontextu horší, než kdyby nedostal žádný a odpovídal jen z natrénovaných znalostí (closed-book: 56 %).
  • Propad se týkal i modelů explicitně trénovaných na dlouhý kontext — delší okno neznamená rovnoměrné okno.
Graf U-křivky: přesnost modelu podle pozice relevantního dokumentu
Graf U-křivky: přesnost modelu podle pozice relevantního dokumentu
Stěžejní graf celého tématu: stejná informace, stejná otázka, stejný model — jediné, co se mění, je pozice v kontextu.

Delší kontextové okno tedy neznamená rovnoměrně využité okno. Model informaci „vidí“, ale při generování odpovědi jí nepřikládá dostatečnou váhu.

Jev se od té doby potvrdil opakovaně napříč modely, úlohami i jazyky. Novější frontier modely mají díky cílenému tréninku na dlouhý kontext propad výrazně mírnější a na jednoduchém „needle in a haystack“ testu často září. Jenže syntetická jehla je nejlehčí varianta úlohy: benchmarky jako RULER ukazují, že jakmile je potřeba informace nejen najít, ale i agregovat nebo s nimi vícekrokově uvažovat, efektivní délka kontextu bývá výrazně kratší než ta inzerovaná. A u menších a open-source modelů, které typicky nasazujete při self-hostingu, s U-křivkou musíte počítat pořád.

Proč U-křivka vzniká

Nabízí se intuice z psychologie: lidé si ze seznamu také nejlépe pamatují první a poslední položky. Tomuto jevu se říká sériový poziční efekt a popsal ho už Hermann Ebbinghaus v 19. století. Podobnost je ale spíš zábavná shoda — u transformerů má U-křivka konkrétní architektonické příčiny, a když je pochopíte, přestane být záhadou a stane se konstrukčním omezením, se kterým jde pracovat.

1. Kauzální maska zvýhodňuje začátek

V autoregresivním dekodéru každý token „vidí“ jen tokeny před sebou. První tokeny kontextu jsou proto v dosahu pozornosti všech následujících tokenů: každá vrstva, každý krok generování se k nim může vztáhnout, a jejich vliv se tak vrství přes celou hloubku sítě. Čím později token v kontextu stojí, tím méně „cest“ k němu vede.

2. Attention sink: první token jako kotva

Souvisejícím a dobře změřeným jevem je attention sink (Xiao et al., 2023): natrénované modely směřují nezanedbatelnou část pozornosti na úplně první tokeny sekvence bez ohledu na jejich obsah. Softmax v attention musí rozdělit váhy tak, aby dávaly součet 1 — a když hlava pozornosti zrovna „nemá co říct“, přebytek odloží na začátek. První pozice tak fungují jako kotva, kterou model prostě sleduje pořád. Prakticky to znamená, že obsah na začátku promptu má systematicky vyšší šanci ovlivnit výstup.

3. Poziční kódování zvýhodňuje konec

Rotační poziční embeddingy (RoPE), které používá většina dnešních modelů, tlumí příspěvek pozornosti s rostoucí vzdáleností mezi dotazem a klíčem. Tokeny na konci kontextu jsou generovanému textu nejblíž, takže na ně model „dosáhne“ nejsilněji — odtud recency bias. K tomu se přidává trénink: při predikci dalšího tokenu je nejbližší okolí statisticky nejužitečnější, takže model se naučí konci kontextu věřit.

4. Prostředek má nejhorší z obou světů

Token uprostřed dlouhého kontextu není ani kotvou ze začátku, ani není blízko konci: má maximální průměrnou vzdálenost od obou krajů a nejméně privilegovanou pozici v kauzálním grafu. Novější teoretická práce Lost in the Middle at Birth (2026) dokonce ukazuje, že U-křivka je geometrickou vlastností kauzálního dekodéru s reziduálními spoji už při náhodné inicializaci vah — tedy ještě před jakýmkoli trénováním. Kauzální maska garantuje primacy, reziduální spoje recency; trénink a poziční kódování tvar křivky jen modulují. Nejde tedy o „bug“, který příští generace modelů prostě opraví, ale o strukturální vlastnost architektury.

Změřte si to sami

Zjednodušenou verzi experimentu (needle in a haystack) si napíšete za večer. Stačí vkládat „jehlu“ do různé hloubky dlouhého textu a měřit, kdy ji model najde:

Python
import random
from openai import OpenAI

client = OpenAI()

NEEDLE = "Tajny kod projektu Vcela je 7412."
FILLER = open("dlouhy_text.txt", encoding="utf-8").read()

def test_pozice(hloubka):     # hloubka: 0.0 = zacatek, 1.0 = konec
    pozice = int(len(FILLER) * hloubka)
    kontext = FILLER[:pozice] + "\n" + NEEDLE + "\n" + FILLER[pozice:]
    odpoved = client.chat.completions.create(
        model="gpt-4.1-mini",
        messages=[{"role": "user",
                   "content": kontext + "\n\nJaky je tajny kod projektu Vcela?"}],
    ).choices[0].message.content
    return "7412" in odpoved

for hloubka in [0.0, 0.25, 0.5, 0.75, 1.0]:
    uspechy = sum(test_pozice(hloubka) for pokus in range(10))
    print(f"hloubka {hloubka:.2f}: {uspechy}/10")

U velkých modelů na krátkém kontextu bude nejspíš všechno na 10/10 — zajímavé to začne být s desítkami tisíc tokenů, s menšími modely a s těžšími variantami úlohy. Pokud chcete jít dál, zkuste tři obměny, které test výrazně přitvrdí:

  • Parafráze místo přesné shody — jehlu formulujte jinak, než zní otázka, aby nešlo o pouhé nalezení řetězce.
  • Více jehel najednou — model má najít a zkombinovat dvě až tři informace z různých hloubek.
  • Distraktory — přidejte do textu podobné, ale nesprávné „kody projektu“; přesně tak vzniká realističtější obraz chování v RAG.

Co s tím v praxi

Z U-křivky plyne několik pravidel, která se vyplatí dodržovat, i když zrovna nepracujete s extrémně dlouhým kontextem:

Klíčové věci na kraje

Systémové instrukce a zásadní fakta patří na začátek promptu, aktuální dotaz a finální pokyn na konec. Prostředek je nejslabší místo — tam patří podpůrný materiál. U dlouhých promptů funguje „sendvič“: kritickou instrukci uveďte na začátku a zopakujte na konci. Zní to lacině, ale opírá se přesně o primacy a recency bias.

V RAG řaďte podle pozice, ne jen podle skóre

Když retriever vrátí deset chunků, neskládejte je do promptu v pořadí, v jakém přišly — nejlepší chunk by skončil první, druhý nejlepší hned za ním a zbytek by zaplnil zbytek kontextu, takže druhá polovina výsledků by se utopila přesně v mrtvém prostředku. Osvědčené je tzv. přeskládání do tvaru U: nejrelevantnější dokumenty na začátek a na konec, nejslabší doprostřed. V LangChainu na to existuje hotový LongContextReorder, ale logika je triviální i ručně:

Python
def preskladat_do_u(dokumenty):
    """Dokumenty serazene podle relevance preusporada tak,
    aby nejlepsi byly na krajich a nejslabsi uprostred."""
    liche = dokumenty[::2]     # 1., 3., 5., ...
    sude = dokumenty[1::2]     # 2., 4., 6., ...
    return liche + sude[::-1]

# skore:  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# poradi: [1, 3, 5, 7, 9, 10, 8, 6, 4, 2]

Méně je více

Každý irelevantní dokument v kontextu zvětšuje „mrtvý prostředek“ a ředí pozornost. Studie Liu et al. mimochodem ukázala i to, že přidávání dalších dokumentů zlepšuje výsledek jen do určitého bodu a pak začne škodit. Agresivnější filtrování a reranking (např. cross-encoderem nebo LLM-based rerankerem) obvykle pomůže víc než větší kontext. Platí to i pro agenty: výsledky nástrojů, které už nejsou potřeba, patří sumarizovat nebo odložit mimo konverzaci.

Testujte, nevěřte

Chování se liší model od modelu a verze od verze. Pokud na dlouhém kontextu stavíte produkt, změřte si vlastní U-křivku na vlastních datech a měření zopakujte při každé výměně modelu. Hodinová investice do testovacího skriptu vám ušetří týdny záhadného debugování kvality.

Mitigace na straně modelu

Pokud model sami provozujete, existují i zásahy do inference: kalibrace poziční pozornosti popsaná v článku Found in the Middle (2024) odděluje pozornost způsobenou pozicí od pozornosti způsobené relevancí a U-křivku prokazatelně vyrovnává; příbuzný přístup attention sorting (Peysakhovich & Lerer, 2023) dokumenty iterativně přeskládává podle toho, kolik pozornosti reálně dostávají. To jsou ale techniky pro provozovatele vlastní inference — uživatel API si vystačí s pravidly výše.

Chcete jít do hloubky?

Pozornost, poziční kódování i práci s kontextem probíráme v kurzu Umělá inteligence pro programátory, vědu a datovou analytiku. A pokud stavíte RAG či agenty, mrkněte na Programátora AI agentů.

← Zpět na blog