Почему RAG не находит ответ в конце длинного документа

В корпоративной инструкции есть раздел о возврате товара после 30 дней. Сотрудник вводит точный вопрос, но RAG находит общие правила возврата из начала документа. Открываете исходный PDF: нужный абзац есть. Проверяете базу: документ загружен и его вектор хранится. Отсутствие ошибки индексации выглядит загадочно, пока не выясняется, что вектор построен по тексту, который модель эмбеддингов успела прочитать до своего предела длины.

Куда исчезает хвост

У модели эмбеддингов есть максимальная длина входной последовательности. Например, у Sentence Transformers её показывает model.max_seq_length; более длинный текст обрезается до первых токенов. Если вы отправили целую страницу или раздел как один «чанк», в базе может остаться строка с полным текстом, но её вектор описывает только начало. При поиске по позднему абзацу расстояние между вопросом и таким вектором не отражает содержание хвоста. Поэтому сама строка в таблице ещё не доказывает, что все её слова участвовали в построении эмбеддинга.

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

Для диагностики полезно выписать одну строку таблицы чанков целиком: chunk_id, первые и последние 200 символов, фактическое число токенов и max_seq_length. Если последние 200 символов содержат правило возврата, но число токенов в несколько раз выше максимума, гипотеза становится проверяемой. Возьмите короткий запрос, составленный по началу чанка, и второй по хвосту. Сравнение позиций в поиске покажет эффект усечения, хотя окончательное решение всё равно нужно оценивать на наборе вопросов, а не на одной паре.

Исправление на стороне индексации

Разбивайте исходник на фрагменты до вызова модели эмбеддингов. Оставьте запас под специальные токены tokenizer и заголовок, если он добавляется к тексту фрагмента. Режьте по предложениям или смысловым абзацам, затем проверяйте длину уже готовой строки для encoder. Если одна таблица или одно предложение длиннее лимита, разбейте и их с небольшим перекрытием, чтобы условие в конце первой части не потеряло объект из начала следующей.

У каждого нового фрагмента сохраняйте document_id, страницу, номер фрагмента и ссылку на исходный участок текста. Эти поля нужны не для эмбеддинга, а для будущей цитаты и обновления документа. При переиндексации используйте новую версию набора чанков: удалить старые векторы и добавить новые следует так, чтобы пользователь не видел смесь двух схем нарезки. Если система переключает активную версию индекса, подготовьте новую целиком и переведите поисковый запрос на неё после проверки.

Оцените исправление на вопросах, где правильный ответ находится в начале, середине и конце длинных документов. Для каждого вопроса заранее запишите ID фрагмента с ответом. Сравните, появился ли он в top-k до и после нарезки; это проверяет поиск, а не красоту итогового текста модели. Затем уже проверьте, что ответ ссылается на верный абзац. На одной удачной инструкции останавливаться рано: слишком мелкие чанки могут потерять связный контекст и ухудшить соседние вопросы.

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

Почему увеличение top-k не лечит причину

Когда вектор не содержит смысла хвоста, запрос может не ранжировать его высоко даже при большем числе кандидатов. Увеличение top-k расширит контекст и расходы, но оставит дефект индекса. Сначала добейтесь, чтобы нужный абзац получил собственный вектор; затем настройте число кандидатов и реранжирование по проверочному набору. Важное соседнее расследование — границы разделов при нарезке: там проблема возникает из-за разрыва смысла между фрагментами, здесь — из-за скрытого усечения внутри одного фрагмента.

В платном курсе Vector DB & RAG Developer вы строите индекс, настраиваете поиск с фильтрами и измеряете Recall@K и nDCG, затем подключаете найденные фрагменты к RAG-боту с цитатами. Именно измерение на вопросах из разных частей документов покажет, помогла ли новая нарезка. Откройте программу курса, чтобы пройти путь от эмбеддингов до проверяемого ответа на своих данных.

Продолжить на практике

  • Vector DB & RAG Developer ↗

    Практический курс по векторным базам данных и семантическому поиску. Вы развернёте Qdrant, Weaviate и FAISS, научитесь индексировать миллионы эмбеддингов, строить k-NN-поиск с фильтрами, объединять его с BM-25 и создавать RAG-ботов, которые отвечают на вопросы на основе ваших данных.