TGViewer
annFlow annFlow @annsflow · 33 subscribers
Post #146 43
Самое трудное в автоотклике, это не submit

Снаружи автоотклик выглядит обманчиво просто.

Открыл вакансию.
Вставил письмо.
Нажал submit.

На практике submit почти не проблема.

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

Именно поэтому apply-flow в таком проекте начинается не с действия, а с распознавания реальности.


def classify_page_variant(snapshot: Dict[str, Any]) -> PageVariant:
if not isinstance(snapshot, dict):
return PageVariant.UNKNOWN

if snapshot.get("auth_required"):
return PageVariant.AUTH_REQUIRED

if snapshot.get("already_applied"):
return PageVariant.ALREADY_APPLIED

if snapshot.get("hh_resume_override"):
return PageVariant.FIXED_RESUME

questionnaire_required = bool(snapshot.get("questionnaire_required"))
if questionnaire_required:
items = list(snapshot.get("questionnaire_items") or [])
safe_types = ApplyFlow.SAFE_QUESTIONNAIRE_TEXT_TYPES
has_unsupported = any(
str(item.get("field_type") or "input").strip().lower() not in safe_types
or bool(item.get("requires_human"))
for item in items
if isinstance(item, dict)
)
if has_unsupported or not items:
return PageVariant.QUESTIONNAIRE_UNSUPPORTED
return PageVariant.QUESTIONNAIRE_TEXT

if snapshot.get("primary_submit_label") and not questionnaire_required:
return PageVariant.SIMPLE_PICKABLE

return PageVariant.UNKNOWN


classify_page_variant(…) нужен для первого решения: что за страница сейчас перед системой.
not isinstance(snapshot, dict) отсекает сломанный snapshot, чтобы проект не принимал решения на мусоре.
AUTH_REQUIRED останавливает pipeline, если вместо отклика открылась стена логина.
ALREADY_APPLIED не даёт системе делать дубли там, где отклик уже существует.
FIXED_RESUME ловит кейс, когда HH сам навязывает резюме и не даёт нормально переключиться.
questionnaire_required переводит страницу в другой класс сложности, где простой submit-path уже не работает.
items = list(…) собирает структурированное представление анкеты, без которого автоматизация становится слепой.
safe_types ограничивает набор полей, которые система вообще считает безопасными для обработки.
has_unsupported проверяет, не встретилась ли форма, в которой уверенности уже недостаточно.
field_type not in safe_types отсекает всё, что выходит за пределы текущего безопасного контура.
requires_human даёт форме право раньше вывести человека в контур, если автоматизм уже сомнителен.
QUESTIONNAIRE_UNSUPPORTED нужен, чтобы проект честно признал предел своей уверенности и не лез дальше.
QUESTIONNAIRE_TEXT фиксирует случай, где анкету ещё можно вести по контролируемому сценарию.
primary_submit_label проверяет, существует ли прямой путь до отклика без анкеты.
SIMPLE_PICKABLE обозначает happy path, где страницу можно обрабатывать почти линейно.
UNKNOWN оставляет системе право не действовать, если состояние страницы не распознано.

В этом и заключается настоящая сложность.

Здесь проект не “нажимает кнопку”.
Здесь он сначала интерпретирует состояние страницы и только потом выбирает один из путей:
автоматический submit,
questionnaire path,
handoff человеку,
пропуск,
или остановку.

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

То есть автоотклик оказался не задачей “нажать кнопку вместо человека”.

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

Оглавление темы 📌
Навигация по проектам канала 📍
Все проекты 🎶

#HHApply@annsFlow

💻 annFlow
Telegram annFlow Как я построил систему автооткликов, или Как превратить поиск работы в форму порядка ● Автоматизация отклика как ответ на асимметрию рынка ● Как бот сравнивает меня с вакансией и собирает отклик под нее ● Почему закрытие соискательского API не убило проект…
More from @annsflow
  1. Sep 24, 2026LiveContext в реальном разговоре. На видео я разбираюсь с претензией по недоставленному за…
  2. Sep 21, 2026Как LiveContext разделяет мой голос и голос собеседника Чтобы LiveContext мог понимать раз…
  3. Sep 16, 2026Почему мне оказалось недостаточно просто сохранять разговоры В VoxBase я решал проблему то…
  4. Sep 15, 2026Как я построил LiveContext, или Как связать живой разговор с памятью проекта ● Почему мне…
  5. Sep 14, 2026annFlow pinned a video
  6. Sep 14, 2026Самая неожиданная вакансия, которую нашел мой бот Моя система постоянно сама ищет вакансии…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →