← Назад к вопросам

Как описать что диалог в мессенджере состоялся?

1.3 Junior🔥 101 комментариев
#Метрики продукта#Работа с продуктом и бизнесом

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как описать что диалог в мессенджере состоялся: определение и измерение

Для Product Analyst'а важно определить что означает "диалог состоялся". Это требует понимания техники определений и бизнес логики.

Проблема: Размытость определения

В мессенджере "диалог" можно интерпретировать по-разному:

  • Пользователь Иван открыл чат?
  • Иван отправил хотя бы одно сообщение?
  • Иван отправил сообщение И получил ответ?
  • Обмен длился больше 5 минут?
  • Диалог закончился с положительным результатом?

Каждое определение даёт разные метрики и выводы.

Что такое "диалог состоялся": 3-уровневый подход

Уровень 1: Техническое определение (для разработчиков)

"Диалог состоялся" = 2-way message exchange

SELECT
    dialog_id,
    initiator_user_id,
    recipient_user_id,
    message_count,
    CASE 
        WHEN message_count >= 2 THEN 'Диалог состоялся'
        WHEN message_count = 1 THEN 'Одностороннее сообщение'
        ELSE 'Диалог не начался'
    END as dialog_status
FROM messenger_dialogs
WHERE created_at >= DATE_SUB(TODAY(), INTERVAL 30 DAY)

Критерии:

  • Минимум 2 сообщения (от разных людей)
  • Временной интервал: сообщения в пределах 24 часов
  • Статус: оба сообщения не в spam

Код для проверки:

def is_dialog_completed(messages):
    """
    Диалог состоялся если:
    1. Минимум 2 сообщения
    2. От разных юзеров (User A → User B)
    3. User B ответил User A
    """
    if len(messages) < 2:
        return False
    
    # Проверим что первое сообщение от одного, второе от другого
    sender_1 = messages[0]['sender_id']
    sender_2 = messages[1]['sender_id']
    
    if sender_1 == sender_2:
        return False  # Оба от одного юзера
    
    # Проверим временную разницу
    time_diff = messages[1]['sent_at'] - messages[0]['sent_at']
    if time_diff > timedelta(hours=24):
        return None  # Слишком поздний ответ, может быть новый диалог
    
    return True

Уровень 2: Бизнес-определение (для Product Manager'а)

"Диалог состоялся" = успешный контакт между пользователями

Диалог состоялся если:

1. User A открыл чат с User B
2. User A отправил сообщение (инициировал контакт)
3. User B получил уведомление
4. User B ответил на сообщение
5. User A прочитал ответ

Это гарантирует что контакт произошёл.

SQL для этого определения:

SELECT
    dialog_id,
    initiator_id,
    recipient_id,
    COUNT(messages) as message_count,
    MAX(CASE WHEN sender_id = recipient_id AND read_at IS NOT NULL THEN 1 ELSE 0 END) as has_read_response,
    DATEDIFF(SECOND, first_message_at, last_message_at) as dialog_duration_seconds,
    CASE 
        WHEN message_count >= 2 
         AND MAX(CASE WHEN sender_id = recipient_id THEN 1 ELSE 0 END) = 1
         AND MAX(CASE WHEN sender_id = recipient_id AND read_at IS NOT NULL THEN 1 ELSE 0 END) = 1
        THEN 'Диалог успешен'
        ELSE 'Диалог неполный'
    END as status
FROM messenger_messages
GROUP BY dialog_id

Уровень 3: Аналитическое определение (для Product Analyst'а)

"Диалог состоялся" — это зависит от цели анализа

ЦельОпределениеФормула
DAU метрикаПользователь открыл мессенджерCOUNT(DISTINCT user_id WHERE opened_app)
EngagementОтправил >= 1 сообщениеCOUNT(DISTINCT user_id WHERE sent_message)
Network effect2-way exchange произошёлCOUNT(dialogs WHERE message_count >= 2)
ConversionДиалог привел к сделкеCOUNT(dialogs WHERE deal_created = true)
NPSДиалог был позитивныйCOUNT(dialogs WHERE user_rating >= 4)

Пример: как бизнес-случай определяет метрику

Сценарий 1: Маркетплейс (Авито, Яндекс.Маркет)

Диалог состоялся когда:

  • Покупатель открыл чат
  • Покупатель отправил вопрос о товаре
  • Продавец ответил
  • Покупатель прочитал ответ

Метрика: Buyer-Seller Response Rate

SELECT
    COUNT(DISTINCT dialog_id) as dialogs_initiated,
    COUNT(DISTINCT CASE WHEN seller_responded = 1 THEN dialog_id END) as dialogs_with_response,
    ROUND(100 * dialogs_with_response / dialogs_initiated, 1) as response_rate_pct
FROM seller_dialogs
WHERE initiated_at >= DATE_SUB(TODAY(), INTERVAL 30 DAY)

Сценарий 2: Социальная сеть (ВК, Telegram)

Диалог состоялся когда:

  • User A отправил первое сообщение в DM
  • User B ответил
  • Обмен продолжился (>=3 сообщений от каждого)

Метрика: Conversation Completion Rate

SELECT
    COUNT(*) as conversations,
    COUNT(DISTINCT CASE 
        WHEN user_a_messages >= 3 AND user_b_messages >= 3 
        THEN conversation_id 
    END) as completed,
    ROUND(100.0 * completed / COUNT(*), 1) as completion_rate_pct
FROM dm_conversations

Сценарий 3: Support (служба поддержки)

Диалог состоялся когда:

  • Пользователь открыл тикет support
  • Support agent ответил на первое сообщение (в течение 2 часов)
  • Тикет помечен resolved или closed

Метрика: First Response Time, Resolution Rate

SELECT
    support_ticket_id,
    DATEDIFF(MINUTE, ticket_created_at, agent_first_response_at) as first_response_minutes,
    CASE 
        WHEN first_response_minutes <= 120 THEN 'On-time'
        ELSE 'Delayed'
    END as response_status,
    CASE 
        WHEN ticket_status = 'resolved' THEN 'Resolved'
        ELSE 'Unresolved'
    END as resolution_status
FROM support_tickets
WHERE created_at >= DATE_SUB(TODAY(), INTERVAL 30 DAY)

Рекомендуемое определение для Product Analyst'а

Я бы рекомендовал использовать комбинированное определение:

class DialogStatus:
    """
    Определение диалога в мессенджере
    """
    
    @staticmethod
    def is_dialog_started(messages: List[Message]) -> bool:
        """Диалог начался если есть минимум 1 сообщение"""
        return len(messages) >= 1
    
    @staticmethod
    def is_dialog_active(messages: List[Message]) -> bool:
        """Диалог активен если есть 2-way exchange"""
        if len(messages) < 2:
            return False
        
        senders = {msg.sender_id for msg in messages}
        if len(senders) < 2:
            return False  # От одного пользователя
        
        return True
    
    @staticmethod
    def is_dialog_complete(messages: List[Message], min_exchange_count=2) -> bool:
        """
        Диалог завершён если:
        - 2-way exchange
        - Минимум exchange_count сообщений от каждого
        """
        if not is_dialog_active(messages):
            return False
        
        # Считаем сообщения от каждого
        user_a = messages[0].sender_id
        user_b = next(m for m in messages if m.sender_id != user_a).sender_id
        
        count_a = sum(1 for m in messages if m.sender_id == user_a)
        count_b = sum(1 for m in messages if m.sender_id == user_b)
        
        return count_a >= min_exchange_count and count_b >= min_exchange_count
    
    @staticmethod
    def dialog_metrics(dialog_id: str) -> Dict:
        """Рассчитать метрики диалога"""
        messages = get_messages(dialog_id)
        
        return {
            'started': is_dialog_started(messages),
            'active': is_dialog_active(messages),
            'complete': is_dialog_complete(messages),
            'message_count': len(messages),
            'participant_count': len(set(m.sender_id for m in messages)),
            'duration_seconds': (messages[-1].sent_at - messages[0].sent_at).total_seconds(),
            'first_response_time': calculate_first_response_time(messages),
        }

Таблица для трекинга

Я рекомендую трекировать диалоги в таблице с разными статусами:

CREATE TABLE dialog_analytics AS
SELECT
    dialog_id,
    user_a_id,
    user_b_id,
    created_at,
    message_count,
    CASE 
        WHEN message_count = 0 THEN 'Not started'
        WHEN message_count = 1 THEN 'One-way'
        WHEN message_count >= 2 AND user_a_messages > 0 AND user_b_messages > 0 THEN 'Active'
        ELSE 'Other'
    END as status,
    DATEDIFF(SECOND, first_message_at, last_message_at) as duration_seconds,
    datediff(MINUTE, user_a_first_message, user_b_first_response) as first_response_time_minutes
FROM (
    SELECT
        dialog_id,
        user_a_id,
        user_b_id,
        created_at,
        COUNT(*) as message_count,
        SUM(CASE WHEN sender_id = user_a_id THEN 1 ELSE 0 END) as user_a_messages,
        SUM(CASE WHEN sender_id = user_b_id THEN 1 ELSE 0 END) as user_b_messages,
        MIN(sent_at) as first_message_at,
        MAX(sent_at) as last_message_at,
        MIN(CASE WHEN sender_id = user_a_id THEN sent_at END) as user_a_first_message,
        MIN(CASE WHEN sender_id = user_b_id THEN sent_at END) as user_b_first_response
    FROM messenger_messages
    GROUP BY dialog_id
)

Вывод

"Диалог состоялся" — это определение, которое зависит от:

  1. Технических возможностей (что мы можем измерить)
  2. Бизнес-целей (что нас волнует)
  3. Контекста продукта (маркетплейс vs социалка vs support)

Перед тем как писать SQL — уточни с Product Manager'ом что именно нужно измерять.