Перейти к содержимому
Основатель, Full-stack Engineer2026
#Python 3.13#aiogram 3#FastAPI#SQLAlchemy 2 async#Alembic#PostgreSQL 16#Redis 7#Celery 5#SQLAdmin#Tailwind CSS v4

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 нет.

Архитектура

Python 3.13aiogram 3FastAPISQLAlchemy 2 asyncAlembicPostgreSQL 16Redis 7Celery 5SQLAdminstructlogJinja2Tailwind CSS v4

Три процесса, одна кодовая база

  • 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

Два слоя интерпретации:

  1. Combined rules — условия на пары/тройки шкал («control HIGH + trust LOW»), у каждой свой приоритет. Первое совпадение с наивысшим приоритетом выигрывает.
  2. 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 в каждом контейнере.

Результаты

MVP
в продакшене
0
LLM-вызовов в рантайме
~145
единиц контента в MDX
100%
контента под Git

Что получилось собрать

  • Полный 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 — минимальный разумный прод для одиночного проекта.

См. также

Похожие материалы

Проекты с похожими технологиями и задачами