Vector Store Persistence

Сохранение и перенос векторного индекса между машинами без повторной векторизации. Ключевая идея: индексация — тяжёлая offline-операция, инференс — лёгкая online. Строим индекс один раз на мощной машине/в облаке, сохраняем на диск, переносим и переиспользуем.

Суть

Превращение корпуса документов в векторы — самая долгая операция конвейера RAG: даже индекс для документа в 3 строки строится ~3.5 секунды на неслабом ноутбуке. Делать это каждый запуск нелогично — индекс надо персистить.

Зачем это нужно

Логика разделения нагрузки: поднять модель эмбеддингов на мощном узле (например, spark-кластере), загрузить корпус на 1000 страниц, преобразовать в векторное представление один раз → сохранить → потом инференс (поиск) гонять хоть на ноутбуке с CPU, без GPU.

Как работает

  • Chroma: параметр persist_directory указывает, куда сохранить базу на диск; потом она поднимается из этой папки без переиндексации.
  • FAISS: объект индекса сохраняется на диск и подгружается обратно (save/load).
  • Embedding-модель тоже кэшируется на диск (~100-150 МБ) — чтобы не перекачивать каждый раз; она «несёт на себе» преобразование запроса в то же пространство, в котором построен индекс.
  • Критическое условие совместимости: индекс и запросы должны использовать одну и ту же модель эмбеддингов (Embeddings) — иначе пространства не совпадут и поиск сломается.
  • Retrieve как функция: retriever = f(vector_store, query) — similarity_search сам переносит короткий запрос в пространство эмбеддингов и достаёт похожие чанки.
  • Для прототипа и переноса между компьютерами проще всего FAISS и Chroma (in-memory + persist); Qdrant требует развёртывания как сервис/облако (см. Vector Databases).
  • Model-lock: индекс «привязан» к модели эмбеддингов — при смене модели старые векторы геометрически несовместимы с новыми запросами → нужна полная переиндексация корпуса. Выбор модели = как миграция схемы БД: дорого откатывать.
  • Zero-downtime миграция (alias-based): строим «теневой» индекс новой моделью параллельно → валидируем на бенчмарк-наборе → атомарно переключаем alias → старый держим для отката. Запрос никогда не видит частичный индекс.
  • Инкрементальное обновление vs пересборка: чтобы не переэмбеддить всё, используют content-hashing (хеш чанка совпал → пропускаем) и upsert с полем valid_from (MVCC-стиль: стейджим контент до публикации). Полная пересборка нужна только при смене модели.
  • Гигиена метаданных: в каждом чанке держим doc_id, заголовок секции, timestamp, версию модели эмбеддингов (детект дрейфа) и content hash; для удаления — Document Registry (doc_id → chunk_ids), т.к. векторные БД не делают нативный update (найти chunk_ids → удалить → вставить новые).

Пример

FAISS: построить индекс один раз → сохранить на диск → поднять без переиндексации. При загрузке передаётся та же модель эмбеддингов (иначе пространства не совпадут).

vs_faiss = FAISS.from_documents(chunks, embedding=embeddings)
vs_faiss.save_local("./faiss_index")            # тяжёлая offline-операция — один раз

store = FAISS.load_local(
    "./faiss_index",
    embeddings,                                  # та же модель эмбеддингов!
    allow_dangerous_deserialization=True,        # индекс хранится через pickle
)
store.similarity_search("отпуск", k=5)           # лёгкий online-инференс, хоть на CPU

Связано с

  • Vector Databases — какие БД умеют persist (Chroma, FAISS) vs требуют сервиса (Qdrant)
  • RAG — индексация (offline) vs поиск (online) в конвейере
  • Embeddings — совместимость индекса и запроса = одна модель
  • Local LLM Deployment — построить индекс в облаке/Colab, перенести на локаль