pg_auto_embeddings — считаем эмбеддинги для текста прямо в Postgres, без экстеншенов
У вас есть PostgreSQL база, где хранится множество текстовых данных. Вы хотите использовать векторные представления (embeddings), к примеру, от OpenAI, чтобы построить систему рекомендаций, улучшенный поиск или реализовать RAG для работы с LLM. Но при этом ставить расширения (extensions) не хочется, а может, и вовсе нельзя — например, в облачных Managed PostgreSQL зачастую нет нужных прав.
pg_auto_embeddings — это лёгкое open-source решение, которое позволяет вычислять embeddings через модели OpenAI непосредственно в PostgreSQL без установки сторонних расширений. Оно использует механизм Foreign Data Wrappers (FDW) «под капотом» для того, чтобы делать запросы в OpenAI API, и работает синхронно и атомарно. В этой статье разберём, чем pg_auto_embeddings может помочь, как его установить (спойлер: очень легко) и в чём ключевые особенности проекта.
Что такое pg_auto_embeddings и какую задачу решает?
pg_auto_embeddings — это open-source-проект под лицензией MIT, который решает ключевую проблему: «Как вычислять текстовые векторные представления (embeddings) прямо из PostgreSQL без лишних телодвижений и без специальных расширений?»
Основные идеи:
1. Вызов через SQL: вы пишете простую функцию `pgae_embedding ('какой-то текст')` и этого достаточно. Функцию можно использовать в триггерах, и таким образом автоматически сохранять эмбеддинги для текстовых колонок.
2. Гибкие настройки: вы можете использовать публичные модели (например, OpenAI) или поднять on-premise-прокси, если нужно больше контроля — например, добавлять свои лимиты, закрытый доступ и т.п.
Благодаря этому, pg_auto_embeddings отлично подходит, когда нужно быстро «прокинуть» вычисление эмбеддингов в существующую БД и не заморачиваться с установкой сторонних бинарных расширений. Удобно для RAG-систем и прочих задач, где embeddings — это базовый функционал.
Ключевые возможности
Без расширений: Никакого дополнительного ПО в PostgreSQL ставить не нужно — только выполнить SQL-файл (да-да, в третий раз пишу, но это правда важно :).
Две варианта развёртывания:
Упрощённая установка: выполните один SQL-скрипт и готово.
On-Premise (через Docker): поднимите свой прокси-сервер, который обрабатывает запросы к API для embedding. Тоже поднимается одной `docker run` командой.
Поддержка OpenAI Embeddings: на данный момент из коробки работают модели OpenAI (text-embedding-3-small/large и некоторые другие).
Автоматический апдейт вектора при вставке или обновлении текстовых данных: можно «повесить» auto-embedding на столбец таблицы и не тратить время на написание триггера.
Удаление «за собой»: при необходимости вы можете полностью удалить pg_auto_embeddings и все его объекты одной функцией.
Шаг 1. Установка
Возьмите файл simple/pgae_simple_install.sql и выполните его в вашей базе данных.
Инициализируйте pg_auto_embeddings:
CALL pgae_init('openai-text-embedding-3-small', 'ВАШ_OPENAI_API_КЛЮЧ');
Вот и всё :)
Шаг 2. Использование
Чтобы получить вектор (массив `double precision[]`):
SELECT pgae_embedding('ваш текст');
Если у вас установлен pgvector и хочется формат vector, то:
SELECT pgae_embedding_vec('some text');
Автоматический подсчет и запись эмбеддинга. Допустим, у вас есть таблица posts со столбцом title, и вы хотите автоматом сохранять эмбеддинги для заголовков в title_embedding:
SELECT pgae_create_auto_embedding(
'public', 'posts', 'title', 'title_embedding'
);
Вуаля.
Если вы вдруг решили снести pg_auto_embeddings, полностью убрав все его функции и объекты из вашей БД, просто выполните:
SELECT pgae_self_destroy();
[Опционально] On-premise вариант (через Docker)
Если у вас есть ограничения на внешние запросы или вы хотите поднять собственный прокси-сервер для пущей безопасности, то это легко сделать.
Для этого нужно поднять 2 сервиса: Postgres базу, которая выступает в роли прокси между FDW и Node.js, и сам Node.js, который будет выполнять запросы к API моделей.
pg_auto_embeddings предоставляет docker-образ, в котором оба этих компонента уже развернуты и настроены для использования.
Пример docker-compose.yml:
services:
server:
image: elkornacio/pg_auto_embeddings:latest
environment:
- PG_HOST=localhost # Хост, где расположена проксирующий Postgres
- PG_PORT=5432 # Порт от него
- PG_USERNAME=root_user # root-юзер от него
- PG_PASSWORD=root_pass # пароль
- DATABASE_SYNC=true
- SERVER_HOST=localhost # Хост, который слушает Node.js-прокси
- SERVER_PORT=3000 # Порт, который он слушает
- SELF_URL=http://localhost:3000 # Главное! URL, по которому проксирующий Postgres обращается к Node.js прокси
ports:
# Порт проксирующего Postgres должен быть открыт - к нему будет подключаться ваша managed БД
- 5432:5432
Далее, вместо pgae_init
потребуется использовать pgae_init_onprem
:
CALL pgae_init_onprem(
'your.host.com', '5432', -- хост и порт вашего проксирующего Postgres
'openai-text-embedding-3-small', 'sk-...' -- тип модели и ключ API
);
Использование такое же, как и в «простом» варианте:
SELECT pgae_embedding('ваш текст');
Как это работает внутри?
pg_auto_embeddings под капотом использует трюк на базе Foreign Data Wrappers (FDW):
При установке создаётся «прокси-таблица» embeddings_* в локальной базе.
Когда вы вызываете `SELECT pgae_embedding ('some text')`, под капотом происходит UPDATE в этой «прокси-таблице» с передачей текста.
FDW перенаправляет запрос на удалённую таблицу (в Docker-прокси или публичный сервер).
На удалённом сервере срабатывает триггер, который вызывает внутреннюю функцию
pgae_embedding_internal()
.Эта функция делает HTTP-запрос к Node.js-прокси.
Node.js-сервер обращается к OpenAI API (или к чему-то ещё, если у вас другой провайдер) и получает вектор.
Вектор возвращается обратно в удалённую БД, дальше в локальную БД и в конечном итоге — в ваш SELECT-запрос.
Заключение
pg_auto_embeddings отлично подходит, когда нужно быстро, а главное — без сложных установок, получить возможность вычислять векторные представления. Он идеально подойдёт для тех, кто хочет «подружить» свою БД с LLM или сделать продвинутый полнотекстовый поиск, не выходя за рамки SQL.
Проект активен и открыт для предложений и PR-ов. А ещё для звездочек:) Если у вас возникли вопросы, смело пишите в Issues на GitHub.
Спасибо за внимание и удачных экспериментов :) Если вдруг в тексте нашли ошибки — напишите мне в ЛС, мигом поправлю.