Представьте медсервис, где AI ассистент отвечает не общими фразами, а ссылками на конкретные клинические рекомендации, выписки и результаты анализов. Он не «угадывает», а быстро находит релевантные фрагменты среди тысяч страниц.
Секрет — в векторах. А аккуратным «чемоданом» для них становится расширение pgvector.
Зачем это вообще нужно
Обычный поиск по словам («болит голова») часто промазывает: термины могут отличаться, формулировки — тоже. Векторный поиск работает по смыслу: «головная боль после физнагрузки у подростка» окажется близок к «эксерционная цефалгия в подростковом возрасте». Для ассистента это критично. Он сначала подбирает контекст, а уже потом генерирует ответ — тот самый RAG.
Вектора на пальцах
Мы превращаем текст в точку в многомерном пространстве — эмбеддинг. Похожие тексты → близкие точки.
Дальше остаётся: хранить, быстро искать ближайшие точки и возвращать оригинальные кусочки текста.
Почему именно PostgreSQL
- Одна база, меньше зоопарка. Документы, пользователи, права, биллинг и векторы — всё рядом.
- ACID и бэкапы. Те же привычные транзакции, реплики, восстановление.
- Прозрачные права. Легко разделять данные по проектам и пользователям.
- Стоимость. Часто дешевле отдельных «векторных» движков, особенно в начале.
Что даёт pgvector
- Поле для «смыслового отпечатка» текста.
В таблице появляется колонка vector(...) — в неё мы кладём числовой «отпечаток смысла» фрагмента текста. Думаете о нём как о компактном резюме содержания в цифрах. - Поиск «по смыслу», а не по словам.
Есть операции сравнения, которые понимают, насколько два отпечатка похожи. Итог: «мигрень после нагрузки» найдёт «эксерционная головная боль», даже если словоформы разные. - Быстрые индексы для такого поиска.
Как и для обычного поиска по тексту, тут есть специальные индексы, чтобы не перебирать всё подряд:
Простой быстрый индекс (под капотом — разбиение на «коробки»). Хорош при больших объёмах, но иногда требует тонкой настройки.
Умный граф-индекс (строит сеть «похожих на похожих»). Очень хорошо ищет на чтении и обычно даёт более точные результаты.
Не важно, как они называются внутри — важнее, что поиск становится быстрым и точным. - Работает прямо в PostgreSQL.
Всё — обычные таблицы и индексы вашей базы. Те же бэкапы, реплики, права доступа. Не нужно поднимать отдельный «векторный» сервис.
Как это выглядит вживую
- Храним фрагменты (кусочки документов) и их эмбеддинги. Например:
CREATE TABLE chunks (
id bigserial PRIMARY KEY,
doc_id bigint NOT NULL,
section text,
content text NOT NULL,
embedding vector(768), -- та самая «точка»
created_at timestamptz default now()
);
- Индекс для ANN-поиска (пример для HNSW*с косинусом):
CREATE INDEX ON chunks
USING hnsw (embedding vector_cosine_ops);
- Запрос «найди смыслово близкие кусочки»:
-- :q — эмбеддинг вопроса пользователя
SELECT id, doc_id, content
FROM chunks
ORDER BY embedding <=> :q -- метрика близости (чем меньше, тем ближе)
LIMIT 5;
- Дальше ассистент склеит эти 5 фрагментов в контекст и сформирует ответ. Тут в ответ можно добавить ссылки на источники и предупредит о границах ответственности.
* Индекс HNSW (Hierarchical Navigable Small World) — это современный, высокоэффективный алгоритм для приблизительного поиска ближайших соседей (ANNS)
Мини-история из медицины
Пациентка спрашивает: «Можно ли сочетать препарат А с Б при гипотиреозе?»
Ассистент превращает вопрос в вектор, за миллисекунды вытягивает из базы пару релевантных фрагментов: выдержку из методички по лекарственным взаимодействиям и пункт из локального протокола. В ответе — аккуратная рекомендация с цитатами, ссылкой на источник и датой версии документа. Магии нет — есть хороший векторный поиск.
Тонкости про которые лучше не забывать
Размерность и нормализация.
«Эмбеддинг» = числовой отпечаток текста. У разных моделей длина этого отпечатка разная (например, 384, 768, 1024 чисел).
Для метрики «косинусное сходство» полезно нормировать вектор (привести длину к 1) — так сравнение работает стабильнее.
Параметры индекса.
- IVFFlat (Inverted File Index). Идея: сначала грубо разбиваем пространство на «корзины», потом ищем внутри лучших корзин.
Два главных рычага:
lists — сколько корзин сделать. Больше списков → точнее, но дольше строится и больше памяти.
probes — в скольких корзинах искать при запросе. Больше зондов → точнее, но медленнее ответ.
Простое правило: начните с lists ≈ sqrt(числа_строк) и probes ≈ 1–5% от lists, дальше подбирайте по замерам. - HNSW (Hierarchical Navigable Small World). Идея: строится «граф похожести» и поиск идёт по «соседям соседей». Обычно даёт отличную точность на чтении.
Параметры:
m — среднее количество связей у узла (чем больше, тем точнее и тяжелее по памяти).
ef_construction — тщательность при построении (влияет на качество и время построения).
ef_search — тщательность при поиске (чем больше, тем точнее и медленнее).
Старт: m=16, ef_construction=200, ef_search=40–100, потом доводите по метрикам.
Гибридный поиск. BM25 + векторы.
BM25 — классический текстовый поиск «по словам» (как в поисковиках).
Схема: сначала BM25 быстро отбирает верхние N кандидатов по словам, потом пересортируем их по «смысловой близости» (косинус по эмбеддингам).
Это уменьшает шум и ускоряет работу, особенно на больших базах.
Гигиена данных.
- Чанки без мусора. Режем документы на фрагменты, но:
не допускаем длинных хвостов «шапка/футер/меню»;
не делаем сильных пересечений между кусками (только небольшой «оверлап» 10–15% при необходимости). - Версии и актуальность. Храним поля вроде valid_from / valid_to, чтобы ассистент не тянул устаревшее.
- Метаданные. Источник, дата, тип материала — потом пригодится для фильтров и объяснимости.
Эксплуатация (чтобы не «грелось» и не падало)
- Память и тепло. Большие индексы = большой RAM и CPU при построении. Планируйте окно построения/перестроения.
- Автовакуум и обслуживание. Следим, чтобы таблицы и индексы не раздувались; подстраиваем autovacuum и maintenance_work_mem под реальные размеры данных.
- Бэкапы и реплики. Это обычный Postgres — используйте ваш стандартный WAL-бэкап, репликацию и мониторинг.
Как понять, что всё работает
- Recall@k (процент «правильных» среди топ-k).
Берём набор тестовых запросов, для каждого смотрим топ-k найденных фрагментов. Recall@k показывает, как часто среди них есть нужный фрагмент. Хотим, чтобы рос при приемлемом времени ответа. - Время ответа P95 / P99.
P95 — 95-й перцентиль: 95% запросов быстрее этого времени. P99 — 99-й перцентиль. Смотрим, чтобы «хвосты» не улетали (иначе пользователи страдают). - Доля ответов с источниками.
Ассистент должен ссылаться на найденные фрагменты. Чем чаще есть понятный источник, тем меньше «придуманных» ответов. - Обратная связь пользователей: простые лайки/дизлайки + комментарии сильно ускоряют улучшения. Простая кнопка сильно ускоряет улучшение качества: можно автоматически переобучать ранжирование и корректировать параметры индекса.
Pgvector превращает PostgreSQL в «память по смыслу» — именно ту, что нужна AI-ассистенту. Вы не строите новый зоопарк сервисов, не таскаете данные туда-сюда, а просто добавляете ещё один тип и индекс. В AI агентах это даёт ощутимую практическую пользу: точные ответы, проверяемые источники и уверенность, что ассистент не выдумает то, чего нет в документах.
Если у вас есть куча PDF, протоколов и «локальных правил» — первый шаг прост: нарежьте тексты на аккуратные чанки, посчитайте эмбеддинги, положите их в pgvector. Остальное — дело инженерной дисциплины и пары удачных параметров индекса.