אוטומציה של ניתוח התנהגות משתמשים עם AI ו-LLM

כשלוחות המחוונים מציגים מספרים אך לא מסבירים מדוע משתמשים עוזבים, אנליטיקה קלאסית אינה מספיקה. אנו מפתחים מערכות ניתוח התנהגות מבוססות בינה מלאכותית המשתמשות ב-machine learning וב-LLM כדי להפוך את הכאוס של היומנים לתובנות טקסטואליות ברורות. הצוות שלנו מספק את הפרויקט במפתח מלא—מביקורת נתונים ועד הטמעה ותמיכה שוטפת—ועוזר לך לראות דפוסים נסתרים וחריגות.

תחומי פיתוח AI

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

מבוא: מדשבורדים לתובנות מבוססות בינה מלאכותית

תארו לעצמכם: בכל שבוע צוות המוצר שלכם מבלה 4-6 שעות בניתוח ידני של לוגים, ובכל זאת מפספס דפוסי נטישה. משפכים מוגדרים, דשבורדים ירוקים, אבל השימור יורד. למה? כי אנליטיקת ווב קלאסית עונה רק על "מה קורה": כל כך הרבה צפיות, כל כך הרבה המרות. אנליטיקת AI מוסיפה שכבת הבנה — "למה" ו"מה לעשות הלאה".

אנו מיישמים מערכת AI (אנליטיקת התנהגות משתמשים) שמגלה אוטומטית דפוסי התנהגות, חריגות וקשרי סיבה-תוצאה בזרם האירועים. במקום אלפי שורות לוג, תובנת טקסט אחת מ-LLM: "44% מהמשתמשים נושרים בשלב השלישי עקב טעינה איטית של טופס ההרשמה." זו לא תחזית — זו עובדה המאושרת על ידי נתונים.

כיצד אנליטיקת AI פותרת בעיות של ניתוח מסורתי

מאפיין אנליטיקה מסורתית (כללים/פילטרים) אנליטיקת AI (ML + LLM)
זמן הגדרה 2-3 שבועות לכל משפך 2-3 ימים לתובנה ראשונה
זיהוי חריגות ידני, עיכוב של 1-2 ימים אוטומטי, 1-2 שעות
פירוש נתונים דשבורדים עם מספרים, ניתוח ידני תובנות טקסט בשפה טבעית
עלות תחזוקה גבוהה (אנליסט במשרה חלקית) נמוכה (ניטור תורן)

ההבדל המהותי: גישות מסורתיות משתמשות בכללים קשיחים (אם event_count > 3 → חריגה), בעוד ש-AI משתמש במודלים הסתברותיים וב-LLM שמבינים הקשר. אנו משתמשים בניתוח Z-score (ראו ויקיפדיה) כדי לזהות חריגים התנהגותיים.

למה LLM עדיף על כללים לפירוש נתוני התנהגות

תארו לעצמכם ניתוח רצף אירועים: login → search → view_product → add_to_cart → payment_error → logout. כלל היה אומר: "יש שגיאת תשלום." LLM היה רואה: "המשתמש מצא מוצר אך אינו יכול להשלים תשלום — כנראה בעיה בשער התשלום או חוסר בכספים. יש להציע אמצעי תשלום חלופיים בשלב add_to_cart."

אנו משתמשים ב-Claude 3.5 Sonnet וב-GPT-4o ליצירת תובנות. המודל מקבל מדדים מצטברים ואירועים מובילים, ומחזיר דוח מובנה עם תצפיות מרכזיות, דפוסים בעייתיים והמלצות. דיוק הפירוש מגיע ל-95% עם פרומפטים מכוונים היטב.

יישום טכני: ML ו-LLM בפעולה

איסוף ועיבוד אירועים

import pandas as pd
import numpy as np
from anthropic import Anthropic
from datetime import datetime, timedelta
import json

class UserBehaviorAnalytics:
    def __init__(self, events_df: pd.DataFrame):
        """
        events_df: user_id, event_name, timestamp, properties (JSON), session_id
        """
        self.events = events_df
        self.llm = Anthropic()
        self._preprocess()

    def _preprocess(self):
        self.events['timestamp'] = pd.to_datetime(self.events['timestamp'])
        self.events = self.events.sort_values(['user_id', 'timestamp'])
        # Сессионизация
        session_gap = timedelta(minutes=30)
        self.events['prev_ts'] = self.events.groupby('user_id')['timestamp'].shift(1)
        self.events['is_new_session'] = (
            (self.events['timestamp'] - self.events['prev_ts'] > session_gap) |
            self.events['prev_ts'].isna()
        )
        self.events['session_id'] = self.events.groupby('user_id')['is_new_session'].cumsum()

    def compute_session_features(self) -> pd.DataFrame:
        """Признаки на уровне сессии"""
        agg = self.events.groupby(['user_id', 'session_id']).agg(
            session_start=('timestamp', 'min'),
            session_end=('timestamp', 'max'),
            event_count=('event_name', 'count'),
            unique_events=('event_name', 'nunique'),
            events_sequence=('event_name', list)
        ).reset_index()
        agg['session_duration_min'] = (
            agg['session_end'] - agg['session_start']
        ).dt.total_seconds() / 60
        return agg

סשניזציה מפצלת את הזרם הרציף לבלוקים לוגיים באמצעות פסק זמן של 30 דקות. זה קריטי לחישוב נכון של המרה בשלבי משפך.

כיצד ML מוצא אוטומטית דפוסים וחריגות

def find_conversion_paths(self, target_event: str, window_days: int = 7) -> dict:
    """Топ путей к конверсионному событию"""
    converted_users = self.events[
        self.events['event_name'] == target_event
    ]['user_id'].unique()
    paths = []
    for user_id in converted_users[:500]:  # Ограничение для производительности
        user_events = self.events[
            self.events['user_id'] == user_id
        ].sort_values('timestamp')
        conversion_time = user_events[
            user_events['event_name'] == target_event
        ]['timestamp'].min()
        # События за N дней до конверсии
        pre_conversion = user_events[
            user_events['timestamp'] <= conversion_time
        ].tail(10)['event_name'].tolist()
        paths.append(' → '.join(pre_conversion))
    # Частотный анализ путей
    from collections import Counter
    path_counts = Counter(paths)
    return {
        'top_paths': path_counts.most_common(10),
        'total_conversions': len(converted_users),
        'median_steps': np.median([len(p.split(' → ')) for p in paths])
    }

def detect_drop_off_points(self, funnel: list[str]) -> list[dict]:
    """Где теряются пользователи в воронке"""
    results = []
    users_at_step = None
    for i, event in enumerate(funnel):
        users_with_event = set(
            self.events[self.events['event_name'] == event]['user_id']
        )
        if users_at_step is None:
            users_at_step = users_with_event
            results.append({
                'step': i + 1,
                'event': event,
                'users': len(users_at_step),
                'conversion_from_prev': 1.0,
                'drop_off': 0
            })
        else:
            continued = users_at_step & users_with_event
            conversion = len(continued) / len(users_at_step) if users_at_step else 0
            drop_off = len(users_at_step) - len(continued)
            results.append({
                'step': i + 1,
                'event': event,
                'users': len(continued),
                'conversion_from_prev': conversion,
                'drop_off': drop_off
            })
            users_at_step = continued
    return results

def detect_behavioral_anomalies(self) -> list[dict]:
    """Обнаружение аномальных паттернов поведения"""
    daily_metrics = self.events.groupby(
        self.events['timestamp'].dt.date
    ).agg(
        dau=('user_id', 'nunique'),
        events_per_user=('event_name', 'count')
    )
    daily_metrics['events_per_user'] = (
        daily_metrics['events_per_user'] / daily_metrics['dau']
    )
    anomalies = []
    # Z-score для обнаружения выбросов
    for col in ['dau', 'events_per_user']:
        mean = daily_metrics[col].mean()
        std = daily_metrics[col].std()
        daily_metrics[f'{col}_zscore'] = (daily_metrics[col] - mean) / std
        outliers = daily_metrics[
            daily_metrics[f'{col}_zscore'].abs() > 2.5
        ]
        for date, row in outliers.iterrows():
            anomalies.append({
                'date': str(date),
                'metric': col,
                'value': row[col],
                'zscore': row[f'{col}_zscore'],
                'direction': 'spike' if row[f'{col}_zscore'] > 0 else 'drop'
            })
    return sorted(anomalies, key=lambda x: abs(x['zscore']), reverse=True)

שיטת import pandas as pd import numpy as np from anthropic import Anthropic from datetime import datetime, timedelta import json class UserBehaviorAnalytics: def __init__(self, events_df: pd.DataFrame): """ events_df: user_id, event_name, timestamp, properties (JSON), session_id """ self.events = events_df self.llm = Anthropic() self._preprocess() def _preprocess(self): self.events['timestamp'] = pd.to_datetime(self.events['timestamp']) self.events = self.events.sort_values(['user_id', 'timestamp']) # Сессионизация session_gap = timedelta(minutes=30) self.events['prev_ts'] = self.events.groupby('user_id')['timestamp'].shift(1) self.events['is_new_session'] = ( (self.events['timestamp'] - self.events['prev_ts'] > session_gap) | self.events['prev_ts'].isna() ) self.events['session_id'] = self.events.groupby('user_id')['is_new_session'].cumsum() def compute_session_features(self) -> pd.DataFrame: """Признаки на уровне сессии""" agg = self.events.groupby(['user_id', 'session_id']).agg( session_start=('timestamp', 'min'), session_end=('timestamp', 'max'), event_count=('event_name', 'count'), unique_events=('event_name', 'nunique'), events_sequence=('event_name', list) ).reset_index() agg['session_duration_min'] = ( agg['session_end'] - agg['session_start'] ).dt.total_seconds() / 60 return agg מציגה את שרשראות האירועים הפופולריות ביותר לפני פעולת היעד. אם יש לכם 10,000 המרות בשבוע אבל 70% עוברות באותו מסלול, זה אות לפשט את ממשק המשתמש. def find_conversion_paths(self, target_event: str, window_days: int = 7) -> dict: """Топ путей к конверсионному событию""" converted_users = self.events[ self.events['event_name'] == target_event ]['user_id'].unique() paths = [] for user_id in converted_users[:500]: # Ограничение для производительности user_events = self.events[ self.events['user_id'] == user_id ].sort_values('timestamp') conversion_time = user_events[ user_events['event_name'] == target_event ]['timestamp'].min() # События за N дней до конверсии pre_conversion = user_events[ user_events['timestamp'] <= conversion_time ].tail(10)['event_name'].tolist() paths.append(' → '.join(pre_conversion)) # Частотный анализ путей from collections import Counter path_counts = Counter(paths) return { 'top_paths': path_counts.most_common(10), 'total_conversions': len(converted_users), 'median_steps': np.median([len(p.split(' → ')) for p in paths]) } def detect_drop_off_points(self, funnel: list[str]) -> list[dict]: """Где теряются пользователи в воронке""" results = [] users_at_step = None for i, event in enumerate(funnel): users_with_event = set( self.events[self.events['event_name'] == event]['user_id'] ) if users_at_step is None: users_at_step = users_with_event results.append({ 'step': i + 1, 'event': event, 'users': len(users_at_step), 'conversion_from_prev': 1.0, 'drop_off': 0 }) else: continued = users_at_step & users_with_event conversion = len(continued) / len(users_at_step) if users_at_step else 0 drop_off = len(users_at_step) - len(continued) results.append({ 'step': i + 1, 'event': event, 'users': len(continued), 'conversion_from_prev': conversion, 'drop_off': drop_off }) users_at_step = continued return results def detect_behavioral_anomalies(self) -> list[dict]: """Обнаружение аномальных паттернов поведения""" daily_metrics = self.events.groupby( self.events['timestamp'].dt.date ).agg( dau=('user_id', 'nunique'), events_per_user=('event_name', 'count') ) daily_metrics['events_per_user'] = ( daily_metrics['events_per_user'] / daily_metrics['dau'] ) anomalies = [] # Z-score для обнаружения выбросов for col in ['dau', 'events_per_user']: mean = daily_metrics[col].mean() std = daily_metrics[col].std() daily_metrics[f'{col}_zscore'] = (daily_metrics[col] - mean) / std outliers = daily_metrics[ daily_metrics[f'{col}_zscore'].abs() > 2.5 ] for date, row in outliers.iterrows(): anomalies.append({ 'date': str(date), 'metric': col, 'value': row[col], 'zscore': row[f'{col}_zscore'], 'direction': 'spike' if row[f'{col}_zscore'] > 0 else 'drop' }) return sorted(anomalies, key=lambda x: abs(x['zscore']), reverse=True) מחשבת הפסדים בכל שלב משפך: אנו רואים לא רק המרה כוללת אלא גם נשירה מצטברת במשפך ההמרה.

כיצד ה-LLM מפרש נתוני התנהגות

def generate_insights(self, analysis_period_days: int = 30) -> dict:
    """Генерация инсайтов через LLM"""
    recent_events = self.events[
        self.events['timestamp'] >= datetime.now() - timedelta(days=analysis_period_days)
    ]
    # Топ событий
    event_counts = recent_events['event_name'].value_counts().head(15).to_dict()
    # Дневная активность
    daily_active = recent_events.groupby(
        recent_events['timestamp'].dt.date
    )['user_id'].nunique()
    # Аномалии
    anomalies = self.detect_behavioral_anomalies()
    # Воронка (если определена)
    stats_summary = {
        'period_days': analysis_period_days,
        'total_users': recent_events['user_id'].nunique(),
        'total_events': len(recent_events),
        'top_events': event_counts,
        'avg_dau': daily_active.mean(),
        'dau_trend': 'growing' if daily_active.iloc[-7:].mean() > daily_active.iloc[:7].mean() else 'declining',
        'anomalies_detected': len(anomalies),
        'top_anomaly': anomalies[0] if anomalies else None
    }
    response = self.llm.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=600,
        messages=[{
            "role": "user",
            "content": f"""Ты аналитик роста (Growth Analyst). Проанализируй данные о поведении пользователей. Статистика за {analysis_period_days} дней: {json.dumps(stats_summary, ensure_ascii=False, indent=2)} Дай анализ в формате: 1. Ключевые наблюдения (3-4 пункта с числами) 2. Проблемные паттерны (если есть) 3. Рекомендации для роста (2-3 конкретных действия) Будь конкретным, используй числа из данных."""
        }]
    )
    return {
        'insights': response.content[0].text,
        'stats': stats_summary,
        'anomalies': anomalies[:5]
    }

ה-LLM מקבל סטטיסטיקות מובנות על פני תקופה ומחזיר דוח מוכן. זה מחליף פגישות אנליסט שבועיות: זמן הניתוח יורד מ-4-6 שעות ל-30-40 דקות, וחוסך כ-$40,000 בשנה על משכורות אנליסטים. אנו משתמשים בפרומפטינג של שרשרת חשיבה כדי להבטיח שהמודל לא יפספס מדדים מרכזיים.

אנליטיקה מתקדמת: קוהורטות וזיהוי חריגות

ניתוח קוהורטות

def cohort_retention_analysis(self) -> pd.DataFrame:
    """Retention по когортам регистрации"""
    # Первое событие = дата регистрации
    first_event = self.events.groupby('user_id')['timestamp'].min().reset_index()
    first_event.columns = ['user_id', 'cohort_date']
    first_event['cohort_month'] = first_event['cohort_date'].dt.to_period('M')

    # Объединение с событиями
    events_with_cohort = self.events.merge(first_event, on='user_id')
    events_with_cohort['event_month'] = events_with_cohort['timestamp'].dt.to_period('M')
    events_with_cohort['periods_since_join'] = (
        events_with_cohort['event_month'] - events_with_cohort['cohort_month']
    ).apply(lambda x: x.n)

    # Матрица retention
    cohort_data = events_with_cohort.groupby(
        ['cohort_month', 'periods_since_join']
    )['user_id'].nunique().reset_index()
    cohort_sizes = cohort_data[cohort_data['periods_since_join'] == 0].set_index('cohort_month')['user_id']
    retention_matrix = cohort_data.pivot(
        index='cohort_month',
        columns='periods_since_join',
        values='user_id'
    ).divide(cohort_sizes, axis=0)
    return retention_matrix

ניתוח קוהורטות מראה כיצד השימור משתנה עבור קבוצות משתמשים שונות. אם קוהורטה אחת נושרת מהר יותר מאחרת, זו סיבה לבדוק שינויים באונבורדינג שבוצעו באותה תקופה.

כמה מהר מתגלות חריגות?

המערכת מחשבת Z-score עבור DAU ו-events_per_user מדי שעה. אם הערך חורג ביותר מ-2.5 סיגמא, מופעלת התראה. בפועל, חריגות מתגלות תוך 1-2 שעות מהופעתן. ניטור ידני עם אותם נתונים היה לוקח 1-2 ימים. ניתן לכייל ספים עבור המוצר שלכם: עבור שירותים עם תעבורה גבוהה אנו משתמשים במרווחי סמך דינמיים (EWMA).

עקרון זיהוי Z-score השיטה מחשבת ממוצע וסטיית תקן על פני חלון נע (לדוגמה, 7 ימים). הערך הנוכחי מומר ל-Z-score: (ערך - ממוצע) / סטיית תקן. אם |Z| > 2.5, התצפית היא חריגה. עבור מוצרים רגישים, ניתן להוריד את הסף ל-2.0, אך עלולות להופיע יותר תוצאות חיוביות שגויות.

מקרה בוחן: הגדלת שימור ב-23%

אחד הלקוחות שלנו (פלטפורמת SaaS לשיווק במייל) חווה ירידה בהמרה מניסיון חינם לתשלום. פרסנו אנליטיקת AI על הנתונים שלהם (500,000 אירועים ביום). המערכת חשפה: 68% מהמשתמשים שלא השלימו אונבורדינג נתקעו בשלב "הגדרת אינטגרציית CRM." הממשק לא היה אינטואיטיבי, ופירוש ה-LLM הצביע על כך ישירות. לאחר בדיקת A/B עם ממשק פשוט יותר, השימור בשבוע השלישי עלה מ-41% ל-64%. החיסכון מהפחתת נטישה הסתכם בכ-$18,000 בשנה. דוגמה זו מראה כיצד אופטימיזציית UX מבוססת נתונים מובילה ל-ROI מדיד.

תהליך היישום ותוצרים

אנו פועלים בגישה מובנית ומשתמשים בעקרונות MLOps לניהול מחזור חיי המודל וגרסאות הצינור.

  1. ניתוח מקורות נתונים — אנו מבינים אילו אירועים כבר נאספים, היכן מאוחסנים הלוגים ובאיזה פורמט. אם חסר טראקינג, אנו מוסיפים SDKs (דפדפן, אפליקציה ניידת, שרת).
  2. עיצוב צינור — אנו בוחרים את המחסנית: PySpark או Polars לעיבוד, ChromaDB / pgvector לאחסון אמבדינגים ארוכי טווח, MLflow לניהול מודלים.
  3. פיתוח מודולים — סשניזציה, זיהוי חריגות, קוהורטות, פירוש LLM. קוד ב-Python 3.12, מודלים דרך Anthropic API / OpenAI API. אנו גם משלבים RAG (Retrieval-Augmented Generation) להעשרת הקשר ה-LLM עם נתונים היסטוריים.
  4. אינטגרציה עם המוצר שלכם — דשבורדים ב-Grafana או רכיבי React מוטמעים, התראות דרך Telegram/Slack.
  5. בדיקות וכיול — אנו מריצים על נתונים היסטוריים (3-6 חודשים), בודקים דיוק/אחזור בזיהוי חריגות, ומתאימים פרומפטים.
  6. פריסה וניטור — קונטיינריזציה עם Docker, אורכסטרציה עם Kubernetes, מדדי ניצול GPU במהלך יצירת תובנות.

תוצרים ומה כלול

  • תיעוד צינור — תיאור ארכיטקטורה, סכמות נתונים, API.
  • קוד — מאגר פרטי עם מודולי ניתוח, בדיקות ו-CI/CD.
  • דשבורדים והתראות — מוגדרים עבור המדדים המרכזיים שלכם.
  • הדרכת צוות — 2-3 מפגשים על עבודה עם המערכת.
  • תמיכה — שבועיים לאחר ההשקה להתאמת ספים ופרומפטים.

לוח זמנים ועלות

שלב משך
ניתוח ועיצוב 3 עד 7 ימים
פיתוח מודולים בסיסיים 10 עד 20 ימים
אינטגרציה ובדיקות 5 עד 10 ימים
פריסה והדרכה 3 עד 5 ימים

לוח זמנים כולל — מ-3 עד 6 שבועות, תלוי במורכבות הנתונים ובמודולים הנדרשים. העלות מחושבת באופן אישי — תלויה בנפח האירועים (מספר משתמשים, תדירות הפקת תובנות), בצורך באינטגרציית UI מותאמת ובמספר הסקות ה-LLM. נספק הערכה תוך יום עסקים אחד לאחר הבריף. תקציב יישום טיפוסי נע בין $5,000 ל-$25,000.

לצוות שלנו ניסיון של 5+ שנים ב-AI/ML, עם למעלה מ-30 פרויקטים באנליטיקה התנהגותית. אנו משתמשים במודלים מוסמכים (Claude 3.5, GPT-4o) ומבטיחים שיעור תוצאות חיוביות שגויות מתחת ל-5% בהגדרות סטנדרטיות.

צרו קשר כדי לדון במצבכם — נשלח פתרון קונספטואלי תוך 24 שעות. בקשו דמו על הנתונים שלכם: קבלו דוח מוכן עם תובנות תוך שבוע בלבד.