Векторные БД: embeddings, pgvector, Qdrant и RAG

↑ 4.3 Поиск и специализированные БД · ← Предыдущая · → Следующая

Желательно~8 мин чтенияУровень: middle

Зачем это на собесе

Семантический поиск и RAG (ответы на вопросы по документам с помощью языковых моделей) стали обычной задачей бэкенда. На собеседовании ждут, что вы объясните, что такое embedding, почему нужен приближённый поиск (ANN), когда хватит расширения pgvector и когда нужна отдельная векторная БД.

Подтемы

  • Embeddings и векторное сходство
  • Метрики расстояния
  • Приближённый поиск: HNSW и IVFFlat
  • pgvector
  • Специализированные БД: Qdrant, Milvus, Weaviate
  • RAG и гибридный поиск

Объяснение

Embedding

Embedding — числовой вектор (обычно сотни или тысячи чисел), который модель строит по тексту, изображению или звуку. Близкие по смыслу объекты получают близкие векторы: «кошка» и «собака» ближе друг к другу, чем «кошка» и «ракета». Поиск по смыслу превращается в поиск ближайших векторов.

Метрики близости

МетрикаЧто измеряетОператор pgvector
Косинусное расстояниеугол между векторами (направление)<=>
Евклидово расстояние (L2)длина отрезка между точками<->
Скалярное произведениепроекция, для нормализованных векторов эквивалентно косинусу<#> (отрицательное)

Для текстовых embeddings чаще берут косинусную метрику. Метрику выбирают так, как обучена модель (смотрите документацию модели).

Точный и приближённый поиск

Точный поиск сравнивает запрос со всеми векторами: на больших объёмах медленно. ANN (approximate nearest neighbours) использует индекс и находит почти ближайших соседей быстро, жертвуя долей точности (recall).

ИндексИдеяОсобенности
HNSWмногослойный граф «малого мира»высокий recall и скорость, больше памяти, дороже построение
IVFFlatкластеризация (списки), поиск в ближайших кластерахбыстрее строится, нужны данные для обучения кластеров, параметр probes

pgvector

Расширение PostgreSQL: тип vector, операторы расстояния и индексы HNSW/IVFFlat. Плюсы: одна база, транзакции, JOIN и фильтры по обычным колонкам.

CREATE EXTENSION IF NOT EXISTS vector;
 
CREATE TABLE docs (
  id serial PRIMARY KEY,
  title text,
  embedding vector(3)          -- в реальности размерность модели: 384, 768, 1536...
);
INSERT INTO docs (title, embedding) VALUES
  ('кошка',  '[0.9, 0.1, 0.0]'),
  ('собака', '[0.8, 0.2, 0.1]'),
  ('ракета', '[0.0, 0.1, 0.9]');
 
-- ближайшие соседи по косинусному расстоянию
SELECT title, embedding <=> '[0.85, 0.15, 0.05]' AS dist
FROM docs ORDER BY embedding <=> '[0.85, 0.15, 0.05]' LIMIT 2;
 
CREATE INDEX ON docs USING hnsw (embedding vector_cosine_ops);

Специализированные векторные БД

РешениеОсобенности
Qdrantна Rust, фильтрация по метаданным (payload), HNSW, простая эксплуатация
Milvusмасштабируемая распределённая архитектура, разные типы индексов
Weaviateвстроенные модули векторизации и гибридный поиск
Elasticsearch / OpenSearchkNN-поиск рядом с полнотекстовым

Выбирают отдельную БД, когда векторов десятки миллионов и больше, нужна быстрая фильтрация по метаданным на большом объёме или отдельное масштабирование. Для небольших и средних объёмов pgvector проще.

RAG

RAG (retrieval-augmented generation) отвечает на вопрос модели по вашим документам: из базы достаются релевантные фрагменты и подставляются в запрос к модели.

flowchart LR
    subgraph Индексация
      D["Документы"] --> CH["Нарезка на фрагменты<br/>(chunks)"] --> EM["Embedding-модель"] --> V[("Векторная БД")]
    end
    subgraph Ответ
      Q["Вопрос"] --> EQ["Embedding запроса"] --> S["Поиск ближайших фрагментов"]
      V --> S
      S --> P["Промпт: вопрос + найденные фрагменты"] --> L["Языковая модель"] --> A["Ответ"]
    end

Качество RAG зависит от нарезки (размер и перекрытие фрагментов), модели embeddings, фильтров и от гибридного поиска: векторный поиск хорошо находит смысл, а полнотекстовый (BM25) точные термины и названия. Результаты объединяют и при необходимости переранжируют.

Примеры

Запрос из .NET через Npgsql и Pgvector

using Npgsql;
using Pgvector;
 
var dsb = new NpgsqlDataSourceBuilder("Host=localhost;Database=app;Username=app;Password=secret");
dsb.UseVector();                                  // регистрирует тип vector
await using var ds = dsb.Build();
 
await using var cmd = ds.CreateCommand("SELECT title FROM docs ORDER BY embedding <=> $1 LIMIT 3");
cmd.Parameters.AddWithValue(new Vector(new float[] { 0.85f, 0.15f, 0.05f }));
 
await using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) Console.WriteLine(reader.GetString(0));

Пакет Pgvector добавляет тип Vector, для EF Core есть Pgvector.EntityFrameworkCore.

Фильтр по метаданным плюс векторный поиск

SELECT title FROM docs
WHERE category = 'faq'
ORDER BY embedding <=> $1
LIMIT 5;

С фильтром по сильно отсекающей колонке планировщик может не использовать ANN-индекс. Проверяйте EXPLAIN и пробуйте составные стратегии (партиции, отдельные индексы).

Нюансы и подводные камни

  • Размерность и модель фиксированы. Все векторы и запросы должны быть получены одной моделью; смена модели требует пересчёта всех embeddings.
  • ANN даёт приближённый результат. Подбирайте параметры (hnsw.ef_search, probes), измеряйте recall на своих данных.
  • Память. HNSW-индекс хранится в памяти, ёмкость считают заранее (число векторов × размерность × 4 байта плюс накладные расходы).
  • Обновления. Частые вставки и удаления ухудшают индекс, иногда нужна перестройка.
  • Нарезка текста сильно влияет на качество RAG: слишком длинные фрагменты размывают смысл, слишком короткие теряют контекст.
  • Безопасность. Не подмешивайте в RAG документы без проверки прав доступа: фильтруйте выдачу по правам пользователя до подстановки в промпт.
  • Не всегда нужна векторная БД. Для точных названий, артикулов и кодов лучше полнотекстовый или обычный индекс.

Вопросы с ответами

Связанные темы