Задача 1
Мы хотим, чтобы ИИ-агент мог работать с большой базой данных и обрабатывать её так, как будто он «читает» всё содержимое.
Причём важно:
- Базу можно обновлять: добавлять новые товары, пользователей, сообщения.
- Доступ к данным должен быть быстрым и экономить ресурсы.
- Агент должен уметь находить в базе релевантную информацию по смыслу, а не только по ключевым словам.
Рассмотрим два типичных сценария:
- Работа с товарами и клиентами — агент помогает вести консультации, отвечает на вопросы покупателей, собирает обратную связь и напоминает о повторной покупке.
- Агент-модератор сообщества (например, в Telegram-группе) — следит за тем, кто за что отвечает, кто что умеет, и напоминает участникам о пропущенных сообщениях.
Сложности
Главная проблема: контекстное окно модели ограничено.
Мы не можем каждый раз загружать в запрос к ИИ:
- всю базу товаров (сотни позиций),
- всех пользователей (тысячи клиентов),
- или всю историю переписки группы.
Это:
- дорого,
- медленно,
- технически ограничено (модель просто «не переварит» весь массив).
Решение — векторная база данных (pgvector)
Чтобы ИИ работал эффективно, мы будем хранить данные в векторном формате в PostgreSQL.
Для этого используется расширение pgvector. Оно добавляет новые типы данных для хранения эмбеддингов (векторов).
Типы:
- vector(n) → обычный массив чисел (float4).
n
= размерность, напримерvector(1536)
. - halfvec(n) → массив в половинной точности (экономия памяти, меньше точность).
- sparsevec(n) → хранит только ненулевые значения (для очень «разреженных» векторов).
👉 В нашей задаче мы будем использовать vector(1536)
или vector(3072)
.
Получение вектора (эмбеддинга) через OpenAI API
Чтобы текст (например, описание БАДа или сообщение пользователя) стал «понятен» машине, его нужно преобразовать в вектор признаков.
Делаем запрос к OpenAI:
curl https://api.openai.com/v1/embeddings \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"input": "Описание БАДа или вопрос пациента",
"model": "text-embedding-3-small"
}'
В ответ мы получаем массив чисел, например:
[0.008011777, 0.029998904, -0.02706108, ... ]
Этот массив и есть наш вектор (embedding).
Его мы сохраняем в колонку типа vector(1536)
в таблице PostgreSQL.
Поиск по смыслу (semantic search)
Теперь, когда у каждого объекта (товара, клиента, сообщения) есть векторное представление, мы можем находить похожие данные.
Например, пользователь спрашивает:
«Что у вас есть для укрепления сердца и сосудов?»
- Запрос пользователя преобразуем в embedding через OpenAI.
- Делаем SQL-запрос:
SELECT id, name, description FROM customatic_products ORDER BY embedding <-> '[вектор из API]' LIMIT 5;
Здесь оператор<->
считает косинусное расстояние между векторами. - На выходе мы получаем 5 наиболее похожих товаров, даже если в тексте не было точных совпадений по словам («сосуды» vs «артерии»).
Это и есть магия — поиск по смыслу, а не по ключевым словам.
Как обновлять базу и добавлять новые данные в pgvector через Make.com
Задача 2
У нас уже есть таблица customatic_products
в Supabase, где каждый товар хранится вместе с вектором (embedding
) для поиска по смыслу. Теперь нужно настроить процесс так, чтобы при добавлении или изменении товара мы автоматически:
- Получали все данные по товару (название, состав, показания, способ применения).
- Склеивали их в единый текст.
- Отправляли этот текст в OpenAI Embeddings API.
- Сохраняли вектор в поле
embedding
(типvector(1536)
) нашей базы.
Шаг 1. Добавляем товар
Представим, что мы добавили новый товар через интерфейс WeWeb или напрямую в Supabase. В таблице появился новый id
.
Шаг 2. Запуск сценария в Make.com
Создаём сценарий:
- Webhook / Database trigger (ловим событие: новый товар или обновление).
- Получаем все данные товара из базы (
Get a record
).
Шаг 3. Склеиваем данные в единый текст
Нам нужно собрать все ключевые поля (название, описание, состав, показания и т.д.) в одну строку, чтобы embedding учитывал всё:
Пример:
Pride Cardio Premium (комплекс 1+2)
Состав: лиственница, гинкго билоба, бакопа Монье...
Показания: артериальная гипертензия, нарушения памяти, профилактика инсультов...
Применение: Pride Cardio 1 — по 1 таблетке в день, Pride Cardio 2 — по 1 капсуле 2 раза в день.
👉 В Make это можно сделать через модуль Set Variable или прямо в поле запроса к OpenAI.
Шаг 4. Делаем embedding
Отправляем текст в OpenAI Embeddings API:
POST https://api.openai.com/v1/embeddings
Authorization: Bearer {{api_key}}
Content-Type: application/json
{
"input": "текст товара",
"model": "text-embedding-3-small"
}
body запроса собирать рекомендуется с помощью модуля Create JSON. Иначе рискуете попасться на типичную проблему типичную проблему: JSON не дружит с “сырными” переносами строк и кавычками и получить ошибку:
[400] We could not parse the JSON body of your request. (HINT: This likely means you aren’t using your HTTP library correctly. The OpenAI API expects a JSON payload, but what was sent was not valid JSON. If you have trouble figuring out how to fix this, please contact us through our help center at help.openai.com.)
ибо перед отправкой в OpenAI Embeddings API нужно экранировать текст:
— заменить все переносы строк на \n
,
— убрать или экранировать кавычки ("
→ \"
).
Вы также можете сделать это таким образом:
{{replace(your_text; «\n»; «\n»)}}
Однако надежнее с помощью модуля Create Json.
На выходе получаем массив из 1536 чисел.
Шаг 5. Подготовка массива для Supabase
Вот тут главная хитрость ⚡️
По умолчанию Make вернёт embedding как JSON-массив (и при вставке в Supabase он уйдёт как строка "[...]"
). Postgres pgvector
это не понимает.
Решение: применяем функцию join
:
[{{join(3.body.data[1].embedding; ",")}}]
Если этого не сделать, то на модуле supabase в make вы рискуете получить ошибку
[400] invalid input syntax for type vector:
👉 Теперь массив превращается в правильную строку без лишних кавычек:
[0.025095636,-0.015868386,-0.012682295,-0.0007661358]
Шаг 6. Записываем в Supabase
Обычный Upsert-запрос в таблицу:
✅ Готово! Теперь каждый новый или изменённый товар автоматически получает вектор в базе.
А теперь нам надо организовать поиск по базе. Для этого нам надо в supabase создать RPC-функцию.
1. Создаем функцию в Supabase Dashboard
Заходим в Supabase Dashboard → SQL Editor и выполняем там этот код:
CREATE OR REPLACE FUNCTION customatic_match_products(
query_embedding vector,
match_count int DEFAULT 3
)
RETURNS TABLE (
id uuid,
name text,
description text,
similarity float
)
LANGUAGE sql
STABLE
AS $$
SELECT
id,
name,
description,
1 - (embedding <=> query_embedding) AS similarity
FROM customatic_products
ORDER BY embedding <=> query_embedding
LIMIT match_count;
$$;
match_products — это название функции
query_embedding — переменная с эмбедингом запроса
match_count — количество возвращаемых строк
id uuid,
name text,
description text
это поля, который мы вытаскиваем из базы
customatic_products — имя таблицы
similarity — это виртуальный столбец (его в базе быть не должно) он показывает насколько релевантен запрос.
В Make делаем Supabase API Call на /rest/v1/rpc/match_products
В body передаем JSON собранный заранее модулем CreateJSON
Не забываем сджойнить дату из модуля ChatGPT по такому же принципу, чтобы не получать ошибку формата данных
[{{join(9.body.data[1].embedding; «,»)}}]
Получаем JSON такого вида:
{
"match_count":2,
"query_embedding":"[-0.00731227...]"
}