Files
pleep24/ARCHITECTURE.md
T

767 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Pleep24 — Архитектура AI Sales Agent
> Аналог [Pleep.app](https://pleep.app/) — мультиканальный AI-агент продаж на стеке FastAPI + DeepSeek + Twenty CRM.
## Оглавление
1. [Обзор системы](#обзор-системы)
2. [Архитектура микросервисов](#архитектура-микросервисов)
3. [Twenty CRM — модель данных и интеграция](#twenty-crm--модель-данных-и-интеграция)
4. [Message Router — маршрутизация сообщений](#message-router--маршрутизация-сообщений)
5. [AI Agent — мозг системы](#ai-agent--мозг-системы)
6. [Twenty Gateway — API-прослойка CRM](#twenty-gateway--api-прослойка-crm)
7. [LLM Prompt Strategy](#llm-prompt-strategy)
8. [Knowledge Base (RAG)](#knowledge-base-rag)
9. [Web Widget](#web-widget)
10. [Voice Integration (calls24)](#voice-integration-calls24)
11. [Каналы связи](#каналы-связи)
12. [База данных](#база-данных)
13. [Деплоймент (Coolify + Docker)](#деплоймент-coolify--docker)
14. [Roadmap](#roadmap)
---
## Обзор системы
Pleep24 — self-hosted мультиканальный AI-агент продаж. Закрывает полный цикл сделки: от первого сообщения до оплаты.
### Каналы связи
| Канал | Статус | Интеграция |
|-------|--------|-----------|
| Telegram | ✅ Есть | MAX Messenger Bot API (nik_bot) |
| WhatsApp | 🔜 План | WhatsApp Business API / Baileys |
| Instagram | 🔜 План | 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/`
```bash
# 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 создаётся
При первом сообщении:
1. Ищем Person по phone/email/telegram_id
2. Если нет → создаём Person + Note "Первое сообщение из {channel}"
3. Если есть → получаем связанные Notes, Deals, Activities
4. Передаём контекст в 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
```
### Формат сообщения
```json
{
"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 таблицы:**
```sql
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
```
**Запрос:**
```json
{
"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 руб/мес..."
}
```
**Ответ:**
```json
{
"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`.
### Структура чанков
```json
{
"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
```bash
python scripts/load_kb.py --file pricing.md --category pricing
python scripts/load_kb.py --file faq.md --category faq
```
### Стратегия поиска
1. Embedding-поиск: top-5 чанков по релевантности
2. Фильтрация: убираем чанки с score < 0.65
3. Сборка: склеиваем в контекст, добавляем в промпт
---
## Web Widget
### Чат-виджет для сайта
```
┌──────────────────────────────┐
│ 💬 Чат с экспертом │
│ ────────────────────────── │
│ │
│ AiAgent │
│ Здравствуйте! Я помогу │
│ подобрать решение. │
│ │
│ User │
│ Интересует CRM для │
│ ресторана │
│ │
│ ────────────────────────── │
│ [Введите сообщение...] 📎 │
└──────────────────────────────┘
```
### Технологии
- **Vanilla JS** (никаких фреймворков, <50KB gzip)
- **WebSocket** → Message Router (быстрее HTTP polling)
- **WebRTC** → звонок через calls24 прямо из виджета
### Встраивание
```html
<script src="https://widget.tarasov.top/chat.js"
data-org="acme-corp"
data-theme="dark">
</script>
```
**Параметры:**
- `data-org` — ID организации (маппится на Twenty workspace)
- `data-theme` — `light` / `dark`
- `data-position` — `right` / `left`
- `data-voice` — `true` если показывать кнопку звонка
---
## Voice Integration (calls24)
### Интеграция с Twenty
После каждого звонка через calls24:
1. calls24 отправляет webhook в Twenty Gateway
2. Twenty Gateway создаёт:
- **Activity** (тип: Call) — длительность, статус, запись
- **Note** — транскрипт звонка (ключевые моменты)
3. Если звонок квалифицирующий → создаётся/обновляется 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
1. Агент решает: `call_voice(phone="+7912...", script="Уточнить бюджет и сроки")`
2. Twenty Gateway → calls24 API: инициировать звонок
3. После завершения → webhook создаёт Activity + Note
---
## Каналы связи
### Telegram (MAX Messenger API)
Уже работает: nik_bot, user_id 140245519.
```python
# Отправка сообщения
curl "https://u0776982.cp.regruhosting.ru/bots/nik_bot/sendMessage" \
-d "user_id=140245519&text=Привет!"
# Webhook приём
# MAX Messenger поддерживает webhooks — настраивается в ЛК
```
### WhatsApp
**Вариант 1 — WhatsApp Business API (Meta):**
- Официальный API через Meta
- Требует верификации бизнеса
- Webhook: `POST /api/webhooks/whatsapp`
**Вариант 2 — Baileys (Node.js):**
- Неофициальная библиотека, reverse-engineered WA Web
- Риск блокировки номера
- Не для production без резервного номера
В архитектуре закладываем абстрактный `WhatsappProvider`, который можно подменить.
### Instagram
Через 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
```yaml
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 для партнёров