Однопроцессное серверное приложение на Python (stdlib, ThreadingHTTPServer): REST API + админка + чат-виджет. Хранилище — JSON-файлы (без СУБД), документы — SQLite с полнотекстовым индексом FTS5. Опционально подключается модель (PyTorch) файлом — без пересборки. Разворачивается в Docker одной командой.
┌────────────────────── САЙТ КЛИЕНТА ──────────────────────┐
│ [виджет widget.js] [лендинг / landing.html] │
└──────────┬───────────────────────────┬───────────────────┘
│ POST /api/chat │ POST /api/lead
▼ ▼
┌────────────────────────── app.py (один процесс) ──────────────────────────┐
│ HTTP (ThreadingHTTPServer) │
│ ├─ RAG: kb.json (словесный поиск) + kb.db (SQLite FTS5, документы) │
│ ├─ очередь генерации (батч до 8 / 50мс) → inference.py → модель (.pt) │
│ ├─ сценарий заявок (4 шага) → leads.json │
│ ├─ безопасность: фильтры, rate-limit, маскировка, security.log │
│ └─ бэкапы: auto_backup (1/сутки, ротация 7) │
└──────────┬──────────────────────────────────────────────────────────────┘
▼
data/ (JSON + kb.db) · models/ (model_300.pt + vocab.json)
Docker · volumes снаружи · HTTPS (nginx + Let's Encrypt)
app.py — сервер: API, RAG, заявки, безопасность, бэкапы inference.py — модель: ленивая загрузка, генерация, generate_batch vercel.html — админка (SPA, fetch к API, пароль, разделы) chat.html — публичный чат widget.js — виджет для сайта клиента (одна строка вставки) landing.html — лендинг (публичный) Dockerfile — python:3.11-slim + torch + pypdf docker-compose.yml — volumes data/ и models/ (снаружи контейнера) install.sh — автоустановка одной командой verify_client.sh — автотест пакета перед передачей клиенту reset_pw.py — сброс пароля админки
| Метод | Путь | Доступ | Назначение |
|---|---|---|---|
| POST | /api/chat | публичный | чат (rate-limit 10/мин/IP) |
| POST | /api/lead | публичный | заявка с лендинга |
| GET | /api/botinfo | публичный | имя и тон бота |
| GET | /api/auth/status|login|setup | публичный | пароль/токены |
| GET/POST | /api/kb, /api/dialogs, /api/leads, /api/fixes | токен | данные админки |
| GET/POST | /api/settings, /api/stats, /api/status | токен | настройки/статистика |
| GET/POST | /api/export, /api/import | токен | перенос базы одним файлом |
| POST | /api/upload | токен | файлы (txt/md/csv/json/pdf/docx), ≤100 МБ |
| GET | /widget.js, /chat, /landing | публичный | фронт |
Аутентификация: X-Auth-Token, пароль sha256(salt+pw), сравнение secrets.compare_digest.
kb.json — база знаний: [{id, q, a, date}]
dialogs.json — диалоги: [{id, q, bot, status, source, date, session}]
fixes.json — пары «вопрос → правильный ответ» (для дообучения)
leads.json — заявки: [{id, name, service, when, phone, status, date}]
settings.json — настройки (имя, правила, version)
auth.json — пароль (хеш+соль), токен
kb.db — документы: SQLite + FTS5 (чанки 3000, поиск bm25)
security.log — подозрительные попытки (IP, время, текст)
backups/ — авто-бэкап раз в сутки, ротация 7 дней
Записи атомарны (tmp + os.replace), права 600, threading.Lock.
esc())Сценарий из 4 шагов (имя → услуга → когда → телефон, валидация телефона). Сохранение в leads.json. Статусы: новая/в работе/закрыта. Отображаются в админке (раздел «Заявки»).
Вся статистика — в админке (раздел «Статистика»): диалоги за неделю, закрытые/неотвеченные, топ вопросов, заявки. Никаких внешних отправок.
./install.sh # Docker: образ + volumes + автозапуск MODEL_PATH=models/model_300.pt # подключение модели файлом (lazy-load) # железо: N305 (CPU ~15 ток/с) / NAG6 RTX 3060 / VPS с GPU # HTTPS: nginx reverse-proxy + Let's Encrypt (пример в nginx_neural.conf)
Три варианта — от самого дешёвого до «с запасом». Цифры из замеров на живом продукте, ответ на 25 слов:
| Вариант | CPU | RAM | Диск | Кого тянет |
|---|---|---|---|---|
| Минимум (старт) | 4 ядра | 4 ГБ | 40 ГБ SSD | 1-2 посетителя, ответ 3-6 с |
| Комфорт (советуем) | 8 ядер | 8 ГБ | 40-60 ГБ SSD | 3-6 посетителей одновременно |
| С запасом на рост | 8 ядер | 16 ГБ | 80 ГБ SSD | 10+ посетителей, логи и документы |
Видеокарта не обязательна. На процессоре ответ приходит за 3-6 секунд, с видеокартой — за 0,3-1 секунду. Для чата на сайте процессорной скорости достаточно: это быстрее, чем человек читает.
Почему именно такие числа. Ядер больше восьми не нужно: модель упирается не в счёт, а в скорость чтения весов из памяти. Замер на одном и том же железе: 4 потока — 16 токенов/с, 8 потоков — 15, 16 потоков на загруженной машине — 2,5 (в шесть раз хуже). Память: модель 1,7 ГБ + сайт с админкой и бот ~1,5 ГБ + система. Чтобы несколько человек получали ответ одновременно, держим два процесса модели по 4 потока — отсюда рекомендация восьми ядер: четыре дают только один процесс.
Нагрузка честно: один ответ — 3-6 секунд. Три одновременных посетителя на четырёх ядрах ждут 3 / 6 / 10 секунд (очередь), на восьми с двумя процессами — по 3 секунды парами.
Модель обучалась на окнах 256 токенов и при генерации видит последние 256 токенов промпта. Память устроена НЕ внутри окна, а снаружи — на сервере:
Сколько можно общаться по символам (1 токен ≈ 2-4 символа русского текста, в среднем ~3):
| Что | Токенов | Символов (примерно) |
|---|---|---|
| Окно генерации (весь промпт) | 256 | ~750 |
| Промпт целиком: правила + пары + вопрос | 150-250 | ~500-750 |
| Вопрос клиента (комфортно) | до 100 | до ~300 символов |
| Ответ модели (max_new 120) | до 120 | ~350-400 (5-8 предложений) |
| Пара из базы знаний | ~50-100 | ~150-300 |
Практические рекомендации: вопрос клиента до ~300 символов отвечается грамотно (больше — RAG-поиск всё равно находит суть по ключевым словам, а сам вопрос не теряется); ответ — до ~400 символов. История диалога: 1-2 короткие реплики помещаются в окно; длинная память и большой контекст (16K+) — на моделях следующего поколения.
© Ассистент · техническое описание · пакет v0.5.0