База знаний, которая действительно ищется
Почему семантический поиск не спасает плохо устроенную базу: где теряется качество, что делает нарезка, зачем нужен реранкер и как измерять, стало ли лучше.
Типичная история: команда складывает документы в векторную базу, подключает семантический поиск и обнаруживает, что находится всё, кроме нужного. Дальше обычно меняют модель эмбеддингов, потом ещё одну, потом решают, что RAG переоценён.
Проблема почти никогда не в эмбеддингах. Проблема в том, что документы писались как отчёты, а ищут в них как в справочнике.
Где именно теряется качество
Полезно разложить путь от вопроса до ответа на участки и посмотреть, на каком из них уходит больше всего.
Это главное свойство схемы: ошибки первых шагов необратимы. Генератор работает с тем, что ему принесли. Если нужный фрагмент не попал в выдачу, модель либо ответит по памяти, либо честно скажет, что не нашла, — и оба исхода одинаково бесполезны.
Отсюда практическое следствие, которое противоречит порядку, в котором обычно тратят силы: улучшать надо слева направо, а не справа налево.
Единица знания — не документ
Документ — это контейнер. Единица знания — утверждение, которое можно применить: при таком условии делаем так, потому что вот это. Один документ может содержать одну такую единицу, а может ни одной.
Отсюда правило, которое меняет больше всего: одна заметка — один факт, и заголовок формулирует сам факт, а не тему. Не «Про кеширование», а «Кеш промптов живёт час, если сессия активна».
Это не про красоту. Заголовок-факт попадает в эмбеддинг куска и в его начало, а начало куска весит в поиске непропорционально много. Заголовок «Про кеширование» не отличим от сорока других заголовков «Про X».
Нарезка: главная точка потери
Резать по фиксированному числу токенов — самый распространённый способ испортить базу, и он выглядит технично, поэтому держится долго.
Что с ним не так, если разобрать по шагам. Граница проходит где попало: условие остаётся в одном куске, действие — в другом, и ни один из них не отвечает на вопрос. Кусок теряет контекст: в тексте было «в этом случае», и «этот случай» описан абзацем выше, которого в куске нет. Заголовок раздела, который и объяснял, о чём вообще речь, остаётся в предыдущем куске.
Что делать вместо:
Резать по структуре. Раздел, подраздел, абзац, элемент списка. Границы уже расставлены автором — он делал это осмысленно.
Тащить заголовки-родители в каждый кусок. Кусок должен начинаться с цепочки «Документ → Раздел → Подраздел», даже если в исходнике этого текста нет. Приём дешёвый и даёт заметный прирост, потому что чинит ровно ту проблему, из-за которой кусок был непонятен вне своего места.
Держать перекрытие небольшим. Перекрытие — это компенсация плохих границ. Если границы хорошие, много перекрытия не нужно, а оно раздувает индекс и плодит почти одинаковые кандидаты, которые вытесняют из выдачи разнообразие.
Гибридный поиск, а не только векторы
Чисто векторный поиск плохо работает там, где важна точная форма: номера
артикулов, названия функций, коды ошибок, редкие термины. Эмбеддинг по
своей природе усредняет, и ERR_2041 для него похож на ERR_2043.
Поэтому в базе, где есть идентификаторы, векторный поиск нужно складывать с обычным полнотекстовым. Способ объединения вторичен: важно, чтобы точное совпадение по редкому токену вообще имело шанс попасть в выдачу.
Проверить, нужен ли гибрид, легко: возьмите двадцать вопросов и посмотрите, в скольких из них есть слово, которое встречается в базе меньше десяти раз. Если таких больше трети — чисто векторный поиск будет вас подводить.
Реранкер
Первичный поиск должен быть быстрым и щедрым: достаём пятьдесят кандидатов любым способом. Потом отдельная модель — та, что смотрит на пару «вопрос + кусок» целиком, а не на два независимых вектора, — выбирает из них пять.
Дешёвый поиск плюс дорогая проверка почти всегда бьёт дорогой поиск сам по себе, и по той же причине, что и в контурах: проверка обходится дешевле выполнения, если её область сужена заранее.
Условие, при котором я бы не стал добавлять реранкер: если у вас меньше нескольких сотен документов и вопросы к ним однотипные. На такой базе первичный поиск и так возвращает нужное в первой тройке, а реранкер добавит задержку и статью расходов без заметной пользы.
Три поля, которые окупаются
К каждой заметке стоит добавлять:
- когда применимо — условие, при котором факт верен;
- почему так — причина, чтобы факт можно было пересмотреть;
- что связано — ссылки на соседние заметки.
Третье поле превращает базу в граф. Именно связи, а не поиск, отвечают на вопрос «что ещё мне нужно знать про это», и именно их не умеет восстанавливать никакой ретривер: связь — это решение автора, а не свойство текста.
Второе поле нужно для устаревания. Факты протухают, и заметка без причины через полгода становится источником ошибок — особенно опасным, потому что выглядит уверенно. Если у факта нет причины, его нельзя обновить, можно только удалить.
Как понять, что стало лучше
«На глаз стало лучше» — не метрика, и это главная организационная проблема таких проектов, а не техническая.
Минимум, который я считаю обязательным: набор из пятидесяти-ста реальных
вопросов, для каждого — какие куски базы обязаны попасть в выдачу. Дальше
считается доля вопросов, где нужный кусок оказался среди первых k. Это
дёшево, автоматизируется и меряет ровно тот участок, где теряется качество.
Тонкость, о которую спотыкаются: размечать надо куски, а не документы. Разметка «ответ есть в документе N» не отличает базу, где нужный абзац находится третьим, от базы, где он находится сороковым, — а это разница между работающей системой и неработающей.
Отдельно измерять качество финального ответа тоже нужно, но позже и другим способом. Смешивать эти две метрики в одну — верный путь к тому, что вы никогда не узнаете, что именно улучшили.
Что я бы не стал делать
Начинать с выбора векторной базы. Это последнее решение, и на объёмах меньше миллиона кусков разница между вариантами не видна за шумом остальных проблем.
Строить граф знаний автоматически из текста на старте. Извлечённые автоматически связи шумны, а чинить их дороже, чем проставить руками те двадцать связей, которые действительно нужны.
Ждать полноты. База, покрывающая три частых сценария и честно молчащая на остальных, полезнее базы, которая покрывает всё и на половине вопросов отвечает правдоподобно и неверно.