Supabase для хранения данных AI-агентов: практическое руководство

Supabase для хранения данных AI-агентов: практическое руководство

Я давно перестала верить в магию, когда дело касается хранения данных. Если AI-агент должен помнить контекст, понимать пользователя и предлагать точные действия — ему нужна системная память. В этой статье показываю, как я использую Supabase как надежную систему хранения данных для AI-агентов: от проектирования схемы в PostgreSQL и RLS до интеграции с n8n и Make.com, реального времени и Edge Functions. Разберем supabase auth, supabase api, самохостинг на supabase docker и нюансы 152-ФЗ. Материал для тех, кто строит автоматизацию без хайпа, предпочитает прозрачные процессы и честные метрики, и хочет, чтобы контент делался сам, а люди возвращали себе время.

Время чтения: ~15 минут

Зачем AI-агенту собственная память и почему здесь Supabase

Утро, кофе остыл, агент бодро реагирует на запросы, но через пять минут не может вспомнить, что обещал пользователю. Знакомо. Без устойчивого хранилища данных в компьютерных системах агент неизбежно превращается в попугая, который красиво отвечает, но не держит контекст и не учится. Для хранения данных используется не просто таблица, а продуманная система хранения баз данных: история сессий, сообщения, свойства пользователя, события, результаты вызовов инструментов, файлы. Мне важно, чтобы это работало прозрачно, с понятной авторизацией, с ролевой безопасностью и без танцев вокруг очередной заглушки. В supabase я получаю PostgreSQL, supabase auth, файловое хранилище, realtime, Edge Functions и удобный supabase api в одном месте — и не гадаю, где какой компонент потеряется.

Актуальность очевидна: AI-агенты переезжают из демо в процессы и требуют дисциплины. Если вы строите пайплайны в n8n или Make.com, если у вас есть требования по 152-ФЗ, если хотите контроль над данными пользователей и кодирование данных при хранении — supabase self hosted решает большую часть головной боли. Я выбираю подход, где хранение данных информации не размазано по трем сервисам, а собрано в систему хранения данных, которую можно поддерживать и масштабировать. И да, я люблю, когда метрики честные, а права доступа проверяются политиками, а не комментариями в чате.

AI-агент без памяти — это чат. AI-агент с продуманным хранением данных — это инструмент.

Архитектура без сюрпризов: что храню и где это живет

Я начинаю с базовой картины, чтобы не тонуть в деталях. Слои простые: интерфейс агента, оркестратор действий, база и очереди. В качестве базы — PostgreSQL внутри Supabase, как ядро системы хранения данных. Таблицы для пользователей и auth не придумываю, беру supabase auth и RLS. Для сообщений и сессий делаю свои таблицы, для файлов — встроенное хранилище. Реальное время использую там, где нужна мгновенная синхронизация: обновление статуса задач, сигнал агенту о новом событии. Edge Functions отдаю под серверную логику рядом с данными: валидация, дергание внешних API, простые вебхуки без лишних прыжков.

Если коротко про сущности: users из auth, profiles с доп. полями, agents с настройками и лимитами, sessions для контекста, messages как поток сообщений, tools_calls для вызовов инструментов агента, events для логов, tasks для операционных задач пользователя. Для векторного поиска включаю pgvector — embeddings в отдельной таблице, связанной с messages или документами. Хранилище файлов беру нативное, чтобы не тянуть отдельный S3. Для хранения базы данных важно не только качество модели, но и дисциплина запросов: индексы по user_id, session_id, created_at, и четкие политики доступа.

Сравнительная инфографика: Supabase vs Pinecone. Автор: Marina Pogodina
Сравнение: Supabase vs Pinecone — когда нужен векторный индекс отдельно, а когда удобнее сначала собрать все в Supabase.

Вопрос, который мне часто задают: почему не только векторная СУБД. Ответ приземленный. Пока агент живет в реальном продукте, ему нужны транзакции, связи, RLS и обычная обработка хранения данных: статусы, флаги, аналитика, внешние ключи. Векторный поиск отлично дополняет, но не заменяет систему хранения данных пользователей. Если объемы большой, добавляю внешний векторный сервис, но Supabase оставляю как источник правды. По пути появляются мелочи: ретенции, политики на удаление, storage buckets для вложений, лимиты на размер. Ничего романтичного, зато работает.

Если у вас уже есть свой PostgreSQL, Supabase можно подключить как удобную обертку с auth, storage и realtime. Но лично мне проще не разводить зоопарк и держать ядро рядом — меньше интеграционных дыр.

Самохостинг без боли: supabase docker и требования 152-ФЗ

Когда речь о данных российских пользователей, я включаю режим white-data-зоны и зашиваю локализацию. Вариантов два: облачный проект на supabase com для прототипа и supabase self hosting для продакшена, когда требуется хранение данных пользователей в РФ. Самый быстрый путь — supabase docker, который поднимает Postgres, Studio, Kong, storage и остальные сервисы. Дальше уже дело привычки: параметры БД, ключи, конфиги для инициализации расширений, ограничители на сетевой доступ. На выходе получается самодостаточная система хранения баз данных, которую можно развернуть на своем сервере или в отечественном облаке.

Пример docker compose для старта я держу под рукой. Да, упрощено, но хорошо передает идею минимального набора сервиса для dev и теста. В продакшене добавляю внешние тома, мониторинг, бэкапы и секреты из хранилища секретов.

version: "3.9"
services:
  db:
    image: supabase/postgres:15.1.0.43
    environment:
      POSTGRES_PASSWORD: supersecret
      POSTGRES_DB: postgres
    volumes:
      - db-data:/var/lib/postgresql/data
  kong:
    image: supabase/kong:latest
    depends_on: [db]
  auth:
    image: supabase/gotrue:latest
    environment:
      GOTRUE_JWT_SECRET: verysecret
      GOTRUE_SITE_URL: http://localhost:3000
  rest:
    image: supabase/postgrest:latest
    environment:
      PGRST_DB_URI: postgres://postgres:supersecret@db:5432/postgres
  realtime:
    image: supabase/realtime:latest
  storage:
    image: supabase/storage-api:latest
volumes:
  db-data:

Чтобы соответствовать 152-ФЗ я делаю три вещи. Первое — география: размещение инстанса в дата-центре в РФ и договор с провайдером на обработку персональных данных. Второе — политика ролей и разграничение доступа: supabase auth, RLS, отдельные ключи для сервисов, и строгий запрет отдавать service_role в клиент. Третье — бэкапы и журналирование с учетом класса систем: расписание, хранение данных в компьютерных системах по регламенту, документированная процедура восстановления. Кодирование данных при хранении можно организовать на уровне диска и отдельных колонок pgcrypto — не серебряная пуля, но снижает риски в случае компрометации диска.

Без плана бэкапов вы не храните данные, вы их временно держите.

Схема данных, RLS и supabase auth: безопасность как по нотам

Я проектирую схему с мыслью, что любая запись должна быть однозначно привязана к пользователю и агенту. Базовый минимальный набор: profiles, agents, sessions, messages, tasks, events, embeddings. В Supabase сразу включаю расширение pgvector и создаю индексы для быстрого поиска. Политики RLS держу максимально простыми: каждая таблица проверяет, что user_id из токена совпадает с owner_id записи. Это снижает количество сюрпризов на проде и делает систему хранения данных предсказуемой.

Ниже — пример SQL, который я использую как стартовую точку. Тут и таблицы, и индексы, и политики. Не идеал для всех, но рабочий каркас для большинства агентских сценариев.

-- включаем pgvector
create extension if not exists vector;

-- профили
create table public.profiles (
  user_id uuid primary key references auth.users(id) on delete cascade,
  full_name text,
  created_at timestamp with time zone default now()
);

-- агенты
create table public.agents (
  id uuid primary key default gen_random_uuid(),
  user_id uuid not null references public.profiles(user_id) on delete cascade,
  name text not null,
  config jsonb not null default '{}'::jsonb,
  created_at timestamptz default now()
);
create index on public.agents (user_id);

-- сессии
create table public.sessions (
  id uuid primary key default gen_random_uuid(),
  agent_id uuid not null references public.agents(id) on delete cascade,
  user_id uuid not null references public.profiles(user_id) on delete cascade,
  title text,
  created_at timestamptz default now()
);
create index on public.sessions (user_id);

-- сообщения
create table public.messages (
  id bigserial primary key,
  session_id uuid not null references public.sessions(id) on delete cascade,
  user_id uuid not null references public.profiles(user_id) on delete cascade,
  role text check (role in ('user','assistant','system')),
  content text not null,
  created_at timestamptz default now()
);
create index on public.messages (session_id, created_at);

-- вектора
create table public.embeddings (
  message_id bigint primary key references public.messages(id) on delete cascade,
  embedding vector(1536)
);
create index on public.embeddings using ivfflat (embedding vector_cosine_ops);

-- задачи
create table public.tasks (
  id uuid primary key default gen_random_uuid(),
  user_id uuid not null references public.profiles(user_id) on delete cascade,
  description text,
  status text default 'pending',
  created_at timestamptz default now()
);

-- включаем RLS и политики
alter table public.profiles enable row level security;
alter table public.agents enable row level security;
alter table public.sessions enable row level security;
alter table public.messages enable row level security;
alter table public.embeddings enable row level security;
alter table public.tasks enable row level security;

create policy "user_is_owner_profiles" on public.profiles
  using (auth.uid() = user_id);

create policy "user_is_owner_agents" on public.agents
  using (auth.uid() = user_id);

create policy "user_is_owner_sessions" on public.sessions
  using (auth.uid() = user_id);

create policy "user_is_owner_messages" on public.messages
  using (auth.uid() = user_id);

create policy "user_is_owner_embeddings" on public.embeddings
  using (exists (
    select 1 from public.messages m
    where m.id = embeddings.message_id and m.user_id = auth.uid()
  ));

create policy "user_is_owner_tasks" on public.tasks
  using (auth.uid() = user_id);

Аутентификацию беру supabase auth, потому что стандартные провайдеры, магия JWT и простые правила. Если нужен сервисный доступ, использую отдельный ключ, в n8n он хранится в креденшелах, а все клиентские операции идут через пользовательский токен. Для внешних интеграций — supabase api на базе PostgREST: можно дергать REST эндпоинты для таблиц, фильтровать и пагинировать без написания лишнего кода. Для примера запрос на выборку задач по статусу выглядит так:

GET /rest/v1/tasks?status=eq.in_progress&select=id,description,created_at
apikey: <public-anon-key>
Authorization: Bearer <user-jwt>
Если нужен сложный бэкенд, Edge Functions закрывают 80% задач. Но я всё равно стараюсь держать бизнес-логику ближе к данным и не плодить микросервисы ради микросервисов.

Интеграции с n8n и Make: события, очереди, логи

Когда в игру вступает оркестровка, главное — предсказуемость. В n8n я использую два пути: нативный Postgres node для вызовов SQL и HTTP Request к supabase api. Для Make.com аналогично — HTTP и Webhooks. Логи пишу в таблицу events, где храню тип события, источник, payload и ссылку на сущность. Это помогает и для отладки, и для метрик. Сама обработка хранения данных получается линейной: пришло событие — записали — посчитали — обновили статус — отправили уведомление в реальном времени.

Типичный сценарий в n8n для агента у меня выглядит так: вебхук принимает запрос, извлекаем токен, проверяем права через supabase auth, пишем сообщение, считаем эмбеддинг, сохраняем в embeddings, дергаем модель, записываем ответ, публикуем через realtime канал обновление интерфейсу. Если интеграция с внешними системами, добавляю очереди и ретраи с backoff. Да, иногда сценарий заводится с третьей попытки — н8n умеет удивлять — но после пары вечеров все стабилизируется.

Я стараюсь не плодить мутаторов состояния в десяти местах. Если агент меняет статус задачи, он делает это через один маршрут, который и пишет в tasks, и публикует событие. Тогда интерфейс и другие сценарии получают одну правду. В Make.com мне нравится визуальная ясность, но критические транзакции я всё равно отдаю на сторону БД, потому что нет ничего надежнее коммита в таблице и RLS, который не даст случайно обновить чужую строку.

Сначала событие в базе, потом уведомление. Не наоборот. Иначе однажды вы потеряете чей-то важный статус при сбое сети.

Для интеграции с MCP инструментами я добавляю простой HTTP слой поверх supabase api. supabase mcp в моей практике — это соглашение: агент через MCP вызывает модуль, модуль читает и пишет в таблицы через сервисный ключ и четко ограниченные эндпоинты. Тут дисциплина важнее модных слов — легко утечь с правами service_role. Всегда храню такой ключ только на сервере, никогда в клиенте. И да, проверку входящих параметров делаю дважды, не стыжусь.

Реальное время, Edge и supabase api: как ускорить реакцию

Реальное время в Supabase — это легкий способ отправить фронту сигнал, что что-то изменилось. Я использую каналы по session_id или user_id, чтобы минимизировать шум. Когда агент записал новое сообщение в messages, подписчик получает событие и обновляет интерфейс. Это лучше, чем опрашивать базу каждые пять секунд и грузить систему хранения данных без пользы. Edge Functions беру там, где нужна быстрая реакция рядом с данными: вебхуки из внешних систем, предварительная валидация перед записью, подготовка агрегатов.

supabase api выручает, когда вам нужен быстрый REST поверх таблиц. Фильтры, сортировка, ограничения — всё есть из коробки. Пример запроса в n8n через HTTP Request я держу стандартным: заголовки apikey и Authorization, параметры в URL, лимиты и пагинация через range. Для долгих операций делаю отложенные задачи: записать событие, подобрать контекст через embeddings, сгенерировать ответ моделью, обновить статус. В кодовой базе я стараюсь без лишней магии — комментарии и названия таблиц говорят сами за себя.

Небольшой штрих: если вы строите свой pipeline по правилам вроде руководство по использованию api open ai, то Supabase отлично сочетается с такой дисциплиной. Сначала логируем вход, затем создаем контекст, затем выполняем вызов, затем фиксируем выход. Если вам ближе генеративные задачи с аудио и музыкой, сохраните где-нибудь ссылку на полное руководство по тегам для suno ai — пригодится для унификации метаданных в таблицах. Я не смешиваю разные домены в одном потоке, но хранение базы данных делает эти миры дружелюбными к аналитике.

Небольшая техника безопасности. Для публичных клиентов используйте anon key, для серверных — service_role, и никогда не отправляйте второй в браузер. На всякий случай проверяйте логи доступа Kong и лимиты.

Эксплуатация: метрики, бэкапы, оптимизация и экономия времени

В эксплуатации меня интересуют три вещи: скорость отклика, надежность коммита и предсказуемая стоимость поддержки. Метрики держу на уровне БД и приложения: время ответа на запись и чтение, количество сообщений в секунду, количество активных сессий, доля ошибок по категориям. Бэкапы по расписанию, проверка восстановления — обязательная рутина раз в квартал. Для хранения данных информации прописываю сроки жизни: события 90 дней, сырые логи 30, агрегаты — бессрочно. Это экономит диск и поддерживает гигиену. Кодирование данных при хранении колонок с чувствительными данными через pgcrypto плюс шифрование диска — достаточно, чтобы спать спокойнее.

Оптимизации стандартные: индексы по часто используемым фильтрам, партиционирование messages по месяцам при больших объемах, сжатие в хранилище файлов, аккуратные политики RLS без сложных подзапросов. На стороне n8n и Make.com — ретраи с джиттером, ограничители для внешних API, очереди с приоритетами. Если судорожно масштабироваться — дороже, чем изначально держать систему хранения данных в порядке. Сколько времени это экономит? Много, я перестала считать, как только количество инцидентов упало вдвое после того, как мы запретили записи без транзакций.

Хорошая система — та, которую легко чинить. Плохая — та, которую страшно трогать.

Из практических наблюдений. supabase co и supabase com как домены в документации встречаются в примерах, но в проде я фиксирую свой домен и не полагаюсь на редиректы. Версионность схемы веду миграциями, а не SQL по памяти. Для realtime лучше сразу ограничить количество каналов, иначе чат с тысячами тем превратится в печь. Для Edge Functions следите за лимитами времени и памяти, иначе внезапные тайм-ауты начнут съедать нервы. И еще маленькая, но важная мелочь: вынесите ключи в секреты окружения, не храните их в репозитории, это 5 минут сегодня и минус одна бессонная ночь завтра.

Практические советы: берите и делайте

Ниже список шагов, которые помогут запустить хранение данных для агента без суеты. Да, звучит базово, но я сама регулярно ловлю себя на том, что перескакиваю через какие-то пункты, потом возвращаюсь и чиню. Лучше сразу ровно пройтись.

  1. Определите схему. Начните с users, agents, sessions, messages, tasks, events, embeddings. Пропишите связи и индексы. Включите pgvector.
  2. Включите RLS и напишите простые политики. Стратегия одна — каждый видит только свое. Проверьте запросами от разных пользователей.
  3. Выберите способ размещения. Для прототипа можно облако, для продакшена с российскими пользователями — supabase self hosted через supabase docker в РФ.
  4. Соберите интеграцию. В n8n используйте Postgres node для транзакций и HTTP для supabase api. В Make.com настройте Webhooks и REST вызовы.
  5. Продумайте эксплуатацию. Бэкапы, журналирование, ретенции, мониторинг. Заведите графики по ответам БД и ошибкам.

Несколько правил, которые я держу перед глазами. Правило 1: сначала запись в базу, потом нотификация. Правило 2: ни один сервисный ключ не попадает в клиент. Правило 3: каждый запрос идет не напрямую, а через контролируемый слой — Edge или подготовленные эндпоинты. Правило 4: раз в месяц прогоняем сценарий восстановления из бэкапа. Правило 5: тесты на RLS, чтобы случайно не открыть доступ чужим данным. Дисциплина скучна, зато хранение данных пользователей становится предсказуемым, а система хранения данных ведет себя взрослым образом.

Если хочется разобрать такой стек на примерах и задачах из живых проектов, я регулярно делюсь ходом работ и маленькими находками у себя в телеграме. Подсматривайте, пробуйте, и не бойтесь перепридумывать под свои реалии.

Тихая развязка: на что ставлю в проектах

Я выбрала Supabase не потому, что модно, а потому что он экономит мне часы в неделе. Это полноценная система хранения данных с PostgreSQL в центре, где supabase auth закрывает аутентификацию, supabase api — быстрый REST к таблицам, realtime — сигналы для фронта, Edge — легкая серверная логика. В самохостинге через supabase docker я выполняю требования 152-ФЗ, оставляю данные в РФ и контролирую окружение. Если нужны вектора — включаю pgvector и живу спокойно. Если нужен рост — добавляю индексы, партиционирование и аккуратно масштабирую оркестровку.

На уровне процессов всё просто: хранение данных не должно быть квестом. Схема, RLS, логи, бэкапы, мониторинг — базовый чеклист. Интеграции с n8n и Make.com строятся вокруг одного принципа: сначала фиксируем, затем уведомляем. Там, где нужна скорость, приходят realtime и Edge. Там, где нужна гибкость, помогает supabase mcp как тонкий слой над API. Главное — не забывать, что это не магия, а ремесло: меньше хака, больше читаемого кода и проверенных маршрутов. Если в какой-то момент вам показалось, что стало слишком сложно — скорее всего, пора вынести часть логики в базу и убрать два лишних шага из своей цепочки.

Если хочется продолжить вместе

Если интересен спокойный разбор таких стеков, у меня есть пространство, где я без спешки показываю, как это собирается у меня: в моем телеграм-канале MAREN я разбираю сценарии n8n supabase, самохостинг и честные метрики. А на сайте MAREN можно посмотреть, чем я занимаюсь, какие продукты и форматы практики использую. Если хочется структурировать эти знания и собрать свой пилот, берите идеи, адаптируйте под свои процессы и не забывайте про белую зону данных.

Частые вопросы по этой теме

Можно ли обойтись без самохостинга и использовать облако

Для прототипов и внутренних экспериментов можно. Если у вас есть обязательства по локализации данных в РФ, смотрите в сторону supabase self hosting. Иногда делю среду: dev в облаке, prod в самохостинге.

Как правильно хранить сервисные ключи и защищать доступ

Храните service_role только на сервере, в менеджере секретов. В клиент отправляйте только anon key. Проверяйте права через RLS и не доверяйте клиенту даже если очень хочется ускориться.

Нужен ли отдельный векторный движок

Если объемы невелики, pgvector внутри Supabase закрывает задачи похожести. При росте и миллионах эмбеддингов можно вынести векторный индекс во внешний сервис, оставив Supabase источником правды.

Как связать n8n и supabase без лишней магии

Для критичных операций используйте Postgres node и транзакции. Для простых CRUD подойдут HTTP вызовы к supabase api. Логи пишите в таблицу events и следите за ретраями.

Что делать с файлами и мультимедиа

Используйте встроенное хранилище файлов и политики доступа. Для больших объектов включайте CDN и ограничение размера. Метаданные держите в таблицах, чтобы не плодить поисковые дырки.

Реальное время нагружает базу, стоит ли его включать везде

Нет, включайте точечно. Подписки по session_id или user_id эффективнее, чем одна большая тема для всех. Остальное лучше обслуживать периодическими задачами или событиями.

Где почитать про настройки моделей и API

Технические тонкости удобно собирать в одном месте, например внутреннее руководство по использованию api open ai. А для генеративных аудио подойдет полное руководство по тегам для suno ai — это помогает нормализовать данные в таблицах.

Метки: , ,