Что должна делать система
Быстро принять обращение, определить сценарий, дать подтверждённый ответ, сохранить контекст и подготовить мастеру структурированную заявку вместо разрозненной переписки.
Сервис автоматизирует первичную обработку обращений в Telegram, отвечает только на основе подтверждённых знаний, собирает данные заявки и передаёт медицинские, конфликтные или нестандартные случаи мастеру.
Клиенты тату-студии часто задают одинаковые вопросы о стоимости, стилях, подготовке и уходе. Но часть диалогов связана со здоровьем, конфликтом, индивидуальной оценкой или реальной записью — такие решения нельзя безусловно отдавать LLM.
Быстро принять обращение, определить сценарий, дать подтверждённый ответ, сохранить контекст и подготовить мастеру структурированную заявку вместо разрозненной переписки.
Webhook не передаёт событие напрямую в бизнес-логику. FastAPI проверяет запрос и атомарно фиксирует update в PostgreSQL Inbox. Очередь получает только идентификатор сохранённого события, а исходящий ответ проходит через отдельный Transactional Outbox.
Планировщик повторно ставит готовые или зависшие Inbox- и Outbox-записи в очередь, поэтому ошибка брокера после commit не уничтожает принятую работу.
Каждый этап показывает вход, выполняемое действие и результат. Переключатель сценариев позволяет увидеть обычную доставку, восстановление после сбоя брокера и передачу диалога мастеру.
Telegram доставляет update и при сетевой неопределённости может отправить тот же event повторно.
API ограничивает размер тела, проверяет Secret Token и применяет rate limit до запуска бизнес-логики.
Update сохраняется до обращения к Redis. Уникальный внешний ID превращает повторную доставку в идемпотентное чтение.
Worker получает UUID, блокирует запись, загружает диалог и определяет intent, safety-сигналы и текущего владельца разговора.
Поиск извлекает релевантные знания, PII маскируется, а генерация разрешается только при достаточной опоре на контекст.
Бизнес-ответ и delivery-запись создаются транзакционно. Временные ошибки Telegram получают отдельный retry-cycle.
При эскалации AI блокируется, а мастер видит диалог, заявку и контекст, отвечает вручную или возвращает управление системе.
Основные решения появились не ради архитектурных терминов, а из конкретных сценариев потери данных, дублей и неконтролируемого AI-ответа.
Сначала фиксируется факт принятия update, затем выполняется best-effort постановка UUID в очередь. Recovery закрывает окно недоступности Redis.
Ответ и задание на отправку коммитятся вместе. Telegram-доставка отделена от формирования бизнес-результата и получает собственные retry-состояния.
После эскалации AI перестаёт отвечать, но сообщения клиента продолжают сохраняться. Владение диалогом становится явным состоянием.
Если подтверждённого знания недостаточно, система не просит LLM заполнить пробел. Она создаёт понятный fallback и передаёт диалог мастеру.
Телефоны и email маскируются перед внешним AI, TLS-проверка не отключается, а оригинальный текст остаётся в собственной PostgreSQL.
FOR UPDATE SKIP LOCKED и частичные уникальные индексы защищают обработку событий, единственный бизнес-ответ и один открытый диалог клиента.
Система разделяет временные и постоянные ошибки, сохраняет технические детали в логах и показывает пользователю безопасный результат.
Inbox остаётся ready; Celery Beat повторно ставит событие в очередь.
Принятое обращение не теряется.
Typed error, bounded backoff с jitter и управляемый refresh token под Redis-lock.
После лимита попыток — fallback и эскалация мастеру.
Outbox delivery возвращается в retrying с exponential backoff.
Бизнес-ответ сохранён независимо от доставки.
Диалог переводится в escalated, AI-ответ блокируется, мастер получает уведомление.
Решение остаётся у человека.
Встроенная панель позволяет мастеру видеть открытые диалоги, отвечать клиенту, вести CRM-карточку, оформлять отдельные тату-проекты и управлять расписанием без отдельного frontend-приложения.
Очередь обращений, непрочитанные сообщения, takeover, возврат к AI, ручной ответ через Outbox и закрытие диалога.
Идея, место, размер, стиль, бюджет, желаемая дата, референсы и неизменяемая история переходов по воронке.
Сеансы, согласованная цена, статусы предоплаты и PostgreSQL-ограничения, не допускающие пересечение активных записей.
CI разделён на независимые уровни: быстрые unit-проверки, интеграционный PostgreSQL-контур и сборку production Docker-образа из lock-файла.
Ruff, Alembic graph, unit suite и текущий coverage gate 74%. В regression dataset — 169 размеченных клиентских фраз.
Миграции с пустой БД и тесты конкурентной обработки Inbox/Outbox, уникальных инвариантов и health endpoints.
Проверка production/development Compose, сборка строго из uv.lock и import/config smoke-test.
Кейс не изображает систему завершённой enterprise-платформой. В репозитории зафиксированы реальные ограничения и следующий уровень production-проверок.
Bot API не принимает idempotency key для sendMessage. В редком окне после успешной отправки и до commit статуса sent возможен повтор на стороне платформы.
Mypy пока остаётся отдельным диагностическим этапом, потому что накопленный type debt ещё не погашен полностью.
Структурированные логи и health endpoints уже есть; метрики, dashboard, нагрузочные, e2e и chaos-тесты остаются следующим этапом.
Проект связывает транспорт, фоновые задачи, AI, данные клиента и реальную работу оператора. Главная ценность кейса — не количество технологий, а явные гарантии на границах между ними.
Проект продолжает развиваться итерациями: каждый крупный патч проходит полный набор проверок и ручную проверку критичного поведения.