Большинство команд по-прежнему собирают свой первый конвейер поиска (retrieval pipeline) одинаково. Они выбирают фиксированный лимит токенов, скажем, 512, разбивают документы на однородные блоки и загружают эти блоки в векторную базу данных. На небольшом наборе данных с простыми вопросами это кажется магией. В продакшене всё разваливается.

Юридические контракты рассыпаются на бессмысленные фрагменты, когда пункт договора разрезается на середине предложения. Документация API превращается в «шумный суп», если один чанк поглощает три несвязанных функции. Тикеты службы поддержки теряют логическую нить без перекрытия (overlap) между сегментами. Результат предсказуем: раздутая задержка, низкая полнота (recall) и ответы, которые заставляют генератор галлюцинировать.

Мы полностью перестроили наш слой поиска. Результатом стал скачок полноты с 78% до 95%, сокращение задержки на 62% и конвейер, который наконец-то ведет себя как настоящая инфраструктура, а не как костыль, собранный на выходных. Вот что действительно сработало.

Умное разбиение: структура важнее токенов

Первая ошибка — считать, что все документы написаны на одном языке. Чанк в 512 токенов имеет смысл для повествовательной прозы и почти нигде больше. Мы перешли к стратегии, которая учитывает анатомию источника.

Для юридических документов мы используем рекурсивное разбиение (recursive chunking). Алгоритм сначала пытается разделить текст по высокоуровневым границам, таким как разделы и статьи. Если раздел всё еще слишком длинный, он ищет подразделы, затем абзацы, а затем предложения. Это сохраняет логическую вложенность пунктов. Соглашение о неконкуренции остается цельным. Определения не смешиваются с условиями возмещения ущерба.

Документация API требует разбиения с учетом структуры (structure-aware chunking). Сигнатура функции, таблица её параметров и пример запроса должны быть вместе. Разбиение по фиксированному количеству токенов часто оставляет параметры в одном чанке, а примеры — в другом. Вместо этого мы разбиваем по объектам документа. Один чанк содержит законченный эндпоинт или одну функцию. Тогда ретривер может вернуть самодостаточную ссылку, которая действительно отвечает на вопрос.

Тикеты поддержки естественным образом подходят для семантического разбиения (semantic chunking). Вместо того чтобы резать текст по границе токенов, мы определяем, где меняется тема. Тикет, который начинается с жалобы на вход в систему, а затем переходит к вопросу об оплате, разбивается на две связные части. Каждая часть несет необходимые ей метаданные, и модели больше не нужно угадывать, какая именно проблема действительно волнует пользователя.

Внутренние вики — более хаотичны. В них смешаны текст, таблицы, диаграммы и встроенные ветки обсуждений. Для них мы используем агентское разбиение (agentic chunking). Маленькая языковая модель читает текст наперед и решает, где заканчивается тематически завершенная единица. Это стоит немного дороже на этапе загрузки данных, но избавляет от рутинной работы по ручной настройке правил для каждого нового формата страниц.

Гибридный поиск: охватывайте все стороны

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

BM25, классический алгоритм поиска по ключевым словам, делает ровно наоборот. Он точно находит строки и редкие термины, но упускает семантические нюансы. Запрос о подписании соглашения может никогда не выдать контент, помеченный тегом «исполнение контракта».