Загрузка...

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 и оплата в рублях доступны для всех моделей.

Чек-лист: агент деградирует

  1. Сколько токенов в запросе на проблемном шаге? Посмотрите usage.prompt_tokens и сравните с началом сессии.
  2. Что занимает контекст? Часто это результаты инструментов, а не диалог.
  3. Где лежит исходное ТЗ? Если в начале под сотнями строк логов, продублируйте его в конце или держите в файле заметок.
  4. Есть ли в контексте противоречащие или устаревшие инструкции?
  5. Нужны ли все фрагменты RAG? Попробуйте уменьшить число чанков и сравнить ответы.
  6. Можно ли выделить подзадачу в субагента с чистым контекстом?
  7. Давно ли была суммаризация истории? Включите compaction или начните новую сессию с кратким итогом.
  8. Стабилен ли префикс? Если да, включите кэширование: оно не лечит деградацию, но снижает цену повторов.
  9. Если дать ту же задачу в сокращённом виде, ответ станет лучше? Если да, причина именно в длине.
  10. Сравните две-три модели на одном и том же длинном входе (скрипт выше).

Связанные термины

Что почитать