Менеджмент

AI-CRM для рекрутинга: как превратить большие данные в инструмент принятия решений о найме

1694 
 

Автор статьи: Иван Манжетов, менеджер портфеля проектов в KODE

Подбор сотрудников в крупных компаниях часто выглядит гораздо менее технологичным процессом, чем принято думать. За этим процессом обычно скрываются десятки таблиц, сотни резюме и огромное количество информации, которая хранится в разных местах и практически не связана между собой.

Резюме приходят с различных job-площадок, заметки после интервью каждый рекрутер ведёт по-своему, результаты технических собеседований хранятся в отдельных документах, а зарплатные ожидания кандидатов сверяются с рыночными данными вручную. Чем больше компания и чем больше вакансий она ведёт одновременно, тем сложнее становится принимать взвешенные решения.

В какой-то момент найм начинает зависеть не от данных, а от субъективных впечатлений. Один рекрутер считает кандидата перспективным, другой видит риски. Нанимающий менеджер делает вывод на основе одного интервью.

Для крупной компании, работающей с большим потоком кандидатов, такой подход становится серьёзной проблемой. Ошибка найма дорого обходится бизнесу: это потерянное время команды, дополнительные затраты на поиск нового сотрудника и риски для проектов.

Нам предстояло создать систему, которая поможет принимать решения о найме не интуитивно, а на основе объективных данных.

Систему мы делали под распределённую HR-команду, которая обрабатывает сотни заявок одновременно по нескольким открытым позициям. При таком объёме информации и её разрозненности решение о найме неизбежно становилось интуитивным, опирающимся на субъективное впечатление от последнего разговора, а не на агрегированные данные по всей воронке.

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

Архитектурные решения

Мы спроектировали систему как SPA с микросервисной архитектурой. Такой подход обеспечивает независимое масштабирование отдельных компонентов и упрощает поддержку по мере роста числа пользователей и объёма данных.

Данные кандидатов хранятся в PostgreSQL внутри собственного контура: персональные данные соискателей разумно держать у себя, а не отдавать во внешние сервисы. Наружу они не уходят в неконтролируемом виде — все AI-вызовы проходят через управляемый слой с явным разграничением того, какие поля попадают в промты.

В качестве AI-модели был выбран Gemini. Выбор был осознанным и основан на сравнительном тестировании нескольких моделей на реальных кейсах нашего подбора. Gemini показал наиболее критичный и детальный подход к оценке кандидатов: модель точнее других выявляла зоны риска и несоответствия требованиям, не склонялась к «мягким» оценкам. Именно это качество принципиально важно в рекрутинге, где цена ошибки найма высока как с точки зрения финансов, так и с точки зрения командной динамики.

Единый профиль кандидата в центре системы: вокруг него — модули, хранилище данных, интеграции и фоновые воркеры.


Тяжёлые операции — транскрибация записей, обращения к AI-модели, сбор рыночных данных — вынесены в асинхронные фоновые процессы. Интерфейс остаётся отзывчивым: пользователь ставит задачу и продолжает работу, а результат подтягивается по мере готовности. Состояние каждого фонового прогона сохраняется в базе, поэтому задача переживает перезапуск сервиса, выполняется идемпотентно (повторный запуск не создаёт дублей) и при следующем проходе догружает только изменения, а не пересобирает всё заново.

В основе — нормализованная модель данных, где кандидат вынесен в отдельную сущность, а его участие в конкретной вакансии описывается отдельной связью «отклик». Это принципиально: один и тот же человек может одновременно вести несколько процессов по разным позициям, но в системе остаётся единой карточкой, а не размножается на копии. История по каждому отклику — этапы и их смена — хранится как последовательность событий со временем, и именно она впоследствии питает аналитику воронки.

Обращения к AI-модели идут через отдельный слой-шлюз. Он собирает промт не из всей карточки кандидата, а из явно перечисленного набора полей под конкретную задачу оценки — так персональные данные не попадают в модель в избыточном объёме, а состав входных данных остаётся предсказуемым и контролируемым.

Что под капотом: техническая реализация

Идея «собрать все данные о кандидате в одном месте» проверяется не на демо, а на реальном потоке в сотни заявок по нескольким вакансиям сразу. Ниже — инженерные решения, которые определяют, работает ли система на таком объёме: как устроены фоновая обработка, синхронизация, модель данных, разбор входных документов и слой обращения к модели.

Асинхронность и переживаемость фоновых задач

Всё, что дольше пары секунд, — разбор резюме, обращения к модели, транскрибация, сбор рыночных данных, синхронизация внешних источников — вынесено в фоновые задачи. Интерфейс от них не зависит: пользователь ставит задачу и продолжает работу, а результат подтягивается по мере готовности. Главное требование к таким задачам — переживаемость. Длинный прогон (например, полная синхронизация базы кандидатов) не должен теряться при деплое или перезапуске сервиса. Поэтому состояние прогона — счётчики, лог, статус — периодически сбрасывается в базу с троттлингом, чтобы не писать на каждую обработанную запись. Если процесс всё же умирает, «зависший» прогон определяется по отсутствию обновлений и помечается прерванным, а не висит вечно. 

Есть и кооперативная остановка: задачу можно прервать из интерфейса — воркер замечает флаг между шагами и завершается штатно, а не продолжает молотить впустую.

Жизненный цикл фонового прогона: сверка с источником, догрузка только дельты, upsert без дублей и устойчивость к перезапуску.

Идемпотентность и инкрементальная синхронизация

Повторный запуск синхронизации не должен плодить копии — на этом держится возможность гонять её хоть каждую ночь. Идемпотентность обеспечивается внешними идентификаторами: каждая сущность из внешнего источника (вакансия, отклик, комментарий, событие) несёт свой source-id, а запись идёт через upsert — INSERT … ON CONFLICT DO UPDATE. Если запись уже есть, она обновляется, если нет — вставляется; дублей не возникает по определению. Второй приём — «докачиваем только изменения»: перед обходом каждой вакансии система сверяет число уже импортированных откликов с числом в источнике и пропускает те, что уже полные, а по неполным дочитывает недостающее. За счёт этого ночная синхронизация стоит дёшево даже на большой базе — она не пересобирает всё заново, а трогает только то, что реально изменилось.

Модель данных: отклики и история как поток событий

Схема нормализована, и две вещи в ней принципиальны. Первая — кандидат и его участие в вакансии разведены: кандидат хранится один раз, а на каждую вакансию заводится отдельная строка-«отклик» со своим этапом (связь многие-ко-многим). Именно это позволяет одному человеку одновременно быть в нескольких воронках, оставаясь единой карточкой, а не размножаясь на копии. Вторая — история этапов хранится не как «текущий статус», который перезатирается при каждом переходе, а как поток событий смены этапа со временем (по сути event sourcing на уровне воронки). Из этого потока потом считаются конверсия, время в этапе и причины отказов — то, что было бы невозможно восстановить, если бы мы держали только последнее состояние. Дедупликация кандидатов идёт по устойчивым ключам — email и телефон — с функциональными индексами (по lower(email) и по нормализованным цифрам телефона), чтобы поиск дубля оставался быстрым на сотнях тысяч записей и не срабатывал ложно по одному лишь совпадению ФИО.

Разбор резюме: где «просто распарсить PDF» стало инженерной задачей

Показательный пример того, как рутинная на вид задача превращается в отдельную инженерную историю. Первая версия извлечения текста стабильно падала в проде на части файлов — причём воспроизводилось только на боевой машине: библиотека, отвечавшая за разбор страницы, роняла процесс низкоуровневым сбоем (SIGILL) из-за процессорных инструкций, которых на этом железе не оказалось. Чинить рендер-движок под конкретный CPU бессмысленно, поэтому извлечение текста мы перевели на poppler (pdftotext): оно не зависит от рендеринга страницы и работает предсказуемо, а для сканов остаётся OCR-фолбэк. Вывод банальный, но купленный дорого: чтобы получить текст, рендерить страницу не нужно, а прод-окружение стоит считать другим железом, чем дев, — и проверять на нём заранее.

Интеграции: токены, лимиты, изоляция ошибок

Внешние источники — это OAuth-токены с ротацией, квоты и периодические отказы. Токены хранятся в базе и обновляются автоматически: доступ живёт своим TTL, при ответе 401 делается один принудительный refresh и повтор запроса — прикладной код об этом не думает. Обходы источников идут последовательно и с паузами, чтобы не упираться в rate-limit; ошибка на одной записи изолируется и попадает в счётчик, а не роняет весь прогон целиком. Такой «пессимистичный» подход к внешнему миру и позволяет синхронизации быть фоновой и незаметной: она не требует присмотра и сама переживает временные сбои источника.


Разместите
тендер бесплатно

Наша система сама подберет вам исполнителей на услуги, связанные с разработкой сайта или приложения, поисковой оптимизацией, контекстной рекламой, маркетингом, SMM и PR.

Заполнить заявку 13691 тендер
проведено за восемь лет работы нашего сайта.


AI-слой: скоуп полей, шаблоны по направлениям, структурированный вывод

Обращения к модели проходят через слой-шлюз, и у него две задачи помимо собственно вызова. Первая — скоуп полей: на вход собирается не вся карточка, а явный список полей под конкретную оценку. Это одновременно минимизация персональных данных и предсказуемость — мы точно знаем, что видит модель. Вторая — форма запроса: логика оценки разложена не в один большой промт, а в набор шаблонов по направлениям (hard skills, soft skills, риски) с фиксированными наборами вопросов, а ответ запрашивается в структурированном, машиночитаемом виде, чтобы раскладывать его по карточке автоматически, а не парсить сплошной текст. Сам выбор модели — не вкусовое решение: несколько кандидатов прогонялись на реальных кейсах нашего подбора с ручной сверкой результатов, и победила та, что давала более критичную и детальную оценку.

Зарплатная аналитика: качество прячется в фильтрах

Модуль бенчмарка — это в основном работа с грязными данными, и именно фильтры, а не сбор, определяют его пользу. Ставки собираются из нескольких открытых источников, приводятся к одной валюте и размечаются по грейду (по стажу и заголовку, если он не указан явно). Дальше идёт очистка: отсекаются непомесячные ставки — почасовые и за смену, которые иначе смешались бы с месячными окладами, — и по окну свежести отбрасываются устаревшие анкеты, чтобы ожидание «сеньора за 30 тысяч» из объявления пятилетней давности не утянуло медиану вниз. Только очищенная выборка сворачивается в перцентили по роли и грейду, а вилка под конкретного кандидата берётся уже из них с поправкой на его уровень и опыт.

Модуль поиска и первичного отбора

Первый модуль решает задачу непрерывного мониторинга и первичной фильтрации входящего потока кандидатов. Система сканирует резюме на всех крупных job-площадках и сопоставляет их с требованиями вакансии в автоматическом режиме.

Требования к вакансии можно задать двумя способами. Первый — заполнить структурированную форму с явными критериями: стек, опыт, уровень, регион и так далее. Второй — загрузить произвольный текст описания вакансии, из которого система самостоятельно извлекает и структурирует требования. Это важно для практики: рекрутеры редко работают с аккуратно формализованными критериями, чаще с текстами от нанимающих менеджеров в свободном формате.

Система обрабатывает резюме в любом формате: PDF, Word, LinkedIn-профили. Отдельной задачей стала работа с неструктурированным текстом — ситуациями, когда кандидат описывает опыт произвольно, без чёткого деления на проекты, роли и технологии. Модель извлекает нужные данные и помещает их в единую структуру профиля.

По результатам анализа рекрутер получает только релевантных кандидатов с объяснением, почему система отнесла их к этой категории, — это позволяет быстро проверить качество фильтрации и скорректировать критерии при необходимости.


Приём резюме из любого формата → извлечение текста → структурирование → матчинг с объяснимостью.

Самая неблагодарная часть приёма резюме — разбор PDF. Файлы приходят из десятков источников, часто с нестандартной вёрсткой, шрифтами и пометками, а часть — вообще сканы. Мы извлекаем текст способом, не зависящим от хрупкого рендеринга страницы, с запасным путём через OCR для сканов, — чтобы «сложный» файл не ронял весь конвейер и не терял данные кандидата.

Извлечённый текст приводится к единой схеме профиля: роли, проекты, стек, периоды. Отдельно решается дедупликация — один и тот же человек нередко приходит из разных источников. Кандидатов объединяем по устойчивым идентификаторам (email, телефон), а не по ФИО: совпадение имени и фамилии ещё не значит, что это один человек, и матчинг по имени склеивал бы однофамильцев в одну карточку. Такой подход убирает ложные объединения и не размывает историю кандидата.

Модуль интервью: транскрибация и таймкоды

Для проведения интервью система интегрирована с собственным Jitsi-сервером заказчика — закрытым корпоративным сервисом видеосвязи, развёрнутым на инфраструктуре клиента. Это решение одновременно закрывает два требования: безопасность передачи данных (запись не уходит на сторонние серверы) и автоматическая постобработка записи внутри системы.

Разговор транскрибируется в реальном времени. После завершения интервью система автоматически формирует структурированное саммари: выделяет ключевые темы разговора, фиксирует конкретные ответы на важные вопросы, проставляет таймкоды к значимым фрагментам. Рекрутер или нанимающий менеджер могут вернуться к любому моменту записи одним кликом, без необходимости перематывать всё интервью целиком в поисках нужного фрагмента.

Это особенно полезно при многоэтапных процессах найма, когда между первым и финальным интервью проходит несколько недель: участники следующего этапа могут быстро освежить контекст по таймкодированному саммари, не запрашивая пересказ у рекрутера.

Запись в собственном контуре → транскрибация с разметкой по спикерам → саммари, тезисы которого связаны со смещениями в записи.


Транскрипт размечается по спикерам, а его фрагменты привязываются к таймкодам записи. Саммари формируется поверх этой разметки: каждый его тезис хранит ссылку на смещение в записи, поэтому переход «к моменту» — это не перемотка на глаз, а прямой прыжок по сохранённой координате. При многоэтапном найме, когда между интервью проходят недели, это снимает необходимость пересматривать запись целиком.

Трёхмерная оценка кандидата

Ключевой аналитический компонент системы — оценка кандидата по трём направлениям: hard skills, soft skills и риски. Логика оценки зашита в промтах: для каждого направления определён набор вопросов, на которые система должна дать развёрнутый ответ на основе доступных данных: резюме, транскрипта интервью, истории взаимодействия.

По hard skills система оценивает соответствие технических компетенций требованиям вакансии, опираясь на конкретные проекты и формулировки из резюме и интервью. По soft skills — анализирует паттерны коммуникации, способность структурировать мысль, реакцию на уточняющие вопросы. Блок рисков выявляет потенциальные проблемы: противоречия между резюме и устными ответами, признаки нестабильной карьерной траектории, несоответствие ожиданий по формату работы.

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

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

Единый вход (резюме, транскрипт, история) → оценщик по направлениям с фиксированным набором вопросов → hard / soft / риски и перечень «чего не хватило».

Логика оценки вынесена в промты и разложена по направлениям: для hard skills, soft skills и рисков — свои наборы вопросов, на которые модель отвечает по одним и тем же доступным данным. Ответ модели структурирован (машиночитаемый формат), поэтому его можно единообразно раскладывать по карточке, а не разбирать сплошной текст. 

Отдельный, полноправный выход оценки — не заключение, а перечень того, чего не хватило для вывода, и конкретные вопросы на следующую встречу. И ключевой принцип реализации: система не выносит вердикт автоматически — она расставляет акценты, а решение остаётся за человеком.

Зарплатный бенчмарк

Отдельный модуль закрывает задачу рыночного ценообразования. Система мониторит рыночные ставки по открытым источникам и дополняет их внутренней базой заказчика, которая обновляется раз в квартал. При оценке стоимости конкретного кандидата учитываются его компетенции, уровень и опыт. 

На практике это позволяет рекрутерам вести переговоры об условиях с опорой на актуальные данные, а не на интуицию или устаревшие внутренние ориентиры. Одним из измеримых эффектов стало снижение числа отказов на этапе офера из-за расхождения ожиданий: когда зарплатная вилка формируется на основе рыночных данных применительно к конкретному профилю кандидата, предложение попадает в ожидания значительно чаще.

Рыночные источники и внутренняя база → нормализация (валюта, грейд, свежесть) → перцентили → вилка под конкретного кандидата.

Рыночные данные собираются из открытых источников и приводятся к сопоставимому виду — и это самая тонкая часть. Валюты конвертируются к базовой; грейд определяется по стажу и заголовку, если он не указан явно; из выборки отсеиваются непомесячные ставки (почасовые, за смену) и устаревшие резюме — по окну свежести, чтобы, например, ожидание «сеньора за 30 тысяч» из анкеты пятилетней давности не искажало вилку. Уже очищенные данные агрегируются в перцентили по роли и грейду, а под конкретного кандидата вилка сужается с учётом его компетенций, уровня и опыта.

Аналитика воронки найма

Этот модуль решает задачу наблюдаемости процесса: до его появления заказчик мог видеть результат найма, но не понимал, где и почему теряются кандидаты.

Система даёт полную картину по воронке в разрезе каждой вакансии: сколько кандидатов находится в работе, на каких этапах они распределены, какова конверсия между этапами и по каким причинам происходят отказы. Причины фиксируются по единым категориям, а не записываются в свободной форме. Это позволяет выявлять системные проблемы, невидимые на уровне отдельного случая. Например, если кандидаты определённого профиля регулярно уходят именно на этапе офера — это сигнал для пересмотра условий или самой структуры офера, а не повод продолжать искать новых людей с теми же ожиданиями.


Модель данных воронки: события смены этапа со временем → конверсия, время в этапе и причины отказов по единому справочнику, в разрезе каждой вакансии.


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

Единый профиль кандидата

Все перечисленные модули замыкаются на единый профиль кандидата — центральный объект системы. В одном окне агрегированы: резюме и история контактов, оценки AI по трём направлениям, саммари интервью с таймкодами, зарплатный бенчмарк и текущий статус в воронке.

Нанимающий менеджер получает полную картину до первого звонка с кандидатом, без необходимости запрашивать контекст у рекрутера и собирать информацию из нескольких источников. Это сократило время на подготовку к интервью и сняло один из главных источников информационных потерь в процессе многоэтапного найма.

Технически профиль — это агрегат: он собирает данные из всех модулей вокруг одной карточки кандидата. Когда человек участвует сразу в нескольких вакансиях, для верхнеуровневого представления выбирается «представительный» отклик (по последней активности), а полная раскладка по всем вакансиям остаётся доступной внутри профиля.

В результате система автоматизировала работу с данными на каждом этапе найма: агрегация информации о кандидате происходит без ручного участия рекрутера. Решение о найме перестало быть интуитивным — оценка компетенций, анализ рисков и зарплатный бенчмарк заменили субъективное впечатление от разговора. По данным заказчика, количество ошибок найма сократилось на 57%. Аналитика воронки добавила измеримость туда, где её прежде не было: теперь видно не только кто нанят, но и где и почему теряются кандидаты на каждом этапе.


Лучшее
Выскажите мнение
Авторизуйтесь, чтобы добавить свой комментарий.




1694

Лучшие статьи

Поделиться: 0 0 0
Лайки за кейсы:  383 Подписчики:  4

Оцените статью
Спасибо за оценку