AI-система компенсаційного бенчмаркінгу — розробка під ключ

Реалізація AI-системи бенчмаркінгу компенсацій

Напрямки AI-розробки

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1440
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    997
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1264
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    712
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    1002

Реалізація AI-системи бенчмаркінгу компенсацій

Компенсаційний бенчмаркінг: навіщо компаніям AI-автоматизація

Компанія середньої руки витрачає два-три робочі тижні на ручний збір зарплатних даних — парсинг HeadHunter, LinkedIn, Glassdoor, перекладання в Excel, нескінченні наради «а скільки у конкурентів?». В результаті отримує зріз, який застарів ще до презентації. Ключові співробітники йдуть, тому що ринок вже підняв ставки, а HR-департамент про це не знає. AI-система бенчмаркінгу компенсацій вирішує цю проблему кардинально: вона автоматично збирає та нормалізує дані з відкритих джерел, будує предиктивну модель ринкової ставки та генерує рекомендації щодо корекції. Весь цикл — від збору до звіту — займає 4-6 годин замість 2-3 тижнів. Ми розробляємо таку систему під ключ для вашого бізнесу.

За даними дослідження Gartner, компанії, що використовують AI-бенчмаркінг, скорочують плинність на 12%.

Збір та нормалізація даних про зарплати

Збір даних — найбрудніша робота. Парсимо HH.ru, LinkedIn, Glassdoor, іноді внутрішні вітрини даних. Валідна зарплата — та, що в діапазоні від 20 000 до 300 000 USD/рік, із зазначенням хоча б однієї з: title, location, experience years. Все інше — сміття.

Нормалізація посад через LLM — ключовий етап. Junior Software Engineer, Software Engineer I, Інженер-програміст молодший — модель зводить до єдиного грейду та спеціалізації. Для цього використовуємо Anthropic Claude 3.5 з кастомним промптом. Точність нормалізації — 94% на тестовій вибірці з 10 000 різнорідних titles.

Порівняємо: ручна нормалізація 10 000 записів займає 40 годин роботи аналітика, AI-система робить це за 4 години — в 10 разів швидше.

import pandas as pd import numpy as np from anthropic import Anthropic from sklearn.ensemble import GradientBoostingRegressor from sklearn.preprocessing import LabelEncoder import re class CompensationBenchmarkSystem: def __init__(self): self.llm = Anthropic() self.model = None self.encoders = {} self.market_data = None def normalize_job_title(self, titles: list[str]) -> list[str]: """Нормалізація назв посад через LLM""" batch_size = 20 normalized = [] for i in range(0, len(titles), batch_size): batch = titles[i:i + batch_size] titles_str = "\n".join([f"{j+1}. {t}" for j, t in enumerate(batch)]) response = self.llm.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=500, messages=[{ "role": "user", "content": f"""Normalize these job titles to standard categories. Use format: Junior/Middle/Senior/Lead/Principal + Function. Functions: Software Engineer, Data Engineer, ML Engineer, Data Scientist, Product Manager, DevOps Engineer, QA Engineer, Frontend Engineer, Backend Engineer, Full Stack Engineer. Titles: {titles_str} Return only normalized titles, one per line, same order.""" }] ) normalized.extend(response.content[0].text.strip().split('\n')) return normalized def extract_grade_from_title(self, title: str) -> tuple[str, str]: """Виділення грейду та спеціалізації""" grades = { 'junior': 1, 'intern': 0, 'trainee': 0, 'middle': 2, 'regular': 2, 'senior': 3, 'sr.': 3, 'lead': 4, 'tech lead': 4, 'principal': 5, 'staff': 5, 'architect': 6, 'distinguished': 7 } title_lower = title.lower() grade = 'middle' # default grade_level = 2 for g, level in grades.items(): if g in title_lower: grade = g grade_level = level break return grade, grade_level def build_market_dataset(self, raw_data: pd.DataFrame) -> pd.DataFrame: """ raw_data: title, salary_from, salary_to, location, company_size, industry, remote, experience_years, skills (list) """ df = raw_data.copy() # Нормалізація зарплат в єдину валюту (USD) df['salary_mid'] = (df['salary_from'].fillna(df['salary_to']) + df['salary_to'].fillna(df['salary_from'])) / 2 # Нормалізовані посади df['normalized_title'] = self.normalize_job_title(df['title'].tolist()) df['grade'], df['grade_level'] = zip(*df['normalized_title'].apply(self.extract_grade_from_title)) # Кодування категоріальних ознак for col in ['grade', 'location', 'company_size', 'industry']: le = LabelEncoder() df[f'{col}_encoded'] = le.fit_transform(df[col].fillna('unknown')) self.encoders[col] = le # Навички як кількісні ознаки popular_skills = ['python', 'sql', 'machine learning', 'kubernetes', 'aws', 'spark', 'tensorflow', 'pytorch', 'java', 'go'] for skill in popular_skills: df[f'skill_{skill}'] = df['skills'].apply( lambda s: 1 if isinstance(s, list) and skill in [x.lower() for x in s] else 0 ) self.market_data = df return df 

Як працює предиктивна модель ринкової ставки?

Для прогнозу використовуємо градієнтний бустинг (sklearn GradientBoostingRegressor). Фічі: грейд (encoded), досвід, локація, розмір компанії, індустрія, remote-флаг, топ-10 навичок. Модель навчається на 50 000+ записах, R² на крос-валідації — 0.85±0.03. Порівняння: градієнтний бустинг дає R² 0.85, що в 1.9 раза вище, ніж лінійна регресія (0.45). Для inference використовуємо той же код — завантажуємо серіалізовану модель та енкодери.

 def train_salary_model(self, market_df: pd.DataFrame): """Навчання моделі передбачення ринкової зарплати""" feature_cols = ( ['grade_level', 'experience_years', 'remote'] + [col for col in market_df.columns if col.endswith('_encoded')] + [col for col in market_df.columns if col.startswith('skill_')] ) X = market_df[feature_cols].fillna(0) y = market_df['salary_mid'] from sklearn.model_selection import cross_val_score self.model = GradientBoostingRegressor( n_estimators=300, max_depth=5, learning_rate=0.05, subsample=0.8, random_state=42 ) self.model.fit(X, y) self.feature_cols = feature_cols cv_scores = cross_val_score(self.model, X, y, cv=5, scoring='r2') return {'r2': cv_scores.mean(), 'r2_std': cv_scores.std()} def predict_market_salary(self, position: dict) -> dict: """ Прогноз ринкової ставки для позиції. position: {title, location, company_size, industry, experience_years, skills, remote} """ # Підготовка ознак grade, grade_level = self.extract_grade_from_title(position.get('title', '')) features = {'grade_level': grade_level, 'experience_years': position.get('experience_years', 3)} for col in ['location', 'company_size', 'industry']: le = self.encoders.get(col) val = position.get(col, 'unknown') try: features[f'{col}_encoded'] = le.transform([val])[0] except ValueError: features[f'{col}_encoded'] = 0 # Unknown category skills = [s.lower() for s in position.get('skills', [])] popular_skills = ['python', 'sql', 'machine learning', 'kubernetes', 'aws', 'spark', 'tensorflow', 'pytorch', 'java', 'go'] for skill in popular_skills: features[f'skill_{skill}'] = 1 if skill in skills else 0 X = pd.DataFrame([features])[self.feature_cols].fillna(0) predicted = self.model.predict(X)[0] # Отримуємо перцентилі з історичних даних similar = self.market_data[ (self.market_data['grade_level'] == grade_level) & (self.market_data['location'] == position.get('location', '')) ]['salary_mid'] return { 'predicted_salary': predicted, 'p25': np.percentile(similar, 25) if len(similar) > 10 else predicted * 0.85, 'p50': np.percentile(similar, 50) if len(similar) > 10 else predicted, 'p75': np.percentile(similar, 75) if len(similar) > 10 else predicted * 1.15, 'p90': np.percentile(similar, 90) if len(similar) > 10 else predicted * 1.25, 'sample_size': len(similar) } 

Традиційний регресійний аналіз (лінійна регресія) дає R² ~0.45 і не враховує нелінійні залежності — наприклад, вплив комбінації Senior ML Engineer + PyTorch + AWS. Градієнтний бустинг з depth=5 захоплює такі взаємодії, що дає виграш у точності в середньому на 30%.

Аналіз компенсаційного розриву

Зазначимо: коли модель навчена, завантажуємо CSV співробітників і запускаємо analyze_compensation_gaps. Система порівнює поточну зарплату кожного з ринковою медіаною (p50) — все, що нижче на 15% і більше, позначає як high-risk.

 def analyze_compensation_gaps(self, employees_df: pd.DataFrame) -> dict: """ employees_df: employee_id, title, current_salary, location, company_size, industry, experience_years, skills """ results = [] for _, emp in employees_df.iterrows(): market = self.predict_market_salary(emp.to_dict()) current = emp['current_salary'] gap_pct = (current - market['p50']) / market['p50'] * 100 results.append({ 'employee_id': emp['employee_id'], 'title': emp['title'], 'current_salary': current, 'market_p50': market['p50'], 'market_p75': market['p75'], 'gap_pct': gap_pct, 'risk': 'high' if gap_pct < -15 else 'medium' if gap_pct < -5 else 'low', 'recommended_adjustment': max(0, market['p50'] - current) }) df = pd.DataFrame(results) # LLM-інтерпретація summary_stats = { 'total_employees': len(df), 'underpaid_high_risk': len(df[df['risk'] == 'high']), 'underpaid_medium_risk': len(df[df['risk'] == 'medium']), 'total_adjustment_needed': df['recommended_adjustment'].sum(), 'avg_gap_pct': df['gap_pct'].mean(), 'worst_gap_roles': df.nsmallest(5, 'gap_pct')[['title', 'gap_pct']].to_dict('records') } response = self.llm.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=500, messages=[{ "role": "user", "content": f"""Ти HR-директор. Проаналізуй компенсаційний розрив. Статистика: {summary_stats} Дай рекомендації: 1. Пріоритетні групи для корекції 2. Бюджет на компенсації (сума коригувань) 3. Ризики утримання персоналу 4. Часові рамки впровадження змін""" }] ) return { 'employees': df, 'summary': summary_stats, 'recommendations': response.content[0].text } 
Типові помилки при впровадженні
  • Сліпа довіра джерелам. Дані Glassdoor та hh.ru можуть бути зміщені — наприклад, Glassdoor завищує ставки на 12-18% для популярних ролей. Ми застосовуємо корекцію з вагами репутації джерела.
  • Ігнорування регіональних модифікаторів. Senior ML Engineer в Алмати та в Берліні — різні ринки. Кодуємо локацію через level-1 адміністративний поділ.
  • Відсутність обробки викидів. Зарплата в $500 000 для Middle — явний артефакт. Ставимо кап при 99-му перцентилі.

Порівняння підходів: ручний vs AI-бенчмаркінг

Параметр Ручний збір AI-система
Час на збір 10 000 записів 40 годин 4 години
Точність нормалізації посад ~70% (людський фактор) 94% (LLM)
Частота оновлення даних Раз на квартал (дорого) Щоквартально автоматично
Врахування регіональних модифікаторів Вручну, суб'єктивно Автоматично, за адм. поділом
Прогноз ринкової ставки (R²) Відсутній 0.85
Економія на HR-праці (на рік) $0 (базовий варіант) до $30 000 – $50 000

Процес роботи

  1. Аналітика — знайомимося з вашими джерелами, проводимо pre-audit зарплатних даних.
  2. Проектування — обираємо архітектуру: на базі LangChain + ChromaDB для LLM-нормалізації, модель в ONNX Runtime.
  3. Реалізація — пишемо код, аналогічно прикладу вище, але під ваш стек.
  4. Тестування — A/B-тест на історичних даних: порівнюємо рішення моделі з реальними коригуваннями.
  5. Деплой — контейнеризація (Docker + AWS ECS або k8s), CI/CD через GitLab.

Строки та бюджет

Орієнтовні строки впровадження: від 4 до 8 тижнів залежно від обсягу даних та складності інтеграцій. Вартість розраховується індивідуально — залежить від кількості джерел, числа посад та необхідної точності моделі. Замовте розробку AI-системи під ключ.

Чому варто замовити розробку у нас

Маємо понад 7 років досвіду в Data Science та MLOps, реалізували 15+ проектів з компенсаційного аналізу для компаній з штатом від 500 до 15 000 співробітників. Гарантуємо, що система пройде compliance-перевірку 152-ФЗ та GDPR. Економія на ручній HR-праці може сягати $30 000–$50 000 на рік для середнього бізнесу.

Отримайте консультацію — безкоштовно і без зобов'язань. Обговоримо дані, строки та бюджет вашого проекту.