Персональный ассистент по поиску работы на hh.ru: Python + LLM + OpenCode. ai-агенты.. ai-агенты. вайбкодинг.. ai-агенты. вайбкодинг. Ненормальное программирование.

Мы можем знать – мы будем знать!

Мне всегда было интересно, что там во всех этих 2000+ вакансиях, которые мне выдает hh.ru, когда я туда захожу? Самому хватало терпения просмотреть 20-30, ну от силы 40 вакансий. Понятно, что можно перелететь в конец (или любую страницу) пэйджинга и там глянуть часть вакансий, но это не дает всей картины в целом, а это важно.

А что если перепоручить задачу (ну хоть какую-то её часть) ai-агенту?

Сказано — сделано (в современную-то эпоху вайбкодинга).

Начала

Первое, что я определил, — это главные функции, которые были нужны:

  1. Собрать все рекомендуемые вакансии в локальную БД.

  2. Проранжировать каждую вакансию относительно того, насколько в неё попадает моё резюме.

  3. Советы по резюме: что добавить, какие скиллы прокачать, чему поучиться.

  4. Рекомендации по вакансиям для отклика: на какие вакансии имеет смысл откликнуться прямо сейчас. Вот ради этого всё и затевалось — просмотреть 2000+ вакансий вручную невозможно, а получить топ-10 «на сегодня» — реально.

  5. Синхронизация откликов: нет смысла получать рекомендации по вакансиям, на которые уже откликнулся или где уже отказали.

По ходу дела появилась ещё одна функция:

  • 1.2. Проверка архивных вакансий — зачем анализировать и рекомендовать то, что уже ушло в архив.

Да, чуть не забыл: чтобы не смотреть всё время в вывод командной строки, сделал ещё и небольшой веб-UI для просмотра результатов работы функций.

Почему через браузер, а не через API

Казалось бы, очевидное решение для такой задачи — официальное API hh.ru. Но в конце 2025 года hh.ru закрыл API для соискателей.

Для проекта это значит простую вещь: браузерная автоматизация — это не костыль и не «обход API», это единственный рабочий способ автоматизировать сбор своих рекомендаций в 2026 году. Поэтому в основе лежит Playwright, запускающий мой настоящий, уже залогиненный Яндекс.Браузер (в отдельном профиле), а капчу, если она выскакивает, я решаю руками — скрипт просто ждёт. Никаких токенов, паролей и скрытых сессий: агент видит ровно то же, что вижу я в своём браузере.

Этот же подход, к слову, заодно решает вопрос «а не забанят ли»: запросы идут из реального браузера с реальной сессией, с человекоподобными задержками между страницами и карточками (лог-нормальное распределение, а не «раз в секунду как робот»).

Базовые функции и зачем тут OpenCode

Фиксированные функции — это «база»

Пять основных функций (плюс функция 1.2) — это так называемые фиксированные функции. Каждая из них — обычный Python-скрипт, который делает строго определённую вещь: собрать, оценить, посоветовать, подобрать, синхронизировать. Они детерминированы, версионируют данные в SQLite и могут работать полностью автономно от OpenCode — хоть из голого терминала.

Логика разделения простая:

  • Повторяющиеся алгоритмы (обход страниц, нормализация, диф по хешу, сохранение версий, выборки) — это Python. Тут не нужен «интеллект», тут нужна надёжность и воспроизводимость.

  • Творческие, исследовательские шаги (понять, насколько резюме подходит под вакансию; сформулировать советы) — отданы LLM.

А зачем тогда вообще OpenCode?

Затем, что фиксированными функциями жизнь не ограничивается. AI-агент (в моём случае OpenCode + подписка z.ai coder plan и LLM glm-5.1) может отвечать на разные вопросы, которые возникают по ходу.

Например: а какие навыки/скиллы входят в топ-20 вакансий аналитиков, которые hh.ru подобрал лично мне?

Скрытый текст

Спойлер: SQL с большим отрывом. Причём интересно, что сам OpenCode для ответа в основном писал именно SQL-код — делал выборки из моей локальной базы и анализировал.

В этом и фокус: имея локальную базу вакансий с их полными описаниями, можно проводить небольшие исследования и понимать, что вообще творится на рынке труда — не по ощущениям, а по данным. OpenCode тут выступает как оркестратор: он знает про функции (через slash-команды и скиллы), умеет сам сходить в базу через sqlite3, дёрнуть локальный REST API веб-UI или открыть браузер, чтобы посмотреть пару свежих страниц.

При этом главные функции (1–5 + 1.2) запускаю я явной командой — никаких авто-запусков и cron. А в opencode.json стоит edit=ask: любые правки файлов агент делает только с моего подтверждения.

Где и на чём всё это собиралось

  • Железо: Mac mini M4. Всё крутится локально — база, браузер, веб-UI.

  • Среда: macOS + conda (Python 3.11), управление через OpenCode (TUI), плюс локальный веб-UI на FastAPI (localhost:8765) для просмотра результатов.

  • LLM: для ранжирования и советов — любой OpenAI-совместимый провайдер (я гонял через aitunnel (модель DeepSeek V4 flash), но можно через Vsegpt, OpenRouter, да хоть локальную Ollama). Для самого агента-оркестратора — glm-5.1 через z.ai coder plan.

Изначально планировал использовать Eigent Ai, но выяснилась пренеприятнейшая вещь о нем и пришлось от него отказаться.

Как именно это разрабатывалось (Осторожно! Вайбкодинг)

Тут есть забавная рекурсия: проект про AI-агента я и сам собирал с помощью AI-агентов.

  • Изначально — в Perplexity Computer (оркестратор Claude Opus 4.7): первичная генерация проекта по подробному промпту плюс первые исправления и доработки.

  • Финальные исправления и последняя функция 1.2 — уже в OpenCode с подпиской z.ai coder plan и LLM glm-5.1.

Проверял не только на своём резюме, но и на резюме супруги — проект позволяет запускать его из разных папок и создаёт разные БД и разные кэши профиля браузера, так что ничего не смешивается. Два независимых «исследователя» по рынку труда на одной машине.

По ходу работы выяснился нюанс hh.ru – отклики хранятся как бы в двух местах: меню “Отклики” и меню “Чаты/облачко”. Причем, именно в Чатах все отклики и отказы, но сейчас Функция 5 собирает отклики из меню “Отклики”, а там не все. Это надо будет доделать.

Проект на GitHub

Репозиторий (публичный, MIT): github.com/Ivanbsp-coder/hh-agent

Ниже — выдержки из README, под спойлером, чтобы не растягивать статью.

Выдержки из README.md

hh-agent

AI-агент-исследователь вакансий hh.ru. Основные функции:

  1. Сканирование вакансий в браузере Яндекс с версионностью описаний (Playwright + SQLite).

    • 1.2. Проверка архивных вакансий — помечает ушедшие в архив, чтобы не рекомендовать закрытые позиции.

  2. Ранжирование относительно вашего резюме по 10-балльной шкале (LLM, OpenAI-совместимый API).

  3. Советы по улучшению резюме и навыков на основе результатов скрининга (LLM).

  4. Подборка вакансий для отклика — топ-N по баллу + бонусу за свежесть, с опциональной «тёмной лошадкой» от LLM. Сохраняет историю подборок и отметки об откликах (отклики делаются вручную в том же браузере).

  5. Синхронизация откликов и отказов с hh.ru: текущие статусы + журнал отказов по работодателям для cooldown.

Управление — через opencode с slash-командами и скиллами, CLI-скрипты, локальный веб-UI.

Архитектура и принципы

  • Повторяющиеся алгоритмы (сканирование, нормализация, диф, сохранение, выборки) написаны на Python.

  • Творческие / исследовательские шаги (понимание соответствия, генерация советов) делегированы LLM через slash-команды.

  • Браузер: установленный у вас Яндекс.Браузер запускается через Playwright в отдельном профиле ~/.hh-agent/browser_profile/ — не конфликтует с вашим обычным сёрфингом, отдельный Chromium качать не нужно.

  • Безопасность: пароль hh.ru вы вводите лично в окне браузера (агент его не видит и не хранит). API-ключ LLM-провайдера берётся из переменной окружения.

Что внутри

hh-agent/
├── README.md                  # этот файл
├── setup.sh                   # установка
├── requirements.txt
├── config.example.yaml        # шаблон конфига
├── hh_agent/                  # Python-пакет
│   ├── config.py              # YAML + интерактивные секреты
│   ├── db.py                  # SQLite-схема и операции с версионностью
│   ├── scraper.py             # Playwright + Яндекс.Браузер
│   ├── llm_client.py          # OpenAI-совместимый клиент
│   ├── scoring.py             # алгоритм 10-балльного ранжирования
│   ├── advisor.py             # генерация советов по резюме
│   ├── recommender.py         # Функция 4: отбор + freshness + llm_pick
│   ├── delays.py              # human-like log-normal задержки
│   └── notifier.py            # macOS уведомления (osascript)
├── scripts/                   # CLI точки входа
│   ├── init_db.py
│   ├── scan_vacancies.py      # Функция 1
│   ├── check_archived.py      # Функция 1.2: проверка архивных вакансий
│   ├── rank_vacancies.py      # Функция 2
│   ├── advise_resume.py       # Функция 3
│   ├── recommend.py           # Функция 4: подборка на отклик
│   ├── sync_responses.py      # Функция 5: синхронизация откликов
│   ├── apply_mark.py          # отметка «откликнулся вручную»
│   ├── delete_vacancy.py      # удаление вакансии из БД
│   ├── add_resume.py
│   └── serve_ui.py
├── ui/                        # FastAPI + HTML/JS
├── .opencode/                 # конфигурация opencode
│   ├── command/               # slash-команды (/scan, /rank, /advise и др.)
│   └── skills/                # скиллы — инструкции для агента (на русском)

Требования

  • macOS (тестируется на Mac mini M4)

  • conda (Miniconda/Anaconda)

  • Python 3.11 (поставит conda)

  • Яндекс.Браузер, установленный в /Applications/Yandex.app

  • Учётная запись на hh.ru (войти нужно лично, агент пароль не хранит)

  • API-ключ OpenAI-совместимого LLM-провайдера для функций 2 и 3 (DeepSeek, VseGPT, ProxyAPI, OpenRouter, локальная Ollama — любой)

Установка

Проект устанавливается на чистый macOS из репозитория:

git clone https://github.com/Ivanbsp-coder/hh-agent.git
cd hh-agent
chmod +x setup.sh
./setup.sh

setup.sh идемпотентен: создаст conda env hh-agent (Python 3.11), установит зависимости, скопирует config.example.yaml → config.yaml, проверит наличие Яндекс.Браузера и инициализирует пустую БД в ~/.hh-agent/hh.db. Отдельный Chromium не качается — используется ваш установленный Яндекс.Браузер.

Подробная пошаговая инструкция — в INSTALL.md. Минимум, что нужно поправить в config.yaml после установки:

browser:
  executable_path: "/Applications/Yandex.app/Contents/MacOS/Yandex"
  headless: false           # лучше false — увидите, как агент работает

hh:
  search_query: ""          # пусто = что предлагает hh; или впишите "python backend"
  search_params: {}         # доп. GET-параметры поиска; {} = ничего не добавлять
  max_pages: 100

llm:
  base_url: "https://api.aitunnel.ru/v1"
  model: "deepseek-v4-flash"  # или другая модель вашего провайдера

Ключ LLM — в env-переменной (имя — из config.yaml → llm.api_key_env).

Использование (кратко)

# Функция 1 — сбор вакансий (откроется Яндекс, первый раз войти в hh.ru вручную):
python scripts/scan_vacancies.py

# Функция 1.2 — пометить ушедшие в архив:
python scripts/check_archived.py --older-than-days 14 --limit 50

# Функция 2 — LLM-ранжирование относительно активного резюме:
python scripts/rank_vacancies.py

# Функция 3 — советы по резюме (топ/антитоп оценок):
python scripts/advise_resume.py --top 30 --bottom 30

# Функция 4 — подборка топ-10 на отклик + «тёмная лошадка» от LLM:
python scripts/recommend.py -n 10 --llm-pick

# Функция 5 — синхронизация откликов и отказов с hh.ru:
python scripts/sync_responses.py

# Веб-UI для просмотра результатов:
python scripts/serve_ui.py    # → http://localhost:8765

Алгоритм подборки (Функция 4): к LLM-баллу добавляется бонус за свежесть

final_score = score + freshness_bonus_max * exp(-age_days / freshness_halflife_days)

(по умолчанию freshness_bonus_max = 1.5, период полураспада 7 дней). Компании, которые отказали в последние 270 дней, исключаются из выдачи Python-алгоритма; LLM может предложить такую в виде исключения, явно пометив это.

Ключевые принципы хранения

  • Версионируем содержимое, не запись. Одна строка на uid в vacancies; новые версии описания — в vacancy_versions. Диф по sha256.

  • Оценки не перезаписываются. Каждый запуск Функции 2 пишет новые строки в rankings — можно сравнить эффект правок весов/промпта/резюме.

  • Резюме версионируется. «Активное» — то, по которому идёт скрининг сейчас.

  • Отклики и отказы версионируются. Журнал отказов по работодателям используется для cooldown в Функции 4.

Что в итоге увидел в данных

Наблюдение 1. Топ востребованных навыков (вакансий в БД = 2909)

Топ востребованных навыков из вакансий

Топ востребованных навыков из вакансий

Наблюдение 2. Как ложатся баллы ранжирования

Распределение оценок 1–10 по всей базе: сколько вакансий реально «мои», сколько пограничных, сколько мимо. Картина, которую невозможно получить, листая выдачу глазами.

UI с возможностями просмотра/фильтрации

UI с возможностями просмотра/фильтрации

На текущий момент 88 вакансий с оценкой 9+ среди актуальных.

Наблюдение 3. Подборка «на сегодня»

Та самая фича, ради которой всё затевалось: топ-10 по баллу + свежести и одна «тёмная лошадка» от LLM с обоснованием.

Пакеты с рекомендациями

Пакеты с рекомендациями
Просмотр рекомендуемых вакансий для откликов

Просмотр рекомендуемых вакансий для откликов

Наблюдение 4. Советы по резюме

Что LLM рекомендует добавить/прокачать — с привязкой к конкретным вакансиям как доказательству.

Совет #2 для резюме id=2
06.06.2026, 19:02 · модель deepseek-v4-flash · вакансий в выборке: 40 · версия скилла v1.0
### Краткое резюме рекомендаций

У вас уникальный 16-летний опыт системного анализа в масштабных государственных проектах, который высоко ценится работодателями (средний балл по топ-20 вакансиям — 9/10). Однако для выхода на новый уровень и расширения возможностей трудоустройства потребуются точечные доработки.

**Резюме:** Ключевые проблемы — в резюме не выделены явно навыки работы в Agile, Git, Docker и инструменты прототипирования, что снижает баллы по hard-скиллам. Также отсутствует демонстрация мотивации работать в коммерческих доменах, что особенно важно для финтеха, ритейла и страхования. Рекомендуется добавить отдельный блок технологий, указать опыт работы по Scrum и добавить в раздел «О себе» фразу о готовности применять опыт в новых отраслях.

**Hard Skills:** Приоритет №1 — освоить базу 1С: ERP (или SAP). Это откроет доступ к 30% релевантных вакансий, которые сейчас получают оценку 2/10. Вторым шагом стоит изучить Python на уровне чтения кода и простые скрипты для автоматизации — требование во многих современных командах. Figma, Miro и Power BI будут полезны, но менее критичны.

**Soft Skills:** Тренируйте навык защиты решений и работы с неопределённостью. Подтяните английский до Intermediate — это даст преимущество при отборе.

**Домены:** Фокус на финтех и банки (ваш опыт с юридически значимыми данными и расчетами идеален). На втором месте — E-commerce и ERP-системы. Начните с изучения одного из них, начав с сертификации по выбранной системе.

Вы — сильный кандидат с отличной базой. Осталось немного скорректировать резюме и небольшое расширение технического кругозора, чтобы стать идеальным кандидатом для 70% вакансий на рынке.

Тут надо добавить оговорку.

LLM (в данном случае deepseek-v4-flash) довольно примитивно, как мне кажется, оценивает то, что написано в резюме и в требованиях вакансий.

Я после первого анализа своего резюме понял, что надо указать использование Postman, работу с REST API и gRPC, разработку BPMN, а также то, что разрабатываемая система имеет микросервисную архитектуру и сразу резюме стало вдруг сильнее ранжироваться.

Понятно, что и hr-ы (и их боты) не особо вникают на первом этапе отбора в суть, а ориентируются на ключевые слова и словосочетания, но все же надо анализировать более предметно (еще один пункт для развития hh-agent-а)

Наблюдение 5.

За 3-4 недели количество предлагаемых вакансий под мое резюме сократилось с примерно 2800 до почти 2000.

За чей счет банкет?

Ранжирование резюме относительно одной вакансии стоит в среднем:

  • deepseek-v4-flash = 15 копеек

  • deepseek-v4-pro = 1 руб

Остальные затраты на LLM – небольшие, так как операции выполняются разово. Все в пределах нескольких рублей даже для deepseek-v4-pro

Вместо вывода

Главный результат — вместо «полистал первые три страницы и составил впечатление» получаешь полную, размеченную базу по всем рекомендованным тебе вакансиям и можешь задавать ей вопросы на естественном языке. А короткий список «откликнись сегодня вот сюда» экономит то самое терпение, которого на 2000 карточек заведомо не хватит.

Продолжение следует…

Автор: ivanbsp

Источник