ИИ-поддержка — это LLM-ассистент, который отвечает клиентам в тикетах и помогает наполнять базу знаний. Работает как fallback за движком автоответов по шаблонам: если ни одно правило не сработало, ассистент собирает контекст (клиент/подписка/сервера/статьи вики/история тикета), спрашивает у LLM ответ + уверенность и либо отправляет клиенту сам (высокая уверенность), либо кладёт черновик админу в чат тикета.
Дополнительно есть База знаний (/ai-knowledge) — внутренняя вики, куда админ вносит статьи, ассистент читает их через семантический поиск и цитирует в ответах. Плюс — вкладка «Предложения ИИ», которая генерирует статьи из истории закрытых тикетов и требует апрува от админа.
Что нужно для работы
- Провайдер LLM с OpenAI-совместимым API. По умолчанию — OpenRouter, потому что один ключ покрывает и chat, и embeddings, и любую модель. Работают также OpenAI, Anthropic (через OpenRouter), Ollama, LM Studio, vLLM — всё, что говорит по контракту
POST {base}/chat/completions. - Отдельная pgvector-БД (сервис
kb-dbв compose) — сюда пишется база знаний. Основная БД pgvector не требует. - Опционально: модель эмбеддингов — включает семантический поиск в вики и умную дедупликацию сгенерированных статей.
Приоритеты стоимости
Дороже всего LLM платит за автоответ клиентам в тикетах: он вызывается на каждое новое сообщение без сработавшего правила. Всё остальное (генерация статей, эмбеддинги) — по требованию.
Быстрый старт
Добавьте сервис KB-БД в
docker-compose.yaml— рядом с существующимdb:yamlservices: # Dedicated pgvector DB for the AI support knowledge base. Optional: # the bot runs fine without it (AI_KB_DATABASE_URL empty = KB/RAG disabled). kb-db: image: pgvector/pgvector:pg18 container_name: 'rwp-shop-kb-db' restart: unless-stopped environment: - POSTGRES_USER=${AI_KB_POSTGRES_USER:-kb} - POSTGRES_PASSWORD=${AI_KB_POSTGRES_PASSWORD:-kb} - POSTGRES_DB=${AI_KB_POSTGRES_DB:-kb} - TZ=UTC ports: - '127.0.0.1:4312:5432' volumes: - kb-db-data:/var/lib/postgresql healthcheck: test: [ 'CMD-SHELL', 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}' ] interval: 3s timeout: 10s retries: 3 # Добавляйте только если бот и Remnawave находятся на одном сервере # и подключены к одной Docker-сети. networks: - remnawave-network volumes: kb-db-data: driver: local external: false name: rwp-shop-kb-db-dataТакже добавьте
AI_KB_DATABASE_URLв environment сервисаbot, чтобы он видел новую БД:yamlservices: bot: environment: - AI_KB_DATABASE_URL=${AI_KB_DATABASE_URL:-}Поднимите:
bashdocker compose up -d kb-dbСхема создаётся автоматически при первом запуске бота — миграцию накатывать не надо.
Пропишите переменную окружения в
.env:AI_KB_DATABASE_URL=postgres://kb:kb@kb-db:5432/kb?sslmode=disableПусто = KB и RAG выключены (ассистент всё равно работает, но без базы знаний).
Перезапустите бота:
bashdocker compose down && docker compose up -dНастройте провайдера LLM в админ-панели →
/ai-settings:- Base URL: пусто (= OpenRouter), либо
https://api.openai.com/v1, либо URL Ollama и т.д. - Модель:
openai/gpt-4o-mini,anthropic/claude-sonnet-5и т.д. (список подгружается автокомплитом из API провайдера). - API-ключ: ключ провайдера.
- Модель эмбеддингов:
openai/text-embedding-3-small(для OpenRouter/OpenAI). Пусто = семантический поиск выключен. - Включите «Включить ассистента».
- Base URL: пусто (= OpenRouter), либо
Наполните базу знаний →
/ai-knowledge→ «Статья». Одна статья = одна тема + плейн-текст. Разметка не нужна — модель читает содержимое напрямую.Готово. Откройте тикет от тестового клиента — при уверенности выше порога ассистент ответит сам, иначе черновик появится над строкой ввода в чате тикета у админа.
Настройки на /ai-settings
Ассистент
- Base URL — OpenAI-совместимый эндпоинт. Пусто = OpenRouter (
https://openrouter.ai/api/v1). - Модель — идентификатор модели у провайдера (например
openai/gpt-4o-mini). Автокомплит из/v1/models. - API-ключ — маскированное поле. Ключ пишется в таблицу
settings(не в env), как остальные секреты платёжных провайдеров. - Системный промпт — что модель делает, тон общения, границы. Пусто = встроенный дефолт с базовыми правилами (только факты из контекста, не менять аккаунт, эскалировать деньги/юридику).
- Язык ответов — фиксированный язык (
ru,en,uk, …). Пусто = язык клиента.
Порог и лимиты
| Настройка | Дефолт | Смысл |
|---|---|---|
| Порог уверенности | 0.75 | Ниже — черновик админу, ≥ — автоответ клиенту. 1.0 = все ответы кладём в черновики. |
| Максимум вызовов на тикет в час | 3 | Защита от петли/раскрутки счёта |
| Дебаунс, сек | 30 | Не отвечать, если бот/админ уже писал недавно |
| Максимум токенов ответа | 1024 | Максимум output-токенов |
| Temperature | 0.3 | Ниже — детерминированнее, выше — креативнее |
| Сообщений истории в контексте | 12 | Сколько последних сообщений тикета видит модель |
| Заметки админа | выкл | Передавать ли модели admin_notes клиента |
Уведомления
- Уведомлять админов о черновиках — Telegram-уведомление, когда модель не уверена и оставила черновик. Приходит с кнопками «Открыть чат» + «Открыть в браузере».
- Очередь эскалации — при неуверенном ответе тикет автоматически перекладывается в выбранную очередь. Пусто = не перекладывать.
Эмбеддинги (RAG)
- Модель эмбеддингов — например
openai/text-embedding-3-small. По умолчанию использует основной Base URL и ключ (OpenRouter умеет и то и другое). Можно переопределить. - Размерность вектора —
1536для OpenAI. Фиксируется при первом создании таблицы KB — смена требует пересоздания таблицы (см. Смена embedding-модели). - Статей в контексте (top-N) — сколько ближайших статей передаётся модели.
5— обычно достаточно. - Категория шаблонов-фолбэка — если KB-БД не подключена, ассистент возьмёт шаблоны сообщений этой категории. Полностью опционально.
- Переэмбеддить базу знаний — кнопка. Запускать после смены embedding-модели: перечитывает все статьи и генерирует эмбеддинги заново.
Плейсхолдеры и разделы
Ассистент умеет вставлять в ответ шаблонные переменные ({plan_name}, {days_left}, {share_link_url}, …) и ссылки на разделы мини-аппа (#[cabinet], #[install], #[billing], …). Модель видит только те переменные и ссылки, которые вы отметили в MultiSelect. Пустой список = разрешены все.
Полный список — см. Плейсхолдеры и разделы.
База знаний (/ai-knowledge)
Вкладка «Статьи»
Обычный CRUD. Каждая статья:
- Заголовок — короткая формулировка вопроса или темы.
- Содержимое — плейн-текст (не Markdown). Хорошо работает шаблон «Проблема → Решение (шаги) → Что делать, если не сработало».
- Теги — свободные, для админ-поиска. Модель их не использует.
- Переключатель «Использовать в ответах» — выключенная статья не участвует в поиске.
При создании/редактировании статьи вычисляется её эмбеддинг и сохраняется. Ассистент делает семантический поиск по последнему сообщению клиента и передаёт модели top-N ближайших.
Вкладка «Предложения ИИ»
Пройтись по всем закрытым тикетам и попросить LLM сгенерировать статьи. Каждое предложение требует апрува админа перед сохранением.
Как работает:
- Админ жмёт «Сгенерировать из тикетов», задаёт период дат и лимит тикетов.
- Кнопка «Рассчитать» показывает количество тикетов, батчей и оценку токенов + предупреждение о стоимости.
- «Запустить» — фоновая генерация через
sequence-очередь. Тикеты режутся на батчи (по умолчанию 15 на батч), каждый батч — один вызов LLM. - Для каждой сгенерированной статьи считается семантический дедуп: если топ-1 сходство с существующей статьей ≥ порога (
0.85по умолчанию) — она помечается «Дубль» и не мешает работать с настоящими новыми статьями. - Админ смотрит карточки, редактирует title/content/tags при желании, жмёт «Принять» (записывается в базу знаний) или «Отклонить».
Стоимость
Генерация — один LLM-вызов на батч. По оценке для средней инсталляции с 500 закрытых тикетов это ~34 батча и порядка 3-4 млн input-токенов. Реальная стоимость зависит от модели и провайдера — модалка показывает оценку в токенах, курс в USD смотрите у провайдера.
Настройки генерации из тикетов
Задаются в таблице settings (в UI не выведены — обычно дефолтов достаточно):
ai_kb_gen_batch_size=15— тикетов на LLM-вызов.ai_kb_gen_max_messages_per_ticket=20— сколько последних сообщений тикета передаётся модели.ai_kb_gen_duplicate_threshold=0.85— порог сходства для дедупа.
Автоответы в тикетах
Как срабатывает
Ассистент подключён как fallback после движка автоответов по шаблонам. Порядок:
- Клиент пишет в тикет.
- Проверяется, есть ли активное правило автоответа для триггера (
new_ticket/new_message) — если да, отправляется шаблон, ассистент не вызывается. - Если правила нет — включается ассистент (только для тикетов основного бота,
bot_id = 0). - Ассистент собирает контекст, спрашивает у LLM и:
- уверен (score ≥ порога) → отправляет ответ клиенту как бот, тикет переходит в
awaiting_reply, дальше существующий cron авто-закрытия закрывает тикет, если клиент не отвечает; - не уверен → пишет строку в
support_ai_draftсstatus='pending_review', админ видит черновик в чате.
- уверен (score ≥ порога) → отправляет ответ клиенту как бот, тикет переходит в
Что видит модель
В каждый запрос уходит:
- Текущее время (UTC) — иначе модель не знает, какой сегодня день, и не может отвечать «сколько дней осталось».
- Клиент — язык, скидка, температура лида, при опции — заметки админа.
- Подписка — тип, дата окончания + предвычисленное
days_left, доп. устройства. - Тариф — имя, тип, лимит трафика/устройств.
- Состояние серверов — сводка из healthcheck + статус VPN-нод из xray-checker (если настроен).
- База знаний — top-N ближайших статей по эмбеддингу последнего сообщения клиента.
- История тикета — последние N сообщений + subject.
- Список разрешённых плейсхолдеров и разделов — только те, что отмечены в настройках.
Черновики в чате тикета
Панель «Черновик AI» появляется над строкой ввода в чате тикета у оператора. Три кнопки:
- В поле — вставить черновик в composer для правки перед отправкой.
- Отправить — доставить клиенту как бот (те же каналы, что автоответ).
- Отклонить — отбросить.
При ручном ответе админа в тикет все pending-черновики этого тикета автоматически помечаются dismissed — не копятся.
Смена embedding-модели
Размерность вектора фиксируется при первом создании таблицы KB. Если вы включили эмбеддинги с text-embedding-3-small (1536), а потом захотели перейти на модель с 3072 — таблицу нужно пересоздать:
docker compose exec kb-db psql -U kb -d kb -c "DROP TABLE support_ai_knowledge"После этого:
- Обновите Модель эмбеддингов и Размерность вектора на
/ai-settings. - Перезапустите бота — таблица создастся с новой размерностью.
- Верните статьи (если экспортировали) или включите «Переэмбеддить базу знаний» — если статьи не удалялись, эмбеддинги перегенерятся.
Плейсхолдеры и ссылки
Модели передаётся список разрешённых плейсхолдеров и разделов. Разворачивает их сервер уже перед доставкой клиенту.
Плейсхолдеры
| Плейсхолдер | Значение |
|---|---|
{username} | @username из Telegram |
{first_name} | Имя |
{telegram_id} | ID клиента в Telegram |
{plan_name} | Текущий тариф |
{sub_name} | Имя основной подписки |
{expire_date} | Дата окончания подписки |
{days_left} | Осталось дней до окончания |
{time_left} | Осталось времени (человекочитаемо) |
{link_sub} | Прямая ссылка на подписку |
{share_link_url} | Одноразовая ссылка «Установить на другое устройство» |
{referral_link} | Реферальная ссылка через бот |
{referral_link_web} | Реферальная ссылка через веб |
Разделы
Разворачиваются в кликабельные ссылки в мини-аппе:
#[dashboard], #[plans], #[billing], #[purchases], #[promos], #[referrals], #[cabinet], #[security], #[server], #[install], #[traffic], #[support], #[partner]
Стоимость и лимиты
Что тратит токены:
| Операция | Частота | Обычная стоимость |
|---|---|---|
| Автоответ клиенту | На каждое новое сообщение без сработавшего правила | Основная строка расхода |
| Эмбеддинг статьи вики | При создании/редактировании статьи | Копейки |
| Эмбеддинг запроса клиента | Каждый автоответ (для RAG) | Копейки |
| Генерация статей из тикетов | По кнопке админа | Единицы-десятки долларов на 500-1000 тикетов |
| Переэмбеддить базу знаний | По кнопке админа | Копейки на десятки статей |
Устранение неполадок
Модель не отвечает / молчит. Проверьте: (а) ai_support_enabled включено, (б) модель настроена, (в) API-ключ настроен, (г) бот перезапущен после смены AI_KB_DATABASE_URL. Логи ai-support: ... в stdout бота.
Клиенту приходит {plan_name} вместо имени тарифа. Проверьте, что в MultiSelect «Доступные плейсхолдеры для ИИ» этот ключ отмечен. Пусто = разрешены все.
«База знаний недоступна». AI_KB_DATABASE_URL не задан или KB-БД не запущена. docker compose up -d kb-db + перезапуск бота.
RAG отключён, ассистент только по шаблонам. Модель эмбеддингов не задана либо неверный ключ. Проверьте /ai-settings → «Эмбеддинги».
Генерация статей не стартует. Убедитесь, что настроен провайдер LLM (кнопка «Запустить» гейтится успешным «Рассчитать» и наличием KB-БД). Если запустилась, но не идут пропозалы — смотрите логи sequence-worker в stdout бота.