Миф о 128K токенах: когда размер контекста не спасает
Вы купили доступ к Claude-3.5 с его 200K контекстом. Залили туда всю документацию проекта на 150 страниц. Задали вопрос про функцию, описанную на 75-й странице. И получили бред.
Поздравляю, вы столкнулись с Lost in the Middle.
Это не баг, это фундаментальное свойство архитектуры Transformer. Тот самый механизм внимания, который сделал LLM возможными, оказался слепым пятном в середине длинных последовательностей. Модели прекрасно помнят начало и конец, но теряют информацию в центре.
Проблема: В контексте из 100K токенов информация на позициях 30K-70K имеет в 2-3 раза меньшую вероятность быть использованной, чем та же информация в начале или конце. Это не теория, а данные из бенчмарка RULER.
Почему это происходит? Механика провала
Transformer'ы используют attention — механизм, который вычисляет «важность» каждого токена относительно других. В идеальном мире — равномерное внимание ко всему контексту. В реальном — внимание скапливается на краях.
Причин две:
- Позиционные эмбеддинги — модель учится, что начало и конец важнее. Как в книгах: введение и заключение содержат самую суть.
- Вычислительная сложность — attention квадратичен по длине. Модель неявно учится экономить внимание, фокусируясь на очевидных точках.
Но вот что интересно: этот эффект нелинейный. Он не просто плавно ухудшается к середине. Есть конкретные «мертвые зоны», где точность падает на 40-60%.
| Модель | Контекст | Падение точности в середине | Бенчмарк |
|---|---|---|---|
| GPT-4 Turbo | 128K | 57% | RULER |
| Claude-3 Opus | 200K | 48% | NoLiMa |
| Llama 3.1 405B | 128K | 62% | RULER |
Цифры шокируют? Они должны. Мы платим за длинный контекст, но используем его на треть.
RULER против NoLiMa: как измерять то, что теряется
Есть два основных бенчмарка для оценки Lost in the Middle:
RULER (Reasoning with Ultra-Long Contexts)
Создает искусственные задачи, где ответ зависит от информации в конкретной позиции контекста. Например: «В тексте на позиции 42,567 есть число X. Какое это число?»
RULER показывает сырые цифры — насколько модель физически способна извлечь информацию из разных частей контекста.
NoLiMa (Needle in a Large Multimodal Array)
Более практический тест. В огромный контекст (техническая документация, код, статьи) вставляется «иголка» — конкретный факт. Модель должна найти его и ответить на вопрос.
NoLiMa измеряет не просто извлечение, а понимание в контексте. И вот здесь провал еще глубже — потому что модель может технически прочитать текст в середине, но не связать его с вопросом.
Кстати, о тестировании. Когда вы тестируете недетерминированные LLM, эффект Lost in the Middle добавляет еще один слой неопределенности. Сегодня модель нашла факт в середине, завтра — нет.
Архитектурные паттерны: как обмануть внимание
Бороться с Lost in the Middle можно на трех уровнях: на уровне данных, на уровне запроса и на уровне архитектуры системы.
1 Перемешивание контекста: самый простой хакинг
Если модель лучше всего помнит начало и конец — поместите важную информацию туда.
Как НЕ делать:
# Плохо: важная документация где-то посередине
context = "Введение... (10K токенов) ... Важная функция API: get_user(id)... (еще 40K токенов) ... Заключение"
Как делать:
# Хорошо: важное — в начало и конец
important_info = "Важная функция API: get_user(id) возвращает..."
context = f"ВАЖНО: {important_info}\n\n{remaining_docs}\n\nПОВТОРЕНИЕ ВАЖНОГО: {important_info}"
Это грубо. Это костыль. Но это работает с точностью до +30% на RULER.
2 Иерархическое внимание: не давайте модели всё сразу
Вместо того чтобы скармливать 100K токенов одной модели, разбейте на chunks и используйте двухуровневую архитектуру:
# Псевдокод иерархического подхода
chunks = split_document(doc, chunk_size=8192) # 8K chunks
# Уровень 1: суммаризация каждого чанка
summaries = [summarize(chunk) for chunk in chunks]
# Уровень 2: работа с суммами (10K токенов вместо 100K)
final_context = "\n\n".join(summaries)
answer = query_llm(f"Вопрос: {question}\nКонтекст: {final_context}")
Вы теряете детали? Да. Но вы теряете их контролируемо, а не случайно в мертвой зоне внимания.
Этот подход напрямую связан с семантическими пайплайнами для LLM. Вы строите конвейер обработки, где каждый этап оптимизирован под свою задачу.
3 Динамическое рефокусирование: задавайте уточняющие вопросы
Когда модель не может найти информацию в длинном контексте — не ждите чуда. Запросите уточнение.
# Пример диалога с рефокусировкой
user: "Найди в документации описание функции process_payment"
assistant: "Я просмотрел документ. Он содержит 45 разделов.
В каком разделе примерно должна быть эта функция?"
user: "В разделе 'API Reference', подраздел 'Payment Methods'"
assistant: "Нашел раздел. Описание функции: ..."
Вы заставляете пользователя (или другую систему) указать приблизительное местоположение информации. Это сжимает область поиска с 100K токенов до 5-10K.
Практический план: 5 шагов к системе, которая не теряет данные
1 Измерьте свой Lost in the Middle коэффициент
Прежде чем что-то оптимизировать — измерьте. Возьмите RULER или создайте свой мини-бенчмарк:
import random
def test_positional_bias(model, context_length=50000):
"""Тестируем, насколько модель теряет информацию в середине"""
results = []
for pos in [0, 0.25, 0.5, 0.75, 1.0]: # Позиции в контексте
# Создаем контекст с маркером на позиции pos
marker = f"СЕКРЕТНЫЙ_КОД: {random.randint(1000, 9999)}"
context = create_context_with_marker_at_position(marker, pos, context_length)
# Запрашиваем у модели маркер
answer = model.query(f"Что такое СЕКРЕТНЫЙ_КОД? Контекст: {context}")
# Записываем, нашла ли
results.append({
'position': pos,
'found': marker in answer,
'response': answer
})
return results
2 Сегментируйте контекст по смыслу, а не по длине
Не просто разбивайте на chunks по 8K токенов. Разбивайте по логическим разделам. Если это код — по модулям. Если документация — по главам.
Каждый сегмент должен иметь:
- Заголовок (для реферирования)
- Ключевые слова (для поиска)
- Длину не более 4-8K токенов (оптимум для внимания)
3 Постройте гибридную систему поиска
Полный RAG (Retrieval-Augmented Generation) с эмбеддингами + ключевые слова + позиционная информация.
class HybridSearch:
def __init__(self, documents):
self.chunks = chunk_documents(documents)
self.embeddings = create_embeddings(self.chunks)
self.keyword_index = build_keyword_index(self.chunks)
def search(self, query, top_k=5):
# 1. Поиск по эмбеддингам (семантический)
semantic_results = semantic_search(query, self.embeddings, top_k*2)
# 2. Поиск по ключевым словам (лексический)
keyword_results = keyword_search(query, self.keyword_index, top_k*2)
# 3. Объединение с приоритетом начала/конца документов
combined = combine_results(semantic_results, keyword_results)
# 4. Смещение в пользу chunks из начала/конца исходных документов
prioritized = prioritize_edges(combined)
return prioritized[:top_k]
4 Добавьте явные позиционные маркеры
Когда передаете контекст в LLM, добавляйте информацию о позиции:
# Вместо просто текста
context = "Раздел 1... Раздел 2..."
# Добавляем маркеры
context = """
[НАЧАЛО ДОКУМЕНТА]
Раздел 1: Введение
...
[СЕРЕДИНА ДОКУМЕНТА, ПОЗИЦИЯ 25000]
Раздел 2: API Reference
...
[КОНЕЦ ДОКУМЕНТА]
"""
Это помогает модели осознавать структуру, даже если внимание расфокусировано.
5 Кэшируйте и переиспользуйте промежуточные результаты
Если вы один раз проанализировали документ и нашли важные разделы — сохраните эту информацию. Не заставляйте модель каждый раз читать всё с нуля.
Создайте «карту документа»:
{
"document_id": "doc_123",
"important_sections": [
{"title": "API Auth", "position": 1200, "length": 1500},
{"title": "Error Codes", "position": 8500, "length": 2000}
],
"summary": "Документация к платежному API...",
"embedding": [0.12, -0.45, ...]
}
Типичные ошибки (и как их не допускать)
Ошибка 1: Думать, что «длинный контекст = хороший контекст». Нет, длинный контекст = сложный контекст. Модель физически не может уделить равное внимание 100K токенам.
Ошибка 2: Использовать один промпт для всего. Разные типы запросов требуют разной обработки контекста. Поиск факта vs. суммаризация vs. анализ — это разные архитектурные паттерны.
Ошибка 3: Игнорировать Interpretation Drift. Lost in the Middle усугубляет дрейф интерпретации. Сегодня модель случайно сфокусировалась на нужном разделе, завтра — пропустила его.
Ошибка 4: Не учитывать стоимость. Длинный контекст стоит дорого. И если вы используете его неэффективно из-за Lost in the Middle — вы платите за токены, которые модель игнорирует.
Будущее: архитектурные изменения или вечные костыли?
Есть надежда? Отчасти.
Новые архитектуры вроде Tuneable Attention пытаются решить проблему на фундаментальном уровне. Но пока это исследования.
Практический совет: проектируйте системы с учетом Lost in the Middle как константы. Не надейтесь, что он исчезнет с новым GPT-5. Архитектура Transformer не меняется кардинально.
И помните: проблема не в том, что модели «глупые». Проблема в том, что мы используем их не по назначению. Transformer создавался для последовательностей в несколько сотен токенов. Мы растягиваем его до 100K и удивляемся, что он трескается по швам.
Лучшая система с длинным контекстом — та, которая имитирует человеческое чтение: сначала беглый просмотр, потом фокусировка на важном, потом углубление в детали. Не пытайтесь скормить всё сразу.
P.S. Если ваша LLM внезапно начала давать странные ответы из длинного контекста — проверьте, не попала ли ключевая информация в мертвую зону. Это частая причина архитектурных аномалий, которые списывают на «халлюцинации».