9axis — Telegram-бот для ежедневного самонаблюдения
Telegram-бот @nineaxis_bot для самонаблюдения по 9 психологическим шкалам. Rule-based интерпретации без ИИ в рантайме, контент в MDX, Celery-scheduler, SQLAdmin, privacy-флоу. Продакшен, живой эксперимент.
Оглавление
Контекст
Между «всё ок» и «пора к специалисту» есть дыра: пользователь замечает перекос слишком поздно. Хотелось положить туда короткий ежедневный инструмент самонаблюдения — не терапию, не диагностику, а ритуал на одну минуту.
Так появился @nineaxis_bot на домене 9axis.ru. Пользователь отмечает состояние по 9 шкалам кнопками 1–10, получает один главный перекос дня, одну короткую интерпретацию и одно конкретное действие. Вечером бот уточняет, получилось ли.
Живой эксперимент в продакшене. Цифры пока не показываю — собираю фидбек и правлю по мелочи. Подробный разбор — в статье-кейсе.
Ключевые продуктовые решения
- 9 шкал: самоценность, доверие, границы, обмен, проявленность, радикализм, честность, открытость, контроль. 1–3 — левый перекос, 4–7 — баланс, 8–10 — правый перекос.
- Главный перекос дня считается как взвешенное отклонение от балансной зоны. Шкалы, приоритетные для выбранной цели пользователя, получают коэффициент ×1.3.
- Одна практика на день, одно действие. Не «работай над границами», а «сегодня скажи «нет» там, где обычно молчишь».
- Follow-up и дисциплина практик. Каждая принятая практика проходит цепочку accept → follow-up → оценка пользы. Всё в
PracticeLog. - Weekly review в двух форматах: free (2 шкалы + фокус) и premium (топ-3 + паттерны + блок дисциплины).
Почему без ИИ
Осознанный выбор. В психологическом продукте важна воспроизводимость, редполитика и стоимость. Rule-based движок + заранее написанные тексты дают всё три. На одни и те же значения шкал бот выдаёт одну и ту же интерпретацию — это можно протестировать, отревьюить и объяснить.
Контент хранится как MDX в репозитории. Изменение текста — pull request. История правок, code review, откат — всё из коробки через Git. CMS нет.
Архитектура
Три процесса, одна кодовая база
- Bot — aiogram 3, по умолчанию polling через tg-proxy (AmneziaWG + microsocks), опционально webhook через
USE_WEBHOOK=true. - Web — FastAPI с лендингом на Jinja2, webhook endpoint,
/health,/metrics, SQLAdmin. - Worker + Beat — Celery: reminders, follow-ups, weekly review, purge удалённых аккаунтов через 30 дней.
Layered backend
bot/ # handlers, keyboards, middlewares, states
api/ # FastAPI: webhook, health, metrics, landing
admin/ # SQLAdmin panel
core/ # config, database, redis, logging, metrics, locks
models/ # SQLAlchemy ORM
repositories/ # единственная точка SQL
services/ # чистая бизнес-логика (алгоритмы, расчёты)
tasks/ # Celery: scheduler и background jobs
content/ # загрузчик и парсер MDXСервисы не знают про aiogram и FastAPI — покрываются unit-тестами без БД и без бота.
Rule-based engine
Два слоя интерпретации:
- Combined rules — условия на пары/тройки шкал («control HIGH + trust LOW»), у каждой свой приоритет. Первое совпадение с наивысшим приоритетом выигрывает.
- Single-scale fallback — интерпретация одиночной шкалы по зоне, если ни одна комбинация не сработала.
Выбор главного перекоса — детерминированная функция от значений шкал и цели пользователя.
Content bundle из файловой системы
На старте приложения loader парсит docs/content/, валидирует структуру через типизированные dataclasses и прокидывает bundle в диспатчер. Ни одного байта контента в БД. Валидация запускается отдельной командой make content-check — до того, как контент доедет до пользователя.
Follow-up state machine
PracticeLog.follow_up_due_atсчитается при accept с учётом вечернего окна пользователя.- Celery Beat забирает pending follow-ups атомарно через
SELECT ... FOR UPDATE SKIP LOCKEDи метитclaimed_at/worker_id. - Advisory lock на пользователя — чтобы два воркера не отправили один и тот же follow-up дважды.
- При отсутствии ответа статус пишется как
no_response.
Privacy и crisis
/export_dataотдаёт пользователю JSON со всем, что по нему есть./delete_accountделает soft-delete: telegram_id зануляется, сообщения перестают отправляться. Через 30 дней отдельная Celery-задача физически удаляет данные.- Crisis middleware на keyword-matching: при совпадении бот прерывает текущий сценарий и отдаёт заранее заготовленный emergency message с телефоном горячей линии.
Quality gate
- Ruff + mypy + pyright — отдельные стадии в CI.
- pytest: unit без БД и integration с реальным Postgres (
alembic upgrade headчерез conftest). - Content validation в pre-commit и в CI.
- Alembic с ручным review автогенерации.
- GitLab CI — test → build → manual deploy через SSH.
- Git hooks ставятся на
make install.
Деплой
Два сервера:
- Сервер A (внутренний) —
docker-compose.prod.yml: API, bot, celery, Postgres, Redis. - Сервер B (внешний) — Nginx Proxy Manager: SSL-терминация, проксирование, access-list на
/admin.
Это даёт SSL и ограничение доступа к админке без Nginx в каждом контейнере.
Результаты
Что получилось собрать
- Полный Telegram-флоу: onboarding → initial check-in → daily check-in → accept/decline → follow-up → usefulness → history → weekly review.
- Rule-based движок с combined rules и приоритетами.
- Scheduler на Celery: reminders, follow-ups, weekly review, purge.
- SQLAdmin с login, readonly dashboard, model views и action для
is_premium. - Privacy-флоу с delayed purge через 30 дней.
- Лендинг на Jinja2 + Tailwind CSS v4 в той же кодовой базе.
- Quality gate, который переживает перезапуск воркера и не дублирует follow-ups.
Извлечённые уроки (предварительные)
- Rule-based движок в 2026 — валидный выбор для продуктов, где важны воспроизводимость и редполитика. Не всё надо решать LLM-ами.
- Контент как код работает для психологического продукта так же хорошо, как для технических курсов — code review, история правок, откат через Git.
- Контент — узкое место, а не код. ~145 единиц текста — реальный риск MVP, и делать их надо параллельно с бэкендом, а не после.
- Polling через свой прокси в российских реалиях надёжнее вебхука — меньше движущихся частей снаружи.
- Два сервера + NPM — минимальный разумный прод для одиночного проекта.
См. также
- Статья-кейс: продуктовая гипотеза и архитектура
- Tech Path Finder — другой пример «контент как код»
- PassWave — ещё один MVP с жёстким фокусом
Похожие материалы
Проекты с похожими технологиями и задачами
Slot-Me.ru — Платформа бронирования встреч
Cal.com для русского рынка: от архитектуры до production. FastAPI + React, FSD, OAuth, календари, email, 196 тестов.
- FastAPI
- React
- PostgreSQL
- Redis
- TypeScript
- +3
django.moscow — Сервисный лендинг с дизайн-системой
Production-ready лендинг с автогенерацией цветовых палитр, кастомными ESLint-правилами и строгой FSD-архитектурой. Создал дизайн-систему нового уровня для масштабируемых проектов.
- Next.js 16
- React 19
- TypeScript
- Tailwind CSS 4
- shadcn/ui
- +3
PassWave — Генератор и хранилище паролей
Минималистичный PWA: генерирует крепкие пароли, шифрует всё на клиенте, синхронизируется по желанию. MVP за 2 недели. Закрыт в апреле 2026 после взлома через уязвимость в axios.
- Next.js 16
- React 19
- TypeScript
- Supabase
- PWA
- +1