Замена LLM-модели в чат-боте маркетплейса: как спланировать A/B
Маркетплейс хочет заменить LLM-модель в чат-боте поддержки покупателей. Текущая модель — топовая (типа GPT-4), дорогая. Новая — в 3 раза дешевле, по бенчмаркам чуть слабее на общих задачах, но возможно достаточная для support-сценариев маркетплейса. Решение нужно принимать на данных, не на бенчмарках.
Простой план: A/B-тест 50/50, метрика CSAT (1-5 stars после диалога). Если новая модель не статистически хуже текущей — мигрируем, экономим деньги. Если хуже — оставляем текущую.
Аналитик должен ответить на три вопроса:
- Сколько диалогов нужно для статистически достоверного решения?
- Какие метрики кроме CSAT нужны, чтобы решение было правильным?
- Что специфично для A/B на LLM-моделях, чего нет в обычном A/B-тесте на UX-фиче?
Эта страница даёт ответы на все три. Симулятор посередине позволяет покрутить параметры и увидеть, как меняется требуемый sample size при разных предположениях.
Что отличает A/B на LLM от обычного A/B
Главное содержание этого кейса. Три специфические угрозы, которых нет в обычном A/B-тесте на UX или продуктовой фиче.
Угроза 1. Stochastic treatment
В обычном A/B treatment детерминирован: пользователь видит фичу A или фичу B. Один и тот же пользователь, тот же контекст, тот же запрос — увидит то же самое. Variance в outcome берётся из вариативности пользователей и контекстов, не из самой treatment.
В A/B на LLM треатмент сам стохастичен. При temperature больше нуля модель даёт разные ответы на тот же запрос. Это не баг, это feature — модель так устроена. Но это добавляет variance, которой не было в стандартной формуле sample size.
Что это значит на практике: тот же пользователь с тем же вопросом получает разный ответ при повторном обращении. CSAT по диалогу зависит не только от того, какая модель ответила, но и от того, как именно она ответила в этот раз. Это дополнительный шум, который маскирует разницу между моделями.
Численно: если variance модели добавляет 30% к общей дисперсии, требуемый N вырастает примерно в 2 раза. При 50% — в 4 раза. Это в формуле напрямую: N пропорционально σ². Игнорировать stochasticity — значит планировать в 2-4 раза меньше выборки чем нужно.
Угроза 2. Survivorship через handoff
Стандартная метрика — CSAT измеряется в конце диалога. Но не все диалоги доходят до конца — часть переключается на живого оператора (handoff). Эти диалоги либо вообще не получают CSAT, либо получают CSAT уже от человека, а не от бота.
Что это меняет в анализе: если новая модель чаще переключает на человека (она хуже справляется и быстрее сдаётся), то на CSAT мы видим только «успешные» диалоги новой модели — те где она дошла до конца. Эти диалоги в среднем легче, и CSAT по ним может быть даже лучше чем у старой модели. А общее качество новой модели хуже — её просто меньше «допускали» до конца.
Это classical survivorship bias. Если измерять только CSAT — можно прийти к выводу, что новая модель лучше, и переключиться на неё, при этом увеличив нагрузку на живых операторов в несколько раз.
Что нужно: мониторить handoff rate как параллельную метрику. Если handoff в новой модели сильно вырос, выигрыш по CSAT — иллюзия selection bias.
Угроза 3. Multi-metric trade-offs
CSAT — одна метрика. Но решение про модель — это multi-objective задача:
- CSAT (качество для пользователя)
- Handoff rate (нагрузка на людей)
- Average tokens per session (стоимость инференса)
- Time-to-resolution (скорость)
- Cost per resolved ticket (общая экономика)
Новая модель может выигрывать в CSAT, проигрывать в handoff и tokens. Чистый эффект на бизнес — сумма этих изменений в денежных терминах. Решение принимается не по одной звёздочке CSAT, а по общему ROI.
Это не уникально для LLM (любое решение про продукт — multi-objective), но для LLM особенно опасно: разница в стоимости моделей в 3-10 раз делает второстепенные метрики первостепенными. Замена дорогой модели на дешёвую при равном CSAT — может всё равно быть провалом, если handoff вырос вдвое.
Что нужно: считать total cost per resolved ticket как итоговую метрику решения. CSAT — один из компонентов, не вердикт.
Симулятор: sample size для A/B на LLM-моделях
Покрути параметры и посмотри, как меняется минимальное число диалогов на группу. Главное отличие от обычного power calculator — поправка на stochasticity модели.
Power curve
Синяя линия — с учётом stochasticity модели. Серая — стандартный расчёт. Горизонтальная линия — целевая power.
Распределения CSAT
Две модели с разницей в среднем = effect size. Variance включает stochasticity. Чем больше σ — тем сильнее перекрываются распределения, тем труднее различить.
Что показывает симулятор
- Без stochasticity (slider в 0) — N расчитывается по классической формуле, как для обычного A/B.
- Реалистичная stochasticity 30% (default) — N в 1.4× больше. Это типично для GPT-class моделей с temperature 0.7.
- Высокая stochasticity 50%+ — N в 2× больше. Это модели с большой вариативностью ответов или с fallback-логикой между несколькими retries.
Главный takeaway: для realistic effect size (0.15 stars) вам нужно ~850 диалогов на группу. Большинство команд закладывают 200-300 — и не видят разницы между моделями, делая вывод что они «эквивалентны». На самом деле — просто не хватает выборки.
Полный eval pipeline
Теперь практическое: что именно собирать в коде и как агрегировать. Это рабочий шаблон для аналитика, который запускает A/B на LLM-модели в продакшене.
Что логировать на уровне диалога
Минимальный набор полей в логе каждого диалога:
dialog_id — уникальный ID
user_id — кто пользователь (для дедупликации
и кластеризации SE)
arm — A или B (какая модель)
started_at — время начала
ended_at — время окончания
end_reason — resolved, handoff, abandoned, timeout
csat — 1-5, если пользователь оценил
num_turns — сколько раундов диалога
tokens_total — суммарно токенов на сессию
resolved_flag — бинарный, успешно ли решена проблема
(нужен для cost-per-resolved)
category — тип запроса (доставка, возврат,
и т.д.) — для гетерогенности
Что считать на уровне эксперимента
Базовая SQL-агрегация:
SELECT
arm,
COUNT(*) AS n_dialogs,
AVG(csat) FILTER (WHERE csat IS NOT NULL) AS csat_avg,
COUNT(csat) * 1.0 / COUNT(*) AS csat_response_rate,
AVG(end_reason = 'handoff'::TEXT) AS handoff_rate,
AVG(resolved_flag::INT) AS resolution_rate,
AVG(tokens_total) AS tokens_avg,
AVG(EXTRACT(EPOCH FROM ended_at - started_at)) AS duration_avg
FROM dialogs
WHERE experiment_id = 'llm_swap_q2'
AND started_at >= '2026-04-01'
GROUP BY arm;
Это даёт baseline-таблицу: что показывает каждая модель по всем ключевым метрикам.
Что считать на уровне дашборда
Дашборд показывает:
- CSAT mean per arm + 95% CI, с поправкой на stochasticity
- Handoff rate per arm + CI
- Resolution rate per arm + CI
- Cost per resolved ticket per arm (комбинированная)
- Heterogeneity: CSAT в разрезе category (топ-5 категорий)
Псевдокод финального вердикта:
def decision(arm_A, arm_B):
# Главный вопрос: total cost per resolved ticket
cost_A = (arm_A.tokens_cost +
arm_A.handoff_rate * COST_PER_HUMAN_HANDOFF)
/ arm_A.resolution_rate
cost_B = (arm_B.tokens_cost +
arm_B.handoff_rate * COST_PER_HUMAN_HANDOFF)
/ arm_B.resolution_rate
# CSAT не должен сильно падать
csat_acceptable = (arm_B.csat >= arm_A.csat - 0.10)
# Statistical significance с поправкой на stochasticity
sig = test_diff(arm_A, arm_B, account_for_stochasticity=True)
if sig and cost_B < cost_A and csat_acceptable:
return 'switch_to_B'
elif sig and cost_A < cost_B:
return 'keep_A'
else:
return 'inconclusive_need_more_data'
Это не «выкатываем по CSAT». Это решение с явным учётом trade-off между качеством и стоимостью, и с честной обработкой «неубедительного» исхода.
Что писать в финальный отчёт
По образцу Module 8 квазиэкспериментов — семь шагов:
- Контекст и вопрос
- Дизайн (A/B 50/50, primary metric = cost per resolved ticket, secondary = CSAT, handoff, tokens)
- Допущения (treatment экзогенен — рандомизация на уровне пользователей; stochasticity модели учтена в sample size)
- Проверки (balance check, sample ratio mismatch, handoff sanity)
- Оценка (главные цифры с CI)
- Ограничения (короткое окно, конкретный сегмент пользователей, эффект на новых vs returning)
- Рекомендация (migrate / keep / continue с планом)
Что делать дальше
Минимальные шаги для аналитика который собирается запустить A/B на LLM-моделях:
- Прежде чем запускать тест — посчитать sample size с поправкой на stochasticity. Симулятор выше делает это. Если получается N > 10× вашего недельного трафика — ваш тест займёт месяцы, и нужно либо принимать меньший effect size как минимальный значимый, либо искать другой способ оценки (offline eval на golden set).
- Выбрать primary metric до начала теста. Не CSAT по умолчанию. Подумать что значит «лучше» для бизнеса — обычно это cost per resolved ticket, а не одна звёздочка пользователя.
- Логировать handoff и tokens с первого дня. Если их нет в логах — CSAT-only тест даст неполную картину, и ретроспективно собрать handoff не получится.
- Договориться с командой что считается success. Должна ли новая модель быть строго лучше старой, или достаточно «не хуже на X% при той же стоимости»? Это политическое решение, и его лучше сделать до того как видеть данные.
- Запланировать heterogeneity-анализ по категориям запросов. Новая модель может проигрывать на сложных кейсах (возвраты, спорные доставки) и выигрывать на простых (FAQ, статус заказа). Решение по среднему CSAT может скрывать важные различия по сегментам.
Связь с курсом
Этот кейс опирается на понятия из курса квазиэкспериментов:
- Модуль 1: causal inference — selection bias и почему рандомизация важна
- Модуль 7: валидность — проверки эксперимента
- Модуль 8: narrative — структура финального отчёта