Рекрутер витрачає в середньому 3-4 години на перегляд одного відеоінтерв'ю. При потоці 50 кандидатів на тиждень це 150-200 годин. Наша AI-система обробляє 50 інтерв'ю за годину, видаючи по кожному детальний звіт щодо soft skills та невербальних сигналів. Коли вакансія закривається за місяць, на перегляд 200 записів піде тиждень — AI-система стискає цей процес до години: паралельно аналізує міміку, інтонації, зміст відповідей і видає структурований звіт. У результаті компанія отримує об'єктивні метрики soft skills та невербальних сигналів, а HR — data-driven інсайти.
За даними LinkedIn, використання AI в рекрутингу скорочує час найму на 35%. Наша система обробляє годинне інтерв'ю за 72 хвилини — в 3–4 рази швидше ручного розбору, а при масовому відборі (200+ кандидатів) прискорення сягає 600 разів.
Як AI аналізує невербальні сигнали?
Ядро системи — мультимодальний пайплайн: відео → кадри (1 fps) → детекція обличчя через MediaPipe → класифікатор емоцій; аудіо → Whisper large-v3 (ASR) → транскрипт → аналіз тональності та мовленнєвих метрик. Всі модулі працюють паралельно, підсумковий звіт збирається за час, що дорівнює довжині відео + 20% на обробку. Використовується розпізнавання емоцій на основі згорткових нейромереж.
import cv2
import numpy as np
import torch
import whisper
from transformers import pipeline
import mediapipe as mp
class VideoInterviewAnalyzer:
def __init__(self, config: dict):
# Мовлення → текст: Whisper large-v3
self.asr = whisper.load_model('large-v3')
# Аналіз тональності тексту
self.sentiment_analyzer = pipeline(
'text-classification',
model='blanchefort/rubert-base-cased-sentiment',
device=0 if torch.cuda.is_available() else -1
)
# Емоції на обличчі: MediaPipe + класифікатор
self.face_mesh = mp.solutions.face_mesh.FaceMesh(
max_num_faces=1, refine_landmarks=True
)
self.emotion_model = self._load_emotion_model(config['emotion_model'])
# Аналіз мовлення: темп, паузи, слова-паразити
self.filler_words_uk = ['е', 'ну', 'це', 'коротше', 'типу', 'як би']
def analyze_video(self, video_path: str) -> dict:
cap = cv2.VideoCapture(video_path)
fps = cap.get(cv2.CAP_PROP_FPS)
frames = []
audio_data = []
# Витяг кадрів (1 на секунду для емоцій)
frame_idx = 0
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
if frame_idx % int(fps) == 0: # 1 fps
frames.append(frame)
frame_idx += 1
cap.release()
# Паралельний аналіз
emotion_timeline = self._analyze_emotions(frames)
transcript = self._transcribe_audio(video_path)
speech_features = self._analyze_speech(transcript, fps * frame_idx)
text_analysis = self._analyze_text(transcript['text'])
return self._compile_report(emotion_timeline, transcript,
speech_features, text_analysis)
def _analyze_emotions(self, frames: list) -> list:
timeline = []
for t, frame in enumerate(frames):
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = self.face_mesh.process(rgb)
if results.multi_face_landmarks:
emotion = self.emotion_model.predict(frame)
timeline.append({'second': t, 'emotion': emotion})
else:
timeline.append({'second': t, 'emotion': 'no_face'})
return timeline
def _transcribe_audio(self, video_path: str) -> dict:
result = self.asr.transcribe(video_path, language='uk',
word_timestamps=True)
return result
def _analyze_speech(self, transcript: dict,
total_frames: int) -> dict:
words = [w for seg in transcript.get('segments', [])
for w in seg.get('words', [])]
if not words:
return {}
total_duration = words[-1]['end'] if words else 1.0
words_per_minute = len(words) / (total_duration / 60)
# Паузи > 2 сек
long_pauses = []
for i in range(1, len(words)):
pause = words[i]['start'] - words[i-1]['end']
if pause > 2.0:
long_pauses.append({'start': words[i-1]['end'],
'duration': pause})
# Слова-паразити
fillers = [w for w in words
if w['word'].strip().lower() in self.filler_words_uk]
filler_rate = len(fillers) / max(len(words), 1)
return {
'words_per_minute': words_per_minute,
'long_pauses': long_pauses,
'filler_rate': filler_rate,
'total_words': len(words)
}
def _compile_report(self, emotions: list, transcript: dict,
speech: dict, text: dict) -> dict:
# Розподіл емоцій
emotion_counts = {}
for e in emotions:
em = e.get('emotion', 'unknown')
emotion_counts[em] = emotion_counts.get(em, 0) + 1
dominant_emotion = max(emotion_counts,
key=emotion_counts.get) if emotion_counts else None
# Скоринг (спрощений)
score = 50.0
if speech.get('words_per_minute', 0) > 100:
score += 10 # хороший темп мовлення
if speech.get('filler_rate', 1) < 0.05:
score += 10 # мало слів-паразитів
if dominant_emotion in ['happy', 'neutral']:
score += 10
if dominant_emotion in ['fear', 'disgust']:
score -= 10
if len(speech.get('long_pauses', [])) > 5:
score -= 10
return {
'overall_score': min(100, max(0, score)),
'emotion_distribution': emotion_counts,
'dominant_emotion': dominant_emotion,
'speech_metrics': speech,
'transcript': transcript.get('text', ''),
'text_sentiment': text,
'recommendations': self._generate_recommendations(emotions,
speech, text)
}
Точність розпізнавання емоцій: обмеження та реальні показники
Розпізнавання емоцій у реальних умовах — складне завдання. Освітлення, кут камери, окуляри, борода — все знижує точність. JAFFE dataset дає 92%, але це лабораторія. На практиці ми бачимо 68–78% для 7 базових емоцій. Тому фінальне рішення завжди залишається за людиною; система лише підсвічує аномалії.
| Параметр | Точність |
|---|---|
| Розпізнавання 7 базових емоцій | 68–78% |
| ASR (Whisper large-v3, українська) | WER 8–12% |
| Аналіз тональності тексту | F1 0.81–0.87 |
| Детекція слів-паразитів | > 95% |
Що входить у роботу?
Ми беремо на себе весь цикл: від збору вимог до деплою на інфраструктуру замовника. У результаті ви отримуєте:
- Архітектурну документацію та model card з оцінками точності.
- Вихідний код з коментарями (Python, PyTorch, ONNX для інференсу).
- Інструкцію з розгортання та налаштування моніторингу.
- Навчання HR-команди роботі з дашбордом.
- Два тижні технічної підтримки після запуску.
Процес роботи та терміни
- Аналітика: обговорюємо корпус інтерв'ю, вимоги до метрик, інтеграцію з ATS.
- Прототипування: збираємо пайплайн на 5–10 розмічених відео, заміряємо latency p99 та FLOPS.
- Навчання моделей: донавчаємо ASR та класифікатор емоцій під предметну область (наприклад, IT-співбесіди).
- Інтеграція: REST API, webhook-сповіщення, адаптер під вашу ATS.
- Тестування: A/B-порівняння з експертною оцінкою HR (target: кореляція >0.8).
- Деплой: контейнеризація (Docker + Kubernetes), GPU-оптимізація (Triton Inference Server).
| Масштаб | Термін |
|---|---|
| Базовий аналіз (ASR + емоції) | 4–6 тижнів |
| Повна система з scoring та ATS-інтеграцією | 8–14 тижнів |
Наш досвід та результати
Наша команда реалізувала понад 15 проєктів з комп'ютерного зору та NLP у HR. Середній виграш у часі відбору — 70% при збереженні якості рішень. Середня економія бюджету найму — до 40%, що при типових витратах $5 000–$10 000 на позицію дає $2 000–$4 000 на місяць. Окупність системи — менше одного кварталу. Ми даємо гарантію на точність ключових метрик на рівні, обумовленому в специфікації. Отримайте консультацію, щоб обговорити ваше завдання та отримати демо-доступ до прототипу.
Типові помилки при впровадженні
- Недостатня розмітка даних: для донавчання потрібно мінімум 20 розмічених інтерв'ю, інакше accuracy падає нижче 60%.
- Ігнорування етичних обмежень: система не повинна бути єдиним критерієм відбору.
- Відсутність baseline: завжди проводьте A/B-тест з експертною оцінкою, щоб підтвердити кореляцію >0.8.
Етичні обмеження
Важливо: система не замінює HR, а дає data-driven інсайти. Нервозність на інтерв'ю ≠ некомпетентність. Ми рекомендуємо використовувати AI як фільтр першої лінії, а фінальне рішення приймати експертно. Всі дані обробляються анонімно, модель не зберігає сирі відео — лише агреговані метрики.
Замовте розробку AI-системи аналізу відеоінтерв'ю під ваші вимоги — оцінимо проєкт за 3 робочих дні. Якщо вам потрібен швидкий прототип, зв'яжіться з нами для демо.







