Суть
Пользователь → production v1 (текущая) → ответ пользователю
↓ копия запроса
candidate v2 → лог + LLM-judge → сравнение пар
Пользователь всегда получает ответ действующей версии. Кандидат работает «в тень»: его выход сохраняется с correlation id и сравнивается с ответом продакшена по тем же запросам.
Отличие от офлайн-эвалов принципиальное. Golden dataset — это то, что придумали инженеры; живой трафик — то, что реально спрашивают пользователи, включая формулировки, о которых никто не подумал. Отличие от canary — в риске: канарейка уже отдаёт ответы людям, тень нет.
Что сравнивают: четыре сигнала
LLM-судья и метрики оценивают пары ответов по четырём осям — тем же, что мониторятся потом в проде:
| Сигнал | Что смотрим |
|---|---|
| Quality | eval pass rate, вердикт судьи на парах ответов |
| Latency | p99 и время до первого токена под живой нагрузкой |
| Cost | стоимость за запрос — новый промпт дорожает молча, без единой ошибки в логах |
| Safety | срабатывания guardrails, инъекции, уход в fallback |
Третий пункт — типичная слепая зона: регресс по деньгам не выглядит как авария, поэтому его ловят только сравнением (см. Cost Anomaly Alerting).
Цена и границы
Тень не бесплатна: кандидат выполняет настоящие вызовы модели, за которые вы платите, ничего не отдавая пользователю. При миллионе запросов в день десятипроцентное зеркало даёт сто тысяч оценённых пар в сутки — и счёт за них.
Отсюда правила:
- Зеркалить 10–25%, а не весь трафик.
- Ограничивать по времени — режим на 1–2 недели, а не постоянный.
- Не зеркалить побочные эффекты. Если агент вызывает инструменты, изменяющие мир (отправка письма, списание), в тени эти инструменты должны быть замоканы — иначе клиент получит два письма.
Последний пункт в источнике прямо не проговаривался, но следует из механики: копия запроса означает копию всех действий.
Побочные эффекты и объём выборки
Опасение про моки обоснованное: сравнивать надо намерение, а не результат. Если кандидату подсунуть заглушки инструментов, дальнейшее поведение разойдётся с боевым уже на втором шаге — заглушка вернула не то, что вернул бы реальный сервис, и весь остаток трассы становится сравнением с выдумкой. Поэтому теневое сравнение честно ровно до первого вызова с побочным эффектом, и правильная единица сравнения там — какой вызов агент собирался сделать и с какими аргументами, а не что получилось. Совпадение намерений при расхождении результатов означает, что различие в инструменте, а не в агенте.
Практическая разбивка: read-path зеркалится целиком и сравнивается по ответу; write-path останавливается на границе действия и сравнивается по решению. Всё, что за этой границей, тень не проверяет — это уже работа канареечной выкатки (Canary Release LLM), где действия настоящие.
Про объём. Здесь нужно заметно меньше, чем для канареечного вывода, и по конкретной причине: обе версии видят один и тот же запрос, поэтому разброс между пользователями из сравнения уходит и остаётся только разница версий. Парное сравнение экономит выборку на порядок относительно независимых групп из раздела статистики в Canary Release LLM. Считать значимость всё равно надо по доле пар, где вердикты разошлись, а не по средним оценкам обеих версий: усреднение прячет как раз то, ради чего тень запускали.
Чего тень не покажет
Реакцию людей. Пользователи не видят ответов кандидата, поэтому нет ни кликов, ни дизлайков, ни поведенческих метрик — только машинная оценка. Всё, что упирается в «понравилось ли», проверяется уже на канарейке.
Место в конвейере
PR + офлайн-эвалы → Shadow (1–2 недели) → Canary 1→5→20→50% → 100% stable
Shadow — второй этап: после того как кандидат прошёл фиксированный тестовый набор, но до того, как его увидел хоть один живой пользователь.
Связано с
- Canary Release LLM — следующий этап, где кандидат начинает отвечать людям
- Agent Evals — офлайн-проверка, предшествующая тени
- LLM as Judge — инструмент сравнения пар; в тени он безопасен, в hot path нет
- Feature Flags LLM — механизм, которым включается зеркалирование
- Cost Anomaly Alerting — почему сигнал стоимости обязателен в сравнении