Context rot (деградация контекста): почему длинный контекст не значит умный ответ
Context rot (деградация контекста) — снижение качества ответов языковой модели по мере роста длины входа. Все токены при этом формально помещаются в контекстное окно, модель отвечает так же уверенно, но на длинном входе чаще теряет факты из середины, путает инструкции и хуже держит порядок шагов.
Context rot — это про качество, а не про лимит. Лимит описывает контекстное окно: сколько токенов модель принимает. Деградация показывает, насколько надёжно она работает с тем, что приняла. Запрос на 200 тыс. токенов может пройти без ошибок и всё равно дать худший ответ, чем тот же вопрос на 5 тыс.
Откуда термин
Идея не нова: в работе «Lost in the Middle» (Liu et al., 2023) показали, что модели лучше используют информацию из начала и конца контекста, а факты из середины теряют. Дальше стали популярны тесты «иголка в стоге сена» (needle in a haystack): в длинный текст прячут одну фразу и просят её найти. Современные модели обычно проходят такой тест почти идеально, из чего легко сделать вывод, что длинный контекст «решён».
Название закрепил технический отчёт Chroma «Context Rot: How Increasing Input Tokens Impacts LLM Performance» (Kelly Hong, Anton Troynikov, Jeff Huber, июль 2025). Авторы проверили 18 моделей, включая GPT-4.1, Claude 4, Gemini 2.5 и Qwen3, на более реалистичных задачах, чем поиск дословной фразы. Что они нашли:
- качество падало с ростом длины входа во всех экспериментах, даже на простых задачах;
- чем меньше вопрос похож на искомый фрагмент, тем быстрее падает точность;
- один отвлекающий фрагмент (distractor) уже ухудшает результат, четыре ухудшают сильнее;
- на длинной истории диалога (LongMemEval, до 113 тыс. токенов) модели отвечали заметно лучше, когда им давали только нужные фрагменты, а не весь текст.
Код отчёта открыт: chroma-core/context-rot. Anthropic в статье про context engineering тоже называет context rot и относит его ко всем моделям: у одних деградация мягче, у других резче.
Как это работает
Единой причины нет, но обычно называют такие факторы:
- Внимание размазывается. В трансформере каждый токен «смотрит» на все остальные, то есть пар взаимосвязей порядка n² на n токенов. Чем длиннее вход, тем слабее выделяется нужное. Anthropic называет это «бюджетом внимания» (attention budget).
- Мало длинных примеров в обучении. Короткие последовательности встречаются в обучающих данных чаще, поэтому длинные модель видела реже. Это тоже объяснение из статьи Anthropic.
- Потеря в середине (lost in the middle). Начало и конец контекста используются лучше, чем середина.
- Шум. Похожие на ответ, но неверные фрагменты (distractors) сбивают модель сильнее, чем явно посторонний текст.
- Конкурирующие инструкции. Чем больше в контексте старых правил, примеров и результатов инструментов, тем выше шанс, что модель выполнит не ту инструкцию.
Эффект зависит от модели и задачи. Универсального «порога», после которого всё ломается, нет.
Что видно на практике
- Агент на 20-м шаге начинает повторять уже сделанные действия, забывает принятые решения или теряет исходное ТЗ: оно осталось в самом начале, под десятками логов и результатов команд.
- RAG с десятками чанков часто отвечает хуже, чем с несколькими отобранными: лишние фрагменты работают как distractors. Это согласуется с выводом Chroma про «сфокусированный» и «полный» вход. Подробнее про нарезку — в статье про чанкинг для RAG.
- Длинный чат начинает противоречить договорённостям из начала разговора.
- Ответ становится общим: вместо конкретных деталей из документа модель пересказывает «как обычно бывает».
Как с этим бороться
Общий принцип из статьи Anthropic: искать наименьший набор токенов с высокой информативностью, а не заполнять окно до предела. Подробнее в статье про контекст-инжиниринг.
| Приём | Что делает | Когда применять |
|---|---|---|
| Сокращать вход | Убирать лишнее до отправки: сжимать логи и вывод инструментов, отбирать фрагменты ретривером | Длинные результаты инструментов, RAG. Для агентов — Headroom |
| Главное в начало и конец | Инструкции и вопрос — в начале, повтор вопроса или формата ответа — в конце | Любой длинный промпт |
| Разбить задачу | Несколько запросов на подзадачи вместо одного огромного | Анализ больших документов, мульти-шаговые задачи |
| Суммаризация истории (compaction) | Старый диалог заменить кратким итогом и продолжить с чистого листа | Долгие сессии агента |
| Субагенты с чистым контекстом | Подзадачу решает отдельный агент и возвращает короткий результат | Поиск по кодовой базе, исследование |
| Структурированные заметки | Агент пишет план и прогресс во внешний файл и читает его, а не держит всё в промпте. См. Agent Memory | Задачи на много шагов |
| Очистка результатов инструментов | Убирать из истории старые выводы команд, которые уже не нужны | Агенты с длинной историей вызовов |
| Кэширование стабильного префикса | Не уменьшает деградацию, но удешевляет повторяющуюся часть: системный промпт, документы. Рядом стоит семантический кэш | Один и тот же длинный префикс в многих запросах |
Compaction, structured note-taking и субагенты Anthropic перечисляет как три основных способа работать с долгими задачами, которые не помещаются в окно или в «бюджет внимания».
Связь с RouterAI
Через RouterAI доступны модели с окном около 1 млн токенов: deepseek/deepseek-v4.1-flash, z-ai/glm-5.3, google/gemini-3.8-flash, anthropic/claude-sonnet-5.5 и другие (см. каталог моделей). Размер окна — только половина выбора: качество на длинном входе у разных моделей отличается, и по заголовку карточки его не угадать. Проверяйте на своих данных.
Одно и то же сообщение можно отправить разным моделям через единый OpenAI-совместимый API, поменяв только model. Пример: прячем «иголку» в длинный текст и спрашиваем про неё, сдвигая позицию.
import os
import sys
from openai import OpenAI
client = OpenAI(base_url="https://routerai.ru/api/v1", api_key=os.environ["ROUTERAI_API_KEY"])
MODELS = sys.argv[1].split(",")
RECORDS = int(sys.argv[2])
NEEDLE = "Код доступа к архиву договоров — ЗЕБРА-7391."
QUESTION = "Каков код доступа к архиву договоров? Ответь одним кодом."
def build(position):
records = [f"Запись {i}: отдел {i % 17} передал партию №{i * 7} на склад {i % 5}, замечаний нет." for i in range(RECORDS)]
records.insert(int(len(records) * position), NEEDLE)
return "\n".join(records)
def ask(model, position):
prompt = build(position) + "\n\n" + QUESTION
resp = client.chat.completions.create(model=model, temperature=0, max_tokens=3000, messages=[{"role": "user", "content": prompt}])
answer = (resp.choices[0].message.content or "").strip()
ok = "ЗЕБРА-7391" in answer
print(model, "позиция", position, "токенов", resp.usage.prompt_tokens, "OK" if ok else "MISS", repr(answer[:40]))
for model in MODELS:
for position in (0.05, 0.5, 0.95):
ask(model, position)
Запуск: python rot_test.py deepseek/deepseek-v4.1-flash,anthropic/claude-haiku-5.5 1500. Ключ берётся из переменной окружения ROUTERAI_API_KEY, в код его вписывать не нужно. max_tokens оставлен большим: модели с рассуждением тратят часть лимита на размышления, и при маленьком значении ответ может прийти пустым или обрезанным.
На таком запросе (около 42 тыс. токенов у DeepSeek и 51 тыс. у Haiku, у моделей разные токенизаторы) обе модели нашли код при всех трёх позициях. Это нормально: дословная «иголка» — самый лёгкий случай, и именно поэтому Chroma использует более трудные задачи. Чтобы увидеть разницу, усложните тест: добавьте похожие, но неверные коды (distractors), задайте вопрос другими словами, чем в тексте, или возьмите свой реальный документ и свой вопрос.
Стоимость сравнения считается по ценам каталога. Один запрос на 1 млн входных токенов — это 1 млн × цену «вход» за токен: для deepseek/deepseek-v4.1-flash около 2,6 ₽, для anthropic/claude-haiku-5.5 около 10,7 ₽, для anthropic/claude-sonnet-5.5 около 214 ₽, а за ответ платится отдельно. Запрос на 40–50 тыс. токенов пропорционально дешевле: у DeepSeek это порядка 0,1 ₽, у Haiku около 0,5 ₽, а весь прогон скрипта (три запроса на модель) укладывается в несколько рублей. Актуальные цены смотрите в каталоге. Работа из России без VPN и оплата в рублях доступны для всех моделей.
Чек-лист: агент деградирует
- Сколько токенов в запросе на проблемном шаге? Посмотрите
usage.prompt_tokensи сравните с началом сессии. - Что занимает контекст? Часто это результаты инструментов, а не диалог.
- Где лежит исходное ТЗ? Если в начале под сотнями строк логов, продублируйте его в конце или держите в файле заметок.
- Есть ли в контексте противоречащие или устаревшие инструкции?
- Нужны ли все фрагменты RAG? Попробуйте уменьшить число чанков и сравнить ответы.
- Можно ли выделить подзадачу в субагента с чистым контекстом?
- Давно ли была суммаризация истории? Включите compaction или начните новую сессию с кратким итогом.
- Стабилен ли префикс? Если да, включите кэширование: оно не лечит деградацию, но снижает цену повторов.
- Если дать ту же задачу в сокращённом виде, ответ станет лучше? Если да, причина именно в длине.
- Сравните две-три модели на одном и том же длинном входе (скрипт выше).
Связанные термины
- Context Window — сколько токенов модель принимает
- Контекст-инжиниринг — как собирать контекст
- Agent Memory — внешняя память агента вместо «всё в промпте»
- Semantic Caching — кэш ответов по смыслу запроса
- Agentic RAG — когда агент сам решает, что и сколько извлекать