История одного бота: как сэкономить 500 часов на откликах и случайно отправить резюме боссу
Привет, Хабр.
Меня зовут Вадим, я бэкенд-разработчик.
Все мы знаем, что поиск работы в IT — это отдельный круг ада. Особенно этап "воронки": чтобы получить один оффер, нужно отправить 100+ откликов. Причем не пустых, а с вменяемым сопроводительным.
Как инженер, я не люблю рутину. Если алгоритм действий повторяется ("прочитать — сопоставить — написать — отправить"), значит, его можно заскриптовать.
Так появился мой pet-проект «Аврора», который на прошлой неделе перерос в закрытую бету на 100 человек. Рассказываю про архитектуру, промпт-инжиниринг и о том, почему if company_name != current_job — это самая важная строчка кода, которую я забыл написать.
Архитектура: не просто кликер
Сделать скрипт на Selenium, который спамит кнопкой "Откликнуться" — дело одного вечера. Но мне нужно было качество. Я хотел, чтобы бот мимикрировал под вдумчивого кандидата.
Стек: Python 3.11, Aiogram (для интерфейса в ТГ), PostgreSQL (хранение контекста юзеров), Redis (очереди задач), Google Gemini (мозг).
Логика работы агента выглядит так:
-
Парсинг и нормализация. Бот забирает вакансии через API hh.ru. Тут первая боль: API отдает много мусора. Пришлось писать сложный фильтр на Pydantic, чтобы отсеивать вакансии, где стек указан "для галочки".
-
Смысловой мэтчинг. Мы не ищем по ключевым словам (
if 'Python' in text). Мы скармливаем LLM "слепок" резюме пользователя и текст вакансии. Модель возвращает score релевантности от 0 до 1. -
Генерация сопроводительного. Это самая интересная часть.
Промпт-инжиниринг: как пройти HR-фильтр
Мой кофаундер (в прошлом IT-рекрутер) объяснил, что HRы ненавидят ChatGPT-style письма ("Я восхищен вашей компанией...").
Мы потратили недели на тюнинг системного промпта.
Вместо "напиши письмо", мы говорим модели:
"Ты — сеньор-разработчик. Найди в резюме пользователя опыт, релевантный именно этим требованиям вакансии. Напиши короткое (3-4 предложения) письмо. Если опыта в конкретной технологии нет — честно укажи это, но подсвети быструю обучаемость на примере прошлого стека".
Результат:
За 3 дня теста пользователи отправили 2025 откликов.
Мы получили скриншоты ответов от HR. Они пишут: "Спасибо за подробное письмо", "Вижу, вы изучили наш стек". Тест Тьюринга пройден: рекрутеры не выкупают, что общаются с нейросетью.
Факап, или почему мы закрыли доступ
В погоне за качеством генерации я упустил базовую логику безопасности.
В чат поддержки прилетает сообщение от пользователя (DevOps):
"Ребят, бот пушка. Но есть нюанс. Он только что нашел вакансию в моей текущей компании. И откликнулся на нее. Моему тимлиду пришло письмо о том, как я хочу работать в [Название Компании], хотя я там уже работаю".
Причина: Я не реализовал Blacklist компаний. Бот увидел идеальное совпадение по стеку (еще бы, человек там работает!) и радостно отправил отклик.
Решение: Экстренно стопнули воркеров. Сейчас переписываем модуль фильтрации, добавляем загрузку списка доменов/названий, куда откликаться нельзя категорически.
Итоги и планы
Сейчас мы временно закрыли регистрацию, чтобы разгрести бэклог.
Что делаем в спринте:
-
Blacklist компаний (приоритет Critical).
-
Улучшение фильтрации. LLM иногда "галлюцинирует" с грейдами, предлагая сеньору вакансии миддла, если стек совпадает. Будем ужесточать правила.
-
Оптимизация очередей. 2000 откликов создали нагрузку, которую не ожидали лимиты API. Переезжаем на более умный шедулер.
Если вам интересна техническая сторона (как мы обходим лимиты, как храним контекст, как боремся с дублями вакансий) — я буду периодически писать об этом в нашем канале.
https://t.me/chatgptdom_telegram_bot>
Готов ответить на технические вопросы в комментариях. Пинайте :)