Привет! На связи команда внутренней разработки, а именно ее тимлид, Тимур Садриев. В этой статье расскажем, как мы обучили Telegram-бота для оповещения о задачах, согласовании бизнес-процессов и очистки каналов от уволившихся сотрудников. Мои коллеги Руслан Мансуров и Руслан Яруллин раскроют детали, как мы боролись с препятствиями во время обучения и фейлах на первых попытках. В конце поделимся статистикой, показывающей, какой положительный эффект оказал бот на работу сотрудников.
Вступление. Какие рабочие задачи решает бот
1. Уведомления о поступающих запросах
Telegram-бот отлично справляется с задачей уведомлять о внутренних и внешних запросах. Бот автоматически оповещает сотрудников о новых задачах, сделках, звонках, запросах или изменениях в проекте. Он освобождает сотрудников от необходимости вручную проверять входящие запросы в системах и почте, поэтому повышает скорость обработки информации и снижает вероятность ошибок.
Примеры использования:
Отправка разработчикам уведомления о новых ошибках в системе или «падении» сервера, что сокращает время реакции на проблему.
Отправка уведомления о новых бизнес-процессах участникам, назначенным ответственными.
Отправка уведомлений о новых контрактах/сделках по проектным направлениям.
Отправка уведомлений о новых заявках/звонках в СТП внутри компании, так и со стороны заказчиков.
2. Согласование бизнес-процессов
Telegram-бот автоматизирует задачи на согласование, уведомление и выполнение задач.
Примеры использования:
Сотрудники активно используют бот для согласования отпусков или командировок. Для этого они отправляют запрос в мастер-системе, а их руководитель получает уведомление и дальше принимает решение одобрить или отклонить заявку, при этом бот фиксирует все этапы процесса.
По аналогичной схеме работает уведомление и согласование процессов, связанных с арендой серверов, рабочей техники и т.п. Запуск бизнес-процесса может проходить в разных системах. Но возможность согласовать заявку имеется в одной точке. У руководителей из-за частых разъездов и отсутствия оперативного доступа к мастер системам или VPN есть возможность согласовывать подобные процессы «на лету».
3. Предоставление справочной информации
Еще одна важная задача, которую решает Telegram-бот, — это предоставление справочной информации для сотрудников. В нашей компании сотрудники часто ищут информацию о процессах (особенно новички), документах или статусах.
Примеры использования:
Внутренний бот настроен на предоставление доступа к корпоративным правилам и документам: от инструкции по безопасности до шаблонов заявлений. Это удобно для сотрудников, так как не требует обращения к менеджерам или искать нужные документы вручную.
В IT-отделах компании бот предоставляет справку по часто задаваемым вопросам (FAQ), касающимся работы систем, снижая нагрузку на техническую поддержку.
Старт работы: меняем библиотеку
Мы уже пользовались ботом с ограниченным функционалом. Однако увеличившиеся мастер-системы и процессы у сотрудников вызвали необходимость в единой точке уведомления о поступающих задачах, их статусах, с возможностью управлять ими.
Прошлую версию бота мы писали на библиотеке telebot с python 3.6 — он синхронный и представлен в виде единственного .py файла. Бот периодически падал, потому что мы изначально его некорректно реализовали. Само собой, это все не нравилось пользователям: функционал ограничен и при этом оставались неразрешенными вопросы по рабочим процессам, ответы на которые тяжело было найти в Confluence.
Сложившаяся обстановка стала поводом написать нового бота, но на этот раз на библиотеке Aiogram. В плане архитектуры он представляет собой модульный монолит, где каждый модуль отвечает за определенную область бизнес-логики, которую легко доработать или удалить. Бот полностью асинхронный — это положительно сказывается на производительности ресурса. Также была достигнута высокая отказоустойчивость.
Автоматизация процесса смены фото профиля с помощью нейросети: как мы решили проблему модерации
У всех пользователей в корпоративной системе есть профиль. Хотя структура профилей может различаться в разных частях системы, все же используются одни и те же данные, в том числе фотография сотрудника. Как и в любом онлайн-пространстве, пользователю рекомендуется прикрепить личное фото, не нарушая при этом правил безопасности и этики.
Процесс загрузки фото раньше всегда вручную контролировал отдел кадров. Когда сотрудник менял свою фотографию, ему приходилось обращаться в отдел кадров. Для специалиста, который и без того занят оформлением сотрудников, контролем личных дел и других своих обязанностей, эта задача создавала лишнюю нагрузку. При этом пользователь тоже вынужден отвлекаться и дожидаться выполнения действия.
Мы решили эту проблему, но перед этим расскажем, какие методы мы перепробовали, пока не пришли к финальному решению.
Первая попытка: ставим задачу Telegram-боту
Наша первоначальная идея заключалась в том, чтобы автоматизировать процесс смены фото в корпоративном портале через Telegram-бота. Мы добавили в меню пункт «личный кабинет»: пользователь видит свое текущее фото и загружает новое. Изображению в формате JPG или PNG с пропорцией 1:1 устанавливается оптимальное разрешение. Затем фото уходит в чат с сотрудником отдела кадров, где он либо одобряет, либо отклоняет с помощью инлайн-кнопок.
В случае отказа специалист указывает причину, чтобы пользователь понимал что именно нужно исправить. В случае одобрения фото бот уведомлял сотрудника об успешной смене изображения. Однако вскоре стало понятно, что эта автоматизация не решает проблему полностью, а лишь переносит ее на другой уровень. Модерация все еще оставалась трудоемким процессом.
Внедрение нейросети: оптимизация модерации
Современные технологии распознавания лиц продвинулись далеко вперёд, и на рынке существует множество качественных Open Source решений. Мы взяли модель DeepFace для автоматизации проверки фото. С ее помощью мы реализовали этапы валидации определения наличия человеческого лица на фото и проверки на факт того, что на изображении объект только одного лица.
async def get_face_from_photo(photo_path: str) -> Optional[Dict]:
"""Функция для извлечения лица с фотографии.
Args:
photo_path (str): Путь к фотографии.
Returns:
dict: Словарь с информацией о лице, извлеченным с фотографии.
Raises:
DetailedAiogramError: Если на фотографии более одного лица,
возбуждается исключение с сообщением NOT_ONE_FACE из класса
AccountError.
DetailedAiogramError: Если на фотографии не обнаружено лицо,
возбуждается исключение с сообщением NOT_FACE из класса AccountError.
"""
try:
faces: List[Dict] = DeepFace.extract_faces(img_path=photo_path)
if len(faces) > 1:
raise DetailedAiogramError(AccountError.NOT_ONE_FACE)
face: Dict = faces[0]
return face
except ValueError:
raise DetailedAiogramError(AccountError.NOT_FACE)
Сравнение нового фото с предыдущим, чтобы убедиться, что это одно и то же лицо:
async def validate_employee_face(
new_photo_path: str,
current_photo_path: str
) -> None:
"""Функция для сравнения лица сотрудника со старой фотографией.
Args:
new_photo_path (str): Путь к новой фотографии.
current_photo_path (str): Путь к старой фотографии.
Raises:
DetailedAiogramError: Если лицо на новой фотографии не принадлежит
сотруднику, возбуждается исключение с сообщением NOT_EMPLOYEE_PHOTO
из класса AccountError.
"""
try:
await get_face_from_photo(current_photo_path)
result: Dict = DeepFace.verify(
img1_path=new_photo_path,
img2_path=current_photo_path,
)
verified: bool = result.get('verified')
except DetailedAiogramError:
verified = False
if not verified:
raise DetailedAiogramError(AccountError.NOT_EMPLOYEE_PHOTO)
Проверяем, что лицо расположено по центру изображения:
async def validate_face_centering(
employee_photo: EmployeePhoto,
face_height: int,
face_width: int,
facial_area: dict,
percentage: float
) -> None:
"""Функция для проверки центрирования лица на фотографии.
Args:
employee_photo (EmployeePhoto): Фото сотрудника.
face_height (int): Высота лица.
face_width (int): Ширина лица.
facial_area (dict): Словарь с координатами лица на фотографии.
percentage (float): Процент отношения для проверки центрирования.
Raises:
DetailedAiogramError: Если лицо не находится в центре фотографии,
возбуждается исключение с сообщением NOT_CENTERED_FACE из класса
AccountError.
"""
face_x: int = facial_area['x']
face_y: int = facial_area['y']
center_x: int = employee_photo.width // 2
center_y: int = employee_photo.height // 2
face_center_x: int = face_x + face_width // 2
face_center_y: int = face_y + face_height // 2
is_x_centered: bool = (abs(face_center_x - center_x)
<= percentage * employee_photo.width)
is_y_centered: bool = (abs(face_center_y - center_y)
<= percentage * employee_photo.height)
if not is_x_centered or not is_y_centered:
raise DetailedAiogramError(AccountError.NOT_CENTERED_FACE
Сверяемся, что лицо занимает не менее 10% от общей площади фото:
async def validate_face_area(
employee_photo: EmployeePhoto,
face_height: int,
face_width: int,
percentage: float
) -> None:
"""Функция для проверки площади лица на фотографии.
Args:
employee_photo (EmployeePhoto): Фото сотрудника.
face_height (int): Высота лица на фотографии.
face_width (int): Ширина лица на фотографии.
percentage (float): Процент площади лица от общей площади изображения.
Raises:
DetailedAiogramError: Если площадь лица меньше заданного процента от
общей площади изображения, возбуждается исключение с сообщением из
класса AccountError - FACE_AREA.
"""
image_area: int = employee_photo.width * employee_photo.height
face_area: int = face_width * face_height
if face_area <= image_area * percentage:
raise DetailedAiogramError(
AccountError.FACE_AREA.format(int(percentage * 100))
)
Если хотя бы одно условие не выполнялось, система выдавала пользователю сообщение с причиной ошибки.
Конечно, нейросеть не может охватить все случаи. Например, если это первая фотография пользователя или предыдущая фотография слишком плохого качества для корректного сравнения, то для таких случаев мы оставили возможность отправлять фото на ручную модерацию в отдел кадров.
Безопасность и обратная связь
Мы понимаем, что при большом желании наш механизм проверки можно обойти. Поэтому, когда пользователь меняет фото, в чат с отделом кадров отправляется уведомление с обеими фотографиями — старой и новой — и кнопкой для отмены изменений в случае необходимости.
Настройка запросов и интеграция с другими системами
Благодаря интеграции с различными корпоративными системами Telegram-бот предоставляет данные из других источников и выполняет сложные запросы. Мы выделили бота в отдельный выделенный сервис-сервер с понятной API для возможности интеграции со сторонними системами.
Примеры использования:
Интеграция с CRM позволяет боту предоставлять информацию о клиентах, последних сделках или уведомлять о сроках выполнения контрактов. Сотрудникам не нужно заходить в несколько систем — вся необходимая информация будет собрана в одном месте.
Бот предоставляет отчеты по финансовым данным, отправляет расчетные листы сотрудникам.
Очищаем внутренние Telegram-группы и –каналы от пользователей
В компании используется большое количество Telegram-групп и –каналов: рабочие чаты, чаты для мероприятий, информационные каналы. Информация, которая там появляется, относится к коммерческой и только для внутреннего пользования. Поэтому бывших сотрудников и тех, кто не работает с компанией в какой-либо роли, важно вовремя исключать.
В некоторых чатах можно увидеть более 1 000 пользователей — это слишком большой объем работы для ручной фильтрации. Мы создали аккаунт в Telegram, который будет удалять из чатов пользователей, которые не имеют отношения к компании.
Взаимодействие с Telegram API
Список пользователей, допущенных к чату, я получаю с помощью API стороннего сервиса. Он получает на вход id чата и возвращает список пользователей, которые в нем должны быть. Далее, казалось бы, ничего сложного: нужно пройтись по всем пользователям чата и удалить тех, кого нет в полученном списке.
Для взаимодействия с чатом я использую заранее созданный аккаунт и библиотеку telethon.
Подключимся к нашему аккаунту через telethon, главное — передайте параметр system_version, иначе вас выкинет из всех сессий аккаунта.
client = TelegramClient(
'barsofficechatbot',
TELEGRAM_APP_API_ID,
TELEGRAM_APP_API_HASH,
system_version='4.16.30-vxCUSTOM'
).start()
Далее со стороннего сервиса получаем список пользователей.
def get_fired_users_from_bo():
"""Получает актуальных пользователей чата.
Returns:
list: Список идентификаторов пользователей в телеграм
"""
try:
r = requests.post(
url=FIRED_EMPLOYEES_URL,
data={
'username': INTEGRATOR_LOGIN,
'password': INTEGRATOR_PASSWORD,
}
)
except requests.exceptions.ConnectionError:
user_ids = []
else:
user_ids = r.json().get('extra', []).get('user_ids', [])
return user_ids
Задаем функцию get_participants. Но проблема в том, что эта функция отдает не более 200 пользователей. Мы использовали функцию GetParticipantsRequest, которая на вход также может принять строку, по которой должен происходит поиск пользователей. В первом варианте искали по всем буквам из кириллицы и латиницы, а также цифры.
alphabets = [
"qwertyuiopasdfghjklzxcvbnm",
"йцукенгшщзхъёфывапролджэячсмитьбю",
"1234567890",
]
Возникла другая проблема: во время перебора alphabets были случаи, когда количество найденных пользователей опять превышала 200 человек. Тогда делаем alphabets2, где были все значения разбитые по парам. Это позволило найти всех пользователей чата, сравнить их с полученным списком из пункта 2, и составить список пользователей на исключение.
alphabets2 = []
for alphabet in alphabets:
for pair in itertools.permutations(alphabet, 2):
alphabets2.append("".join(pair))
Результаты
Когда руководитель высшего звена находится в командировке, без доступа к VPN, а ему отправляют запрос на согласование счета на оплату или командировку, сейчас это возможно сделать напрямую через бота, без необходимости подключаться к мастер-системе через VPN.
Количество обращений в техподдержку после появления справочника и ИИ-помощника уменьшилось на 8%. Соответственно, больше рабочего времени служб было затрачено на выполнение прикладных задач, вместо консультаций.
Применение ИИ для обновления фото профиля на внутренних ресурсах помогло сократить цепочку подобных обращений, минуя первую линию СТП и кадровую службу. Такие заявки больше не будут отвлекать от работы с приоритетными запросами.
Среднее время на исполнение одной заявки раньше составляло от 1 до 1,5 часов реального времени, а по факту могло затянуться до недели (такие заявки берут с низким приоритетом), поэтому и сами пользователи лишний раз не желали обращаться для обновления фотографии. С момента запуска бота было загружено более 300 фото, соответственно, это десятки часов СТП, которые удалось сократить в данном процессе.
На исключение сотрудников из корпоративных каналов и групп каждый отдел, департамент, структура тратила на это свое личное время и приходилось сверяться со списками на сотни пользователей. Наш метод сэкономил за первый месяц около 16 часов работы коллег одного из направлений. Предоставлен открытый доступ внутри компании к функционалу бота: видно количество групп и каналов, к которым он подключен, автоматически корректирует состав.
Мы в «БАРС Груп» разрабатываем цифровые решения для государства, бизнеса и людей. Принимает активное участие в развитии Цифровой экономики России и создаем цифровые решения для импортозамещения программного обеспечения – 88 решений компании зарегистрировано в реестре российского ПО. Рассказываем о наших продуктах и ИТ-трендах в Telegram-канале.
Тимур Садриев
Руководитель отдела внутренней автоматизации
Руслан Мансуров
Руководитель группы
Руслан Яруллин
Разработчик