cabinet: микросервис Cabinet
model.py
: методы для работы с моделью и форматирования ответаmodel_config.py
: экзмепляр модели, настройки выдачи (в том числе черный список пользователей)server.py
: отправная точка процесса-сервера, обработчики для всех REST/HTTP
методовfetch_data.py
: получение данныхmodel_config.py
: список стоп-словpreprocess.py
: предобработка данныхtrain_model.py
: инициализация и обучение моделиtrainer.py
: отправная точка процесса-тренера.env
: конфигурационный файл для настройки времени обученияDockerfile
: файл с Docker
образом микросервисаPipfile
: файл со списком всех необходимых библиотек-зависимостейPipfile.lock
: файл с полным списком необходимых библиотек и их зависимостей, а также версий всех библиотекlogging_debug.conf
: файл с настройкой логирования для режима DEBUG
logging_default.conf
: файл с настройкой логирования для режима DEFAULT
chat_bot: чат-бот
main.py
: описание класса – чат-бота.env
: конфигурационный файл для настройки сбора статистики использования ботаDockerfile
: файл с Docker
образом микросервисаPipfile
: файл со списком всех необходимых библиотек-зависимостейPipfile.lock
: файл с полным списком необходимых библиотек и их зависимостей, а также версий всех библиотекlogging_debug.conf
: файл с настройкой логирования для режима DEBUG
logging_default.conf
: файл с настройкой логирования для режима DEFAULT
colab_notebooks: jupyter-ноутбуки с тестированием работы моделей
cabinet.ipynb
: тест модели Личный кабинет МИЭМscaling_test.ipynb
: тест времени обучения в зависимости от количества документовwiki_tests.ipynb
: предобработка данных Wiki и тесты оценокzulip_tests.ipynb
: предобработка данных Zulip и тесты оценокcompetence_search: микросервис Competence Search
main.py
: отправная точка, обработчики для всех REST/HTTP
методовservices.py
: получение данных из всех микросервисов-обработчиков, вычисление общей оценки релевантностиDockerfile
: файл с Docker
образом микросервисаPipfile
: файл со списком всех необходимых библиотек-зависимостейPipfile.lock
: файл с полным списком необходимых библиотек и их зависимостей, а также версий всех библиотекlogging_debug.conf
: файл с настройкой логирования для режима DEBUG
logging_default.conf
: файл с настройкой логирования для режима DEFAULT
credentials: файлы с учетными данными
credentials.json.example
: пример файла с учетными данными для микросервиса Gtablezulip_admin.txt.example
: пример файла с учетными данными администратора Zulip (для работы с API
Zulip)zulip_bot.txt.example
: пример файла с учетными данными для бота Поисковик компетенцийzulip_bot_dev.json.example
: пример файла с учетными данными для бота test_bot_240gtable: микросервис Gtable
main.py
: отправная точка, обработчики для всех REST/HTTP
методовAPI
google_sheets_api.py
: класс для работы с Google Таблицамиgoogle_sheets_data.py
: экзмепляр класса для работы с Google Таблицами, функция для вставки данных в таблицу.env
: файл с id
таблицы GoogleDockerfile
: файл с Docker
образом микросервисаPipfile
: файл со списком всех необходимых библиотек-зависимостейPipfile.lock
: файл с полным списком необходимых библиотек и их зависимостей, а также версий всех библиотекlogging_debug.conf
: файл с настройкой логирования для режима DEBUG
logging_default.conf
: файл с настройкой логирования для режима DEFAULT
lib: модели и обертки над API
сервисов
cabinet_data.py
: функции для работы с API
Личного Кабинета МИЭМsbertop2vec.py
: описание класса – тематической модели для сервисов Zulip и МИЭМ Wikismartsearch.py
: описание класса – модели для Личного Кабинета МИЭМwiki_data.py
: функции для работы с API
МИЭМ Wikizulip_data.py
: функции для работы с API
Zuliplogs: директория с логами от всех микросервисов
wiki: микросервис Wiki
model.py
: методы для работы с моделью и форматирования ответаmodel_config.py
: экзмепляр модели, настройки выдачи (в том числе черный список пользователей)server.py
: отправная точка процесса-сервера, обработчики для всех REST/HTTP
методовfetch_data.py
: получение данныхmodel_config.py
: список стоп-слов и параметры при обучении моделиpreprocess.py
: предобработка данныхtrain_model.py
: инициализация и обучение моделиtrainer.py
: отправная точка процесса-тренера.env.example
: пример конфигурационного файла для настройки времени обучения и токеном для работы с API
МИЭМ WikiDockerfile
: файл с Docker
образом микросервисаPipfile
: файл со списком всех необходимых библиотек-зависимостейPipfile.lock
: файл с полным списком необходимых библиотек и их зависимостей, а также версий всех библиотекlogging_debug.conf
: файл с настройкой логирования для режима DEBUG
logging_default.conf
: файл с настройкой логирования для режима DEFAULT
zulip: микросервис Zulip
model.py
: методы для работы с моделью и форматирования ответаmodel_config.py
: экзмепляр модели, настройки выдачи (в том числе черный список пользователей)server.py
: отправная точка процесса-сервера, обработчики для всех REST/HTTP
методовfetch_data.py
: получение данныхmodel_config.py
: список стоп-слов и параметры при обучении моделиpreprocess.py
: предобработка данныхtrain_model.py
: инициализация и обучение моделиtrainer.py
: отправная точка процесса-тренера.env
: конфигурационный файл для настройки времени обученияDockerfile
: файл с Docker
образом микросервисаPipfile
: файл со списком всех необходимых библиотек-зависимостейPipfile.lock
: файл с полным списком необходимых библиотек и их зависимостей, а также версий всех библиотекlogging_debug.conf
: файл с настройкой логирования для режима DEBUG
logging_default.conf
: файл с настройкой логирования для режима DEFAULT
Архитектура проекта описана на странице по ссылке.
Алгоритм работы системы можно описать следующей последовательностью действий:
Асинхронный сбор ответов, а также параллельная и независимая друг от друга работа микросервисов позволяет ускорить время получения данных от микросервисов-обработчиков.
В случае, если один из сервисов не доступен, главный микросервис может отследить ошибку в отправке запроса и сформировать результат без использования данных проблемного сервиса, указав в ответе, что проблемный сервис недоступен. Это повышает степень отказоустойчивости приложения.
Внимание!
Все ссылки с учетом строк приведены для программы на момент следующего коммита. В будущем номера строк могут быть смещены в следствие редактирования исходного кода!
Алгоритм работы микросервисов-обработчиков представлен на рисунке.
Всего в программе находятся три микросервиса-обработчика:
Далее все ссылки на участки кода будут приведены в виде гиперссылок со следующими условными обозначениями:
Z
- ссылка на код из обработчика данных Zulip;W
- ссылка на код из обработчика данных МИЭМ Wiki;C
- ссылка на код из обработчика данных Личный Кабинет МИЭМ;Каждый обработчик в начале своей работы запускает два параллельных процесса (Z
, W
, C
). Точкой входа для запуска процессов является строчка с ключом command
в конфигурации запуска в файле docker-compose.yml
или docker-compose.prod.yml
.
Слева изображен процесс-сервер. Большую часть времени сервер ожидает получения запроса. Как только запрос был отправлен, сервер принимает его и определяет тип запроса. Если запрос является поисковым, сервер использует обученную модель обработки данных для формирования ответа на поисковой запрос (метод GET
/users Z
, W
, C
). В случае, если модель обработки данных не загружена в оперативную память (это происходит при первом запуске или при перезагрузке контейнера в случае сбоя), модель сначала загружается программой, затем формируется ответ. В конце ответ на запрос отправляется клиенту. HTTP
статус запроса – 200
(«Ok», «Успешно»). Если запрос является запросом на обновление модели (метод PATCH
/handler Z
, W
, C
), программа загружает из файла модель и отправляет клиенту сообщение с HTTP
статусом 200
(«Ok», «Успешно»). Все остальные запросы являются не валидными и клиенту возвращается сообщение с ошибкой и статусом HTTP 404
(«Not found», «Не найден»).
Второй процесс ожидает времени начала обучения, установленного в конфигурационном файле (о настройке конфигурационных файлов можно прочитать здесь). При наступлении времени обучения модель запускает функцию обучения модели (Z
, W
, C
), которая включает в себя загрузку новых данных сервиса и обучение самой модели. По завершении обучения модель сохраняется в бинарный файл (<имя_сервиса>/data/model.model
). После этого процесс отправляет запрос (метод PATCH
/handler Z
, W
, C
) на обновление на процесс-сервер и возвращается в режим ожидания времени обучения.
Все запросы локального REST API
процесса-сервера представлены в таблице:
URL и метод запроса | Описание |
---|---|
GET /ping (Z , W , C ) |
Запрос, возвращающий пустую JSON строку. Используется в отладочных целях для определения доступности сервиса. Возвращает ответ HTTP со статусом 200 («Ok», «Успешно»). |
PATCH /handler (Z , W , C ) |
Запрос на обновление модели обработчика. Возвращает ответ HTTP со статусом 200 («Ok», «Успешно») в случае успешной загрузки модели. В случае отсутствия файла возвращает сообщение с HTTP статусом 500 («Internal Server Error», «Внутренняя ошибка сервера»). |
GET /users (Z , W , C ) |
Запрос на поиск информации по запросу. В параметрах запроса должно быть задано ключевое слово query со значением в виде строки (поисковой запрос). Опционально можно передать параметр more . Если его значение равно true , запрос вернет дополнительную информацию. В иных случаях запрос вернет префиксы пользователей и оценку модели обработки данных. В случае отсутствия параметра query возвращает ответ с HTTP статусом 400 («Bad request», «Неправильный, некорректный запрос»). В случае, если модель не загружена, выполняет загрузку и, в случае ошибок, возвращает ошибки запроса PATCH /handle. В случае успешного выполнения запроса возвращает сообщение с HTTP статусом 200 («Ok», «Успешно»), а также телом в виде JSON строки. |
Формат возвращаемой запросом GET
/users JSON
строки:
{
"data": {
"user_1": {
"<имя>_score": 9.3
},
"user_2": {
"<имя>_score": 6.4
}
},
"message": "Сообщение"
}
Ключами корневого объекта JSON
являются data
и message
. Ключ message
является опциональным и служит для передачи служебных сообщений (например, если запрос был выполнен, но часть данных не была загружена). По ключу data
находится объект, хранящий префиксы почты пользователей в виде ключей объекта. Значениями этих ключей являются объекты, содержащие как минимум один ключ – имя модели (или обрабатываемого сервиса), нижнее подчеркивание и слово score
. Значением является оценка релевантности пользователя моделей обработки данных. В объектах пользователей также может быть представлена любая другая дополнительная информация (например, имя пользователя в корпоративном чате Zulip), если в параметрах запроса был указан параметр more
со значением true
.
Алгоритм работы микросервиса Competence Search можно описать следующей последовательностью действий:
Список публичных REST API
запросов, на которые может отвечать данные сервис:
URL и метод запроса | Описание |
---|---|
GET /ping (код) |
Запрос, возвращающий JSON строку, содержащей все ответы модулей-обработчиков на запрос GET /ping. Используется в отладочных целях. Возвращает ответ HTTP со статусом 200 («Ok», «Успешно»). |
GET /help (код) |
Запрос на получение справочной информации. Возвращает информацию в виде JSON строки, где по ключу message находится строка со справочной информацией. HTTP статус ответа – 200 («Ok», «Успешно»). |
GET /users (код) |
Запрос на поиск кандидатов. В параметрах запроса должно быть задано ключевое слово query со значением в виде строки (поисковой запрос). Опционально можно передать параметр more . Если его значение равно true , запрос вернет дополнительную информацию. В случае отсутствия параметра query возвращает ответ с HTTP статусом 400 («Bad request», «Неправильный, некорректный запрос»). Иначе возвращает JSON ответ с HTTP статусом 200 («Ok», «Успешно»). |
POST /feedback (код) |
Запрос, сохраняющий обратную связь. В теле запроса должна находиться JSON строка с ключами bot_answer , user_answer и user_info . Перенаправляет всю информацию на метод POST /feedback микросервиса Gtable и возвращает те же сообщения, что и микросервис с обратной связью. |
Формат JSON
ответа на запрос GET
/ping:
{
"cabinet": {
"alert_message": "Could not connect to the service container.",
"status_code": 503
},
"wiki" : {
"status_code": 200
},
"zulip": {
"status_code": 200
}
}
В возвращаемом объекте JSON
представлены ответы всех модулей-обработчиков данных на запрос GET
/ping. Если значение поля status_code
внутри объектов по ключу с именем сервиса равно 200
(«Ok», «Успешно»), обработчик доступен поисковику. Иначе модуль-обработчик не доступен поисковику и его данные не будут использованы для выполнения запроса.
Формат JSON
ответа на запрос GET
/users:
{
"data": [
{
"email_prefix": "user_1",
"total_score": 1.0,
"wiki_score": 0.7,
"zulip_score": 4.3
},
{
"email_prefix": "user_2",
"total_score": 0.8,
"wiki_score": 0.4,
"zulip_score": 6.2
},
],
"cabinet": {
"alert_message": "Could not connect to the service container.",
"status_code": 503
}
}
Обязательным ключом корневого объекта JSON
является ключ data
. Он содержит массив, отсортированный по убыванию значения total_score
(итоговой оценки релевантности).
Элементами массива являются объекты, содержащие префикс корпоративной почты пользователя, оценку релевантности каждого модуля-обработчика, суммарную оценку релевантности пользователя и дополнительную информацию (если в параметрах запроса был указан параметр more
со значением true
).
В случае, если модуль-обработчик не смог обработать запрос, к корневому объекту JSON
добавляется ключ с названием сервиса. В нем хранится объект, содержащий сообщение-предупреждение, а также HTTP
статус ошибки 503
– «Service unavailable», «Сервис недоступен».
Алгоритм работы микросервиса по сбору обратной связи можно описать следующей последовательностью действий:
Запросы локального REST API
данного микросервиса представлены в таблице:
URL и метод запроса | Описание |
---|---|
GET /ping (код) |
Запрос, возвращающий пустую JSON строку. Используется в отладочных целях. Возвращает ответ HTTP со статусом 200 («Ok», «Успешно»). |
POST /feedback (код) |
Запрос, сохраняющий обратную связь. В теле запроса должна находиться JSON строка. Корневой объект должен иметь ключи: bot_answer (ответ бота, результат поиска), user_answer (отзыв пользователя) и user_info (информация о пользователе). В случае успешного сохранения возвращает ответ HTTP со статусом 200 («Ok», «Успешно»). В случае ошибки в теле запроса возвращает ответ HTTP со статусом 400 («Bad request», «Неправильный, некорректный запрос»). |
Алгоритм работы чат бота представлен на рисунке.
В начале работы чат-бот читает файл с реквизитами (о файлах с реквизитами можно прочитать здесь).
Затем запускается цикл ожидания сообщений. При поступлении сообщения чат-бот определяет тип сообщения. Если сообщение является обратной связью (код), бот вычленяет отзыв пользователя из сообщения, собирает информацию о пользователе и запоминает поисковой ответ. Далее бот отправляет информацию об обратной связи главному микросервису Competence Search, а также отправляет сообщение пользователю об успешной отправке обратной связи.
Если сообщение пользователя является командой (код), пользователю отправляется сообщение, содержащее справочную информацию.
Если сообщение не является командой или сообщением обратной связи (код), бот классифицирует сообщение как поисковой запрос. После этого бот отправляет данное сообщение главному микросервису Comptence Search. После получения ответа от главного микросервиса бот форматирует полученные данные (код) и отправляет пользователю ответ. В конце всех операций бот возвращается в режим ожидания новых сообщений.
Для получения информации используется API
Zulip. Полученный массив, включающий в себя темы, содержание сообщений и id
авторов, записывается в текстовые файлы messages.txt
и ids.json
в директорию zulip/data
.
Далее этот файл поступает в модуль, взаимодействующий с моделью машинного обучения (код) и модель машинного обучения начинает обучаться.
Алгоритм получения сообщений выглядит следующим образом:
Программа получает id
последнего сообщения, которое написано на момент выполнения программы, и записывает его в переменную last_anchor
(код);
При помощи эндпоинта GET
/api/v1/messages (API
Zulip) сервис циклично производит запрос на получение 4500
сообщений за раз (код). Такое число обусловлено лимитом сервиса на единовременное получение сообщений. Вводится переменная anchor
, которой присваивается id
последнего написанного сообщения в запросе. Когда ее значение превышает значение параметра last_anchor
(код), запросы на сообщения прекращаются;
При получении массива сообщений каждое из них обрабатывается в отдельной функции preprocess_message
(код). В ней происходит удаление HTML
-тегов и специальных слов и предложений, которые часто повторяются в Zulip и не несут ценности для разрабатываемого сервиса;
Если в результате сообщение является непустым и содержит больше одного слова, оно записывается в текстовый файл messages.txt
в директорию zulip/data
, в котором указываются тема и содержание сообщения (код);
После обработки всех сообщений, полученных при запросе, переменной anchor
присваивается идентификатор последнего полученного сообщения.
Принцип интеграции МИЭМ Wiki в сервис Поисковик компетенций следующий: сервис собирает тексты страниц и идентификаторы авторов, затем загружает их в модель машинного обучения и, найдя наиболее релевантные статьи под запрос, выдает список их авторов.
Для сбора информации о статьях, которые пользователи пишут непосредственно в МИЭМ Wiki, разрабатываемый сервис использует GraphQL API
.
Специфика данного сервиса заключается в том, что помимо оригинальных страниц, написанных студентами и преподавателями, он также агрегирует информацию из сервиса Taiga – инструмента для управления проектами. Соответственно, при получении данных из МИЭМ Wiki следует обрабатывать их по-разному.
Данные из сервиса Taiga являются наиболее полезными, так как содержат проектную документацию, в тексте которой возможно найти фразы, подтверждающие компетентность авторов (предполагается, что, например, если человек написал руководство администратора, то он имеет компетенции в решении вопросов администрирования). Однако существуют авторы таких статей, которые не авторизованы в МИЭМ Wiki, поэтому брать информацию о их не представляется возможным. Для этого анализируется содержание страниц на предмет наличия специального блока текста, который указывает, что данная страница поступила из сервиса Taiga.
Первоначально в модель машинного обучения загружались страницы как единые документы. Однако средняя страница содержит слишком много тем в объеме текста, что не подходит для точного выделения компетенций. Поэтому было принято решение делить страницы на более мелкие документы по подзаголовкам. Так как для создания страниц в МИЭМ Wiki используются два способа:
HTML
иMarkdown
разметки – программа выделяет отдельные документы по тегам заголовков.
В результате модуль по работе с моделью машинного обучения передается два файла: текстовый массив с содержанием статей - wiki.txt
в директории wiki/data
, и файл в формате JSON
- ids.json
в директории wiki/data
, в котором порядок документов в первом файле (id
документа, или же номер строки в файле) соответствует данным об идентификаторе самой страницы в МИЭМ Wiki и списке ее авторов.
Итоговый алгоритм получения данных из МИЭМ Wiki выглядит следующим образом:
GraphQL API
(код);1
– это id
административного аккаунта, а эта информация является нерелевантной для нашего проекта);HTML
-теги, кроме тегов заголовков, так как они нужны для последующего разбиения страницы на отдельные документы;API
МИЭМ Wiki (код);id
пользователей Wiki в финальный список авторов;HTML
или Markdown
заголовкам;HTML
и Markdown
тегов (код);JSON
-файл идентификатора страницы и префиксов почт ее авторов (код).Личный кабинет МИЭМ хранит информацию об исследовательских и прикладных проектах факультета.
Сервис получает данные по следующему алгоритму:
API
Личного кабинета программа получает список проектов, находящихся в статусе «Рабочий» (GET
/public-api/projects) и «В архиве» (GET
/public-api/projects/archive) (код);GET
/public-api/project/body/{projectId}) (код);projects.txt
записываются описание самого проекта и общие для всех студентов компетенции;GET
/public-api/project/vacancies/{projectId} получаем список вакансий (ролей) для проекта, для каждой вакансии указан список компетенций (код);GET
/public-api/project/students/{projectId}) (код);students_info.json
id
студентов и соответствующие им компетенции.В модель передаются три типа документов: текстовый массив с описанием каждого проекта projects.txt
в директории cabinet/data
, JSON
-файл projects_info.json
в директории cabinet/data
, в котором помещены данные с id
проекта и соответствующему ему списком id
участников, а также файл с информацией о компетенциях каждого пользователя Личного кабинета - students_info.json
в директории cabinet/data
.
О размещении приложения на сервере подробно описано здесь.
Далее будут описаны основные методы и составляющие части библиотеки в том порядке, в котором они используются при инициализации модели и обработке данных (файл lib/sbertop2vec.py
).
Первым по порядку идет метод инициализации (код), выполняемый при создании модели. На этом этапе задаются основные параметры модели, её свойства и создаются важные внутренние переменные.
После того, как модель инициализировалась, ее следует обучить методом fit
или загрузить методом load_model
.
В случае выполнения метода fit
, модель выполняет разбиение передаваемых ей документов и соответствующих им авторов по темам. Данный этап может быть разделён на несколько основных частей:
TensorFlow Hub
. Также реализована возможность передачи методу уже готовых векторов.UMAP
(код). Нейросеть возвращает вектора размерности 512
, что является слишком большим числом для обработки на следующем этапе. В данном месте явно используются параметры (n_neighbors
и n_components
), которые были заданы при инициализации модели. Здесь они отвечают за размерность выходных векторов.HDBSCAN
(код). На данном этапе явно используется параметр (min_topic_size
), который был задан при инициализации модели. Здесь он отвечает за минимальное количество документов в группе.После завершения всех описанных выше этапов становится возможно получение полезной информации. Для этого в библиотеке реализован ряд методов. Например, метод get_topics_by_query
позволяет найти заданное количество групп документов, которые наиболее точно передают суть указанного запроса.
Метод get_documents_by_topic
просто возвращает документы, которые принадлежат указанной группе. Отметим, что документы отранжированы по тому, насколько хорошо они относятся к своей группе. Также существует возможность вывода указанного количества документов.
Метод get_documents_by_query
возвращает максимально близкие (в векторном пространстве) к запросу документы, причем такие документы берутся только из заданного количества ближайших к запросу групп.
Метод get_users_by_query
позволяет находить заданное количество пользователей по заданному запросу. Каждому сообщению присваивается определенное значение смысловой близости к указанному запросу, и пользователь является релевантным, если по сумме всех его сообщений метрика больше, чем у остальных. Именно этот метод является основным в контексте всей работы, поскольку с его помощью выполняется поиск людей по заданным компетенциям.
Также модель имеет внутренние методы, необходимые для работы описанных ранее основных методов. Одним из них является метод _c_tf_idf
, помогающий найти слова, которые максимально ясно описывают каждый из топиков, за счёт использования информации о других группах. То есть выделяются слова, которые имеются в документах одной группы, но редко или никогда не встречаются в документах других групп.
Для составления оценок релевантности пользователей по данным сервиса Личный кабинет МИЭМ был разработан программный модуль, осуществляющий поиск по ключевым словам в проектных «вакансиях» студентов (файл lib/smartsearch.py
).
Работа модели основана на векторизации документов с использованием TF-IDF
меры. Для этого данные в «вакансиях» проходят через несколько этапов обработки:
TF-IDF
вектора).Пример обработки документа:
После этого составляется матрица документ-слово, в ячейках которой содержатся значения TF-IDF
меры (код).
Те же методы применяются к входному запросу, состоящему из набора слов — желаемых компетенций.
Компетенции каждого пользователя сервиса Личный кабинет МИЭМ представляются единым документом. Из этого следует, что по токену из запроса можно обратиться ко всему столбцу TF-IDF
матрицы и посчитать релевантность каждого пользователя по запрашиваемому токену. Просуммировав оценки для каждого токена из запроса, можно вычислить итоговую релевантность пользователя по всему запросу. На выходе модели итоговая оценка для каждого пользователя проходит через min-max
нормализацию, чтобы иметь одинаковый вес с оценками других моделей.
Модель возвращает словарь, в котором ключи — это идентификаторы пользователей, а в значениях записаны финальные оценки релевантности пользователей вместе со списками всех проектов, в которых они участвовали.
В модели реализован метод fit
, создающий и сохраняющий в памяти векторные представления всех документов в корпусе и другие необходимые для работы модели данные. Это позволяет экономить время и вычислительные мощности при запросе, так как основные вычисления и преобразования данных должны быть произведены всего один раз (до следующей подгрузки данных из сервиса).
Также были реализованы методы save
и load
, позволяющие хранить все необходимые данные и параметры модели в постоянной памяти или, наоборот, загружать их.