Разбор проекта / 01
Проектирование, разработка с ИИ и внедрение · Январь — апрель 2026 · Системой пользовались все сотрудники компании
Операторы ждали вторую линию по 2–3 дня, чтобы выполнить действия с тарифом или обещанным платежом. Я разобрал, какие знания и проверки удерживали эти операции у двух специалистов, и вынес их в отдельную платформу поверх действующей системы. После внедрения предусмотренные действия оператор выполнял сам примерно за 2 минуты; перенос всех коммуникаций из Битрикс24 остался следующим этапом.
Почему первая линия зависела от двух специалистов
До внедрения не было единого рабочего места с подробной карточкой клиента. В Битрикс24 использовался бот, который предоставлял поверхностную информацию и принимал команды. Операторам приходилось восстанавливать контекст и искать ответы, очевидные специалистам, хорошо знавшим устройство системы.
Обещанные платежи и временные тарифы выдавались двумя сотрудниками вручную. Сроки возврата отслеживались по календарю, объём списания рассчитывался на калькуляторе. Изменения тарифов также требовали участия специалиста. Обычному оператору не хватало и инструментов, и встроенных проверок для самостоятельного выполнения этих действий.
Исходный API позволял менять состояние клиента без необходимых ограничений предметной области: некорректная команда могла привести к несоразмерному отрицательному остатку дней тарифа. Простая передача таких команд первой линии сохраняла риск ошибки. В результате запросы направлялись на вторую линию; ожидание ручной обработки этих операций составляло 2–3 дня.
Решение: рабочее место и правила поверх прежнего API
Исходное пожелание бизнеса — расширить бота и поместить в него больше информации. При разборе рабочих сценариев выявилось ограничение этого подхода: клиентские связи, платежи, тарифы, история и условия операций не укладывались в линейный диалог. Большая часть сведений требовалась только в конкретных ситуациях, а вывод всего контекста в чат усложнял бы работу.
Поэтому выбрано отдельное веб-приложение с карточкой клиента и переходами к подробностям по необходимости. Основные сведения доступны сразу; связанные боты, платежи и история раскрываются при разборе конкретного вопроса. Такая организация позволяет менять порядок действий в зависимости от ситуации.
Второе решение — сохранить существующий механизм исполнения команд, добавив собственный слой данных и правил. Платформа должна была собирать контекст, проверять полномочия и условия, рассчитывать параметры операций, передавать команды основной системе и завершать временные изменения автоматически. Это позволяло передать предусмотренные сценарии первой линии и снять зависимость от ручных расчётов и напоминаний.
Постановка задачи выросла из практики ручного обслуживания; готового ТЗ не было. Требования, модель данных, архитектура и сценарии определялись автором проекта; проверка рабочих сценариев и внедрение также выполнялись им. ИИ-модели использовались для реализации принятых решений; код целиком создавался моделями.
Внедрение: самостоятельная работа вместо ожидания
Демонстрация состоялась примерно в начале марта 2026 года. Через 3–4 дня после неё сотрудники получили доступ к платформе. Отдельного курса обучения не потребовалось.
Основной барьер при переходе оказался организационным: сотрудники по привычке избегали действий, которые раньше были запрещены первой линии. Для начала работы оказалось достаточно объяснить принцип интерфейса: видимые операции соответствуют полномочиям сотрудника, неактивные объясняют ограничения конкретного случая, недоступные роли — скрыты.
| Что изменилось | До платформы | После внедрения |
|---|---|---|
| Выполнение операций, ранее требовавших второй линии | Тикет специалисту; ожидание обработки — 2–3 дня | Самостоятельное выполнение оператором примерно за 2 минуты |
| Зависимость от специалистов | Обещанные платежи и временные тарифы вручную обслуживали два сотрудника; изменения тарифов требовали участия специалиста | Для перенесённых в платформу сценариев необходимость обращения на вторую линию отпала |
| Завершение временных условий | Календарные напоминания, ручной расчёт и команда возврата | Сохранённое состояние операции, расчёт по правилам и фоновые задачи завершения |
| Доступ к клиентскому контексту | Поверхностные сведения в боте и дополнительные поиски | Карточка клиента, связанные сущности, история обращений и комментарии |
| Освоение инструмента | Привычка передавать операции специалисту | Работа без отдельного курса обучения; ограничения объясняются в интерфейсе |
| Охват внедрения | Единой платформы поддержки не было | Платформой пользовались все сотрудники компании |
Внедрение также послужило импульсом к пересмотру технологического стека клиентской части основной системы.
Как платформа сохранила действующую систему
Совместимость с исходным API
Исходная система уже хранила клиентов и исполняла операции обслуживания. При этом её API задавал ограничения: изменения выполнялись через GET-запрос с текстом команды вместо обновления ресурсов через PATCH; история платежей возвращалась целым списком без серверной пагинации. Формат внешних ответов также требовалось преобразовать в сущности и связи, пригодные для нового интерфейса.
Совместимость с существующим механизмом команд сохранена. Поверх него организованы два пути работы:
- Чтение: внешний API → нормализация и локальные связи → кеш и база данных → собранный контекст клиента.
- Действие: параметры из интерфейса → серверные проверки → сериализация в прежнюю текстовую команду → API → обработка результата и обновление данных.
Так новая платформа могла развивать правила, интерфейс и автоматизацию поддержки, сохраняя действующий способ исполнения операций в основной системе.
Архитектура и стек
Приложение развёрнуто на сервере в инфраструктуре компании. В Docker Compose разделены пять сервисов: веб-приложение hub, Telegram-бот bot, фоновые обработчики worker, PostgreSQL и Valkey. Интерфейс и серверный API находятся в одном Next.js-приложении; бот и обработчики запускаются отдельными процессами. Dify развёрнут отдельно и подключён через API.
Telegram-бот — отдельный компонент новой платформы для входа и управления доступом сотрудников. Исходный бот внутри Битрикс24 использовался для получения клиентских сведений и выполнения команд.
| Часть системы | Технологии | Ответственность |
|---|---|---|
| Интерфейс и сервер | TypeScript, Next.js 16 App Router, React 19, Tailwind CSS 4 | Server Components и SSR для первоначальной загрузки клиентских страниц; Client Components для интерактивности; серверный API |
| Постоянные данные | PostgreSQL 16, Prisma 7 | Сотрудники, клиентские связи, история операций, обращения, задачи и знания |
| Быстрое и временное состояние | Valkey 7, Redis-клиенты | Кеш данных, сессии, контекстные разрешения и хранение очередей |
| Фоновые операции | BullMQ | Возврат тарифа, погашение обещанного платежа, отзыв доступа, синхронизация активности Битрикс24 |
| Telegram-бот | grammY, TypeScript | Вход по ссылке, приглашения и согласование доступа сотрудников |
| Сборка и запуск | Node.js 20, Docker Buildx, Docker Registry, Docker Compose | Сборка и публикация образов на отдельном сервере; запуск на сервере в инфраструктуре компании |
| Диагностика | OpenTelemetry | Трассировка серверных запросов, работы с БД и внешними API |
Основные связи компонентов:
flowchart LR
Operator["Оператор"] --> Hub["Платформа поддержки"]
B24["История обращений · Битрикс24"] --> Hub
Hub -->|"Контекст и проверки"| Rules["Правила обслуживания"]
Rules -->|"Проверенная команда"| Core["Основная система"]
Core -->|"Результат"| HubПодробная карта ниже показывает, какие процессы и внешние системы обеспечивают этот путь.
flowchart TB
User["Оператор в браузере"] --> Hub
TG["Telegram Bot API"] <--> Bot
subgraph CompanyRuntime["Сервер в инфраструктуре компании · Docker Compose"]
Hub["hub · Next.js<br/>Интерфейс, модель данных<br/>Правила и адаптер команд"]
Bot["bot · grammY"]
Worker["worker · BullMQ<br/>Четыре обработчика очередей"]
DB[("PostgreSQL")]
Cache[("Valkey")]
Bot -->|"Ссылка для входа"| Hub
Hub <--> DB
Hub <--> Cache
Worker <--> Cache
Worker <--> DB
end
Hub -->|"Данные и команды"| Core["API основной бизнес-системы"]
Worker -->|"Отложенные команды"| Core
Hub -->|"REST API"| B24["Битрикс24"]
B24 -->|"События обращений"| Hub
Worker -->|"Синхронизация активности"| B24
Hub <-->|"API и поток ответов"| Dify["Dify · отдельное развёртывание"]
Dify -->|"Запрос контекста"| MCP["Внешний MCP-сервер"]
MCP -->|"API истории и клиентского контекста"| HubВнешние системы участвуют в конкретных рабочих процессах:
| Куда обращается система | Что передаётся и для чего |
|---|---|
| API основной бизнес-системы | Профили, боты, тарифы и платежи; команды изменения условий обслуживания; создание индивидуального тарифа и получение платёжных ссылок |
| Битрикс24 | Запрос истории, участников и файлов; входящие события обращений связывают работу оператора с клиентом и запускают обновление локальных данных |
| Dify | Запросы к помощникам, потоковые ответы, вложения, оценки и исправленные ответы; получение данных платформы через внешний MCP-сервер; подключения помощников настраиваются в панели |
| Telegram | Команды сотрудников боту; бот запрашивает у приложения ссылку для входа, сотрудник открывает её в браузере |
Как оператор получает контекст клиента
Собственная модель данных и кеширование
В PostgreSQL выделены клиенты, боты, их владельцы и администраторы, платежи, тарифы и реферальная история. Внешние идентификаторы связывают локальные записи с источником; нормализованные поля позволяют работать с ними внутри продукта. Для ряда сущностей сохраняется и исходный ответ API. Эту модель дополняют собственные сущности платформы: сотрудники и роли, правила и журналы действий, временные обязательства, тикеты и знания.
Например, вложенные списки ботов из ответа API преобразуются в отдельные записи и связи «клиент — владелец бота» и «клиент — администратор бота». При синхронизации обновляются участники и удаляются устаревшие связи администрирования. Эти отношения затем используются и для отображения, и для определения доступа.
Для платежей предусмотрено сохранение накопленной истории: если ранее известный платёж исчезает из очередного полного ответа API, локальная запись остаётся и получает признак отсутствия в источнике и время обнаружения. База хранит сведения, которых уже может не быть в текущем снимке внешней системы.
Кеш и постоянное хранилище выполняют разные задачи. Valkey сокращает повторные обращения к API: профиль кешируется на пять минут, платежи и реферальная история — на две. PostgreSQL хранит связанные сущности, историю и состояние собственных процессов. Вместе с кешем сохраняется время получения данных; предусмотрены ручное обновление и сброс клиентского кеша после операций.
Открытие клиента собирает связанные данные
Карточка объединяет профиль и тариф, собственных и администрируемых ботов, сведения об их владельцах, платежи, пригласившего клиента и реферальную историю. Этот контекст дополняется локальными действиями и историей обслуживания.
Сервер собирает его из нескольких источников: часть связей уже содержится во внешнем ответе, часть читается из локальной базы, недостающие сведения запрашиваются отдельно. Идентификаторы владельцев разрешённых ботов объединяются без повторов; их сводки загружаются пакетами по три параллельных запроса. Для связанных клиентов сначала могут создаваться минимальные записи, которые затем обогащаются по мере необходимости.
При открытии карточки клиента оператор получает связанный рабочий контекст. Загрузка ограничена нужными ветвями и доступами сотрудника.
История обращений и комментарии к клиенту сохраняют накопленный контекст для следующего оператора: сведения о предыдущем обслуживании доступны в той же рабочей среде.
Первичная загрузка клиентских данных выполняется на сервере
Для страниц клиента, его платежей и ботов используются Server Components и серверный рендеринг. На странице клиента сначала проверяются сотрудник и его доступ, затем сервер собирает данные профиля и связанных сущностей. Интерактивная часть получает подготовленные данные через props.
При первоначальном открытии страницы браузеру не нужен отдельный JSON-запрос за профилем клиента: обращения к источникам и сборка данных выполняются сервером. Ответственность разделена: получение данных и проверки — на сервере, формы, диалоги и состояние интерфейса — в Client Components.
При ручном обновлении браузер отправляет POST, сервер обновляет кеш и возвращает только { ok: true }. Затем router.refresh() запрашивает обновлённое серверное представление. Такой подход сохраняет единый путь подготовки данных и для первого открытия, и для обновления страницы.
Связи клиентов определяют доступ и контекст обслуживания
Для истории обращений предусмотрены разные способы загрузки. Закрытые диалоги читаются из локальной базы. Открытые обновляются из Битрикс24 с сохранением сообщений, участников и файлов. Если нужной сессии ещё нет локально, система запрашивает и сохраняет её. Это позволяет использовать накопленную историю без повторного запроса закрытого диалога во внешнюю систему.
Помимо загрузки при открытии обращения, реализована фоновая синхронизация: обработчик BullMQ обновляет индекс завершённых обращений за предыдущий день, связывает их с клиентами и догружает недостающую историю. Для известных сессий, ещё не сохранённых локально, запросы объединяются через batch API Битрикс24 — до 50 за вызов. Переписки, участники и файлы сохраняются в собственной базе. Этот слой обеспечивает доступ к истории в панели и формирует основу для последующего анализа обращений и эффективности операторов.
Модель доступа учитывает связи между участниками: обратиться может администратор бота, а его владельцем быть другой клиент. Для первой линии поддержки система строит набор разрешений по связям «обратившийся клиент — бот — владелец». Например, если человек администрирует один из ботов другого клиента, в набор включаются этот бот и его владелец. Эта связь не открывает доступ к остальным ботам владельца, которыми обратившийся человек не управляет. Отдельно учитываются ограничения на доступ к защищённым клиентам.
Контекстные разрешения ограничены сроком и хранятся в Redis вместе с данными обращения. При выполнении действий первой линией сервер берёт ID диалога из этого контекста. В одной операции сходятся проверки сотрудника, доступного ему клиента, разрешённого действия и бизнес-условий.
Как операции перешли к первой линии
Правила обслуживания стали частью системы
Операции описаны через правила: разрешённые роли, условия доступности, параметры и комментарии. На их основе формируется список действий в интерфейсе; перед выполнением сервер проверяет разрешения и бизнес-условия. Администратор может менять предусмотренные роли, ограничения и параметры через настройки.
В интерфейсе разделены два ограничения. Действия, недоступные роли сотрудника, скрыты. Разрешённая для роли операция, которую нельзя выполнить в текущем состоянии клиента, отображается неактивной с объяснением причины. Разграничение полномочий и бизнес-условий распространяется в том числе на операции лояльности. Проверки выполняются повторно на сервере при запуске действия.
Проверки опираются на разные данные: роли, журналы и незавершённые обязательства — на собственное состояние платформы, параметры внешнего профиля — на слой загрузки с указанным TTL кеша. Исполнение команды подтверждает основная система; после успешного изменения клиентский кеш сбрасывается.
Например, система учитывает, выдавался ли клиенту пробный период, есть ли незавершённый обещанный платёж, действует ли временное повышение тарифа. Состояние одной операции влияет на доступность следующих. В журнале сохраняются сотрудник, клиент, контекст обращения, результат, а для предусмотренных операций — состояние до и после изменения.
Для индивидуальных предложений спроектирован конструктор тарифов: ресурсные параметры преобразуются в стоимость, периоды оплаты и платёжную ссылку. Сценарий применения учитывает остаток оплаченного периода. Параметры калькулятора также вынесены в настройки. Коммерческие условия, расчёт и действия сотрудника связаны в одном продукте.
Действия интерфейса превращаются в прежние команды
Оператор выбирает действие и заполняет параметры. Сервер проверяет роль, доступ к клиенту и условия операции, после чего отдельный адаптер сериализует параметры в формат исходного протокола. Например, для добавления дней обслуживания используется такая команда; идентификатор клиента в примере условный:
Действие: добавить 7 дней тарифа paid клиенту 12345
Параметры: clientId = 12345, plan = "paid", days = 7
Команда: add_days 12345 paid 7
Текст передаётся в GET-запросе через параметр command, вместе с методом вызова runChasMasterCommand. Основная система исполняет ту же команду, которую раньше вручную отправлял оператор. Знание её синтаксиса сосредоточено в адаптере, а интерфейс работает с действиями и параметрами предметной области.
Адаптер отдельно проверяет успешность HTTP-запроса и подтверждение выполнения команды внутри ответа. Этот же путь исполнения используется в фоновых процессах: например, погашение обещанного платежа формирует команду изменения числа дней с отрицательным значением. Поверх прежнего протокола платформа добавляет проверки, журналирование и операции, продолжающиеся после закрытия страницы.
Временные изменения имеют продолжение после нажатия кнопки
Для операций с ограниченным сроком действия предусмотрено отложенное завершение.
Этот механизм заменяет прежнюю последовательность «выдать вручную → поставить напоминание в календаре → рассчитать возврат → выполнить команду». Срок, исходное состояние и задача завершения становятся частью данных операции.
Для временного повышения тарифа предусмотрена следующая последовательность:
- Сервер проверяет доступность изменения и допустимый срок.
- До обращения к внешнему API создаётся запись о попытке с исходным состоянием клиента.
- После успешного изменения планируется фоновый возврат тарифа; сохраняются исходный тариф, срок и задача отката.
- Пока повышение активно, система ограничивает конфликтующие действия. Предусмотрен и ручной откат.
- Результаты операций отражаются в истории клиента.
Обещанный платёж учитывает изменения, которые могут произойти между выдачей и погашением. Сумма хранится в рублях, а при погашении пересчитывается в дни обслуживания по актуальному тарифу клиента. Если тариф изменился, используется его текущая стоимость.
Перед списанием обработчик проверяет доступный оплаченный период. Если списывать пока нечего или период недостаточен, он сохраняет причину и планирует повторную попытку. Повторы ограничены, неуспешное завершение получает отдельный статус. Наличие ожидающего или неуспешного погашения проверяется при следующей выдаче обещанного платежа.
Для временного доступа к ботам предусмотрен автоматический отзыв. Эти процессы исполняются отдельными обработчиками очередей BullMQ. Их состояние и отложенные действия продолжают существовать после закрытия страницы оператором.
Как платформа поддерживает работу команды
ИИ встроен в рабочие процессы и управление знаниями
В рамках проекта развёрнут и настроен Dify, организовано использование накопленных диалогов с клиентами, помощник интегрирован в панель поддержки.
В приложении предусмотрено несколько настраиваемых помощников: со своими подключениями, доступом по ролям и параметрами. Запрос проходит через сервер, который проверяет доступ оператора и принадлежность беседы. История хранится в панели и связана с разговором в Dify; сотрудники могут делиться диалогами.
Помощник доступен непосредственно на странице клиента через компактный чат-виджет. Оператор может обратиться к ИИ, оставаясь в карточке, продолжить предыдущую беседу или открыть её в полном интерфейсе. Виджет и основной чат используют общую логику сообщений, истории и оценок ответов.
Для потокового ответа предусмотрены две ветки обработки: одна показывает текст пользователю, другая считывает поток на сервере и сохраняет ответ в базу. Сохранение таким образом вынесено из браузера — уход со страницы не должен обрывать запись ответа вместе с интерфейсом.
Предусмотрен цикл обратной связи: оценка ответа, комментарий и отправка исправленной пары «вопрос — ответ» в Dify непосредственно из панели.
Для получения данных платформы из Dify использовался внешний MCP-сервер. На стороне платформы реализован HTTP API с авторизацией по Bearer-токену: история конкретного обращения, связанные сессии того же чата и выборка сообщений за период с пагинацией. Ответы приводятся к удобному для модели формату: роли клиента, оператора и системы, текст, время и сведения о вложениях. Системные события можно исключить, оставить только ключевые или получить полностью.
История обращения дополняется сведениями о клиенте и расчётной оценкой его приоритета. Для неё используется та же конфигурируемая логика, что и в карточке клиента. Так Dify получает подготовленный контекст из платформы через отдельный интеграционный путь.
Тикеты и база знаний
На общих данных о клиентах и сотрудниках построен внутренний тикетинг: назначения, категории, обязательные поля, связи и история статусов. База знаний поддерживает версии, владельцев материалов, проверку актуальности и восстановление предыдущей редакции. Это расширяет продукт от выполнения отдельных операций до организации работы поддержки.
Обратная связь сотрудников и развитие продукта
В платформу встроен канал связи сотрудников с владельцем системы: сообщения об ошибках интерфейса, некорректных данных, предложения и замечания по рабочим процессам. Вместе с записью сохраняются автор, страница, контекст клиента или бота; поддерживаются вложения и привязка к технической трассе через trace ID.
Администратор получает уведомления, назначает приоритет и меняет статус: новое, запланировано, отклонено или реализовано. Записи доступны в общей ленте, поддерживают комментарии и ответы. Обсуждение улучшения связано с исходным рабочим контекстом и состоянием его обработки.
Как система доставляется и сопровождается
Сборка и развёртывание
На отдельном сервере сборки подготовлена среда и размещён частный Docker Registry. Сервер приложения в инфраструктуре компании забирал из реестра готовые образы и запускал сервисы. Подготовка среды и доставка образов выполнены в рамках проекта; вычислительная работа по сборке вынесена с сервера приложения.
flowchart LR
Source["Исходный код"] --> Build
subgraph BuildServer["Сервер сборки и реестр образов"]
Build["Сборка Docker-образов"] --> Registry["Частный Docker Registry"]
end
Registry -->|"Сервер приложения забирает образы · pull"| Runtime["Сервер в инфраструктуре компании<br/>Docker Compose · hub / bot / worker"]Сборочные скрипты используют Docker Buildx: готовят образы под linux/amd64 и публикуют их в registry с тегом версии и latest. В сборку приложения передаются коммит, ветка и время сборки — эти сведения позволяют определить, какая версия запущена.
Собираются два образа: общий для приложения и worker, отдельный для Telegram-бота. Многостадийная сборка приложения включает Next.js, генерацию Prisma-клиента и подготовку фоновых обработчиков; финальный слой использует production-зависимости. Серверный скрипт обновления выполняет pull и перезапуск выбранных прикладных сервисов без пересоздания PostgreSQL и Valkey. По умолчанию обновляются приложение и бот; обновление worker включается отдельным флагом.
Перед запуском основной команды общего образа выполняются миграции Prisma. Данные БД и Valkey вынесены в отдельные Docker volumes, загруженные файлы — в каталог на сервере. Worker получает до двух минут на штатную остановку и закрывает обработчики через worker.close(). Серверный скрипт сохраняет предыдущий образ приложения отдельным тегом для возможного ручного возврата.
Для сопровождения предусмотрены два уровня: журнал бизнес-операций с исполнителем и результатом, а также технические трассы запросов. OpenTelemetry включается при настройке OTLP endpoint и инструментирует HTTP, Prisma и обращения к внешним API. Это позволяет разбирать путь запроса от серверного обработчика до зависимостей.
Что осталось за границей внедрения
Следующее направление развития — перенос работы с обращениями в единое рабочее место, графики работы и аналитика эффективности операторов. К моменту завершения работы над проектом были реализованы сбор и локальное хранение истории Битрикс24, подготовлены интерфейсные прототипы аналитики и расписания на демонстрационных данных, описана архитектура собственного клиентского чата. Полный перенос коммуникаций и подключение аналитических экранов к реальным показателям оставались следующим этапом.