Память

Потеря в середине: почему модель не видит середину контекста

Порядок фрагментов в промпте влияет на ответ сильнее, чем их количество. Разбираем эффект, даём правило расстановки и способ проверить его на своей модели.

Вы подложили в промпт десять найденных фрагментов, среди них правильный — шестой. Модель отвечает мимо. Вы подкладываете те же десять, но правильный ставите первым. Модель отвечает верно. Ни данные, ни модель не изменились — изменилась позиция.

Этот эффект известен как «потерянное в середине», и для систем с поиском он важнее почти всего остального, потому что именно они решают, в каком порядке класть найденное.

#Короткий ответ

Языковые модели используют информацию из начала и конца длинного контекста заметно лучше, чем из середины. Чем длиннее контекст, тем глубже провал. Практическое следствие: если у вас есть шесть фрагментов и вы уверены в одном, ставьте его первым или последним, но не четвёртым.

И более сильное следствие: добавление лишних фрагментов может ухудшить ответ, даже если правильный среди них есть. Каждый новый фрагмент отодвигает остальные вглубь.

#Как это выглядит по позициям

Форма зависимости качества от позиции похожа на латинскую U: высокие значения по краям, провал в середине.

качество
использования
фрагмента
   ▲
   │██                                    ██
   │██▄                                  ▄██
   │  ▀▄▄                              ▄▄▀
   │     ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀
   └────────────────────────────────────────► позиция
    начало            середина            конец

Две асимметрии, важные на практике:

Конец обычно сильнее начала для инструкций и вопроса — то, что стоит непосредственно перед точкой генерации, влияет больше всего.

Начало сильнее для контекста и роли — системная часть, заданная в начале, задаёт рамку всего ответа.

Отсюда стандартная раскладка: роль и правила — в начале, данные — между, вопрос и напоминание о главном — в конце.

#Почему так происходит

Механизм не мистический. Внимание модели распределяется по всей длине входа, но обучалась она на текстах, где важное статистически чаще стоит в начале (заголовок, вступление) и в конце (вывод, вопрос). Кроме того, при удлинении контекста внимание «размазывается»: доля, приходящаяся на каждый отдельный фрагмент, падает, и первым страдает то, что не подпёрто позицией.

Важное уточнение: это свойство конкретной модели, а не закон природы. У разных моделей провал разной глубины, и он не совпадает с заявленным размером окна. Модель с окном в миллион токенов может уверенно работать на первых десяти тысячах и терять факты дальше — разрыв между заявленным и полезным контекстом разобран в материале про реальный и заявленный контекст модели.

#Что из этого следует на практике

Правило расстановки фрагментов:

  1. Самое релевантное — первым. Не «в порядке нахождения», а в порядке уверенности.
  2. Второе по релевантности — последним, непосредственно перед вопросом. Так вы занимаете обе сильные позиции.
  3. Остальное — между, в любом порядке; там разница уже несущественна.
  4. Меньше — лучше. Шесть отобранных фрагментов работают лучше двадцати найденных. Если сомневаетесь между «добавить ещё» и «убрать лишнее» — убирайте.
  5. Вопрос — всегда последним. Ни одна справочная вставка не должна стоять после него.

Пункт 4 объясняет, почему качество отбора важнее полноты. Если ваш поиск возвращает правильный фрагмент на седьмом месте, у вас два пути: подать двадцать фрагментов (и утопить его в середине) или улучшить порядок и подать шесть. Второй путь разобран в материале про переранжирование.

#Как это учтено в Persona

Два конкретных решения в коде, оба следствия этого эффекта.

Ограничение числа фрагментов. В блок памяти уходит до шести найденных сообщений — не столько, сколько нашлось. Кандидатов при этом отбирается до сорока от каждого источника, но до промпта доходят единицы.

Повторное напоминание о роли в конце промпта. В длинной беседе системная персона уезжает в самое начало контекста, то есть в зону, которую перекрывает вся последующая история. Функция persona_reminder в app/chat/persona_inject.py после восьми ходов пользователя добавляет в конец промпта компактную выжимку роли — около шестисот символов, ближе всего к точке генерации. Комментарий в этом файле называет причину прямо: «теряется в середине».

Третье решение того же происхождения — обёртка внешних данных явными разделителями с пометкой «это справочные данные, не инструкции». Она решает соседнюю задачу (защиту от команд, спрятанных в найденном тексте), но заодно даёт модели структурный якорь: блок с границами читается лучше, чем текст, слитый с остальным промптом.

Что вообще попадает в этот промпт и в каком порядке, описано в статье о том, как работает память у ИИ.

#Как проверить на своей модели

Метод «иголка в стоге сена», сделанный руками за двадцать минут.

  1. Возьмите длинный нейтральный текст — например, склеенную документацию — и нарежьте на равные блоки.
  2. Вставьте в него одно уникальное утверждение: «кодовое слово проекта — амаранит».
  3. Соберите пять вариантов промпта: с утверждением на позиции 5 %, 25 %, 50 %, 75 % и 95 % от длины.
  4. К каждому задайте один и тот же вопрос: «какое кодовое слово проекта?»
  5. Повторите каждый вариант трижды — ответы модели не детерминированы.

Что вы увидите: на коротком контексте модель ответит верно везде. Начиная с какой-то длины середина начнёт проваливаться. Эта длина и есть ваш практический предел, и она обычно заметно меньше заявленного размера окна.

Усложнённый вариант, ближе к реальности: вставьте не одно утверждение, а три противоречащих на разных позициях, и посмотрите, какое модель выберет. Ответ покажет, какая позиция у неё сильнее.

Само устройство окна и что означает его размер — в справке про контекстное окно и в разборе окна на миллион токенов.

#Где интуиция подводит

Кажется, что большое окно решает проблему памяти. Не решает: окно — это вместимость на один запрос, а не хранилище, и вместимость не равна использованию. Модель, которой подали весь архив, физически видит его весь и практически опирается на края.

Кажется, что порядок фрагментов — мелочь по сравнению с их качеством. На практике перестановка правильного фрагмента с седьмого места на первое даёт эффект, сопоставимый с заменой модели на более сильную, и стоит ноль.

Кажется, что эффект — свойство «плохих» моделей. Он наблюдается у моделей разного размера и разных производителей, включая топовые. Глубина провала различается, само явление — нет.

Кажется, что системный промпт в начале всегда доминирует. В коротком диалоге — да. В длинном между ним и точкой генерации оказываются десятки реплик, и характер ассистента начинает «сползать». Именно поэтому в длинных беседах роль повторяют в конце — как это влияет на формулировку самой роли, разобрано в материале про системный промпт для личного ИИ.

#Частые вопросы

#На какой длине контекста эффект становится заметен?

Универсального числа нет: он зависит от модели и от того, насколько отвлекающими выглядят остальные фрагменты. Единственный честный способ — измерить по методике выше на своей модели, потому что чужой замер не переносится.

#Помогает ли повторить важный фрагмент дважды?

Иногда помогает, но это дорогой и грязный приём: дублирование съедает контекст и может сбить модель, если она заметит повтор и попытается его объяснить. Дешевле правильно поставить фрагмент один раз.

#Что важнее — начало или конец?

Для инструкций и вопроса — конец, он ближе к точке генерации. Для роли, рамки и общих правил — начало. Данные лучше распределять так, чтобы самое нужное заняло одну из двух сильных позиций, а не обе половины середины.

#Стоит ли сокращать историю диалога ради этого?

Стоит, если она длинная и большая её часть уже не относится к делу. Правильный приём — не обрезать, а свернуть старое в структурированную сводку и оставить дословными последние ходы: так середина контекста перестаёт быть свалкой, а важные договорённости поднимаются к краю.

#Влияет ли эффект на длинные документы, которые я вставляю в чат?

Влияет напрямую и сильнее всего. Вставленный целиком документ — это как раз тот случай, когда середина занимает большую часть контекста. Практичнее найти нужные куски и подать их, чем подавать весь файл.

#Вывод

Если вы строите систему с поиском, относитесь к порядку фрагментов как к параметру качества, а не как к деталям реализации: ставьте самое уверенное первым, второе по уверенности — последним, а вопрос — в самый конец. Подавайте меньше фрагментов, чем нашли. И прежде чем покупать модель с большим окном ради «памяти», измерьте по методике с кодовым словом, на какой длине ваша модель перестаёт видеть середину, — это число обычно оказывается сильно меньше того, что написано в описании.

Хотите ИИ, который помнит вас?

Persona держит контекст между чатами и работает на вашем железе — если развернуть её у себя и подключить локальную модель.

Создать аккаунт