Привет, Хабр.

Меня зовут Вадим, я бэкенд-разработчик.

Все мы знаем, что поиск работы в IT — это отдельный круг ада. Особенно этап "воронки": чтобы получить один оффер, нужно отправить 100+ откликов. Причем не пустых, а с вменяемым сопроводительным.

Как инженер, я не люблю рутину. Если алгоритм действий повторяется ("прочитать — сопоставить — написать — отправить"), значит, его можно заскриптовать.

Так появился мой pet-проект «Аврора», который на прошлой неделе перерос в закрытую бету на 100 человек. Рассказываю про архитектуру, промпт-инжиниринг и о том, почему if company_name != current_job — это самая важная строчка кода, которую я забыл написать.

Архитектура: не просто кликер

Сделать скрипт на Selenium, который спамит кнопкой "Откликнуться" — дело одного вечера. Но мне нужно было качество. Я хотел, чтобы бот мимикрировал под вдумчивого кандидата.

Стек: Python 3.11, Aiogram (для интерфейса в ТГ), PostgreSQL (хранение контекста юзеров), Redis (очереди задач), Google Gemini (мозг).

Логика работы агента выглядит так:

  1. Парсинг и нормализация. Бот забирает вакансии через API hh.ru. Тут первая боль: API отдает много мусора. Пришлось писать сложный фильтр на Pydantic, чтобы отсеивать вакансии, где стек указан "для галочки".

  2. Смысловой мэтчинг. Мы не ищем по ключевым словам (if 'Python' in text). Мы скармливаем LLM "слепок" резюме пользователя и текст вакансии. Модель возвращает score релевантности от 0 до 1.

  3. Генерация сопроводительного. Это самая интересная часть.

Промпт-инжиниринг: как пройти HR-фильтр

Мой кофаундер (в прошлом IT-рекрутер) объяснил, что HRы ненавидят ChatGPT-style письма ("Я восхищен вашей компанией...").

Мы потратили недели на тюнинг системного промпта.

Вместо "напиши письмо", мы говорим модели:

"Ты — сеньор-разработчик. Найди в резюме пользователя опыт, релевантный именно этим требованиям вакансии. Напиши короткое (3-4 предложения) письмо. Если опыта в конкретной технологии нет — честно укажи это, но подсвети быструю обучаемость на примере прошлого стека".

Результат:

За 3 дня теста пользователи отправили 2025 откликов.

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

Факап, или почему мы закрыли доступ

В погоне за качеством генерации я упустил базовую логику безопасности.

В чат поддержки прилетает сообщение от пользователя (DevOps):

"Ребят, бот пушка. Но есть нюанс. Он только что нашел вакансию в моей текущей компании. И откликнулся на нее. Моему тимлиду пришло письмо о том, как я хочу работать в [Название Компании], хотя я там уже работаю".

Причина: Я не реализовал Blacklist компаний. Бот увидел идеальное совпадение по стеку (еще бы, человек там работает!) и радостно отправил отклик.

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

Итоги и планы

Сейчас мы временно закрыли регистрацию, чтобы разгрести бэклог.

Что делаем в спринте:

  1. Blacklist компаний (приоритет Critical).

  2. Улучшение фильтрации. LLM иногда "галлюцинирует" с грейдами, предлагая сеньору вакансии миддла, если стек совпадает. Будем ужесточать правила.

  3. Оптимизация очередей. 2000 откликов создали нагрузку, которую не ожидали лимиты API. Переезжаем на более умный шедулер.

Если вам интересна техническая сторона (как мы обходим лимиты, как храним контекст, как боремся с дублями вакансий) — я буду периодически писать об этом в нашем канале.

https://t.me/auroracareer

Готов ответить на технические вопросы в комментариях. Пинайте :-)