EastPay v1 → v2: Техническое задание

Детальный разбор логики legacy-приложения, Supabase-архитектура и стратегия интеграции AI Chat-Agent


1. Обзор системы

EastPay v1 — Telegram Mini App для обмена валют и зарубежных платежей, построенная на связке PHP + MySQL + Telegram Bot API.

Технологический стек (старый)

Компонент Технология
Frontend Vanilla JS, HTML5, CSS3, Chart.js
Backend PHP (native, без фреймворков)
БД MySQL / MariaDB (PDO)
Интеграция Telegram Bot API (Webhook)
Хостинг Классический shared hosting (eastpaycripto.ru)

Технологический стек (новый — v2)

Компонент Технология
Frontend React, Vite, Tailwind v4, Shadcn/UI
Backend Supabase (Auth, DB, Edge Functions, Realtime)
AI Orchestration n8n (Telegram AI Agent workflow)
LLM OpenRouter (GPT-4o-mini) + Google Gemini (image/video)
Монорепа Nx
Интеграция Telegram Bot API (через n8n Webhook)
Хостинг Netlify + Supabase Cloud

2. Текущая Supabase-архитектура (проект cvzsgjksswowqgfxvrsb)

2.1. Схема базы данных — 12 таблиц

erDiagram
    clients ||--o{ orders : creates
    clients ||--o{ chat_logs : has_chat
    clients ||--o| clients : "referrer_id"
    profiles ||--o{ orders : manages
    locations ||--o{ orders : located_in
    locations ||--o{ market_rates : has_rates
    locations ||--o{ profiles : assigned_to
    instruments ||--o{ orders : uses
    instruments ||--o{ market_rates : priced_in
    orders ||--o{ chat_logs : has_messages

    clients {
        bigint id PK
        text telegram_id UK
        text telegram_username
        text full_name
        text first_name
        text last_name
        text phone
        text email
        text verification_status "unverified"
        text risk_level "low"
        numeric total_volume_usd "0"
        int deals_count "0"
        text notes
        boolean is_ai_paused "false"
        text passport_series
        text passport_number
        text_arr passport_files
        bigint referrer_id FK
        boolean is_ambassador "false"
        timestamptz created_at
    }

    profiles {
        uuid id PK "FK auth.users"
        text email
        text full_name
        user_role role "pending"
        bigint assigned_location_id FK
        text telegram_username
        text status "active"
        text avatar_url
        timestamptz created_at
    }

    orders {
        bigint id PK
        bigint client_id FK
        uuid manager_id FK
        bigint location_id FK
        bigint instrument_id FK
        deal_direction direction "buy|sell"
        numeric amount_give
        text currency_give
        numeric amount_get
        text currency_get
        numeric exchange_rate
        numeric cost_price_rate
        numeric service_fee_amount
        numeric estimated_profit_usdt
        order_status status "new"
        text delivery_method
        text meeting_address
        text client_wallet_address
        text client_bank_details
        text manager_comment
        text payment_link
        timestamptz created_at
        timestamptz updated_at
    }

    locations {
        bigint id PK
        text name
        text country
        text currency_local
        text timezone "UTC"
        boolean is_active "true"
        numeric surcharge_percent "0"
        text partner_tag
    }

    instruments {
        bigint id PK
        text name
        text description
        text base_currency
        boolean is_active "true"
        text cost_rate_expression
        numeric fixed_fee_usdt "0"
    }

    market_rates {
        bigint id PK
        bigint location_id FK
        bigint instrument_id FK
        numeric cost_price_percent "0"
        numeric selling_rate_percent "0"
        numeric min_amount "0"
        timestamptz updated_at
    }

    daily_rates {
        text symbol PK
        numeric rate "0"
        boolean is_active "true"
        timestamptz updated_at
    }

    chat_logs {
        bigint id PK
        bigint client_id FK
        bigint order_id FK
        text sender
        text message_content
        boolean is_read "false"
        timestamptz created_at
    }

    message_templates {
        bigint id PK
        text title
        text category
        text content
        text language "ru"
        text_arr trigger_keywords
        boolean is_active "true"
    }

    audit_logs {
        bigint id PK
        uuid user_id FK
        text action
        text table_name
        text record_id
        jsonb old_data
        jsonb new_data
        text method
        jsonb payload
        text ip_address
        text user_agent
        text severity "info"
        text entity_type
        timestamptz created_at
    }

    documents {
        bigint id PK
        text content
        jsonb metadata
        vector embedding
    }

    project_activity {
        bigint id PK
        text stat_name
        jsonb stat_value
        text description
        timestamptz timestamp
    }

2.2. Enums (типы данных)

Enum Значения
order_status new, calculated, payment_pending, processing, delivery, office, done, cancelled, dispute
deal_direction buy, sell
user_role admin, operator, city_manager, pending, super_admin

2.3. RLS-политики (Row Level Security)

Таблица RLS Примечание
clients ✅ Вкл Менеджеры видят клиентов
orders ✅ Вкл Менеджеры — свои заявки; клиенты — через API
chat_logs ✅ Вкл Привязка к client_id / order_id
market_rates ✅ Вкл Чтение для всех, запись для админов
daily_rates ✅ Вкл Чтение для всех, запись для админов
audit_logs ✅ Вкл Только super_admin
profiles ✅ Вкл Каждый видит свой профиль
locations ❌ Откл Публичные данные
instruments ❌ Откл Публичные данные
documents ❌ Откл Внутреннее использование (RAG)
message_templates ❌ Откл Публичные шаблоны
project_activity ❌ Откл Системный мониторинг

2.4. Существующие RPC-функции (Supabase Functions)

Функция Возврат Назначение
bot_create_order(p_tg_id, p_city_name, p_amount, p_currency_pair) json Создание заявки из Telegram бота — находит/создаёт пользователя, рассчитывает сделку, вставляет в orders
bot_log_chat_message(p_tg_id, p_content, p_sender) void Логирование сообщения в chat_logs по telegram_id клиента
calculate_deal(in_city_name, in_amount, in_base_pair) json Расчёт сделки: base rate × (1 + city surcharge), возвращает {rate, amount_get, currency_get}
get_active_cities_for_bot() TABLE(name) Список активных городов для бота
handle_new_user() trigger Автоматическое создание profiles при регистрации в auth.users
process_audit_log() trigger Автоматическая запись в audit_logs при INSERT/UPDATE/DELETE
is_admin() boolean Проверка роли admin/super_admin
is_operator_level() boolean Проверка роли operator и выше
is_staff() boolean Проверка, что пользователь — сотрудник
record_project_heartbeat() void Записывает heartbeat в project_activity (cron)
match_documents(query_embedding, match_threshold, match_count, filter_category) TABLE(id, content, metadata, similarity) Семантический поиск по векторной БД (RAG). См. Раздел 12.5

2.5. Edge Functions (развёрнутые)

Slug JWT Назначение
invite-user ❌ Нет Приглашение пользователя в систему (отправка email)
logger-service ✅ Да Сервис логирования

2.6. Cron-задачи (pg_cron)

Задача Расписание SQL
supabase-keep-alive-job Каждые 12 ч (0 */12 * * *) SELECT public.record_project_heartbeat()
cleanup-keep-alive-logs Еженедельно (0 0 * * 0) DELETE FROM project_activity WHERE timestamp < now() - interval '90 days'

2.7. Расширения PostgreSQL

Ключевые установленные расширения:


3. Текущая реализация AI-Агента в n8n

3.1. Обзор Workflow «Telegram AI Agent Base»

[!IMPORTANT] Текущая архитектура: AI-агент реализован и работает через n8n (workflow ID: YiKrHeitj7K100Vd). Это ключевой компонент системы, который координирует Telegram ↔ AI ↔ Supabase.

flowchart LR
    TG["📱 Telegram Trigger"]
    SW{"🔀 Switch"}
    WELCOME["👋 Send Welcome"]
    AUDIO["🎵 Get Audio File"]
    PHOTO["📷 Get Photo"]
    VIDEO["🎬 Get Video"]
    TRANSCRIBE["🗣️ OpenAI Transcribe"]
    ANALYZE_IMG["🧠 Gemini: Analyze Image"]
    ANALYZE_VID["🧠 Gemini: Analyze Video"]
    MERGE["🔗 Merge"]
    FIELDS["📝 Edit Fields"]
    AGENT["🤖 AI Agent"]
    SEND["📤 Send to Telegram"]

    TG --> SW
    SW -->|"/start"| WELCOME
    SW -->|"voice"| AUDIO --> TRANSCRIBE --> MERGE
    SW -->|"photo"| PHOTO --> ANALYZE_IMG --> MERGE
    SW -->|"video"| VIDEO --> ANALYZE_VID --> MERGE
    SW -->|"text"| MERGE
    MERGE --> FIELDS --> AGENT --> SEND

    subgraph "AI Agent Tools"
        T1["📊 get_rates_tool"]
        T2["🏙️ get_locations_tool"]
        T3["🧮 calculate_deal_tool"]
        T4["📋 create_order_tool"]
        T5["💬 log_chat_tool"]
        T6["📅 Date & Time"]
    end

    AGENT -.-> T1 & T2 & T3 & T4 & T5 & T6

    subgraph "LLM & Memory"
        LLM["OpenRouter GPT-4o-mini"]
        MEM["Simple Memory (50 msg)"]
    end

    LLM -.-> AGENT
    MEM -.-> AGENT

3.2. Компоненты Workflow

Нода Тип Конфигурация
Telegram Trigger telegramTrigger Webhook, слушает message events, credentials: EastPay
Switch switch 5 веток: /start, voice, photo, video, text
Send Welcome telegram HTML-сообщение + 2 inline-кнопки: «Совершить обмен» (URL) и «Чат с поддержкой» (URL)
Get Audio/Photo/Video telegram Скачивание файлов через Bot API file_id
Transcribe openAi OpenAI Whisper для транскрипции голосовых
Analyze Image/Video googleGemini Gemini 2.5 Flash для анализа медиа (с caption)
Merge merge 4 входа → унифицированный поток
Edit Fields set Формирует User Input (текст) и telegram_id (из from.id)
AI Agent agent OpenRouter LLM + 6 инструментов + Simple Memory (50 сообщений)
Send Regular Message telegram Отправляет AI Agent.output обратно в чат

3.3. System Prompt AI-агента (текущая версия)

Вы — старший менеджер EastPay. Вы общаетесь с клиентами в Telegram.

Ваши задачи:
1. Квалифицировать клиента: Город, Сумма, Валютная пара.
2. Предоставлять актуальные курсы: get_rates_tool / calculate_deal_tool.
3. Создавать заявки: create_order_tool (когда клиент согласен).
4. Поддержка: log_chat_tool для сохранения сообщений менеджеру.

Правила:
- Язык: СТРОГО Русский.
- Стиль: Профессиональный, деловой, но дружелюбный.
- Никаких выдуманных данных. Только инструменты.
- Голосовые транскрибированы — работать как с текстом.

3.4. Инструменты AI-агента (Tool Bindings → Supabase)

n8n Tool Supabase RPC / Table Параметры Описание
get_rates_tool daily_rates (getAll) Получить все актуальные курсы
get_locations_tool locations (getAll) Получить все доступные города
calculate_deal_tool calculate_deal() RPC in_city_name, in_amount, in_base_pair Рассчитать сделку с наценкой города
create_order_tool bot_create_order() RPC p_tg_id, p_city_name, p_amount, p_currency_pair Создать заявку от клиента
log_chat_tool bot_log_chat_message() RPC p_tg_id, p_content Записать сообщение для менеджера
Date & Time (встроенный) Текущая дата/время

[!WARNING] Текущие ограничения n8n-реализации:

  1. Simple Memory (50 сообщений) хранится в оперативной памяти n8n, не в Supabase — при перезапуске n8n контекст теряется.
  2. bot_create_order ссылается на таблицу users, которой нет в текущей Supabase-схеме (есть clients). Нужна синхронизация.
  3. Нет привязки order_id к чат-логам — log_chat_tool пишет в chat_logs без order_id.
  4. Нет обратной связи: если менеджер ответил из админки — ответ не доставляется в Telegram.

4. Путь клиента (Customer Journey) — Детальный разбор

4.1. Точка входа: Telegram бот

Команда /start:

  1. Пользователь находит бота (ссылка, реферал, поиск).
  2. n8n Switch → ветка «/start» → Send Welcome Message (HTML).
  3. Сообщение: «EAST PAY — Обмен валют и зарубежные платежи» + 2 кнопки:
    • [Совершить обмен] → URL на Mini App
    • [Чат с поддержкой] → URL на @eastpay_support

Любое другое текстовое сообщение:

  1. n8n Switch → ветка «text» → Merge → Edit Fields → AI Agent.
  2. AI-агент обрабатывает, вызывает tools, отвечает.

[!IMPORTANT] Проблема v1: Нет промежуточных шагов между /start и кнопкой Mini App. Нет онбординга, нет FAQ.


4.2. Mini App: Навигация (6 табов)

# Таб Функция Обязательность
1 Курсы Просмотр актуальных курсов + график динамики Обязательный первый экран
2 Обмен Калькулятор и создание заявки на обмен Основная функция
3 WeChat / Alipay Пополнение китайских кошельков Дополнительная функция
4 Профиль Контакты, KYC, история заявок, чат по заявкам Личный кабинет
5 Реферальная система Ссылка, статистика, заработок Монетизация
6 Поддержка Чат с менеджером Сервис

4.3. Сценарий «Обмен валюты» (Core Flow)

Шаг 1: Просмотр курсов

Шаг 2: Заполнение формы

Шаг 3: Калькулятор

finalRate = daily_rates.rate × (1 + locations.surcharge_percent / 100)
amount_get = amount_give / finalRate

В v2 реализовано через RPC calculate_deal().

Шаг 4: Отправка заявки

Шаг 5–7: Обработка, чат, завершение

Аналогично legacy (описано выше), но с Supabase Realtime вместо polling.


5. Архитектура чата (JSONB + Realtime)

5.1. Проблема текущей реализации

Текущая таблица chat_logs хранит сообщения как простой текст:

-- Текущая структура (упрощённая)
chat_logs (
  id BIGINT,
  client_id BIGINT,    -- FK → clients
  order_id BIGINT,     -- FK → orders (nullable)
  sender TEXT,         -- 'client' | 'manager' | 'ai_agent'
  message_content TEXT,-- Простой текст
  is_read BOOLEAN,
  created_at TIMESTAMPTZ
)

Проблемы:

  1. Нет структуры сообщения (фото, голосовые, кнопки, файлы).
  2. Нет metadata (intent, tool_calls, token usage).
  3. Нет thread/conversation_id для группировки диалогов.
  4. Нет синхронизации между Telegram ↔ Личный кабинет.

5.2. Целевая архитектура: таблица conversations + messages

-- Таблица диалогов (sessions)
CREATE TABLE conversations (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  client_id BIGINT NOT NULL REFERENCES clients(id),
  order_id BIGINT REFERENCES orders(id),        -- NULL = общий чат, NOT NULL = чат по заявке
  type TEXT NOT NULL DEFAULT 'support'
    CHECK (type IN ('support', 'order', 'ai_consultation', 'onboarding')),
  status TEXT NOT NULL DEFAULT 'active'
    CHECK (status IN ('active', 'resolved', 'escalated', 'archived')),
  assigned_manager_id UUID REFERENCES profiles(id),
  ai_enabled BOOLEAN DEFAULT true,               -- AI-агент включён для этого чата
  metadata JSONB DEFAULT '{}',                    -- Произвольные данные: {source, campaign, etc}
  created_at TIMESTAMPTZ DEFAULT now(),
  updated_at TIMESTAMPTZ DEFAULT now(),
  last_message_at TIMESTAMPTZ DEFAULT now()
);

-- Таблица сообщений (JSONB-формат, как ChatGPT)
CREATE TABLE messages (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  conversation_id UUID NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
  
  -- Роль отправителя (как в OpenAI Chat API)
  role TEXT NOT NULL CHECK (role IN ('user', 'assistant', 'system', 'manager', 'tool')),
  
  -- Основной контент (JSONB — поддержка multimodal)
  content JSONB NOT NULL,
  -- Формат content для разных типов:
  -- Текст:    {"type": "text", "text": "Привет!"}
  -- Фото:     {"type": "image", "url": "https://...", "caption": "Чек", "file_id": "..."}
  -- Голос:    {"type": "voice", "url": "https://...", "transcript": "...", "duration": 12}
  -- Видео:    {"type": "video", "url": "https://...", "analysis": "...", "file_id": "..."}
  -- Документ: {"type": "document", "url": "https://...", "filename": "passport.pdf"}
  -- Tool:     {"type": "tool_result", "tool_name": "calculate_deal", "result": {...}}
  -- System:   {"type": "system", "text": "Заявка #42 создана"}
  -- Multi:    {"type": "multi", "parts": [{"type":"text","text":"..."}, {"type":"image","url":"..."}]}
  
  -- AI-метаданные
  metadata JSONB DEFAULT '{}',
  -- Формат metadata:
  -- {
  --   "intent": "exchange_request",
  --   "model": "gpt-4o-mini",
  --   "tokens": {"input": 150, "output": 80},
  --   "tool_calls": [{"name": "calculate_deal", "args": {...}}],
  --   "telegram_message_id": 12345,
  --   "source": "telegram" | "webapp" | "admin_panel",
  --   "n8n_execution_id": "abc-123"
  -- }
  
  -- Статус доставки
  is_read BOOLEAN DEFAULT false,
  delivered_to_telegram BOOLEAN DEFAULT false,
  delivered_to_webapp BOOLEAN DEFAULT false,
  
  created_at TIMESTAMPTZ DEFAULT now()
);

-- Индексы для быстрого доступа
CREATE INDEX idx_messages_conversation ON messages(conversation_id, created_at DESC);
CREATE INDEX idx_messages_role ON messages(role) WHERE role = 'user';
CREATE INDEX idx_conversations_client ON conversations(client_id);
CREATE INDEX idx_conversations_order ON conversations(order_id);
CREATE INDEX idx_messages_unread ON messages(conversation_id, is_read) WHERE is_read = false;

5.3. Синхронизация Telegram ↔ Личный кабинет ↔ Админ-панель

sequenceDiagram
    participant TG as 📱 Telegram
    participant N8N as ⚙️ n8n
    participant SB as 🗄️ Supabase
    participant RT as 🔴 Realtime
    participant WEB as 🖥️ Личный кабинет
    participant ADM as 👨‍💼 Админ-панель

    Note over TG,ADM: === Клиент пишет в Telegram ===
    TG->>N8N: Webhook: message
    N8N->>N8N: AI Agent обрабатывает
    N8N->>SB: INSERT messages (role='user', content=JSONB)
    N8N->>SB: INSERT messages (role='assistant', content=JSONB)
    SB->>RT: postgres_changes event
    RT-->>WEB: Subscription: new message
    RT-->>ADM: Subscription: new message
    N8N->>TG: sendMessage (AI ответ)
    
    Note over TG,ADM: === Менеджер пишет из Админки ===
    ADM->>SB: INSERT messages (role='manager', source='admin_panel')
    SB->>RT: postgres_changes event
    RT-->>WEB: Subscription: new message
    SB-->>N8N: Webhook / Edge Function trigger
    N8N->>TG: sendMessage (ответ менеджера)

    Note over TG,ADM: === Клиент пишет из ЛК ===
    WEB->>SB: INSERT messages (role='user', source='webapp')
    SB->>RT: postgres_changes event
    RT-->>ADM: Subscription: new message
    SB-->>N8N: Webhook / Edge Function trigger
    N8N->>TG: Mirror message to Telegram (если нужно)

5.4. Supabase Realtime — конфигурация каналов

// Подписка на сообщения конкретного чата (Личный кабинет / Админка)
const channel = supabase
  .channel(`conversation:${conversationId}`)
  .on(
    'postgres_changes',
    {
      event: 'INSERT',
      schema: 'public',
      table: 'messages',
      filter: `conversation_id=eq.${conversationId}`
    },
    (payload) => {
      // Мгновенно отобразить новое сообщение в UI
      appendMessage(payload.new);
    }
  )
  .on(
    'postgres_changes',
    {
      event: 'UPDATE',
      schema: 'public',
      table: 'messages',
      filter: `conversation_id=eq.${conversationId}`
    },
    (payload) => {
      // Обновить статус прочтения, доставки
      updateMessageStatus(payload.new);
    }
  )
  .subscribe();

// Подписка на все чаты менеджера (Админ-панель — лента)
const managerChannel = supabase
  .channel('manager-feed')
  .on(
    'postgres_changes',
    {
      event: 'INSERT',
      schema: 'public',
      table: 'messages',
      filter: `role=in.(user,assistant)`
    },
    (payload) => {
      updateChatList(payload.new);
    }
  )
  .subscribe();

6. Интеграция курсов: daily_rates ↔ Telegram ↔ Mini App

6.1. Текущая схема курсов

daily_rates (symbol PK, rate, is_active, updated_at)

Данные (7 записей):
- RUB/THB, RUB/CNY, RUB/USD, RUB/AED, RUB/USDT и т.д.

6.2. Цепочка обновления курсов

flowchart TD
    ADMIN["👨‍💼 Админ-панель\n(обновляет daily_rates)"]
    SB["🗄️ Supabase\n(daily_rates)"]
    RT["🔴 Realtime\n(postgres_changes)"]
    MINIAPP["📱 Mini App\n(Realtime subscription)"]
    CRON["⏰ pg_cron\n(проверка изменений)"]
    N8N["⚙️ n8n\n(get_rates_tool)"]
    TG["📱 Telegram\n(ответ AI-агента)"]
    ALERT["📢 Rate Alert\n(Edge Function)"]

    ADMIN -->|"UPDATE rate"| SB
    SB -->|"Мгновенно"| RT
    RT -->|"ws: INSERT/UPDATE"| MINIAPP
    
    SB -->|"Каждые 5 мин"| CRON
    CRON -->|"Если изменение > 3%"| ALERT
    ALERT -->|"sendMessage"| TG
    
    N8N -->|"SELECT * FROM daily_rates"| SB
    N8N -->|"AI-ответ с курсом"| TG

6.3. Подписка Mini App на курсы (Realtime)

// Mini App: подписка на актуальные курсы
const ratesChannel = supabase
  .channel('rates-live')
  .on(
    'postgres_changes',
    {
      event: '*', // INSERT, UPDATE, DELETE
      schema: 'public',
      table: 'daily_rates'
    },
    (payload) => {
      updateRatesUI(payload.new); // Мгновенное обновление UI
    }
  )
  .subscribe();

7. Edge Functions и Backend-функции: план реализации

7.1. Существующие Edge Functions

Функция Статус Описание
invite-user ✅ Active Приглашение пользователя
logger-service ✅ Active Логирование

7.2. Необходимые новые Edge Functions

# Edge Function Триггер Назначение
1 telegram-webhook HTTP POST (Telegram Webhook) Принимает Telegram updates → пересылает в n8n ИЛИ обрабатывает самостоятельно
2 send-telegram-message HTTP POST (internal) Отправка сообщений в Telegram от имени бота. Вызывается из админки/ЛК при ответе менеджера
3 rate-alert-notifier pg_cron (каждые 5 мин) Проверяет изменение курсов > threshold%, отправляет уведомления подписавшимся клиентам
4 retention-scheduler pg_cron (ежечасно) Проверяет «замёрзших» пользователей, отправляет follow-up сообщения
5 order-status-notifier Database Webhook (trigger на orders.status) При смене статуса заявки — уведомляет клиента в Telegram
6 sync-chat-to-telegram Database Webhook (trigger на INSERT в messages) Когда менеджер пишет из админки → доставляет в Telegram
7 process-media-upload Storage trigger (новый файл в bucket) Обработка загруженных файлов (KYC, чеки) — OCR, валидация

7.3. Необходимые новые RPC-функции (PostgreSQL)

# Функция Параметры Назначение
1 get_or_create_conversation(p_client_id, p_order_id, p_type) client_id, order_id?, type Найти или создать диалог
2 save_chat_message(p_conversation_id, p_role, p_content_jsonb, p_metadata_jsonb) conversation_id, role, content, metadata Сохранить сообщение в JSONB-формате
3 get_conversation_history(p_conversation_id, p_limit, p_offset) conversation_id, limit, offset Получить историю чата с пагинацией
4 mark_messages_read(p_conversation_id, p_reader_role) conversation_id, reader_role Пометить сообщения как прочитанные
5 get_client_conversations(p_client_id) client_id Все диалоги клиента с last_message preview
6 get_unread_count(p_conversation_id, p_reader_role) conversation_id, reader_role Количество непрочитанных сообщений
7 check_rate_changes() Сравнить текущие курсы с предыдущими, вернуть changed > threshold
8 get_stale_sessions(p_timeout_minutes) timeout_minutes Найти клиентов без активности дольше N минут

7.4. Необходимые новые pg_cron задачи

# Задача Расписание SQL
1 check-rate-alerts */5 * * * * (каждые 5 мин) SELECT net.http_post('EDGE_FN_URL/rate-alert-notifier', ...)
2 run-retention 0 10 * * * (ежедневно в 10:00) SELECT net.http_post('EDGE_FN_URL/retention-scheduler', ...)
3 archive-old-conversations 0 3 * * 0 (раз в неделю) UPDATE conversations SET status='archived' WHERE last_message_at < now() - interval '90 days'

8. Стратегия интеграции AI Chat-Agent

8.1. Концепция: «Невидимый сопроводитель»

AI Chat-Agent НЕ заменяет кнопочный флоу. Он:

8.2. Точки включения

graph TD
    A["🟢 /start"] --> B{"Действие?"}
    B -->|"Нажал «Обмен»"| C["Mini App"]
    B -->|"❄️ Молчит 3 мин"| D["🤖 AI: Приветствие"]
    B -->|"Пишет вопрос"| E["🤖 AI: Консультация"]
    
    C --> F{"Действия?"}
    F -->|"Заполняет форму"| G["✅ Бот молчит"]
    F -->|"❄️ Закрыл Mini App"| H["🤖 Follow-up 5 мин"]
    
    G --> I["Заявка создана"]
    I --> J{"Статус?"}
    J -->|"new > 10 мин"| K["🤖 «Менеджер скоро возьмёт»"]
    J -->|"processing"| L["🤖 Мониторит"]
    J -->|"done"| M["🤖 Запрос отзыва"]
    J -->|"cancelled"| N["🤖 «Новая заявка?»"]
    M --> O["🤖 Ретеншн D+7/14/30"]

8.3. Функции Chat-Agent

Функция 1: Онбординг — Таймаут 3 мин после /start без действия.

Функция 2: Консультация — Intent detection через LLM: question → AI отвечает, exchange_request → Mini App, complaint → эскалация менеджеру.

Функция 3: Follow-up — 5 триггеров с таймаутами (от 5 до 30 минут).

Функция 4: Deal Escort — Уведомления при смене статуса заявки (newprocessingdone → ретеншн).

Функция 5: Ретеншн — Периодические напоминания (D+1, D+7, D+14, D+30), курсовые алерты (>3%), реферальные пуши.


9. Принцип «Кнопки vs AI»

Ситуация Инструмент Почему
Выбор валюты, города, суммы 🔘 Кнопки / Mini App Быстрее, точнее
Расчёт суммы 🔘 Калькулятор Мгновенно
Смена статуса 🔘 Inline-кнопки Одно нажатие
«Какой курс бата?» 🤖 AI Agent Естественный язык
Клиент молчит 5 мин 🤖 AI Agent Follow-up
Клиент не был 2 недели 🤖 AI Agent Ретеншн
Жалоба 🤖 AI → 👨‍💼 Менеджер Эскалация

10. Необходимые доработки (Action Items)

[!CAUTION] Критические проблемы для исправления:

10.1. Синхронизация n8n ↔ Supabase

# Проблема Решение
1 bot_create_order ссылается на таблицу users (не существует) Обновить RPC: заменить usersclients, tg_idtelegram_id (text)
2 Simple Memory в n8n хранится в RAM Мигрировать на Supabase-backed memory: сохранять в messages таблицу
3 log_chat_tool не привязывает к conversation/order Обновить RPC: добавить p_conversation_id, p_order_id параметры
4 Нет обратной связи Admin → Telegram Добавить Edge Function sync-chat-to-telegram + trigger на INSERT в messages

10.2. Миграция структуры чата

# Действие Приоритет
1 Создать таблицы conversations + messages 🔴 Высокий
2 Мигрировать данные из chat_logsmessages 🟡 Средний
3 Обновить n8n workflow: записывать в messages (JSONB) 🔴 Высокий
4 Настроить Supabase Realtime на messages 🔴 Высокий
5 Обновить RLS-политики для conversations и messages 🔴 Высокий

10.3. Новые Edge Functions (приоритизация)

Приоритет Edge Function Когда
🔴 P0 send-telegram-message Фаза 1 — без этого менеджер не ответит в TG
🔴 P0 order-status-notifier Фаза 1 — клиент должен знать про смену статуса
🟡 P1 sync-chat-to-telegram Фаза 2 — двусторонний чат
🟡 P1 rate-alert-notifier Фаза 2 — повышение вовлечённости
🟢 P2 retention-scheduler Фаза 4 — ретеншн
🟢 P2 process-media-upload Фаза 5 — KYC automation

11. Следующие шаги реализации

Фаза 1: Фундамент (DB + Chat)

Фаза 2: Синхронизация n8n ↔ Supabase ↔ Telegram + RAG

Фаза 3: Mini App v2 (React)

Фаза 4: AI Agent v2 (Follow-up + Retention)

Фаза 5: Продвинутые функции


12. RAG & Vector Database архитектура

[!IMPORTANT] RAG (Retrieval-Augmented Generation) — ключевая технология для AI Agent'а EastPay. Она позволяет агенту обращаться к актуальной базе знаний компании перед генерацией ответа, снижая галлюцинации и обеспечивая точность информации о курсах, услугах и правилах.

12.1. Обзор RAG-архитектуры

flowchart LR
    subgraph INPUT["📥 Входящий запрос"]
        USER_MSG["💬 Сообщение клиента"]
    end

    subgraph EMBED["🧮 Embedding"]
        EMBED_Q["OpenAI text-embedding-3-small<br/>→ vector(1536)"]
    end

    subgraph SEARCH["🔍 Vector Search"]
        MATCH["match_documents()<br/>cosine similarity ≥ 0.78"]
        DOCS["📄 Релевантные документы<br/>(top 5)"]
    end

    subgraph GENERATE["🤖 Генерация ответа"]
        CONTEXT["📋 System prompt +<br/>RAG-контекст +<br/>История чата"]
        LLM["GPT-4o-mini<br/>(OpenRouter)"]
        RESPONSE["📨 Ответ клиенту"]
    end

    USER_MSG --> EMBED_Q
    EMBED_Q --> MATCH
    MATCH --> DOCS
    DOCS --> CONTEXT
    USER_MSG --> CONTEXT
    CONTEXT --> LLM
    LLM --> RESPONSE

Зачем RAG для EastPay:

Проблема без RAG Решение с RAG
AI не знает текущие курсы Подтягивает данные из daily_rates + контексты из documents
AI выдумывает условия обмена Берёт реальные правила из базы знаний
Нет информации о городах/офисах Ищет по категории location_info
Забывает историю клиента Суммарии прошлых диалогов в conversation_summary
Не знает AML/KYC правила Ищет по категории compliance

12.2. Supabase как Vector Database

pgvector — конфигурация

-- Расширение уже установлено в проекте cvzsgjksswowqgfxvrsb
-- Поддерживаемые типы: vector, halfvec, sparsevec

-- Текущая таблица documents (существует, 0 строк):
-- id       bigint PK (identity)
-- content  text
-- metadata jsonb
-- embedding vector (без ограничения размерности)

Целевая схема таблицы documents

-- Обновление таблицы documents (добавление ограничений и колонок)
ALTER TABLE documents
  ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ DEFAULT now(),
  ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ DEFAULT now(),
  ADD COLUMN IF NOT EXISTS source TEXT DEFAULT 'manual',
  ADD COLUMN IF NOT EXISTS is_active BOOLEAN DEFAULT true;

-- Ограничение размерности embedding (1536 = text-embedding-3-small)
-- Примечание: ALTER COLUMN для vector dimension делается через пересоздание
-- при первом заполнении — INSERT с vector(1536)

-- Комментарий к таблице
COMMENT ON TABLE documents IS 'RAG knowledge base — векторная база знаний AI Agent';
COMMENT ON COLUMN documents.content IS 'Текстовый контент документа (чанк)';
COMMENT ON COLUMN documents.metadata IS 'JSONB: {category, title, source_url, chunk_index, total_chunks, language}';
COMMENT ON COLUMN documents.embedding IS 'vector(1536) — OpenAI text-embedding-3-small';

Модель эмбеддингов

Параметр Значение
Модель text-embedding-3-small (OpenAI)
Размерность 1536
Метрика расстояния Cosine similarity (<=> оператор)
Тип индекса HNSW (рекомендуется для < 100k записей)
Стоимость ~$0.02 / 1M tokens

HNSW-индекс

-- Создать HNSW-индекс для косинусного расстояния
CREATE INDEX idx_documents_embedding_hnsw
ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);

-- m = 16 — кол-во связей на узел (больше = точнее, больше RAM)
-- ef_construction = 64 — точность построения (больше = медленнее build, точнее поиск)
-- Для < 10k документов эти значения оптимальны

12.3. Knowledge Base Categories (категории знаний)

Все документы хранятся в таблице documents, категоризованные через metadata->>'category':

mindmap
  root((📚 Knowledge Base))
    company_info
      FAQ
      Условия обслуживания
      О компании
      Лицензии
    service_docs
      Инструкции по обмену
      Лимиты и комиссии
      KYC требования
      Способы доставки
    rate_context
      Рыночные комментарии
      Прогнозы курсов
      Волатильность
    location_info
      Адреса офисов
      Часы работы
      Контакты по городам
      Доступные валюты
    compliance
      AML правила
      Ограничения по суммам
      Запрещённые операции
      Верификация
    conversation_summary
      AI-резюме длинных чатов
      Профиль предпочтений клиента

Формат metadata JSONB

{
  "category": "company_info",
  "title": "FAQ — Часто задаваемые вопросы",
  "language": "ru",
  "source": "manual",
  "source_url": null,
  "chunk_index": 0,
  "total_chunks": 1,
  "tags": ["faq", "general"],
  "version": "1.0",
  "last_reviewed": "2026-02-10"
}

Пример начальных документов

# metadata.category metadata.title content (чанк)
1 company_info FAQ — Что такое EastPay? EastPay — сервис обмена валют и зарубежных платежей. Работаем в Таиланде, Китае, ОАЭ...
2 service_docs Инструкция — Обмен RUB → THB Шаг 1: Выберите город. Шаг 2: Укажите сумму в рублях...
3 service_docs Лимиты и комиссии Минимальная сумма обмена: от $100. Комиссия: от 1.5%...
4 location_info Бангкок — информация Офис: Sukhumvit Rd. Часы работы: 10:00-20:00 (ICT)...
5 compliance AML/KYC правила При сумме свыше $1000 требуется верификация паспорта...
6 rate_context Текущая ситуация RUB/THB Курс рубля к бату стабилен на уровне... (обновляется ежедневно)

12.4. Embedding Pipeline (автоматическая генерация)

sequenceDiagram
    participant ADMIN as 👨‍💼 Админ / n8n
    participant DB as 🗄️ Supabase DB
    participant QUEUE as 📬 pgmq Queue
    participant CRON as ⏰ pg_cron
    participant EDGE as ⚡ Edge Function 'embed'
    participant OPENAI as 🧠 OpenAI API

    ADMIN->>DB: INSERT INTO documents (content, metadata)
    DB->>QUEUE: Trigger → pgmq.send('embed_queue', ...)
    Note over QUEUE: Сообщение в очереди:<br/>{document_id: 42}
    
    CRON->>QUEUE: Каждые 5 сек: pgmq.read('embed_queue')
    QUEUE-->>CRON: {document_id: 42}
    CRON->>EDGE: HTTP POST /embed {document_id: 42}
    
    EDGE->>DB: SELECT content FROM documents WHERE id = 42
    DB-->>EDGE: "EastPay — сервис обмена..."
    
    EDGE->>OPENAI: POST /v1/embeddings<br/>{model: "text-embedding-3-small", input: "..."}
    OPENAI-->>EDGE: [0.0023, -0.0145, 0.0391, ...]
    
    EDGE->>DB: UPDATE documents SET embedding = '[...]' WHERE id = 42
    EDGE->>QUEUE: pgmq.delete('embed_queue', msg_id)

SQL: Триггер для очереди

-- Создать очередь для embeddings
SELECT pgmq.create('embed_queue');

-- Триггер: при INSERT в documents — добавить в очередь
CREATE OR REPLACE FUNCTION queue_document_for_embedding()
RETURNS TRIGGER AS $$
BEGIN
  -- Только если embedding ещё не сгенерирован
  IF NEW.embedding IS NULL THEN
    PERFORM pgmq.send(
      'embed_queue',
      jsonb_build_object(
        'document_id', NEW.id,
        'retry_count', 0
      )
    );
  END IF;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_queue_embedding
  AFTER INSERT OR UPDATE OF content ON documents
  FOR EACH ROW
  EXECUTE FUNCTION queue_document_for_embedding();

Edge Function embed (псевдокод)

// supabase/functions/embed/index.ts
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { createClient } from "jsr:@supabase/supabase-js@2";

Deno.serve(async (req: Request) => {
  const { document_id } = await req.json();
  
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL')!,
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
  );

  // 1. Получить контент документа
  const { data: doc } = await supabase
    .from('documents')
    .select('content')
    .eq('id', document_id)
    .single();

  if (!doc) return new Response('Not found', { status: 404 });

  // 2. Сгенерировать embedding через OpenAI
  const embeddingResponse = await fetch('https://api.openai.com/v1/embeddings', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${Deno.env.get('OPENAI_API_KEY')}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: 'text-embedding-3-small',
      input: doc.content,
    }),
  });

  const { data: [{ embedding }] } = await embeddingResponse.json();

  // 3. Сохранить embedding в документ
  await supabase
    .from('documents')
    .update({ 
      embedding,
      updated_at: new Date().toISOString()
    })
    .eq('id', document_id);

  return new Response(JSON.stringify({ success: true }), {
    headers: { 'Content-Type': 'application/json' },
  });
});

12.5. Функция семантического поиска match_documents

-- RPC для семантического поиска по базе знаний
CREATE OR REPLACE FUNCTION match_documents(
  query_embedding vector(1536),
  match_threshold float DEFAULT 0.78,
  match_count int DEFAULT 5,
  filter_category text DEFAULT NULL
)
RETURNS TABLE (
  id bigint,
  content text,
  metadata jsonb,
  similarity float
)
LANGUAGE plpgsql
AS $$
BEGIN
  RETURN QUERY
  SELECT
    d.id,
    d.content,
    d.metadata,
    1 - (d.embedding <=> query_embedding) AS similarity
  FROM documents d
  WHERE
    d.is_active = true
    AND d.embedding IS NOT NULL
    AND 1 - (d.embedding <=> query_embedding) > match_threshold
    AND (filter_category IS NULL OR d.metadata->>'category' = filter_category)
  ORDER BY d.embedding <=> query_embedding
  LIMIT match_count;
END;
$$;

-- Разрешить вызов через Supabase API
GRANT EXECUTE ON FUNCTION match_documents TO anon, authenticated, service_role;

Пример вызова из n8n / Edge Function

// Шаг 1: Получить embedding запроса пользователя
const embeddingResponse = await fetch('https://api.openai.com/v1/embeddings', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${OPENAI_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'text-embedding-3-small',
    input: 'Какой курс рубля к бату в Бангкоке?',
  }),
});
const { data: [{ embedding }] } = await embeddingResponse.json();

// Шаг 2: Семантический поиск по базе знаний
const { data: docs } = await supabase
  .rpc('match_documents', {
    query_embedding: embedding,
    match_threshold: 0.78,
    match_count: 5,
    filter_category: null, // null = искать по всем категориям
  });

// Шаг 3: Инжектировать контекст в prompt
const ragContext = docs
  .map((d: any) => `[${d.metadata.category}] ${d.content}`)
  .join('\n---\n');

const systemPrompt = `
Вы — AI-ассистент EastPay. Используйте следующий контекст для ответа:

${ragContext}

Если информация не найдена в контексте — честно скажите, что не знаете.
`;

12.6. Интеграция RAG с AI Agent (n8n)

flowchart TD
    subgraph TELEGRAM["📱 Telegram"]
        MSG["Сообщение клиента"]
    end

    subgraph N8N["⚙️ n8n Workflow"]
        TRIGGER["Telegram Trigger"]
        SWITCH{{"Switch: тип сообщения"}}
        
        subgraph RAG_PIPELINE["🔍 RAG Pipeline"]
            EMBED_NODE["HTTP Request:<br/>OpenAI Embeddings API"]
            SEARCH_NODE["Supabase RPC:<br/>match_documents()"]
            FORMAT["Code Node:<br/>Форматирование контекста"]
        end

        subgraph AI_AGENT["🤖 AI Agent"]
            MEMORY["Supabase Memory Store<br/>(conversations + messages)"]
            TOOLS["Tools:<br/>get_rates, calculate_deal,<br/>create_order, log_chat"]
            AGENT["OpenAI Chat Agent<br/>(GPT-4o-mini)"]
        end
    end

    subgraph SUPABASE["🗄️ Supabase"]
        DOCS_TABLE[("documents<br/>(vector search)")]
        CONV_TABLE[("conversations +<br/>messages")]
        BIZ_DATA[("orders, clients,<br/>daily_rates")]
    end

    MSG --> TRIGGER
    TRIGGER --> SWITCH
    SWITCH -->|text| EMBED_NODE
    EMBED_NODE --> SEARCH_NODE
    SEARCH_NODE --> DOCS_TABLE
    DOCS_TABLE --> FORMAT
    FORMAT --> AGENT
    MEMORY <--> CONV_TABLE
    TOOLS <--> BIZ_DATA
    MEMORY --> AGENT
    TOOLS --> AGENT
    AGENT -->|Ответ| MSG

Supabase Memory Store (замена Simple Memory)

[!WARNING] Текущий n8n workflow использует Simple Memory (хранение в RAM). При перезапуске n8n вся история диалогов теряется. Необходимо мигрировать на Supabase-backed storage.

Архитектура памяти:

Уровень Хранилище Содержимое TTL
Short-term messages таблица Последние 20 сообщений диалога Бессрочно
Long-term documents (category: conversation_summary) AI-сжатое резюме длинных разговоров Бессрочно
Knowledge documents (все категории) Статические знания компании Ручное обновление
Tools orders, clients, daily_rates Актуальные бизнес-данные Realtime

12.7. Гибридный поиск (Vector + Full-Text)

Для повышения точности поиска используется комбинация векторного сходства и полнотекстового поиска (tsvector):

-- Гибридный поиск: vector similarity + full-text search
CREATE OR REPLACE FUNCTION hybrid_search(
  query_text text,
  query_embedding vector(1536),
  match_count int DEFAULT 5,
  full_text_weight float DEFAULT 0.3,
  semantic_weight float DEFAULT 0.7,
  filter_category text DEFAULT NULL
)
RETURNS TABLE (
  id bigint,
  content text,
  metadata jsonb,
  similarity float,
  fts_rank float,
  combined_score float
)
LANGUAGE plpgsql
AS $$
BEGIN
  RETURN QUERY
  SELECT
    d.id,
    d.content,
    d.metadata,
    1 - (d.embedding <=> query_embedding) AS similarity,
    ts_rank(to_tsvector('russian', d.content), plainto_tsquery('russian', query_text)) AS fts_rank,
    (
      semantic_weight * (1 - (d.embedding <=> query_embedding)) +
      full_text_weight * ts_rank(to_tsvector('russian', d.content), plainto_tsquery('russian', query_text))
    ) AS combined_score
  FROM documents d
  WHERE
    d.is_active = true
    AND d.embedding IS NOT NULL
    AND (filter_category IS NULL OR d.metadata->>'category' = filter_category)
  ORDER BY combined_score DESC
  LIMIT match_count;
END;
$$;

Когда использовать какой тип поиска:

Сценарий Метод Почему
«Какой курс бата?» 🧮 Vector (semantic) Смысловой поиск, не точное совпадение слов
«AML правила для суммы $5000» 🔍 Hybrid Нужен смысл + точный термин
«FAQ» 📝 Full-text Точное совпадение ключевого слова
«Расскажи про ваш сервис» 🧮 Vector Широкий семантический запрос

12.8. Action Items для RAG (приоритизация)

# Действие Приоритет Фаза Зависимости
1 Добавить колонки created_at, updated_at, source, is_active в documents 🔴 P0 2
2 Создать HNSW-индекс на documents.embedding 🔴 P0 2 #1
3 Создать RPC match_documents() 🔴 P0 2 #2
4 Развернуть Edge Function embed 🔴 P0 2 #1
5 Настроить pgmq + триггер для auto-embedding 🟡 P1 2 #4
6 Наполнить documents базовыми знаниями (≥20 документов) 🔴 P0 2 #4
7 Добавить RAG-retrieval ноду в n8n workflow 🔴 P0 2 #3, #6
8 Мигрировать n8n Simple Memory → Supabase messages 🟡 P1 2 conversations таблица
9 Создать RPC hybrid_search() 🟢 P2 5 #3
10 Автоматическое резюмирование длинных диалогов → documents 🟢 P2 5 #4

[!NOTE] Оценка объёма: При ≤ 1000 документах и HNSW-индексе — поиск занимает < 10ms. OpenAI embedding стоит ~$0.02 / 1M токенов. Для EastPay с ~100 документами базы знаний стоимость пренебрежимо мала.


12.9. Supabase AI Chat Agent Memory для n8n

[!TIP] Контекст задачи: Мы заменяем "Simple Memory" (которая хранит данные в RAM и стирается при перезагрузке n8n) на надежную базу данных Supabase. Это позволит агенту помнить диалоги даже спустя дни.

12.9.1. Концепция памяти

В реализации AI Agent есть два типа памяти:

  1. Short-Term Memory (Conversation History): То, что мы реализуем здесь. Хранение текущего диалога (последние N сообщений).
  2. Long-Term Memory (RAG Vector Store): То, что мы описали выше (поиск по базе знаний).

12.9.2. Подготовка базы данных (SQL)

n8n использует стандартную структуру таблицы для хранения истории чатов langchain. Нам нужно создать её в Supabase.

-- 1. Создаем таблицу для истории чатов n8n
CREATE TABLE IF NOT EXISTS n8n_chat_histories (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    session_id TEXT NOT NULL,
    message JSONB NOT NULL,
    created_at TIMESTAMPTZ DEFAULT now()
);

-- 2. Индекс для быстрого поиска по сессии (chat_id)
CREATE INDEX IF NOT EXISTS idx_n8n_chat_histories_session_id ON n8n_chat_histories(session_id);

-- 3. (Опционально) RLS, если нужно, но для сервисного n8n подключения можно full access
ALTER TABLE n8n_chat_histories ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Enable all access for service role" ON n8n_chat_histories
    FOR ALL
    TO service_role
    USING (true)
    WITH CHECK (true);

12.9.3. Настройка n8n Workflow

В n8n нам нужно заменить ноду "Simple Memory" на Postgres Chat Memory (так как Supabase — это Postgres).

Шаги настройки:

  1. Добавить ноду: Найдите @n8n/n8n-nodes-langchain.memoryPostgresChat (или просто "Postgres Chat Memory").
  2. Подключить к AI Agent: Соедините выход Memory ноды с входом Memory у AI Agent.
  3. Настройка Credentials:
    • Создайте новое Credential: Postgres.
    • Host: db.cvzsgjksswowqgfxvrsb.supabase.co (из настроек Supabase).
    • Database: postgres.
    • User: postgres (или ваш пользователь бд).
    • Password: (ваш пароль от БД).
    • Port: 5432.
    • SSL: Отметьте, если требуется (обычно для Supabase нужно).
  4. Параметры ноды:
    • Table Name: n8n_chat_histories (как создали выше).
    • Session ID: ОЧЕНЬ ВАЖНО! Здесь должен быть динамический ID чата из Telegram.
      • Expression: {{ $('Telegram Trigger').item.json.message.chat.id }}
      • Или если используете node "Edit Fields" перед агентом: {{ $json.telegram_id }}.
    • Context Window Length: Установите 20 (помнить последние 20 сообщений). Не ставьте слишком много, чтобы не переплачивать за токены.

12.9.4. Как это работает вместе

  1. Приходит сообщение в Telegram Trigger.
  2. Извлекается chat_id.
  3. AI Agent перед генерацией ответа "смотрит" в таблицу n8n_chat_histories по этому session_id.
  4. Он видит контекст: "А, мы только что обсуждали курс USDT".
  5. Генерирует ответ.
  6. Новое сообщение (вопрос юзера и ответ агента) автоматически записывается обратно в n8n_chat_histories.

[!IMPORTANT] Отличие от таблицы messages (раздел 1.2 plan-11):

  • n8n_chat_histories — это "сырая" память для работы LangChain внутри n8n. Она хранит JSON-структуры.
  • messages — это наша бизнесовая таблица для отображения истории в Личном Кабинете (Mini App) и админке менеджера. Рекомендация: Используйте пока обе. n8n_chat_histories для работы "мозгов" бота, а messages для отображения в UI (через log_chat_tool или save_message RPC).