27 KiB
Pleep24 — Архитектура AI Sales Agent
Аналог Pleep.app — мультиканальный AI-агент продаж на стеке FastAPI + DeepSeek + Twenty CRM.
Оглавление
- Обзор системы
- Архитектура микросервисов
- Twenty CRM — модель данных и интеграция
- Message Router — маршрутизация сообщений
- AI Agent — мозг системы
- Twenty Gateway — API-прослойка CRM
- LLM Prompt Strategy
- Knowledge Base (RAG)
- Web Widget
- Voice Integration (calls24)
- Каналы связи
- База данных
- Деплоймент (Coolify + Docker)
- Roadmap
Обзор системы
Pleep24 — self-hosted мультиканальный AI-агент продаж. Закрывает полный цикл сделки: от первого сообщения до оплаты.
Каналы связи
| Канал | Статус | Интеграция |
|---|---|---|
| Telegram | ✅ Есть | MAX Messenger Bot API (nik_bot) |
| 🔜 План | WhatsApp Business API / Baileys | |
| 🔜 План | Instagram Messaging API | |
| Web Widget | 🔜 План | WebSocket + JS виджет |
| Voice (телефон) | ✅ Есть | calls24 + Yandex AI Realtime API |
Ключевые возможности
- Единый inbox — все каналы → одна лента переписки, контекст сохраняется при переходе между каналами
- AI-агент — квалифицирует лиды, отвечает на вопросы, обрабатывает возражения, назначает встречи
- Twenty CRM — все лиды автоматически создаются/обновляются в CRM, AI сам двигает сделки по воронке
- RAG — база знаний (Qdrant): каталог, FAQ, скрипты — AI использует их при ответе
- Голос — исходящие звонки через calls24, логирование в Twenty
- Оплата — счёт прямо в чате (YooKassa / CloudPayments)
- Dashboard — админка: настройка агента, просмотр диалогов, аналитика
Что НЕ делаем (в отличие от Pleep)
- Свой CRM — используем Twenty (open source, self-hosted, уже работает)
- Свой voice engine — используем calls24 (Yandex Realtime API)
- Свой payment gateway — интеграция через YooKassa
- Своё хранилище файлов — Twenty уже хранит вложения
Архитектура микросервисов
┌──────────────────────┐
Telegram ────────────→│ │
WhatsApp ────────────→│ Message Router │──→ Redis (сессии)
Instagram ───────────→│ FastAPI :8100 │──→ Postgres (логи)
Web Widget ──────────→│ │
└──────────┬───────────┘
│ Internal HTTP
┌──────────▼───────────┐
│ AI Brain │
│ FastAPI :8101 │──→ Qdrant (RAG)
│ LLM + Tools │──→ DeepSeek / Ollama
└──────────┬───────────┘
│ Internal HTTP
┌──────────▼───────────┐
│ Twenty Gateway │
│ FastAPI :8102 │──→ Twenty CRM REST API
│ │──→ YooKassa (платежи)
└──────────────────────┘
calls24 (голос) ──→ Twenty Gateway (Notes / Activities)
Разделение на сервисы — почему три, а не один
| Сервис | Ответственность | Меняется когда |
|---|---|---|
| Message Router | Приём/отправка сообщений, сессии | Новый канал |
| AI Brain | LLM-логика, промпты, принятие решений | Меняем модель/логику |
| Twenty Gateway | API Twenty, маппинг полей, платежи | Меняем CRM |
Разделение позволяет:
- Менять LLM-модель, не трогая каналы
- Добавлять каналы, не трогая логику агента
- Мигрировать на другую CRM заменой только Gateway
Twenty CRM — модель данных и интеграция
Объекты Twenty, которые использует агент
Person (контакт)
├─ firstName, lastName
├─ email, phone
├─ city (из Telegram/чата)
├─ position (должность)
└─ avatarUrl
Company (компания)
├─ name
├─ domainName
└─ address
Deal / Opportunity (сделка)
├─ name, amount, closeDate
├─ stage (этап воронки)
├─ pipeline (какая воронка)
└─ personId, companyId
Note (заметка)
├─ title, body
└─ personId, companyId, opportunityId
Activity (активность)
├─ title, body, type (Call, Meeting, Email, Task)
├─ dueAt, completedAt
└─ personId, companyId, opportunityId
Task (задача)
├─ title, body, status
└─ assigneeId, dueAt
Воронка продаж (Pipeline)
New Lead → Contacted → Qualified → Meeting Scheduled →
Proposal Sent → Negotiation → Won / Lost
AI-агент сам определяет стадию по контексту диалога и двигает Deal через POST/PATCH.
REST API Twenty (ключевые эндпоинты)
Twenty auto-generates REST API из схемы воркспейса. Standard base: http://192.168.1.230/rest/
# Auth
Authorization: Bearer YOUR_API_KEY
# Поиск/создание Person по телефону
GET /rest/people?filter=phone[eq]{phone}&limit=1
POST /rest/people
# Компании
POST /rest/companies
GET /rest/companies?filter=name[ilike]%{query}%
# Сделки
POST /rest/opportunities
PATCH /rest/opportunities/{id} # смена stage
GET /rest/opportunities?filter=personId[eq]{id}
# Заметки
POST /rest/notes
# Активности
POST /rest/activities
# Batch (до 60 записей)
POST /rest/batch
Лимиты: 100 RPM, batch до 60 записей.
Стадия "New Lead" → Person создаётся
При первом сообщении:
- Ищем Person по phone/email/telegram_id
- Если нет → создаём Person + Note "Первое сообщение из {channel}"
- Если есть → получаем связанные Notes, Deals, Activities
- Передаём контекст в AI Brain
Message Router — маршрутизация сообщений
Назначение
Единая точка входа для всех каналов. Принимает сообщение, определяет сессию, маршрутизирует в AI Brain, возвращает ответ в канал.
API эндпоинты
POST /api/messages — принять сообщение (общий)
POST /api/webhooks/telegram — Telegram webhook
POST /api/webhooks/whatsapp — WhatsApp webhook
POST /api/webhooks/instagram — Instagram webhook
GET /api/conversations/{id} — история диалога
GET /api/conversations — список активных диалогов
GET /health
Формат сообщения
{
"channel": "telegram",
"external_id": "140245519",
"text": "Здравствуйте, интересует цена",
"timestamp": "2026-07-05T12:00:00+05:00",
"metadata": {
"chat_id": "-1001234567890",
"message_id": 456,
"username": "user123",
"first_name": "Иван"
}
}
Сессионный ключ
session_key = sha256(f"{channel}:{external_id}")
Один ключ → одна сессия, даже если пользователь переключается между каналами (если указал телефон/email).
Хранение
Postgres таблицы:
CREATE TABLE conversations (
id UUID PRIMARY KEY,
channel VARCHAR(32) NOT NULL, -- telegram, whatsapp, etc.
external_id VARCHAR(256) NOT NULL, -- ID пользователя в канале
twenty_person_id VARCHAR(64), -- UUID Person в Twenty
twenty_company_id VARCHAR(64), -- UUID Company
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(channel, external_id)
);
CREATE TABLE messages (
id UUID PRIMARY KEY,
conversation_id UUID REFERENCES conversations(id),
direction VARCHAR(8) NOT NULL, -- in, out
content TEXT NOT NULL,
channel_message_id VARCHAR(128), -- ID сообщения в канале
llm_prompt_tokens INT,
llm_completion_tokens INT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE channel_configs (
id UUID PRIMARY KEY,
channel_type VARCHAR(32) UNIQUE,
config JSONB NOT NULL, -- токены, webhook URLs
enabled BOOLEAN DEFAULT true
);
Redis (опционально)
Для rate-limiting и кеширования сессий:
conversation:{session_key}:last_activity → timestamp
conversation:{session_key}:person_id → twenty_person_id
rate_limit:{channel}:{external_id} → счётчик
AI Agent — мозг системы
Назначение
Принимает сообщение + контекст из Twenty Gateway, генерирует ответ и список действий.
API
POST /api/process
Запрос:
{
"session": {
"key": "sha256...",
"channel": "telegram",
"person": {
"id": "uuid-person",
"name": "Иван Петров",
"phone": "+79123456789"
},
"company": null,
"active_deal": {
"id": "uuid-deal",
"name": "КП на CRM",
"stage": "Contacted",
"amount": 150000
},
"last_messages": [
{"role": "user", "text": "Интересует цена"},
{"role": "assistant", "text": "Добрый день! Для какого бизнеса?"}
]
},
"message": {
"text": "Ресторан, доставка",
"timestamp": "2026-07-05T12:01:00+05:00"
},
"kb_context": "### Прайс\nБазовая — 4500 руб/мес. Pro — 9500 руб/мес..."
}
Ответ:
{
"reply": "Отлично, для ресторана с доставкой подойдёт Pro-тариф — 9500 ₽/мес. Там есть интеграция с iiko и Яндекс.Едой.",
"actions": [
{
"type": "update_deal_stage",
"data": {"deal_id": "uuid-deal", "stage": "Qualified"}
},
{
"type": "update_person",
"data": {"person_id": "uuid-person", "position": "Владелец"}
},
{
"type": "schedule_followup",
"data": {"at": "2026-07-07T14:00:00+05:00", "text": "Уточнить потребность в iiko"}
}
],
"kb_sources": ["Прайс-лист (релевантность 0.94)", "FAQ по интеграциям (0.87)"]
}
Инструменты (tools), доступные агенту
| Инструмент | Описание |
|---|---|
search_kb(query) |
Поиск в Qdrant по KB (прайс, FAQ, скрипты) |
update_person(data) |
Обновить поля контакта (должность, город и т.д.) |
update_deal_stage(deal_id, stage) |
Передвинуть сделку по воронке |
create_deal(data) |
Создать новую сделку |
add_note(text) |
Добавить заметку в CRM |
schedule_followup(at, text) |
Запланировать звонок/встречу |
handover_to_human(reason) |
Передать диалог оператору |
send_invoice(amount, description) |
Выставить счёт |
call_voice(phone, script) |
Инициировать звонок через calls24 |
Структура сервиса
ai-brain/
main.py — FastAPI app
agent.py — LLM-оркестрация (DeepSeek + tool calling)
prompts/
system_v1.md — Системный промпт (редактируемый)
personas/ — Персоны агента
sales.md — Продавец (основной)
support.md — Поддержка
qualifier.md — Квалификатор лидов
tools.py — Реализация инструментов
rag.py — Qdrant-клиент для KB
Twenty Gateway — API-прослойка CRM
Назначение
Прокси для Twenty CRM API. Скрывает детали авторизации, кеширует частые запросы, нормализует ошибки.
API
POST /api/persons/find-or-create — найти по phone/email или создать
GET /api/persons/{id} — контакт + связанные объекты
GET /api/persons/{id}/deals — активные сделки
GET /api/persons/{id}/notes — последние заметки
POST /api/persons/{id}/notes — добавить заметку
GET /api/companies/search — поиск по названию
POST /api/deals — создать сделку
PATCH /api/deals/{id}/stage — сменить этап
POST /api/activities — создать активность
GET /api/pipelines/stages — список этапов воронки
Кеширование
person:{phone} → TTL 5 min
person:{id} → TTL 1 min
deal:{id} → TTL 1 min
pipeline_stages → TTL 30 min
Управление ошибками
- 401 — ротация API-ключа
- 429 — retry с backoff (100 RPM лимит)
- Сетевые ошибки — 3 ретрая с экспоненциальной задержкой
LLM Prompt Strategy
Системный промпт (sales.md)
Ты — ИИ-продавец компании {company_name}.
ТВОЯ РОЛЬ: квалифицировать лидов, отвечать на вопросы о продукте,
обрабатывать возражения, назначать встречи, доводить до сделки.
ПРАВИЛА:
1. ВСЕГДА начинай с приветствия и представления (только в первом сообщении)
2. Квалифицируй лида: имя → потребность → бюджет → сроки → ЛПР
3. Используй базу знаний для ответов о продукте — НЕ придумывай
4. Если вопрос вне твоей компетенции → честно скажи "уточню у коллег"
и передай диалог оператору
5. Двигай сделку по воронке по мере прогресса диалога
6. Не будь навязчивым — если лид просит "подумать", предложи
вернуться к диалогу через 2-3 дня
ТОН ОБЩЕНИЯ:
- Дружелюбный, но деловой
- Короткие сообщения (2-4 предложения), не полотна текста
- Один вопрос за сообщение
- Эмодзи умеренно, без перебора
КОНТЕКСТ:
- Продукт: {product_summary}
- Прайс: {pricing_summary}
- Текущий лид: {person_name}, {person_position}, {person_city}
- Стадия сделки: {deal_stage}
ДОСТУПНЫЕ ИНСТРУМЕНТЫ:
{available_tools}
Пайплайн сообщения (как LLM принимает решение)
1. Сообщение от пользователя
2. Поиск в KB (Qdrant) → релевантные куски
3. Формирование промпта: system + KB + контекст CRM + история
4. LLM (DeepSeek) → reply + actions
5. Парсинг actions → выполнение через Twenty Gateway
6. Отправка reply в Message Router → канал
Fallback
Если DeepSeek недоступен → локальный Ollama (qwen3.6/gemma4).
Мониторинг качества
handover_rate— % диалогов, переданных оператору (норма <15%)response_time_p95— время ответа (цель <3 сек)deal_progression— % сделок, дошедших до Won
Knowledge Base (RAG)
Хранилище: Qdrant
Уже запущен на a2000. Коллекция: pleep24_kb.
Структура чанков
{
"id": "pricing_pro_tariff",
"text": "Pro-тариф: 9500 ₽/мес. Включает: до 1000 диалогов/мес, интеграцию с CRM, ...",
"metadata": {
"category": "pricing",
"source": "pricing.md",
"updated_at": "2026-07-01",
"tags": ["тариф", "цена", "pro"]
}
}
Категории KB
| Категория | Пример | Обновляется |
|---|---|---|
pricing |
Прайс-лист, тарифы, скидки | При изменении цен |
faq |
Частые вопросы и ответы | По мере накопления |
scripts |
Скрипты обработки возражений | Версионируется |
catalog |
Каталог продуктов/услуг | При изменении ассортимента |
cases |
Кейсы, отзывы, примеры | По мере появления |
legal |
Договор, оферта, реквизиты | При изменении |
Загрузка в Qdrant
python scripts/load_kb.py --file pricing.md --category pricing
python scripts/load_kb.py --file faq.md --category faq
Стратегия поиска
- Embedding-поиск: top-5 чанков по релевантности
- Фильтрация: убираем чанки с score < 0.65
- Сборка: склеиваем в контекст, добавляем в промпт
Web Widget
Чат-виджет для сайта
┌──────────────────────────────┐
│ 💬 Чат с экспертом │
│ ────────────────────────── │
│ │
│ AiAgent │
│ Здравствуйте! Я помогу │
│ подобрать решение. │
│ │
│ User │
│ Интересует CRM для │
│ ресторана │
│ │
│ ────────────────────────── │
│ [Введите сообщение...] 📎 │
└──────────────────────────────┘
Технологии
- Vanilla JS (никаких фреймворков, <50KB gzip)
- WebSocket → Message Router (быстрее HTTP polling)
- WebRTC → звонок через calls24 прямо из виджета
Встраивание
<script src="https://widget.tarasov.top/chat.js"
data-org="acme-corp"
data-theme="dark">
</script>
Параметры:
data-org— ID организации (маппится на Twenty workspace)data-theme—light/darkdata-position—right/leftdata-voice—trueесли показывать кнопку звонка
Voice Integration (calls24)
Интеграция с Twenty
После каждого звонка через calls24:
- calls24 отправляет webhook в Twenty Gateway
- Twenty Gateway создаёт:
- Activity (тип: Call) — длительность, статус, запись
- Note — транскрипт звонка (ключевые моменты)
- Если звонок квалифицирующий → создаётся/обновляется Deal
Webhook от calls24
POST /api/webhooks/calls24
{
"call_id": "call-uuid",
"direction": "outbound",
"from": "+74951234567",
"to": "+79123456789",
"status": "completed",
"duration": 245,
"recording_url": "https://...",
"transcript": "полный текст разговора...",
"summary": "Лид заинтересован в Pro-тарифе, бюджет до 15К, ЛПР — директор",
"person_id": "uuid-person" // если уже есть в Twenty
}
Исходящий звонок через AI Agent
- Агент решает:
call_voice(phone="+7912...", script="Уточнить бюджет и сроки") - Twenty Gateway → calls24 API: инициировать звонок
- После завершения → webhook создаёт Activity + Note
Каналы связи
Telegram (MAX Messenger API)
Уже работает: nik_bot, user_id 140245519.
# Отправка сообщения
curl "https://u0776982.cp.regruhosting.ru/bots/nik_bot/sendMessage" \
-d "user_id=140245519&text=Привет!"
# Webhook приём
# MAX Messenger поддерживает webhooks — настраивается в ЛК
Вариант 1 — WhatsApp Business API (Meta):
- Официальный API через Meta
- Требует верификации бизнеса
- Webhook:
POST /api/webhooks/whatsapp
Вариант 2 — Baileys (Node.js):
- Неофициальная библиотека, reverse-engineered WA Web
- Риск блокировки номера
- Не для production без резервного номера
В архитектуре закладываем абстрактный WhatsappProvider, который можно подменить.
Через Instagram Messaging API (Meta), тот же механизм что и WhatsApp.
Web Widget
WebSocket-соединение к Message Router. Не зависит от платформ.
База данных
Postgres (общая для всех сервисов)
База: pleep24
Таблицы:
| Таблица | Сервис | Назначение |
|---|---|---|
conversations |
Router | Сессии диалогов |
messages |
Router | Лог всех сообщений |
channel_configs |
Router | Конфиги каналов |
webhook_logs |
Router | Лог входящих webhook'ов |
agent_decisions |
Brain | Лог решений AI (audit trail) |
kb_documents |
Brain | Метаданные документов KB |
call_logs |
Gateway | Лог звонков calls24 |
payment_intents |
Gateway | Платёжные намерения |
Redis (опционально)
- Кеш сессий (conversation_key → person_id)
- Rate limiting
- Очередь сообщений (если нужна асинхронная обработка)
Деплоймент (Coolify + Docker)
Docker Compose
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: pleep24
POSTGRES_USER: pleep24
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7-alpine
message-router:
build: ./message-router
ports:
- "8100:8100"
environment:
DATABASE_URL: postgresql+asyncpg://...
REDIS_URL: redis://redis:6379
TELEGRAM_BOT_TOKEN: ${TELEGRAM_BOT_TOKEN}
depends_on:
- postgres
- redis
ai-brain:
build: ./ai-brain
ports:
- "8101:8101"
environment:
DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY}
QDRANT_URL: http://192.168.1.230:6333
TWENTY_GATEWAY_URL: http://twenty-gateway:8102
depends_on:
- postgres
twenty-gateway:
build: ./twenty-gateway
ports:
- "8102:8102"
environment:
TWENTY_API_KEY: ${TWENTY_API_KEY}
TWENTY_BASE_URL: http://192.168.1.230
YOOKASSA_SHOP_ID: ${YOOKASSA_SHOP_ID}
YOOKASSA_SECRET: ${YOOKASSA_SECRET}
Требования к серверу
| Ресурс | Минимум | Рекомендовано |
|---|---|---|
| RAM | 2 GB | 4 GB |
| CPU | 2 cores | 4 cores |
| Диск | 10 GB | 20 GB |
Что уже есть на a2000
| Сервис | Статус |
|---|---|
| Postgres | Есть (много инстансов, можно новый) |
| Redis | Нет → добавить в compose |
| Qdrant | ✅ 192.168.1.230:6333 |
| Twenty CRM | ✅ 192.168.1.230 (за Traefik) |
| DeepSeek API | ✅ через провайдера |
| Ollama (local fallback) | ✅ |
| Silero TTS | ✅ |
| calls24 | ✅ |
| Coolify | ✅ 192.168.1.230:8000 |
| Telegram (MAX API) | ✅ |
Roadmap
Фаза 1 — MVP (2-3 недели)
- Twenty Gateway — базовые CRUD (Person, Deal, Note)
- AI Brain — единый промпт + DeepSeek + поиск в Qdrant
- Message Router — Telegram приём/отправка
- Web Widget — простой чат на WebSocket
- Docker Compose — деплой на Coolify
Фаза 2 — Production (4-6 недель)
- WhatsApp-канал
- Интеграция calls24 → Twenty (Activity + Note после звонка)
- Админ-панель (диалоги, аналитика, настройка KB)
- Мультиарендность (несколько организаций = разные Twenty workspace)
- Мониторинг и алерты
Фаза 3 — Расширение (8-12 недель)
- Instagram-канал
- Приём платежей (YooKassa)
- A/B тестирование промптов
- Self-improving KB (AI сам предлагает дополнить FAQ)
- API для партнёров