logo-icon
Все статьи

AI-портал для аутстаффинга: внутренний кейс ITS

Как мы за неделю собрали AI-агента для подбора инженеров из 50 партнёрских Telegram-чатов. Стек: Django + Telethon + gpt-4o-mini + o3-mini. $40/мес на проде.

Содержание
  1. TL;DR
  2. Что такое AI-портал для аутстаффинга
  3. Контекст: почему ручной процесс перестал масштабироваться
  4. Почему это раньше не автоматизировали
  5. Что рассматривали и почему отбросили
  6. Архитектура AI-портала
  7. Telethon-воркер
  8. Экстракция: набор узких агентов на gpt-4o-mini
  9. Матчинг: o3-mini с 8-dimension scoring
  10. Хранение и Django-дашборд
  11. Деплой и доступ
  12. Цифры на проде
  13. Что мы получили "побочкой"
  14. Чему мы научились (что бы сделали иначе)
  15. Когда такой подход применим у вас
  16. Когда такой подход НЕ работает
  17. FAQ
  18. Итог

AI-агент для подбора кандидатов и автоматизации аутстаффинга — внутренний инструмент, который мы собрали в ITS за неделю и который сейчас держит поток позиций из ~50 партнёрских Telegram-чатов на одном операторе вместо CEO. Стек предсказуемый: Django + Telethon + LangChain + связка gpt-4o-mini (структурирование) и o3-mini (reasoning-матчинг). Бюджет на проде — около $40 в месяц. Ниже — что собрали, как устроена архитектура, какие цифры на проде и в каких случаях такой подход применим у вас.

TL;DR

  • Задача: автоматизировать обработку позиций из ~50 партнёрских Telegram-каналов и подбор инженеров из bench.
  • Стек: Django 5.1 + SQLAlchemy + SQLite, Telethon (user-сессия, не Bot API), LangChain поверх OpenAI (gpt-4o-mini на экстракцию, o3-mini на матчинг), TaskIQ + Redis на фоновые задачи, Caddy + oauth2-proxy + Keycloak на доступ.
  • Бюджет: один VPS ~10/мес+ 10/мес + ~30/мес на токены = ~$40/мес на проде.
  • Объём: ~1500 позиций и ~200 откликов в месяц через одного оператора вместо CEO.
  • Скорость сборки: 3 часовых созвона на планирование, неделя вайбкодинга силами одного senior-инженера.
  • Ключевой принцип: финальное решение по отклику остаётся за человеком — это сознательный выбор, не ограничение.

Что такое AI-портал для аутстаффинга

AI-портал для аутстаффинга — внутренняя система, которая принимает поток позиций из мессенджеров и почты в свободной форме, структурирует их через LLM-экстракцию, сопоставляет с базой инженеров через reasoning-модель и предлагает оператору готовые пары "позиция — кандидат" с обоснованием по нескольким критериям. Финальный отклик отправляет человек, не агент.

Это не "AI-рекрутер", который заменяет человека, и не RPA-робот, который скроллит чаты. Это инструмент, который снимает с оператора рутинную часть (мониторинг, экстракция, первичный матчинг) и оставляет ему то, что требует суждения: финальное "да/нет" и коммуникацию с партнёром.

Контекст: почему ручной процесс перестал масштабироваться

У ITS аутстафф-направление: даём инженеров на внешние проекты партнёрам. Источник позиций — закрытые Telegram-каналы, в которых другие IT-компании постят роли, не закрытые своими силами. Канал — это поток сообщений в свободной форме: где-то с требованиями, где-то без, где-то на английском, где-то вперемешку, иногда со ставкой, иногда без.

До автоматизации процесс держался на одном человеке — CEO. Не потому что он любил Telegram, а потому что для адекватной оценки позиции нужно одновременно держать в голове состав bench, текущую загрузку каждого инженера, исторический контекст по партнёру и интуицию по адекватности роли. Делегировать это младшему сотруднику не получалось: формальные критерии не покрывают "позиция со ставкой ниже рынка от партнёра, который всегда платит вовремя — соглашаемся ради отношений".

Узкое горлышко выглядело так: на сезонных пиках поток позиций обгонял оператора, и часть просто пропускалась. Не "обрабатывалась хуже", а пропускалась — потому что физически некому было прочитать.

Почему это раньше не автоматизировали

Если бы вы спросили нас в 2023-м, мы бы ответили: "теоретически можно RPA-роботом скроллить чат, но что дальше делать с описанием — непонятно".

Главная проблема была в матчинге. Подбор кандидата под позицию — это не SQL-запрос. Описание позиции — свободный текст. CV кандидата — свободный текст. Между ними нет общего ключа: "Senior Python Developer, 5+ years" и "разработчик с опытом в финтехе на FastAPI" формально про одно и то же, но любой регулярный матчер тут ломается.

В 2024-м у нас впервые появилось ощущение, что LLM-модели стали достаточно умными, чтобы делать этот матчинг не хуже живого оператора, и достаточно дешёвыми, чтобы это можно было гонять на потоке. До появления reasoning-моделей вроде o1/o3-mini подобный матчинг ломался на edge-кейсах, и ROI автоматизации был отрицательным: ручные правки съедали выигрыш в скорости.

Что рассматривали и почему отбросили

Bot API. Очевидное первое решение. Не подошло: в большинстве партнёрских чатов бот не имеет прав на чтение истории, и пригласить его требует разрешения админа. Telethon с user-сессией таких ограничений не знает.

Регулярки и эвристики на экстракции. Тестировали на выборке. Базовые поля (название роли, грейд) — точность ~70%, составные (требования, длительность, локация в свободной форме) — резко падает. Edge-кейсы съедали ровно столько времени оператора, сколько мы хотели сэкономить.

Эмбеддинги + cosine similarity для матчинга. Дёшево, но проигрывает на нюансах: "5 лет коммерческого Go" против "разработчик с pet-проектом на Go" эмбеддинги ставят слишком близко. На дорогих B2B-позициях это много false positives.

LLM-only архитектура (одна большая модель на всё). Дорого и медленно. Экстракция структурированных полей — задача на дешёвую модель, reasoning-матчинг — на дорогую. Смешивать — дороже без выигрыша в качестве.

В итоге пришли к двухконтурной архитектуре: набор узкоспециализированных дешёвых экстракторов на gpt-4o-mini плюс один reasoning-агент на o3-mini для матчинга свежих позиций.

Архитектура AI-портала

flowchart LR
    A[Партнёрские TG-каналы] -->|Telethon NewMessage<br/>user session| B[Bot worker]
    B --> Q[(TaskIQ + Redis)]
    Q --> X[Extractor agents:<br/>title / skills / rate / ...<br/>gpt-4o-mini]
    X --> DB[(SQLAlchemy + SQLite<br/>Position, Engineer, Match)]
    DB --> M[Semantic matcher<br/>o3-mini, 8-dim scoring]
    M --> DB
    DB --> P[Django dashboard<br/>unmanaged ORM, Bootstrap 5]
    P --> O[Оператор]
    O -->|Отклик через портал| A2[Партнёр]
    P --> Auth[oauth2-proxy → Keycloak OIDC]
    Net[Caddy reverse proxy] --> P

Двухконтурная архитектура: дешёвые узкие экстракторы — на структурирование, дорогая reasoning-модель — только на матчинг свежих позиций.

Telethon-воркер

User-сессия Telethon, подписка на events.NewMessage(incoming=True) для списка каналов из конфига. Каждое новое сообщение нормализуется, кладётся в БД и порождает фоновую задачу TaskIQ на экстракцию.

client = TelegramClient(TG_SESSION, TG_API_ID, TG_API_HASH)

@client.on(events.NewMessage(incoming=True))
async def on_message(event):
    nm = await normalize(event)
    await process_position.kiq(nm.model_dump())

Из неприятного: FloodWaitError от Telethon обрабатываем явным await asyncio.sleep, а не пропускаем offset; .session-файл регулярно бэкапим (потеря — повторная авторизация через SMS); подписку на events.MessageEdited пока не сделали — партнёры иногда правят сообщение через 30 минут после публикации, это в TODO.

Экстракция: набор узких агентов на gpt-4o-mini

Здесь reasoning не нужен. Вместо одного "большого" экстрактора у нас набор специализированных агентов под отдельные поля — каждый со своим коротким промптом и температурой 0.0:

  • TITLE_EXTRACTOR — нормализованное название роли;
  • SKILLS_EXTRACTOR — primary/secondary стек, с разделением "требуется" vs "упомянуто";
  • RATE_EXTRACTOR — ставка, валюта, период (час/день/месяц);
  • EXT_POSITION_ID_EXTRACTOR — внешний ID, если партнёр его указывает;
  • ещё несколько на категорию, условия оплаты, опыт.

Через LangChain каждый агент получает только релевантный кусок текста и возвращает строго заданное поле. Паттерн "много мелких агентов вместо одного большого" даёт фокус (промпты короткие и обозримые), стоимость (на полях достаточно дешёвой модели) и контролируемость (промпт можно править, не ломая остальные).

Матчинг: o3-mini с 8-dimension scoring

Сюда уходит только то, что прошло экстракцию и помечено как валидная позиция. Системный промпт матчера — один из самых длинных в проекте: вместо короткого "найди подходящих" мы прописали взвешенную модель оценки по 8 измерениям с явными весами:

Dimension Вес
job_title_alignment 25%
skills_technical 25%
rate_compatibility 20%
location_match 15%
seniority_level 8%
domain_experience 4%
soft_requirements 2%
culture_fit 1%

Плюс жёсткие "предохранители" в самом промпте: если job_title_alignment < 0.5, то overall_match_score MUST be < 0.6; если skills_technical < 0.5 — < 0.65; и так далее. Без этих правил reasoning-модели охотно компенсируют слабое попадание по одному критерию сильным по другому, и итоговый score становится оптимистичнее, чем стоит.

На выходе матчер возвращает overall_match_score, recommendation, dimension_scores, strengths, concerns, verdict, interview_questions. Свободный текст в concerns/strengths сознательно оставлен — оператор видит логику решения и за 5 секунд понимает, согласен он с моделью или нет.

Хранение и Django-дашборд

Production-данные пишутся через SQLAlchemy + Alembic: миграции, типобезопасный domain-слой, никакой Django ORM на write-пути. outstaff.db — SQLite в WAL-режиме. На наш объём (БД меньше гигабайта, один воркер на запись) этого хватает с запасом.

Поверх той же базы поднят Django 5.1 dashboard — но через unmanaged-модели, отзеркаленные 1-в-1 со схемой SQLAlchemy. Это компромисс: получили готовый Django admin и шаблоны на Bootstrap 5, не дублируя миграции и не создавая два источника правды для схемы.

Главный экран — список свежих позиций с топ-кандидатами, скором по 8 измерениям, рисками и кнопками "одобрить и отправить" / "отклонить" / "переподобрать". Все действия логируются в отдельную таблицу — это даёт и аналитику по каналам, и аудит.

Деплой и доступ

Один VPS, Docker Compose, Caddy как reverse proxy и автоматический TLS. Аутентификация в дашборд — через oauth2-proxy → Keycloak (OIDC): команда логинится корпоративным SSO, отдельных паролей под Django нет.


Если у вас есть похожий процесс — поток сообщений в свободной форме плюс одно узкое горлышко в виде живого человека, — мы можем бесплатно посмотреть на сценарий и сказать, есть ли смысл его автоматизировать. Обсудить с командой ITS.


Цифры на проде

Метрика До После
Кто держит процесс CEO Один оператор
Покрытие чатов ~30 каналов вручную ~50 каналов автоматически, 24/7
Время от позиции до отклика от 2 часов до "пропустили" около 15 минут, без пропусков
Прямые расходы ~40/мес(40/мес (10 хостинг + $30 токены)
Объём в месяц "сколько успел" ~1500 позиций, ~200 откликов
Размер кодовой базы ~10 300 строк Python + ~1 800 HTML/JS

Цифры по точности матчинга мы пока не публикуем — внутренние замеры есть, но методология не готова к внешнему обсуждению. Правило простое: метрика без замера — не метрика.

Главный бенефит, который не ложится в таблицу: фокус CEO. Он перестал быть бутылочным горлышком и переключился на то, что действительно требует его участия — стратегические партнёрства, найм, продукт.

Что мы получили "побочкой"

Когда переписываешь ручной процесс в систему, обычно получаешь больше, чем планировал:

  • Аналитика по каналам. Раньше мы не знали, какой канал даёт больше всего качественных позиций. Теперь это видно в портале и помогает решать, какие чаты держать подключёнными.
  • История взаимодействий. Партнёр через полгода спрашивает: "вы у нас по такой-то позиции в феврале откликались?" — раньше CEO рылся в личке, сейчас открываем портал и видим всё.
  • Учёт фидбека от инженеров. Раньше "не хочу embedded-системы, готов только на удалёнку, занят до конца квартала" жило в голове CEO. Сейчас — в портале, и матчер учитывает это на этапе подбора.

Чему мы научились (что бы сделали иначе)

  • Один большой "промпт-всемогутер" не сработал. Пришлось разбить экстракцию на узкие шаги: отдельный агент на название, отдельный — на стек, отдельный — на ставку. Так короче промпты, проще править и дешевле по токенам.
  • Жёсткие правила для матчера понадобились явно. Без MANDATORY RULES reasoning-модель компенсировала слабое попадание по роли сильным попаданием по стеку, и финальный score становился оптимистичнее, чем стоило бы.
  • Финальный шаг сразу решили оставить за человеком. Соблазн сделать авто-отклик при высоком score был, но в B2B цена ложноположительного отклика выше, чем выигрыш в скорости. Это решение мы менять не планируем.
  • CV-база — главное узкое место. Модель хороша ровно настолько, насколько структурирована информация об инженерах. Половина усилий следующего квартала — разметка bench-профилей.

Когда такой подход применим у вас

Логика воспроизводится в любой нише, где сходятся четыре условия:

  1. Источник данных — мессенджер, почта или CRM в свободной форме. Если у вас уже структурированный ATS со стандартными формами вакансий — LLM-экстракция добавит шума, а не точности.
  2. Один человек (часто фаундер или операционный директор) держит процесс в голове, и это узкое горлышко. Без этой боли проект не окупится.
  3. Решение требует двух шагов: структурировать вход и сравнить с базой. Не reasoning через весь домен, не сложный workflow с десятками веток.
  4. Цена ошибки терпимая, и финальное решение можно оставить за человеком. Если ошибка стоит сделки на $200K — лучше потратить больше времени на проектирование, чем спешить.

Если все четыре условия выполняются — есть шанс, что вы соберёте свою версию за похожие деньги и сроки. Если хотя бы одно ломается — лучше начать с разбора процесса, а не с выбора стека.

Когда такой подход НЕ работает

Чтобы не звучало как "стройте AI-агентов везде":

  • Цена ошибки высокая, а human-in-the-loop недоступен — не делайте. Авто-действие в B2B без оператора — стабильно плохая идея.
  • Входные данные хорошо структурированы. ATS со стандартными формами вакансий не нуждаются в LLM-экстракции — добавите шум и стоимость.
  • Объём — десятки сообщений в день. Ручная работа дешевле инфраструктуры.
  • Матчинг строится на жёстких правилах (точное совпадение сертификатов, лицензий) — LLM добавит шума, а не точности.

В ITS мы обычно начинаем такие внутренние и клиентские проекты с разметки процесса по этим четырём вопросам — раньше выбора стека или модели. Большая часть "это нельзя автоматизировать" на проверке оказывается "это можно автоматизировать, если оставить финальное решение за человеком" — что сильно меняет картину.

FAQ

Сколько стоит собрать AI-портал для подбора кандидатов? В нашем случае — около 35–40 часов работы senior-инженера за неделю плюс ~40/месоперационныхрасходовнапроде(40/мес операционных расходов на проде (10 хостинг + ~$30 токены OpenAI). Сборка с нуля под другую нишу займёт сопоставимое время, если вход — Telegram/email и матчинг укладывается в двухконтурную схему "экстракция + reasoning".

Зачем o3-mini для матчинга, если есть более дешёвые модели? Мы пробовали gpt-4o-mini и для матчинга — точность падает на нюансах вроде "человек 2 года писал на Go, остальное — Python; подходит на Senior Go?". На объёме это даёт десятки лишних некорректных откликов в месяц, что ломает доверие партнёров. Разница в стоимости — порядка $0.05–0.10 на матчинг — на 200 откликов в месяц это считаные доллары.

Почему SQLite, а не Postgres? Один воркер на запись, один пользователь UI на чтение, объём данных меньше гигабайта. SQLite в WAL-режиме держит это с запасом и не требует отдельного контейнера, бэкапа сложнее cp database.db backup.db или мониторинга. Когда упрёмся в лимиты — мигрируем на Postgres за вечер.

Почему Django, если основной слой данных — SQLAlchemy? Нужен был дашборд с минимальным фронтенд-усилием. Django + admin + готовые шаблоны на Bootstrap покрыли это за день. Чтобы не поддерживать две схемы, Django-модели отражают SQLAlchemy-схему как unmanaged. Единая истина — миграции Alembic.

Почему Telethon, а не Bot API? В большинстве партнёрских Telegram-чатов бот не имеет прав на чтение истории, а пригласить его требует согласия админа. Telethon с user-сессией работает от имени реального аккаунта, который уже состоит в этих чатах, и таких ограничений не знает. Telegram официально разрешает автоматизацию через MTProto в рамках общих rate limits.

Можно ли это сделать без инженера, на n8n или Make? Парсинг и экстракцию — да. Матчинг через LLM с приличной точностью — затруднительно: нужна работа с контекстом CV-базы, дедупликация, история, фидбек-петля. На low-code уровень MVP вы получите, но он быстро упрётся в потолок: либо в стоимость токенов на каждый прогон, либо в качество подбора.

Как обходим Telegram TOS для user-аккаунтов? Никак не обходим. Telegram явно разрешает автоматизацию через Telethon/MTProto при выполнении общих rate limits и ToS. Партнёрские чаты — это закрытые сообщества, в которые мы приглашены как участники.

Где здесь самое узкое место сейчас? Качество CV-базы. Модель хороша ровно настолько, насколько структурированы данные о наших инженерах. Половина усилий за следующий квартал уходит на разметку bench-профилей: явные теги по стеку, история фидбека от партнёров, доступность.

Сколько времени реально ушло у инженера на сборку? Примерно 35–40 часов чистой работы за неделю. Без переработок, без героизма. Большая часть — не код, а переписывание промптов и борьба с edge-кейсами в чатах.

Можно ли заменить o3-mini на Claude или DeepSeek? Архитектурно — да, это смена 1–2 строк через LangChain. Главное — повторно прогнать промпт матчера на отложенной выборке, потому что MANDATORY RULES чувствительны к тому, как конкретная модель интерпретирует "MUST" в инструкциях.

Итог

3 созвона на планирование. Неделя работы senior-инженера. ~$40/мес на проде. CEO, которому больше не нужно жить в Telegram. Один оператор вместо одного фаундера.

Главный для нас урок не про код, а про то, что граница "это можно автоматизировать" сильно сдвинулась за последние полтора года. Задачи, которые казались слишком завязанными на человеческое суждение, сейчас стоят $30/мес токенов, если правильно их декомпозировать на структурирование вход + reasoning-сравнение.

Самое неудобное: мы откладывали этот проект полтора года, потому что "ну такое же не автоматизируешь". Возможно, у вас в компании есть похожая задача, которую тоже пора пересмотреть.


Если хотите обсудить, как AI-агенты лягут на ваши процессы — напишите нам. Разберём задачу, покажем похожие реализации и предложим формат пилота. Для первых 3 компаний с релевантным сценарием готовы провести бесплатный технический разбор и помочь спроектировать MVP под ваш процесс.

Обсудить с командой ITS

Контакты: