Чаще всего поиск для RAG делают в один проход: у каждого фрагмента лежит один вектор, запрос превращается в такой же вектор, база отдаёт ближайшие. Это быстро, потому что представления фрагментов посчитаны заранее и лежат в базе статикой - на запрос считается только вопрос. Когда качества такого поиска не хватает, сверху обычно ставят реранкер: кросс-энкодер или внешний API, который заново читает пары "вопрос + фрагмент". Порядок он правит хорошо, но платить приходится вызовом модели на каждый кандидат, и задержка растёт вместе с их числом.

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

Мы решили выяснить, окупается ли этот второй проход. Короткий ответ: на нашем корпусе он вдвое повышает точность первого места - нужный фрагмент оказывается первым в 73% случаев вместо 36%. Длинный ответ интереснее: по дороге выяснилось, что мы неправильно вызывали модель, и увидеть это удалось только потому, что рядом считалась базовая линия.

https://iconicompany.com/ru/research/late-interaction-measured-maxsim-doubles-hit-rate