Перейти к содержимому
26 авг. 2024 г.·8 мин чтения

Эмбеддинги для русскоязычного поиска: сравнение на своих данных

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

Эмбеддинги для русскоязычного поиска: сравнение на своих данных

Почему чужой лидерборд не решает вашу задачу

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

Пользователи пишут коротко и неровно. В запросах встречаются артикулы, обрывки фраз, опечатки, внутренний жаргон, названия брендов и смесь русского с английским. Запросы вроде "s23 ultra 256 серый", "1с эдо не грузит", "vpn otp не приходит" или "ошибка 0x800 agent" для продукта часто важнее любого красивого вопроса полным предложением. Если бенчмарк почти целиком состоит из чистых естественных фраз, его итоговый балл мало что говорит о вашей выдаче.

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

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

Последний пункт часто недооценивают. Команда меняет эмбеддинги, но на деле сравнивает разные индексы, chunking, фильтры и параметры ANN-поиска. Если у одной модели стоит более широкий recall, а у другой включен жесткий фильтр по метаданным, вы измеряете не качество векторов, а поведение всей системы. Поэтому честное сравнение начинается не с таблицы лидеров, а с одинакового пайплайна: один корпус, одинаковая нарезка, одинаковые фильтры и один rerank. Только после этого видно, какая модель действительно лучше работает на ваших данных.

Что должен находить поиск

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

Обычно хватает четырех сценариев:

  • Вопрос целым предложением: "как сменить base_url" или "где хранятся логи".
  • Поиск темы, а не точной фразы: "152-ФЗ", "биллинг в рублях", "prompt caching".
  • Поиск точного объекта: артикул, код ошибки, название модели, номер документа.
  • Запрос с шумом: опечатка, сокращение, русско-английская смесь, другой порядок слов.

Такое деление быстро показывает слабые места. Для русскоязычного поиска это особенно важно: в живых запросах смешиваются падежи, англицизмы, бренды, транслит и служебные слова. Если свалить все типы в одну группу, средняя метрика спрячет проблему. Модель может выглядеть приемлемо, хотя проваливает самый дорогой для вас класс запросов.

Дальше зафиксируйте, что считать хорошим ответом для каждого сценария. Для вопроса обычно достаточно, чтобы в топ-3 был документ с прямым ответом или инструкцией. Для тематического запроса полезнее набор из нескольких материалов: обзорный документ и 1-2 более узких. Для артикула, кода ошибки или названия модели планка жестче: нужный документ должен быть на первом месте. Для опечаток разумное ожидание почти такое же, как для правильного написания.

Лучше записывать это не общими словами, а простыми правилами. Не "релевантный документ", а "страница с шагами по смене endpoint" или "точная карточка ошибки 401". Тогда разметка и метрики перестают быть спором на вкус.

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

Как собрать набор запросов и документов

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

Самый полезный источник - то, что пользователи уже пытались найти. Обычно достаточно логов поиска, обращений в поддержку, названий разделов и статей, а также внутренних ссылок и якорных фраз, если они есть. Логи показывают живой язык. Поддержка помогает увидеть, что люди не нашли сами. Названия разделов закрывают очевидные формулировки из навигации.

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

Русский язык быстро разрушает красивые тесты, собранные вручную. Поэтому в выборку стоит специально добавить сложные пары: синонимы, разные склонения, аббревиатуры, латиницу и смешанное написание. Для базы знаний это могут быть пары вроде "перевыпуск карты" и "перевыпустить карту", "СБП лимит" и "лимит sbp", "1С" и "1c".

Как собрать документы

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

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

Разделите набор сразу

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

На практике удобно из 300 запросов оставить около 200 для основной оценки и 100 для финальной проверки. Если данных мало, лучше взять меньше запросов, но живых, чем собрать большой набор из примеров, придуманных в переговорке.

Как разметить релевантность без долгого проекта

Если шкала сложная, разметка быстро начинает плавать. Для первого сравнения обычно хватает трех статусов:

  • Подходит - документ отвечает на запрос прямо и без натяжки.
  • Частично подходит - тема рядом, но ответа не хватает, он слишком общий или неполный.
  • Не подходит - документ про другое или совпадает только по словам.

Дальше нужен короткий лист правил, буквально одна страница. На ней полезно зафиксировать 5-7 примеров из вашей предметной области: что считать хорошим попаданием, что считать пограничным случаем, а что сразу отправлять в "не подходит".

Рабочее правило звучит так: разметчик сравнивает запрос и документ, а не пытается оценить, насколько текст сам по себе "качественный". Если человек ищет "как перевыпустить карту", статья "как закрыть счет" не подходит, даже если в ней есть слово "карта". А материал "сроки перевыпуска карты" можно пометить как "частично подходит", если он не объясняет сам процесс.

Не берите весь корпус сразу. Гораздо полезнее размечать по 20-30 запросов в день и смотреть, где начинаются споры. Уже через несколько дней станет видно, какие формулировки правил работают, а какие нет.

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

Спорные случаи не стоит неделями разбирать в чате. Лучше собрать короткий созвон на 15 минут с владельцем продукта или человеком, который отвечает за базу знаний, пройти 5-10 конфликтных примеров, принять решение и сразу добавить его в правила.

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

Какие метрики смотреть кроме среднего балла

Сведите качество и цену
Сравнивайте выдачу, задержку и стоимость в одной рабочей таблице.

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

Если для вас дорого пропустить нужный документ, сначала смотрите Recall@k. Эта метрика показывает, как часто нужный материал вообще попадает в первые k результатов. Для базы знаний поддержки Recall@10 часто полезнее, чем красивый общий скор: если ответ не вошел даже в топ-10, ранжирование уже не спасет.

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

Есть и более приземленные метрики, про которые часто забывают: доля пустых выдач, доля плохих результатов в топ-3, средняя задержка ответа, размер индекса и цена одного запроса.

Пустая выдача сразу бьет по пользовательскому опыту. А доля плохих результатов в топ-3 показывает, как часто человек увидит мусор раньше полезного ответа. Порог лучше задать заранее. Например, можно считать плохим результатом выдачу, где в первых трех позициях нет ни одного документа со статусом "подходит" или хотя бы "частично подходит" по вашей шкале.

Технические метрики тоже влияют на выбор модели. Если модель дает чуть лучшее качество, но добавляет 300-400 мс к каждому запросу, это уже заметно в продукте. Крупный вектор может увеличить индекс в полтора-два раза. Тогда вы платите больше не только за инференс, но и за хранение, память и обновление базы.

На практике удобно смотреть на качество, скорость и стоимость в одной таблице. Если вы прогоняете несколько моделей через единый API-шлюз, такой формат быстро показывает, где модель реально лучше, а где она просто медленнее и дороже без заметной пользы.

Как провести сравнение по шагам

Если вы тестируете эмбеддинги, не пытайтесь сразу прогнать двадцать вариантов. Обычно достаточно 3-5 моделей: одна сильная универсальная, одна с хорошей поддержкой русского, одна компактная и, если нужно, еще 1-2 кандидата под ваш бюджет или задержку. Так разница видна быстрее, а шума меньше.

Сначала зафиксируйте все, что не относится к самой модели. Оставьте один и тот же индекс, одинаковый chunking документов, те же фильтры, один размер top-k и один набор запросов. Если вы меняете модель и одновременно режете документы по-новому, результат уже нельзя честно сравнить.

Рабочий порядок

  1. Возьмите один тестовый набор запросов и разбейте его по типам: короткие, длинные, с опечатками, с терминами предметной области, с точным названием документа.
  2. Выберите 3-5 моделей-кандидатов и постройте для каждой одинаковый индекс.
  3. Прогоните каждый запрос через каждую модель в одних и тех же условиях.
  4. Сведите результаты в одну таблицу по сегментам запросов, а не только в общий средний балл.
  5. Отдельно разберите руками 20-30 запросов, где модели дали самый разный top-10.

Таблица должна быть простой. Обычно хватает столбцов: модель, Recall@5, Recall@10, MRR, средняя задержка, цена на 1000 запросов и качество по сегментам. Общий средний балл полезен, но почти всегда что-то прячет. Модель может хорошо искать по общим фразам и проваливаться на внутренних кодах, названиях тарифов или юридических терминах.

Ручная проверка нужна всегда. Возьмите запросы, где одна модель нашла нужный документ на 1-3 месте, а другая подняла наверх похожий, но бесполезный текст. На таких примерах быстро видно, что именно ловит модель: смысл, точный термин или просто общий контекст.

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

Если вы меняете модели через единый OpenAI-совместимый endpoint, как в RU LLM, такой тест проще организовать без правок в клиентском коде. Это удобно, но важнее другое: все кандидаты проходят через одинаковую среду, и сравнение получается чище.

Пример для базы знаний на русском

Запустите честный пилот поиска
Держите один корпус и один rerank, а модели меняйте через RU LLM.

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

У вас есть статья "Проблемы с SMS-подтверждением", а в реальных запросах люди вводят "не приходит код", "смс не дошла" или "не могу подтвердить вход". Модель, которая хорошо держит смысл, поднимет нужную статью в топ даже без прямого совпадения слов. Более слабая модель часто цепляется за текстовое сходство и отдает соседний материал, где просто много раз встречается слово "код".

Та же проверка полезна на брендах. Один пользователь пишет "Acme Pay", другой - "Акми Пэй". Если модель плохо переносит латиницу, кириллицу и смешанное написание, поиск ломается на обычных сценариях. На тесте это видно сразу: один и тот же документ то на первом месте, то уезжает далеко вниз только из-за способа записи названия.

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

Полезно проверить хотя бы такие пары:

  • "не приходит код" -> статья "Проблемы с SMS-подтверждением"
  • "Acme Pay не открывается" -> статья "Сбой входа в Акми Пэй"
  • "как вернуть деньги за подписку" -> длинная инструкция по возврату
  • "ошибка 403 в личном кабинете" -> короткая заметка с точным ответом

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

Если вы прогоняете сравнение через единый API-шлюз, можно быстро протестировать несколько моделей без смены SDK и кода. Но сам принцип важнее инструмента: один набор запросов, одна индексация, один top-k. Только тогда сравнение показывает не "лучшую модель вообще", а ту, которая лучше ищет по вашей базе знаний.

Где команды чаще ошибаются

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

Хороший тест держит почти все постоянным. Меняйте один фактор за раз, иначе выводы будут случайными.

Рабочее правило простое:

  • сначала фиксируйте набор запросов и документов;
  • потом фиксируйте chunking;
  • затем меняйте только модель;
  • после этого отдельно проверяйте другой chunking;
  • в конце сравнивайте связки целиком.

Вторая ошибка менее заметна. Команда смотрит на один средний балл и успокаивается. Но среднее легко прячет провал в важном сегменте: например, поиск хорошо работает по длинным вопросам из базы знаний, но сыпется на коротких запросах вроде "лимит по карте" или "сброс 2FA".

Поэтому режьте тест хотя бы на несколько групп: короткие запросы, длинные запросы, опечатки, внутренний жаргон, названия продуктов. Для банка, ритейла или телеком-команды это часто важнее, чем разница в общей метрике на 1-2 пункта.

Еще одна частая проблема - слишком чистый тест. В нем нет опечаток, сокращений, старых названий сервисов и разговорных формулировок. В жизни люди пишут "дебетова карта", "лич каб", "возврат по sberpay" и другие неровные запросы. Если такого слоя нет в наборе, вы измеряете красивую демо-версию поиска, а не продукт.

Многие оценивают retrieval без фильтров, хотя в реальной системе они почти всегда включены. Это ломает картину. Если в продукте поиск идет только по нужному региону, типу документа, роли пользователя или дате, тест должен повторять те же условия. Иначе модель может казаться сильной только потому, что ей дали искать по всему корпусу.

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

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

Быстрый чек перед запуском

Переведите пилот в прод
Используйте тот же совместимый эндпоинт, когда тест закончится и пойдет живой трафик.

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

Перед релизом полезно ответить на четыре вопроса:

  • Есть ли у вас независимый набор, который не участвовал в настройке?
  • Попали ли в него и частые запросы, и редкие ошибки с высокой ценой?
  • Видно ли по результатам, где именно модель слаба?
  • Сможете ли вы после релиза считать качество по тем же правилам?

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

Отдельно проверьте, понимаете ли вы тип проигрыша. Иногда модель не ловит синонимы, и "расторгнуть договор" оказывается далеко от "закрыть договор". Иногда она ломается на артикулах, кодах и названиях тарифов. Иногда теряет смысл в длинных документах, где ответ спрятан в середине страницы. Для этих провалов нужны разные решения: другая модель, другой chunking, гибридный поиск или нормализация справочников.

Еще один признак готовности - одинаковые правила до и после запуска. Если до релиза вы мерили Recall@10 и долю запросов без ответа, а после релиза смотрите только клики, вы не поймете, поиск стал лучше или просто изменилось поведение пользователей. Зафиксируйте набор метрик, порог качества и способ разметки заранее.

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

Что делать после пилота

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

На живом трафике

Рабочий порядок простой: оставьте старую схему как контроль, а победившую модель пустите на 10-20% запросов. Смотрите не на один общий балл, а на несколько сигналов: долю кликов по первым результатам, долю переформулированных запросов, время до полезного документа, долю запросов, после которых пользователь уходит без действия, и число ручных жалоб от саппорта или внутренних пользователей.

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

После запуска не закрывайте тему на квартал. Собирайте все провальные запросы: пустые результаты, странные попадания в топ-3, частые переформулировки, ручные обходы через теги и фильтры. Раз в месяц добавляйте их в тестовый набор. Так вы не теряете связь с реальной речью пользователей, которая меняется быстрее, чем кажется.

Если поиск связан с LLM

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

Для такого контура часто используют единый OpenAI-совместимый эндпоинт, чтобы не переписывать SDK и маршруты между моделями. В российском контуре это можно собрать через RU LLM: команды меняют base_url на api.rullm.com и продолжают использовать тот же код, а логи, бэкапы и аудит остаются внутри РФ. Но сам выбор эмбеддингов это не отменяет. Его все равно нужно проверять на своих запросах, своей базе и своих ошибках.