What the system needs to do
Accept an enquiry quickly, identify the scenario, respond from verified knowledge, preserve context, and turn fragmented chat messages into a structured application for the artist.
The service handles initial Telegram enquiries, answers from verified studio knowledge, gathers application details, and hands medical, sensitive, or unusual conversations to the artist.
Tattoo clients often ask the same questions about pricing, styles, preparation, and aftercare. Other conversations involve health, conflict, personal assessment, or an actual booking — decisions that should not be delegated blindly to an LLM.
Accept an enquiry quickly, identify the scenario, respond from verified knowledge, preserve context, and turn fragmented chat messages into a structured application for the artist.
The webhook does not pass an update directly into business logic. FastAPI validates and atomically stores it in PostgreSQL Inbox. The queue receives only the persisted event ID, while each outgoing reply is delivered through a separate Transactional Outbox.
The scheduler re-enqueues ready or stuck Inbox and Outbox records, so a broker failure after the database commit does not erase accepted work.
Each stage exposes its input, action, and result. Scenario controls show normal delivery, broker recovery, and a conversation handoff to the artist.
Telegram delivers an update and may send the same event again when network acknowledgement is uncertain.
The API limits request size, verifies the Secret Token, and applies rate limiting before business processing begins.
The update is stored before Redis is contacted. A unique external ID turns repeated delivery into an idempotent lookup.
The worker receives the UUID, locks the record, loads the conversation, and evaluates intent, safety signals, and ownership.
Retrieval finds relevant knowledge, PII is masked, and generation proceeds only when the available context is strong enough.
The business reply and delivery record commit together. Temporary Telegram failures enter an independent retry cycle.
After escalation, AI is blocked while the artist sees the conversation, lead data, and context, then replies or returns ownership.
The core decisions respond to concrete risks: lost work, duplicate processing, unreliable delivery, and AI answers without sufficient evidence.
The accepted update is committed first; queueing its UUID is best effort. Recovery covers the broker-unavailable window.
The reply and its delivery job commit together. Telegram delivery is separated from the business result and has its own retry state.
Once escalated, AI stops replying while new client messages remain stored. Conversation ownership becomes explicit state.
If verified knowledge is insufficient, the system does not ask the LLM to fill the gap. It creates a safe fallback and transfers the conversation.
Phone numbers and email addresses are masked before external AI calls, TLS verification remains enabled, and original text stays in PostgreSQL.
FOR UPDATE SKIP LOCKED and partial unique indexes protect event processing, the single business reply, and one open conversation per client.
The system distinguishes temporary from permanent failures, keeps technical detail in logs, and produces a safe user-facing outcome.
Inbox remains ready; Celery Beat re-enqueues the event.
Accepted work is not lost.
Typed errors, bounded jittered backoff, and controlled token refresh behind a Redis lock.
After the retry limit, the assistant falls back and escalates.
The Outbox delivery returns to retrying with exponential backoff.
The business reply remains persisted independently.
The conversation becomes escalated, AI replies stop, and the artist is notified.
The decision remains with a person.
The built-in workspace lets the artist review conversations, reply to clients, maintain CRM context, create separate tattoo applications, and manage appointments without a separate frontend application.
An enquiry queue, unread counters, takeover, return to AI, manual replies through Outbox, and conversation closure.
Idea, placement, size, style, budget, preferred date, references, and an immutable history of funnel transitions.
Sessions, agreed price, deposit states, and PostgreSQL constraints that prevent overlapping active appointments.
CI is split into independent layers: fast unit checks, a PostgreSQL integration environment, and production Docker image validation from the lock file.
Ruff, Alembic graph checks, the unit suite, and the current 74% coverage gate. The regression dataset contains 169 labelled client phrases.
Migrations from an empty database and concurrent Inbox/Outbox, uniqueness, and health endpoint tests.
Production and development Compose validation, an image built strictly from uv.lock, and import/config smoke tests.
The case study does not present the system as a finished enterprise platform. The repository documents current limitations and the next level of production validation.
The Bot API does not accept an idempotency key for sendMessage. A rare crash after a successful send but before committing sent state can duplicate a platform message.
Mypy remains a diagnostic stage while accumulated type debt is being reduced rather than a blocking quality gate.
Structured logs and health endpoints exist; metrics, dashboards, load, end-to-end, and chaos tests remain on the roadmap.
The project connects transport, background work, AI, client data, and the operator's real workflow. Its value is not the number of technologies but the explicit guarantees between them.
The project continues through verified iterations: each substantial patch passes the complete check suite and manual validation of critical behaviour.