Как я переехал на новую модель эмбеддингов и не уронил поиск
Понедельник, 09:47. В чат техподдержки пишет продажник: «Поиск по клиентам отвалился. Ищу поставщика гидравлики из Самары — отдаёт стоматологов и автосервисы». Я открываю мониторинг и вижу: за выходные выкатили новую модель, recall@5 на синтетике вырос, а на проде — полный кошмар.
Это классическая история миграции эмбеддингов. Тесты врут, пользователи ругаются, а индекс на полтора миллиона документов просто продолжает жить по-старому. Расскажу, как я теперь провожу такие переезды.
Зачем вообще менять модель
Эмбеддинги в проде живут годами. У меня был клиент, который два года сидел на paraphrase-multilingual-MiniLM-L12-v2. Потом понадобился поиск по техописаниям с цифрами и по смешанному русско-английскому тексту. Старая модель начала проседать.
Обычно причины три: качество, лицензия/импортозамещение или размер вектора. Но главное — эмбеддинги разных моделей живут в разных пространствах. Косинус между вектором от модели А и модели Б — это просто число, а не осмысленное расстояние.
Почему «выключить и переиндексировать» — плохая идея
Самый простой план — ночью пересчитать все векторы и перестроить индекс. На практике у меня это заняло 4+ часа, куча ошибок парсинга и race condition: пока шёл пересчёт, новые документы продолжали индексироваться старой моделью и потом затирались.
После такого инцидента я выработал правило: никогда не перезаписывать эмбеддинги на месте.
Двухколоночная схема
Теперь я всегда добавляю новую колонку:
ALTER TABLE documents
ADD COLUMN embedding_v2 vector(1024),
ADD COLUMN embedding_v2_model text;
Старая колонка остаётся в покое. Новый воркер фоном заполняет embedding_v2. Все свежие документы сразу пишутся в обе колонки. Да, на время миграции расход на эмбеддинги растёт вдвое, но это плата за спокойствие.
Теневой режим вместо синтетики
Перед переключением я запускаю shadow-трафик на две недели. Каждый запрос уходит и в старую, и в новую колонку. Логирую overlap@5, recall по реальным кликам пользователя и latency.
На одном проекте синтетика обещала +13 пунктов, а по реальным данным получилось +5, при этом overlap был всего 0.41. Это сразу показало: просто так кнопку жать нельзя.
Постепенный rollout и откат
Переключение делаю через конфиг и rollout по процентам пользователей. На каждом этапе смотрю метрики и количество обращений в поддержку. Откат — это просто поменять цифру в конфиге, без redeploy.
Старую колонку держу ещё месяц-два на всякий случай.
Что я вынес
Эмбеддинги — это не просто параметр, а актив в базе. К нему нужно относиться как к схеме: версионировать, мигрировать постепенно и всегда иметь откат. Три недели работы и +30–40 % расходов на время миграции, но зато не приходится объяснять продажникам, почему поиск внезапно стал хуже.
