Перейти к основному контенту

Воронка найма: hiring_project

Для разработчиков. Состояние на 28.09.2026. Переменные, все ручки, порядок выкатки и отката — README и RUNBOOK репозитория Buharestskie/hiring_project.

Коротко

hiring_project — воронка найма менеджеров по продажам («охотников») для самой компании: от входа кандидата на сайт до выхода на обучение. Кандидат входит через Telegram, VK или MAX, заполняет анкету и записывает голосовой ответ. Голос оценивает Gemini, решение принимает РОП прямо в Google Sheet, дальше кандидату приходит сообщение в мессенджер, приглашение на групповое собеседование в Телемосте и ссылка на онлайн-обучение. В amoCRM кандидат попадает только после собеседования.

Сейчас сервис на наших серверах не развёрнут: проект передан новому владельцу и ждёт переезда. Домен hiring.donula.online, заложенный в целевую схему, пока не настроен. Внешней пробы в мониторинге нет.

Путь кандидата

  1. Вход. Сайт запрашивает одноразовый токен (/api/auth/token, живёт 10 минут) и открывает бота в выбранном мессенджере. Кандидат отправляет боту телефон, сайт опрашивает /api/auth/check и получает имя и телефон из мессенджера, а не из формы.
  2. Анкета. /api/submit принимает анкету и голос (audio/webm, audio/mp4, audio/m4a, от 1 KiB до 10 MiB). Анкета сначала пишется в журнал на диске, затем строкой в лист «Кандидаты» через Apps Script. Повтор с тем же request_id вторую строку не создаёт.
  3. Оценка. Бэкенд отправляет голос в Gemini (через ProxyAPI): пять критериев от 1 до 5. 20 баллов и выше — PASS, кандидату сразу уходит приглашение на собеседование; 10–19 — GRAY, решает РОП; меньше 10 — FAIL, кандидату уходит отказ. Если модель недоступна, ставится GRAY с пометкой «AI недоступен, слушайте вручную».
  4. Решение РОПа — в таблице: столбец T («Одобрен» / «Отклонён») и W — итог собеседования («Принят» / «Отклонён» / «Не пришел»). Apps Script складывает изменения в лист «Webhook Outbox» и доставляет их в бэкенд подписанным запросом на /api/webhook/rop; бэкенд пишет кандидату.
  5. Собеседование — ближайший понедельник, среда или пятница не раньше чем через сутки, в 12:00 по Москве, в Телемосте. Напоминания — за 24 часа и за час.
  6. Обучение. После «Принят» создаются контакт и сделка Найм: <имя> в воронке найма amoCRM, кандидат получает персональную ссылку на курс (живёт 90 дней). Сайт обучения отмечает визит и прохождение, сделка двигается только вперёд: принят → зашёл на обучение → готов к ролевке. После курса открывается ссылка в закрытое сообщество.

Как устроен

  • Бэкенд — один процесс FastAPI на Python 3.12 (runtime/backend/). Состояние — в каталоге DATA_DIR: SQLite для переписки, JSON-файлы для соответствия чатов, записей на собеседование и токенов amoCRM, и журнал анкет. Журнал одновременно служит очередью эффектов: после рестарта недоделанное доигрывается.
  • Google Sheet и Apps Script (runtime/apps-script/google-apps-script-hiring.js) — рабочее место РОПа: листы «Кандидаты», «Обучение», «Трафик», «Расписание МР», «Дашборд», «Системное», «Webhook Outbox». Скрипт выкатывается вручную в редакторе таблицы.
  • Мессенджеры — своя реализация, не общая с messenger_connector: Telegram через long polling, VK и MAX через вебхуки.
  • Inbox (runtime/sites/inbox) — общая лента переписки со всеми кандидатами из трёх мессенджеров и ответ из веб-интерфейса.
  • Фоновые циклы внутри процесса: напоминания, вечерняя сводка РОПу в Telegram-группу в 20:00 и утренняя в 09:00 по Москве.
  • Статика runtime/sites/{auth,inbox,training} отдаётся nginx и обращается к бэкенду по относительным /api/… — открывать её нужно с того же адреса, что и бэкенд.

Где код и как запустить

  • Репозиторий Buharestskie/hiring_project. Документы о передаче проекта (STATUS.md, TRANSFER.md, ROUTE_MAP.md) описывают старое размещение и устарели.
  • Офлайн-тесты (внешняя сеть заблокирована, нужен ещё Node.js для тестов Apps Script): bash runtime/tests/run_all.sh после установки runtime/backend/requirements.lock в venv.
  • Бэкенд локально — uvicorn main:app из runtime/backend с явными DATA_DIR, DISABLE_SCHEDULED=true и TG_POLLING_ENABLED=false. Без явного DATA_DIR запуск упадёт на правах доступа. /api/submit без настоящего Apps Script тестовой таблицы отвечает 503.
  • Целевая выкатка — релизными архивами: scripts/build-release.sh собирает архив, scripts/deploy-release.sh раскладывает его на хост и переключает симлинк, при провале /health возвращает предыдущий; scripts/rollback-release.sh — откат. systemd-юниты и шаблон nginx — в infra/. Когда сервис будет развёрнут, сначала завести внешнюю пробу, потом выкатывать в окне обслуживания.

На что наступают

  • Только один Telegram-поллер и один планировщик. Второй процесс на тех же токенах без DISABLE_SCHEDULED=true и TG_POLLING_ENABLED=false даёт дубли сообщений кандидатам.
  • DISABLE_SCHEDULED не выключает эффекты анкеты. Оценка, сообщение кандидату и уведомление РОПу запускаются прямо из /api/submit. Стенд с боевыми токенами пишет живым людям.
  • Утренняя сводка включена по умолчанию. Если ENABLE_ROP_MORNING_NOTIFICATION не задана, она уходит, хотя по решению проекта выключена.
  • Сессии входа живут в памяти. Рестарт сбрасывает незавершённые входы кандидатов и начатую авторизацию amoCRM. Токен входа удаляется через 10 минут, поэтому напоминания «через 30/60 минут» почти не срабатывают.
  • Строки в «Кандидатах» не удалять и не сортировать. Строки находятся по скрытому request_id в столбце X. Листы «Системное» и «Webhook Outbox» руками не редактируются.
  • Триггер Apps Script должен быть installable и работает от имени создателя. Потерял аккаунт доступ — решения РОПа копятся в «Webhook Outbox» и не уходят.
  • Поток GRAY с пометкой «AI недоступен» — почти всегда кончился ключ или баланс ProxyAPI.
  • Сделку в amoCRM бэкенд ищет по названию Найм: <имя> (первый результат) — однофамильцы могут перепутаться. Ошибка создания сделки только логируется.
  • MAX: user_id не равен chat_id, соответствие хранится в JSON-файле в DATA_DIR. Потеря файла ломает доставку в MAX.
  • Запись голоса во встроенном браузере мессенджера не работает — сайт просит открыть ссылку в Safari или Chrome.
  • build-release.sh не проверяет чистоту дерева (проверка унаследована из монорепозитория и смотрит не туда): незакоммиченные правки попадут в архив под чужим SHA. Перед сборкой — git status.
  • runtime/backend/Dockerfile, backup_sheets.sh и legacy/ — наследие старого размещения, целевая схема их не использует.