Перейти к содержимому

Resume2human: git-контакты инженеров, скоринг и воронка откликов после запуска

Неделя после запуска resume2human: как Git вернул контакты инженеров, а скоринг подарил 50 баллов

Шесть дней назад я рассказывал о resume2human - Windows-приложении, которое превращает резюме в подборку подходящих вакансий и помогает найти по каждой из них одного-трёх реальных людей: с именем, должностью и способом связаться напрямую.

За последнюю неделю в проект вошёл 21 коммит, объединённый в девять pull request. Количество тестов выросло с 345 до 746. При этом выяснилось, что половина обещаний из предыдущего материала уже устарела.

Главные изменения оказались неожиданными:

- открытые Git-коммиты стали источником инженерных контактов;
- значительная часть кода теперь занимается отбраковкой результатов;
- Telegram работает через пользовательскую сессию, а не через бота;
- скоринг некоторое время раздавал по 50 бесплатных баллов;
- география больше не участвует в оценке совпадения;
- в приложение добавились воронка откликов, статусы и напоминания;
- отчёты научились проверять сами себя;
- документация теперь сверяется с реальным поведением программы.

Что делает resume2human

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

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

resume2human собирает данные из 24 источников: ATS-досок 111 компаний, региональных сервисов, LinkedIn, Telegram, Threads и обычного веб-поиска. Затем программа сопоставляет резюме с описанием вакансии и показывает несколько подходящих контактов.

В приложении нет автоматической рассылки, платных API-ключей и обязательного использования ИИ. Пользователь сам проверяет найденные сведения и самостоятельно пишет письмо.

Git оказался открытой базой контактов

Первоначальная система отвечала на вопрос: "Кто участвует в найме?". Однако имя, должность и ссылка на профиль не всегда помогают связаться с человеком. Второй вопрос - "Как ему написать?" - оставался без ответа.

Платные сервисы вроде ContactOut, Lusha, Apollo и RocketReach решают эту задачу за счёт подписных баз. Но для инженеров существует другой источник - история Git-коммитов.

Каждый коммит содержит поле `author.email`. Оно является обязательной частью метаданных. Конечно, пользователь может настроить публичный адрес вида `noreply` или скрыть почту в профиле GitHub. Но старые коммиты нередко создавались до появления таких настроек - с личного компьютера и с настоящим адресом автора.

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

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

Почему половина модуля - это фильтры

Получить адрес технически несложно. Сложнее понять, действительно ли он относится к нужному человеку.

В репозиториях встречаются:

- служебные адреса;
- временные почтовые ящики;
- адреса CI-систем;
- чужие аккаунты;
- старые контакты;
- домены, не связанные с работодателем;
- почты из форков и автоматических зеркал.

Поэтому модуль поиска не просто извлекает строки после `author.email`. Он сопоставляет домен, имя автора, профиль, репозиторий, организацию и контекст коммита. Большая часть логики посвящена удалению сомнительных совпадений.

Именно здесь проявляется разница между "нашли адрес" и "нашли пригодный контакт". Первый результат можно получить быстро. Второй требует проверок, ограничений и понятного объяснения происхождения данных.

Законность и этика

Открытость информации не отменяет требований к её использованию. Публичный адрес нельзя автоматически считать разрешением на массовую рассылку.

Безопасный сценарий выглядит так:

1. письмо связано с конкретной вакансией;
2. получатель действительно участвует в найме или работает в соответствующей команде;
3. сообщение персонализировано;
4. в нём нет давления и массового рекламного текста;
5. автор письма готов прекратить коммуникацию по просьбе адресата.

Программа не должна превращать поиск контактов в спам-машину. Поэтому автоматическая отправка намеренно не добавлялась: последнее решение остаётся за человеком.

Как изменились прежние ограничения

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

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

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

В приложение добавились:

- статусы откликов;
- воронка;
- напоминания;
- шесть аналитических разрезов;
- история взаимодействий;
- возможность возвращаться к ранее найденным вакансиям.

Таблица `applications` существовала в схеме с самого начала, однако долго оставалась пустым заделом. Реальное использование показало, что пользователь хочет видеть не только найденную вакансию, но и дальнейшую судьбу отклика.

Ошибки, которые долго маскировались под успех

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

Флаки-тест

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

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

`Argument list too long`

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

Русская раскладка и Ctrl+C

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

Переводы без gettext

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

Скоринг пришлось пересматривать

Наиболее неприятная ошибка оказалась в оценке совпадения резюме и вакансии. Один из факторов давал до 50 баллов практически бесплатно.

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

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

Исправления включили:

- отдельную обработку пустых наборов;
- запрет начисления баллов при отсутствии подтверждённых признаков;
- тесты на нулевые и неполные данные;
- раздельное отображение сильных и слабых факторов;
- проверку итогового балла на реалистичный диапазон.

География также перестала быть самостоятельным слагаемым скоринга. Локация может быть полезным фильтром, но не должна компенсировать отсутствие нужных навыков и опыта.

Telegram: пользовательская сессия вместо бота

Для работы с Telegram выбран не бот, а пользовательская сессия. Это позволяет получать данные из тех сценариев, где бот ограничен правами доступа и поведением платформы.

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

Доверие к отчётам

Отчёт должен быть проверяемым не только визуально. В проекте появились контрольные суммы SHA-256, подпись артефактов и автоматическая проверка ссылок.

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

Теперь витрина проверяет:

- существование файлов;
- доступность внутренних ссылок;
- соответствие заявленных функций текущей версии;
- наличие обязательных изображений;
- целостность собранных артефактов.

Воронка откликов и измерение результата

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

Воронка теперь может включать этапы:

1. найдено;
2. проверено;
3. выбран контакт;
4. подготовлено письмо;
5. отправлено;
6. получен ответ;
7. назначен следующий шаг;
8. закрыто или отложено.

Для небольших выборок используется интервал Уилсона. Он показывает неопределённость доли ответов честнее, чем простое "ответили 2 из 5". При малом числе наблюдений один успешный отклик не должен выглядеть как доказательство высокой эффективности.

Что осталось неизменным

Инструмент не обещает:

- автоматическую отправку писем;
- гарантированное получение ответа;
- абсолютную точность найденных контактов;
- замену профессиональной CRM;
- обход закрытых баз;
- достоверность каждого адреса без дополнительной проверки.

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

Числа недели

За шесть дней:

- 21 коммит;
- 9 pull request;
- 746 тестов вместо 345;
- 24 источника данных;
- 111 корпоративных ATS-досок;
- до трёх контактов на вакансию;
- шесть аналитических разрезов по откликам.

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

Что дальше

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

Также предстоит продолжить работу над ограничениями:

- более точной дедупликацией людей;
- определением устаревших адресов;
- защитой от повторных обращений;
- контролем частоты писем;
- улучшением шаблонов персонализации;
- расширением тестов для редких и ошибочных входных данных.

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

Прокрутить вверх