Repository navigation
Description
Исследование 5 референсов hh.ru-автоматизации (субагент, 2026-07-28). Цель — идеи для hhru_bot. Метод: WebFetch README + ключевых исходников. Лицензии: s3rgeym Non-Commercial (паттерны берём, код нет), fikstt2/Steev193/tgeruzov/konard — MIT/Unlicense.
Главный вывод: наш проект ВПЕРЕДИ референсов в нескольких местах — LLMScoringProvider с circuit breaker (#74, нет ни у кого), fail-closed state machine для reply-employers в cli-spec (лучше простого retry s3rgeym), launchd/cron schedule #18 (нативнее бесконечного цикла fikstt2), account-scope responses #12. Но есть зазоры.
Сравнительная таблица
Обновлено 2026-07-31. Источник статусов — пере-аудит по исходному коду
(docs/research/issue-84-references.md, PR #114) и текущее состояние src/hhru_bot/.
Первая версия таблицы делалась по README через WebFetch и в ряде мест была неверной.
Колонки отсортированы по числу фич, общих с нашим проектом (в скобках — сколько).
Строки сгруппированы: сначала то, что у нас ГОТОВО, затем В РАБОТЕ, НЕТ и ОТКЛОНЕНО.
| Фича | s3rgeym (16) | fikstt2 (8) | Steev193 (7) | konard (5) | tgeruzov (4) | Наш проект |
|---|---|---|---|---|---|---|
| Дедупликация откликов | да | да | да | да | да | ГОТОВО (SQLite, #3) |
| Персистентная сессия (storage_state) | нет | да | да | да | да | ГОТОВО (#10) |
| Скоринг/ранжирование вакансий | да (--ai-filter) | да | да (3 score) | нет | нет | ГОТОВО (#15/#74, circuit breaker) |
| AI-генерация сопроводительных | да | да | да | нет | нет | ГОТОВО (#17, fallback на шаблон) |
| SQLite-история откликов | да | нет | нет | да | нет | ГОТОВО (#3) |
| Троттлинг/дневные лимиты | да | да | нет | нет | нет | ГОТОВО (throttle.py) |
| Стоп-слова по заголовку | да | да | нет | нет | нет | ГОТОВО (exclude_keywords); нет дефолтов |
| Таблица skipped + clear-skipped | да (--reason) | нет | нет | да | нет | ГОТОВО (#87) |
| Мониторинг ответов HR (responses) | да | да | нет | нет | нет | ГОТОВО (#12, account-scope) |
| Воронка/статистика откликов | да | нет | да | нет | нет | ГОТОВО (#11/#13) |
| Расписание/автопилот | да (docker cron) | да (loop 30м) | нет | нет | нет | ГОТОВО (#18, launchd/cron) |
| Детект тест-вопросов формы | да | нет | нет | да | нет | ГОТОВО (#95, detect-only) |
| Вынесенные селекторы + fallback | нет | нет | да | нет | да | ГОТОВО (#7, selector_groups) |
| Healthcheck селекторов | нет | нет | нет | нет | да | ГОТОВО (#88) |
| Few-shot стиль писем | нет | нет | да | нет | нет | ГОТОВО (#96) |
| Рандомизация шаблонов {a | b | c} | да (rand_text) | нет | нет | нет |
| Поднятие резюме (bump) | да | нет | нет | нет | нет | ГОТОВО (кулдаун 4ч) |
| SQL-доступ к БД из CLI | да | нет | нет | нет | нет | ГОТОВО (#45 query) |
| Хвост лога (log -f) | да | нет | нет | нет | нет | ГОТОВО (#58) |
| whoami / список резюме | да | нет | нет | нет | нет | ГОТОВО (#56/#57) |
| Probe-дамп формы без отправки | нет | нет | нет | нет | нет | ГОТОВО (#8) - НИ У КОГО НЕТ |
| Рыночные агрегаты по ЗП | нет | нет | нет | нет | нет | ГОТОВО (#66/#93) - НИ У КОГО НЕТ |
| reply-employers (ответы HR) | да | да (TG) | нет | нет | нет | В РАБОТЕ (#55 -> #107-#112) |
| LLM-фильтрация (бинарный отсев) | да | да | да | нет | нет | НЕТ (скоринг есть, порога нет) |
| Детект капчи -> стоп прогона | нет | нет | нет | нет | да | НЕТ - P1 идея #114 |
| Codegen селекторов | нет | нет | да | нет | нет | НЕТ - P3 идея #114 |
| Учёт правок писем пользователем | нет | нет | да | нет | нет | НЕТ - P3 идея #114 |
| Авто-ответы на тест-вопросы | да (LLM) | нет | нет | да (Q&A-файл) | нет | НЕТ - #97 под вопросом |
| clear-negotiations (отзыв) | да | нет | нет | нет | нет | НЕТ (#111, ждёт #107) |
| clone-resume | да | нет | нет | нет | нет | НЕТ (спроектирована) |
| REST API-клиент (мимикрия) | да | нет | нет | нет | нет | ОТКЛОНЕНО (cli-spec) |
| Multi-account (--profile) | да | нет | нет | нет | нет | ОТКЛОНЕНО (один аккаунт) |
| Telegram-бот | нет | да | да | нет | нет | ОТКЛОНЕНО (CLI-only) |
| Web-дашборд | да (ui) | нет | да (:3849) | нет | нет | ОТКЛОНЕНО (CLI-only) |
| Bezier-траектории курсора | нет | нет | нет | да | да | ОТКЛОНЕНО (оверинжиниринг) |
| Отправка email напрямую | да (SMTP) | нет | нет | нет | нет | ОТКЛОНЕНО (только hh.ru) |
| AI-капча Vision | нет | нет | нет | нет | нет | ОТКЛОНЕНО (решаем вручную) |
Что показывает сортировка. s3rgeym перекрывается с нами по 16 фичам из 25 своих —
это ближайший функциональный аналог и главный источник паттернов (при этом его код брать
нельзя, Non-Commercial). Дальше идут fikstt2 (8) и Steev193 (7) — у них ценны точечные
решения (персистентная сессия, few-shot, codegen), а не архитектура целиком. konard (5)
и tgeruzov (4) перекрываются меньше всех, но именно у них лежат две незакрытые идеи:
Q&A-файл и детект капчи.
Две фичи есть только у нас и ни у одного из пяти референсов: probe-дамп формы без
отправки (#8) и рыночные агрегаты по зарплатам (#66/#93).
Рекомендации в бэклог
HIGH (простые, переиспользуют существующее)
- Бинарный AI-отсев по порогу —
ai_reject_thresholdв scoring-конфиг; вfilter_candidatesкарточки сLLMScoringProvider.score() < threshold→[skip] low LLM score. Бесплатно (уже посчитанный score), эквивалент s3rgeym --ai-filter без 2-го LLM-вызова. - Рандомизация шаблонов писём {a|b|c} — 10-20 строк в
apply/letter.py._format_template(),random.choice. Анти-фрод + AI-детекторы писём. Паттерн s3rgeym rand_text(). - Таблица skipped + clear-skipped — CREATE TABLE IF NOT EXISTS в SCHEMA-блоке (Рефакторинг: убрать migrations (ПРИЧИНА — оверинжиниринг для такого простого проекта) #50, без миграций), partial-UNIQUE (resume_id, vacancy_id, reason). Тройная польза: cli-spec clear-skipped, stats по причинам отсева, кэш LLM-отсева (экономия как s3rgeym _is_vacancy_already_skipped).
MEDIUM
- reply-employers — cli-spec §3.3 (claim→in_flight→sent с reconcile). Подсмотреть у s3rgeym: viewed_by_opponent, AI с историей чата.
- Few-shot стиль писём — cover_letter_examples в AIProfile, few-shot в промпт.
- probe --healthcheck — проверка N селекторов из selector_groups без отклика, рапорт устаревших (CLAUDE.md «первый подозреваемый — устаревший селектор»).
- Детект тест-вопросов формы (без авто-ответа) — probe/apply-step видит task_* → [INFO] пропущено: тест-вопросы + skipped reason=has_questions.
LOW / оверинжиниринг (НЕ наш масштаб)
- AI-Vision капча (manual правильнее), 9. Multi-account (нарушает принцип), 10. REST API-мимикрия (cli-spec запретил), 11. Telegram/web-дашборд (CLI-only), 12. Bézier (оверинжиниринг), 13. --send-email (только hh.ru), 14. Бесконечный цикл (schedule лучше).
Принципы (не нарушать)
- CLI-only, read-only для hh.ru, один аккаунт, ручной запуск, троттлинг/лимиты, cookie-сессия (не API-мимикрия).
- «Из говна и палок», переиспользовать существующее, без оверинжиниринга.
Исследование. Sub-issues создаются отдельно по мерджу текущей волны (#80 timeout, #81 wiring).
Activity
axisrow commentedon Jul 28, 2026
Матрица «фича × референс» (да/нет)
Легенда:
+= есть,–= нет,~= частично.Зазоры hhru_bot (где у референсов есть, у нас нет/частично)
~— есть скоринг, но не отсекает low → HIGH: ai_reject_threshold.~— лог [skip], не пишет в БД → HIGH: таблица skipped.–— спроектирована в cli-spec, нереализована → MEDIUM.~— probe близок → MEDIUM: probe --healthcheck.–→ HIGH (10-20 строк).~(тrottle.wait) → LOW/оверинжиниринг.Где hhru_bot впереди (unique / лучше референсов)
axisrow commentedon Jul 28, 2026
Матрица «фича × референс» (да/нет)
Легенда: ✅ есть, ❌ нет,⚠️ частично.
Зазоры hhru_bot (где у референсов есть, у нас нет/частично)
Где hhru_bot впереди (unique / лучше референсов)
axisrow commentedon Jul 28, 2026
Связано: README.md — Roadmap и бэклог (раздел со ссылками на этот research + эпик #65 + milestones M1–M7). Milestone: Этап 1 (рынок/развитие).
Актуализация (2026-07-28): реализовано после research —
probe --healthcheck— read-only проверка селекторов.{a|b|c}(вмержен).skipped+clear-skipped(PR CLEAN, ребейзнут на feat(scoring): эвристический pre-LLM фильтр по tier/trusted/rating/invitation (отсев до LLM, 0 токенов) #85, ждёт мерджа).Полная обновлённая матрица + приоритезация фич (3+/5 → 2/5 → 1/5 → 0) — в комментариях ниже.
axisrow commentedon Jul 28, 2026
Уточнение HIGH-идеи №1 (бинарный отсев) — отзыв пользователя
Оригинальная рекомендация «бинарный AI-отсев по порогу score» — плохая: это платный LLM-вызов ради решения, которое дешёвая эвристика принимает бесплатно. LLM-отсев = трата токенов.
Правильный подход (решение пользователя): бинарный отсев делает ЭВРИСТИКА (tier/trusted/rating/invitation) ДО LLM-скоринга. LLM-скоринг (#74/#81) остаётся, но работает только по релевантным → меньше вызовов, дешевле, быстрее.
Архитектура
```
сбор карточек
→ [эвристический фильтр: tier/trusted/rating/приглашение] ← отсекает мусор БЕСПЛАТНО
→ [LLM-скоринг оставшихся] ← LLM только по релевантным
→ apply
```
Сигналы для эвристического отсева (всё уже есть в проекте)
Решение эвристики «стоит ли откликаться» (чистая функция, 0 токенов)
Отсечённые → таблица `skipped` (идея №3) с reason `low_employer_tier`/`no_interaction` → кэш, повторный search не дёргает LLM.
Итог: HIGH-идея №1 переформулирована с «LLM-бинарный отсев» на «эвристический pre-LLM фильтр по tier/trusted/rating/invitation». LLM-скоринг (#74/#81) не трогаем — он работает ПОСЛЕ фильтра, по меньшему числу релевантных.
axisrow commentedon Jul 28, 2026
Матрица «фича × референс» (да/нет) — обновлено 2026-07-28
Легенда: ✅ есть, ❌ нет,⚠️ частично. Статус hhru_bot актуализирован после волны реализаций (#74/#81/#85/#88 вмержены; #86/#87 — PR CLEAN, ждут мерджа).
Зазоры hhru_bot — актуально после волны реализаций
LLM-фильтрация (бинарный отсев)✅ — сделано feat(scoring): эвристический pre-LLM фильтр по tier/trusted/rating/invitation (отсев до LLM, 0 токенов) #85 (pre-LLM эвристика по tier/trusted/rating/invitation, НЕ LLM-отсев — см. уточнение ниже).Healthcheck селекторов✅ — сделано feat(probe): probe --healthcheck — read-only проверка селекторов hh.ru (MEDIUM #84) #88/feat(probe): probe --healthcheck — read-only проверка селекторов (#88) #91 (probe --healthcheck).Уточнение HIGH-идеи №1 (бинарный отсев) — отзыв пользователя
Оригинал «бинарный AI-отсев по порогу LLM-score» — плохая (трата токенов). Реализовано как эвристический pre-LLM фильтр #85 (tier/trusted/rating/invitation, 0 токенов) — отсекает мусор ДО LLM-скоринга. LLM-скоринг (#74/#81) работает только по релевантным. См. комментарий-уточнение.
Где hhru_bot впереди (unique / лучше референсов) — без изменений
Оставшиеся бэклог-идеи (после волны)
axisrow commentedon Jul 28, 2026
Приоритезация фич по «популярности у конкурентов» (из 5)
Принцип: фичи, которые есть у 3+ из 5 конкурентов = индустриальный стандарт (проверены рынком) → высший приоритет. Потом 2/5, потом 1/5, потом 0 (только у нас/ни у кого). ✅=1,⚠️ =0.5. Учитываются ТОЛЬКО фичи, которых у нас НЕТ или частично (зазоры) — реализованное (#85/#88) исключено из приоритета (уже есть).
ТИР 1 — есть у 3+ из 5 конкурентов (стандарт рынка, высший приоритет)
→ В ТИР-1 почти всё у нас уже есть. Реализовать новых — нет.
ТИР 2 — есть у 2 из 5 конкурентов
→ Новый в ТИР-2: reply-employers (после разблокировки #44).
ТИР 3 — есть у 1 из 5 конкурентов
ТИР 4 — ни у кого из конкурентов (unique hhru_bot, не приоритет для «добавить»)
Circuit breaker (#74), воронка (#13), probe (#8), рынок-анализ по доходу (#66), pre-LLM эвристический фильтр (#85), fail-closed state machine для reply (cli-spec). Это наши преимущества — держать, не «добавлять».
ИТОГОВЫЙ ПРИОРИТЕТНЫЙ БЭКЛОГ (что делать дальше)
P1 (после текущей волны #89/#92/#93):
P2:
2. Детект тест-вопросов формы (detect-only) (ТИР-3, ~1.5/5) — probe/apply-step видит
task_*→[skip] has_questions+ skipped (#87). Безопаснее авто-ответа.3. Few-shot стиль писём (ТИР-3, 1/5) —
cover_letter_examplesв AIProfile, few-shot в промпт (#17 расширение).4. clone-resume (ТИР-3, 1/5) — только если упрёмся в дневной лимит откликов.
P3 (низкий/оверинжиниринг — НЕ делать):
Принцип
Приоритезация матрицы #84. Sub-issues создаются после текущей волны.
axisrow commentedon Jul 28, 2026
Все фичи, отсортированные по популярности у конкурентов (из 5)
Принцип: 3+/5 (стандарт рынка) → 2/5 → 1/5 → 0/5. ✅=1,⚠️ =0.5. Внутри тира — по убыванию доли. Включает ВСЕ фичи (в т.ч. реализованные у нас), чтобы видеть полную картину.
ТИР 1 — есть у 3+ из 5 конкурентов (стандарт рынка)
ТИР 2 — есть у 2 из 5 конкурентов
ТИР 3 — есть у 1 из 5 конкурентов
ТИР 4 — 0 у конкурентов (unique hhru_bot)
Приоритетный бэклог (ЧТО ДЕЛАТЬ, отсортировано)
Исключая реализованное (✅) и нарушающее принципы (LOW/оверинжиниринг):
[skip] has_questions.НЕ делать (нарушают принципы / оверинжиниринг): Telegram-бот, web-дашборд, REST API-мимикрия, --send-email, Bézier, AI-Vision, multi-account, clear-negotiations (редкая деструктивная).
Полная сортировка матрицы #84. Принцип: 3+/5 → 2/5 → 1/5 → 0. Включает все фичи.
feat(letter): рандомизация шаблонов {a|b|c} (#86) (#89)
docs(research): фактические правки после review (#84)
docs(research): уточнить факты по коду референсов (#84)
docs(research): round-2 фактические правки (#84)
docs(research): правки по code-reviewer + Codex round 3 (#84)
axisrow commentedon Jul 31, 2026
Пере-аудит референсов ПО КОДУ — итоговая таблица для бэклога
Прошлые итерации в этом ишью строились на WebFetch README. Здесь референсы склонированы и прочитаны по исходникам — из-за этого часть прежних выводов изменилась, а часть идей отклонена именно потому, что код показал другое.
Полный документ:
docs/research/issue-84-references.md(PR #114). Ниже — сжатая версия.Что изменилось против прошлых итераций
POST /applicant/vacancy_response/popupс_xsrf/uidPk/X-Xsrftoken— запрещённаяcli-specAPI-мимикрия, в браузерном стеке не переиспользуется. Второй блокер принципиальный: работодателю уходит ответ от лица кандидата, которого тот не писал. Предложена замена — статический Q&A-файл (konard)title_rejection_reason()вызывается доanalyzer.assess(). Уникален не отсев до LLM, а наш признак — сигналы работодателя vs стоп-слова. По стоп-словам у нас как раз зазор (идея №4)Новые идеи
script.js:2217-2245qa-database.mjsvacancy_filter.pycodegen— обновление селекторовscripts/codegen-hh.mjscover-letter-user-edits.mjsПриоритизация
P1 — детект капчи. Единственная идея, которая закрывает активный риск, а не отсутствие удобства. Суть: в этом сценарии отключается единственный тормоз проекта.
count_todayсчитает толькоstatus='success', а капча даёт сплошныеfailed— значит серия неудач идёт до исчерпания--limit, и дневной лимит её не остановит вообще. Каждая итерация — новый запрос к hh.ru ровно тогда, когда он уже показал challenge. Весь троттлинг написан против такого поведения, но здесь не срабатывает. Это защита аккаунта, не обход: капчу не решаем, а отдаём человеку. Дёшево, изолированно, не требует живого аккаунта (тестируется на фейковой странице).P2 — №3, затем №2. Обе достраивают уже вложенное: #95 научился распознавать вопросы и останавливаться, но дальше вакансия теряется. №3 дешевле и без блокеров, №2 требует сверки формы на живом аккаунте и попутно решает судьбу #97.
P3 — №4, №5, №6. Полезные, но узкие.
Отклонено (12 пунктов, с причинами)
LLM-ответы на тест-вопросы по образцу s3rgeym, авто-ответ «не хожу по ссылкам», REST API-мимикрия, Bézier-курсор, multi-account, Telegram-бот, графический интерфейс (у s3rgeym десктопный pywebview, у Steev193 веб-дашборд),
--send-email(SMTP), AI-Vision капча, бесконечный цикл,clear-negotiations,clone-resume, regex по полной description.Каждое — с конкретной причиной по принципам проекта (CLI-only, без API-мимикрии, без обхода троттлинга/лимитов, максимальная простота). Отклонение — тоже результат исследования.
Рекомендация по открытому ишью
#97 (LLM-ответы на тест-вопросы) — переформулировать в идею №2 (статический Q&A: отвечает человек, один раз, своими словами) либо закрыть. Обоснование выше.
Где мы впереди
Circuit breaker на LLM (#74), воронка откликов (#13),
probeс дампом формы (#8), анализ рынка по доходу (#66) — ни у кого из пяти.scheduleчерез launchd/cron (#18) чище бесконечного цикла, detect-only на тест-вопросы (#95) честнее их авто-ответов.Документ прошёл cycle-review вручную: Codex (gpt-5.4, xhigh) 3 раунда + code-reviewer, 9 находок — все исправлены. Детали в PR #114.
Ишью закроется автоматически при мердже PR #114 (
Closes #84).docs(README): вычистить по принципу «пиши, сокращай», таблица фич наверх
docs(README): переписать под пользователя + таблица сравнения с рефер…