Когда-то, ещё лет десять назад, я был админом сервера World of Warcraft на движке MaNGOS (олдам привет). Для непосвящённых — это open-source эмулятор «мира», где можно поднять свой WoW-сервер.

Но именно там я впервые понял, что один процесс не вытянет всё сразу.

  • Авторизация (realmd) жила своей жизнью. Если она падала — мир продолжал работать, но игроки не могли зайти.

  • Мир (worldd) был самым тяжёлым: квесты, спеллы, рейды, скрипты. Малейший баг — и процесс жрал 100% CPU, лагали все.

  • База данных (MySQL) пухла с каждым новым патчем и логами игроков. Один неудачный запрос — и сервер уходил в ступор.

  • Веб-кабинет для регистрации и донатов мы держали отдельно, потому что PHP-скрипты конфликтовали с основным сервером.

  • Позже появился даже отдельный FTP для патчей и аддонов, потому что загрузки «клали» игровой сервер.

Тогда я впервые сформулировал правило:

«Каждый сервис должен жить отдельно, иначе падение одного тянет за собой всех».

На MaNGOS это выглядело как realmd + worldd + MySQL + вебкабинет. Сегодня, в проекте с ChatGPT-ботом в Телеграме, ситуация ровно такая же — только вместо эльфов и орков у нас платежи, AI-агенты и медиафайлы.

Когда смотришь на Telegram-ботов с ChatGPT, кажется: ну что там сложного? Взял API-ключ, накатал 50 строк на Python, задеплоил на бесплатный VPS — и готово.

Но как только бот начинает обрастать (ну будет обрастать) тысячами пользователей, простая картинка разваливается.
Банально: одному серверу сложно и слушать вебхуки Телеграма, и общаться с банком по реккурентным платежам, и держать базы, и тянуть видео-транскодинг. Поэтому мы ушли в кластерную архитектуру: каждый модуль живёт на своём железе.

так чисто для олдов добавил окно мангоса
так чисто для олдов добавил окно мангоса
вот на такой балолайке работал вов сервак с онлайном в 1360 человек
вот на такой балолайке работал вов сервак с онлайном в 1360 человек

Из WoW в Телеграм: те же грабли, только в другой обёртке

Опыт с MaNGOS стал для меня настоящей «боевой школой». Тогда я понял, что даже маленький личный сервер с сотней игроков требует раздельных процессов, мониторинга и планирования ресурсов. Сегодня, когда мы строим кластер под ChatGPT-бота в Телеграме, всё выглядит гораздо современнее, но логика осталась той же.

В WoW у нас было:

  • realmd — авторизация.

  • worldd — игровой мир.

  • MySQL — база для аккаунтов и мира.

  • вебкабинет + FTP для патчей.

В Телеграм-боте с AI у нас теперь:

  • Модуль авторизации — то же самое «realmd», только для пользователей. Встречает нового юзера, проверяет баланс и подписку.

  • Модуль платежей — аналог донат-модуля. Отвечает за деньги, чеки, реккурентные списания.

  • Две базы — PostgreSQL и MySQL, как когда-то world и accounts. Каждая со своей ролью.

  • Текстовый модуль — наш «worldd», только вместо квестов он гоняет запросы к LLM.

  • Видео, аудио, изображения — как отдельные «подсистемы», каждая со своим прожорливым характером.

  • S3-хранилище — вместо FTP, где мы когда-то держали патчи и клиент.

  • Mini-web-app — эволюция того самого вебкабинета, только теперь внутри Телеграма.

  • Тестовый сервер — аналог PTR (Public Test Realm). Всё новое сначала идёт туда.

Почему модульность — не блажь, а необходимость

Тут важно подчеркнуть мысль: разделение на модули — это не «модно» и не «по канонам микросервисов». Это проверенный временем способ не уронить всё разом.

Когда я админил WoW-сервер, игроки прощали лаги мобов, но не прощали падения авторизации или багов с донатом.

Когда мы делаем Telegram-бота, пользователи прощают задержку в генерации картинки, но не простят списание денег без услуги или вечный «Загрузка…» при старте диалога.

Вот почему у каждого критичного элемента — свой сервер. Так падает только он, а не весь проект.

Экономика: токены, батчи и маржинальность

аждый диалог с моделью — это не только входящий текст пользователя, но и выход, который генерирует ИИ. И если вход обычно короткий («привет», «напиши план проекта», «сделай резюме»), то выход может быть в десять раз длиннее. Именно за это OpenAI, DeepSeek и прочие берут деньги.

На практике это выглядит так: пользователь написал один короткий вопрос, а модель вернула целую простыню на полторы тысячи символов. В терминах токенов это 30 на вход и 200 на выход. Себестоимость такого диалога — копейки, но когда умножаешь на тысячи пользователей и сотни сообщений каждый день, счёт растёт лавинообразно.

Экономика и решения о которых забывают.

батчи

Это стратегия складывать несколько однотипных задач в общую партию и отправлять/обрабатывать их за один проход вместо десятков отдельных обращений. Цель — меньше накладных расходов на запрослучше утилизация модели/сети, ниже цена за токены (за счёт уменьшения служебного контекста и числа сетевых раундов).

Как это работает по шагам

  1. Очередь: все мелкие задания попадают в одну (или несколько) очередей с метками типа задачи.

  2. Агрегация: воркер «снимает» из очереди до N элементов или ждёт T миллисекунд, чтобы набрать партию.

  3. Формирование партии: строится один запрос, где каждая подзадача имеет свой идентификатор (ID) и минимальный контекст.

  4. Вызов модели: отправляем один запрос с массивом подзадач (логически — одно общение).

  5. Разбор ответа: распределяем результаты по исходным ID, сохраняем/отправляем пользователям.

  6. Недоставленные/ошибки: частичный успех — ок, «плохие» элементы улетают в повторную очередь/Dead-Letter Queue (DLQ).

Ключевые настройки

  • Политика набораN (размер батча), T (макс. задержка набора). Баланс между задержкой и экономией.

  • Idempotency: каждому подпункту — устойчивый ID; повторная отправка не должна дублировать результат.

  • Пределы модели: следим за лимитами токенов/веса запроса; при переполнении — делим партию.

  • Сортировка: объединяйте действительно похожие задачи — модели легче «входить в ритм», меньше контекстного «мусора».

  • Мониторинг: собирайте метрики «экономия/латентность/ошибки» отдельно для батч-контуров.

Плюсы / Минусы

  • Плюсы: меньше сетевых раундов, меньше повторяющегося системного контекста, выше пропускная, дешевле.

  • Минусы: дополнительная задержка на набор партии; при всплесках — сложнее локализовать сбой внутри пакета.

Что такое «фоллбеки» (fallbacks)


Это маршрутизация запросов по уровням надёжности/стоимости, когда «дорогой» или «умный» путь заменяется «более дешёвым/простым/надёжным» при определённых условиях: ошибка, таймаут, перегрузка, лимиты, или задача слишком простая для heavy-модели.

Модель уровней (tiers)

  • Tier A (премиум/дорого): мощные модели/длинные контексты/лучшее качество.

  • Tier B (сбалансированный): средние по цене/качеству, покрывают 70–80% задач.

  • Tier C (эконом): дешёвые или локальные модели для «привет/как дела/суммируй 3 строки».

Варианты фоллбеков

  1. По простоте задачи («routing by intent/complexity»): простые запросы сразу идут на Tier C; сложные — на A/B.

  2. По SLA/таймаутам: если Tier A не ответил за X мс — переключаем на Tier B, затем на C.

  3. По ошибкам/лимитам: при 429/5xx у провайдера — мгновенный переход вниз по лестнице.

  4. По стоимости: если прогнозируемый объём ответа слишком велик — предлагаем пользователю краткую версию (эконом-вариант) или ведём на модель подешевле.

  5. По безопасности/модерации: если модерация «подозревает» риск — принудительно через модель с более строгыми фильтрами.

Как это устроить по шагам

  1. Классификация запроса: лёгкий/средний/сложный; нужен ли инструмент (код, поиск, RAG) или хватит «болталки».

  2. План маршрута: первичный таргет (например, Tier B) + политика деградации (B→C, B→A, A→C и т. п.).

  3. Ограничители: глобальные лимиты токенов, макс. длина ответа, температурные пресеты для каждого Tier.

  4. Hedging (опционально): параллельный запуск двух дешёвых запросов с ранней отдачей первого успешного.

  5. Объединитель результата: если есть частичный контекст или подсказка от «простого» варианта — используем её как черновик для «умного» (иногда наоборот).

  6. Логирование: фиксируем маршрут, время, стоимость и качество (оценку), чтобы «учить» маршрутизатор.

Плюсы / Минусы

  • Плюсы: устойчивость к сбоям провайдера, предсказуемая стоимость, быстрые ответы на простые вопросы, возможность держать SLA.

  • Минусы: сложнее отладка и аналитика; риск «переэкономить» и ухудшить качество, если маршрутизация агрессивна.

Как это сочетается в одном проекте

  • Входящий поток сначала попадает в роутер (определяем сложность/намерение/ограничения).

  • Если задача мелкая и массовая → батч-контур (экономим сетевые раунды).

  • Если задача диалоговая и непредсказуемая → сразу в Tier B, с фоллбеком на Tier C по таймауту или стоимости; для «важных» запросов — наоборот, B→A.

  • Модерация/безопасность всегда может перехватить и изменить маршрут (например, отправить в более строгую модель).

  • Метрики: отдельно считаем выгоду батчей (экономия токенов/сетевых вызовов) и эффективность фоллбеков (доля переключений, спасённые запросы, прирост SLA, разница в цене).

Практические правила (коротко)

  • Батчируйте только однотипные микро-задачи; держите гибкий размер партии и предел по времени набора.

  • Всегда используйте идемпотентность (устойчивые ID) и DLQ для частичных сбоев в батчах.

  • В фоллбеке начинайте с Tier B как «рабочей лошадки», опускайтесь на C при перегрузке/лимитах, поднимайтесь на A для VIP-кейсов и «тяжёлых» задач.

  • Держите жёсткие таймауты и лимиты токенов на каждом Tier; логируйте маршрут и стоимость — это ваш «спидометр экономики».

  • Не забывайте про UX: если ответ «урезан» экономией — дайте пользователю кнопку «получить развёрнутый» (и осознанно потратить больше токенов).

ПРОБЛЕМА ВСЕХ БОТОВ - ТАМ НЕТ ВОЗМОЖНОСТИ ПЕРЕКЛЮЧАТЬСЯ МЕЖДУ ДИАЛОГАМИ А У НАС ВСЕ ЭТО ЕСТЬ!

Транспорт.

  • Telegram доставляет апдейты до нашего вебхука по HTTPS. Это не E2E, но трафик в пути защищён TLS.

  • Мы не полагаемся на Telegram для хранения истории: всё нужное сохраняем у себя.

Шифрование в БД (at rest).

  • Каждое сообщение (и метаданные) мы шифруем перед записью в БД, чтобы даже дамп БД был бесполезен без ключей.

  • Используем конвертное шифрование:

    • Генерируем случайный ключ данных (DEK) на диалог/юзера.

    • DEK оборачиваем (wrap) мастер-ключом из KMS/хранилища секретов (Vault/KMS).

    • Сами payload’ы шифруются AEAD (например, AES-GCM) с уникальным nonce на запись; в тег аутентичности включаем метаданные (время, user_id), чтобы защититься от подмены.

  • Ключи в памяти держим временно: при старте/завершении запроса, с авто-очисткой.

Опциональный «псевдо-E2E» режим.

  • Пользователь может задать свой passphrase. Мы через Argon2id/PAKE получаем материал ключа, дополнительно им шифруем DEK (двойная обёртка).

  • Без passphrase расшифровать диалог нельзя — даже нам; это компромиссный режим «повышенной приватности» для параноиков.

Жизненный цикл данных.

  • Для каждого диалога есть TTL/retention-политики: сырые сообщения, сводки, эмбеддинги.

  • Есть «инкогнито»: не храним сырое, оставляем только краткую сводку или вообще ничего.

  • Удаление — «в два шага»: логическое (сразу) и физическое (асинхронная очистка/затирание).

Как пользователь «продолжает диалог» и почему это дёшево

Главная идея: мы не шлём в LLM всю историю. Мы собираем тонкий контекст из трёх слоёв и строго держим бюджет токенов

1) Последние короткие реплики.

  • Держим N последних ходов (обычно 3–5), потому что они определяют локальный контекст.

  • Это дешёвый кусок: немного токенов, а пользы максимум.

2) Свертка старой истории в «роллинг-сводку».

  • Всё, что старше последних N ходов, регулярно сжимаем в короткий абзац: «о чём вообще чат, какие решения/факты уже приняты».

  • Сжатие делает дешёвая модель (типа 4o-mini/Qwen-small), мы запускаем её офлайн, а результат кэшируем и шифруем.

  • Таким образом, при «продолжить» мы подмешиваем 1–3 предложения вместо 5–50 сообщений. Экономия токенов колоссальная.

3) «Память-факты» (key–value).

  • Отдельно храним короткие «факты» о пользователе: имя, предпочтения, выбраны ли платные настройки, ID проектов — не как длинный текст, а как маленькие поля.

  • Перед запросом формируем узкий заголовок-память: 1–2 строки с этими фактами.

  • Это дешевле и надёжнее, чем таскать длинный диалог ради пары важных деталей.

даешь хабр эффект

https://t.me/CheapChatBot

Комментарии (1)


  1. ititkov
    03.09.2025 15:28

    Слои - очень мощная вещь для экономии. Мы пошли дальше и для разных задач использовали разные LLM. Anthropic - для распознания команды, Gemini 2.0 для парсинга и Gemini 2.5 для умничанья. Дело не только в цене, но и во времени отклика