Восемь месяцев работы с очередью слияния (merge queue) в GitHub Actions дают понимание, которое никогда не дадут матрицы сравнения функций. Фреймворк может предлагать пятьдесят метрик, великолепные дашборды и ссылки на авторитетные исследовательские лаборатории. Но если он блокирует ваш деплой из-за того, что оценка «vibe check» изменилась с 0,72 до 0,68 на идентичном коде, он хуже, чем бесполезен. Он становится прямой угрозой вашей скорости поставки (shipping velocity).
Это тот фильтр, который упускают большинство обзоров оценки LLM. Они подсчитывают возможности. Но они редко задают единственный вопрос, имеющий значение в очереди слияния: проходит ли этот тест или проваливается ли он абсолютно одинаково при каждом запуске?
Я узнал это на собственном горьком опыте. Я интегрировал шесть open-source фреймворков для оценки LLM в реальный CI-конвейер. Они работали с реальными pull request-ами в продакшене в течение восьми месяцев. Два из них заслужили право остаться в качестве «гейткиперов» (gatekeepers). Остальные были понижены до уровня консультативных дашбордов, перенесены в ночные задания (nightly jobs) или полностью удалены. Урок был жестким и дорогостоящим: детерминированная структура важнее вероятностного качества, когда вы охраняете основную ветку (main branch).
Настоящая задача шлюза слияния (Merge Gate)
CI-шлюз — это не исследовательская среда. Это вышибала. Его единственная цель — посмотреть на конкретное изменение и ответить «да» или «нет». Да, этот PR может войти в основную ветку. Нет, не может. Этот ответ должен приходить за секунды, стоить копейки и никогда не меняться задним числом. Если вы перезапустите тот же конвейер для того же коммита в спокойный вторник и в суматошную пятницу, результат должен быть идентичным.
Именно здесь спотыкается большинство фреймворков для оценки LLM. Их создают дата-сайентисты для дата-сайентистов. Они оптимизированы для получения инсайтов, исследований и нюансированных оценок. Очередь слияния оптимизирована для бинарных решений, скорости и отсутствия нестабильности (flakiness). Эти две цели пересекаются лишь частично.
Почему подход LLM-as-Judge ломает очередь
Инструменты, которые провалили мое тестирование, имели один и тот же конструктивный грех: они слишком сильно полагались на вызовы LLM-as-judge как на основной механизм шлюза.
Промпт LLM-as-judge просит модель оценить результат по шкале от одного до десяти, выбрать лучший из двух ответов или оценить фактическую точность. Этот подход эффективен для понимания трендов качества. Но он — яд для блокирующей проверки в CI. Один и тот же входной сигнал может давать разные оценки в разные дни, потому что температура (temperature), версионность моделей и форматирование промптов вносят шум. Когда эта оценка привязана к жесткому порогу и жесткому коду выхода (exit code), ваша очередь блокируется из-за «призраков».
Сбои быстро накапливаются как лавина. Недетерминированная проверка создает заторы в очереди. Инженеры привыкают перезапускать проверку, пока число не станет благоприятным, что приучает команду игнорировать «красные» сборки. Расходы на токены растут, так как каждый перезапуск сжигает API-кредиты. Хуже всего то, что сигнал теряет смысл. «Красная» сборка должна означать: «вы внедрили баг». Если же она означает: «модель-судья сегодня проснулась привередливой», доверие к системе рушится.
Чем отличаются выжившие
Promptfoo и DeepEval выжили, потому что они относятся к детерминированным проверкам как к первоклассным инструментам (first-class citizens), а к оценкам LLM-судьи — как к второстепенным, неблокирующим сигналам. Они понимают, что шлюзу нужен код выхода, а не число с плавающей запятой, выражающее «мнение».
Promptfoo, выпущенный под лицензией MIT, создан для командной строки. Он выполняет такие проверки (assertions), как соответствие регулярным выражениям, валидация JSON-схемы, проверка на наличие подстроки и точное сравнение строк. В этом нет ничего изысканного. Это просто продвинутые команды grep и jq. Именно поэтому они работают в CI. Регулярное выражение либо совпадает, либо нет. JSON-схема либо проходит валидацию, либо выдает ошибку. Promptfoo возвращает стандартные коды выхода Unix, поэтому GitHub Actions нативно понимает, когда нужно остановить слияние. Он не зависит от языка программирования, так как работает как CLI-инструмент. Вам не нужно устанавливать экосистему Python внутри репозитория Node.js-сервиса только для того, чтобы валидировать выходные данные.
DeepEval, лицензированный под Apache 2.0, — это выбор команд, работающих на Python. Он интегрируется подобно pytest. Вы пишете тесты на знакомом синтаксисе, и сбой естественным образом блокирует весь набор тестов. DeepEval предлагает огромный каталог метрик, но критически важно использовать их осторожно. Для шлюзов полагайтесь на детерминированные или эвристические метрики. Если вы подключаете G-Eval или другие скоринговые методы на основе LLM-судей, оборачивайте их в неблокирующие генераторы отчетов, а не в жесткие проверки (hard asserts). При таком подходе DeepEval дает эргономику фреймворка для тестирования без нестабильности исследовательских блокнотов.
Где место остальным четырем
Четыре фреймворка, которые не выжили в качестве шлюзов, все еще представляют ценность. Просто они должны занимать другие места в вашем инструментарии.
Future AGI (Apache 2.0) поставляется с более чем пятьюдесятью метриками и ориентирован на команды, разрабатывающие собственные SDK. Метрики очень подробные. Проблема в том, что инструмент требует от вас написания собственной среды (harness) для запуска в очереди CI. В исследовательском контексте это разумный компромисс. В очереди слияния (merge queue) каждый слой кастомных связей становится новым источником нестабильности. Это способный движок для оценки, но не готовый контролер.
RAGAS (Apache 2.0) отлично справляется с измерением качества генерации с использованием поиска (RAG). Его метрики достоверности (faithfulness) и релевантности ответов действительно полезны для понимания того, как база знаний работает с течением времени. К сожалению, эти метрики сильно зависят от LLM-судей. Они отлично подходят для ночных проверок качества, которые отправляют отчеты о трендах в Slack. Но они плохие «вышибалы» для pull request. Перенесите RAGAS в свой пайплайн планового анализа, а не в блокировщики слияния.
Arize Phoenix распространяется под лицензией Elastic License 2.0 и находится в совершенно другой плоскости. Он связывает распределенную трассировку с оценкой, обеспечивая наблюдаемость (observability) того, почему модель повела себя именно так. Это нужно, когда вы отлаживаете инцидент в продакшене или отслеживаете галлюцинацию, пытаясь найти проблемный фрагмент поиска. Но вам не нужен инструмент трассировки, который будет решать, может ли ветка фичи младшего разработчика уйти в релиз. Его архитектура создана для получения инсайтов, а не для бинарных фильтров.
MLflow Evaluate (Apache 2.0) унаследовал свою специализацию от инструментов отслеживания экспериментов. Он тяжеловесный. Его добавление в легковесный CI-образ увеличивает время запуска и добавляет зависимости, которые замедляют каждую задачу. Если вам абсолютно необходимо использовать его внутри пайплайна, придерживайтесь его эвристических метрик для структурных проверок. Даже в этом случае вы будете бороться с фундаментальной архитектурой фреймворка. MLflow предназначен для логирования запусков и сравнения экспериментов на протяжении недель. Очереди слияния нужен вердикт менее чем за минуту.
Практические правила для контроля (Gating)
Если вы не вынесете из этого эксперимента ничего другого, запомните эти три правила.
Во-первых, контролируйте структуру, а не ощущения. Вы можете принудительно проверить, является ли вывод валидным JSON. Вы можете проверить наличие обязательных ключей. Вы можете проверить, относится ли метка классификации к разрешенному перечислению (enum). Эти проверки быстрые, дешевые и детерминированные. Вы не можете надежно проверить, является ли резюме «дружелюбным» или является ли перефразирование «творческим». Эти качества относятся к области человеческой проверки или периодической пакетной оценки, а не автоматизированных фильтров.
Во-вторых, если оценка меняется при неизменных входных данных, немедленно понижайте её статус. Запустите ваш набор тестов дважды на одном и том же артефакте. Если какая-либо метрика меняется с «пройдено» на «провалено», она теряет право блокировать слияние. Перенесите её в консультационный дашборд, где вариативность ожидаема и допустима.
В-третьих, уважайте код выхода. Красивый HTML-отчет с красным баннером не остановит слияние. Его остановит ненулевой код выхода. Ваш инструмент оценки должен говорить на родном языке вашей CI-платформы. Стандартный вывод (stdout) предназначен для людей. Коды выхода — для машин.
Итог
Мы всё еще находимся на ранней стадии понимания того, как тестировать приложения на базе LLM. Возникает искушение относиться к оценке как к человеческой системе оценивания: нюансированной, контекстуальной и слегка субъективной. Это работает в научной статье. Это рушится в очереди слияния.
После восьми месяцев продакшн-трафика мой пайплайн теперь использует Promptfoo для проверок структуры и схем в различных сервисах, и DeepEval для поведенческих проверок на стороне Python, которые четко соотносятся с условиями «пройдено/провалено». Всё остальное отправляется на ночные дашборды. Очередь стабильна. Сигнал чист. Команда снова доверяет «красной» сборке.
Вам не нужно больше метрик на контрольном этапе. Вам нужно меньше метрик, которые каждый раз говорят правду.
Основано на оригинальном тестировании и статье, опубликованной на Dev.to. Для дальнейшего обсуждения построения надежных AI-систем присоединяйтесь к сообществу GyaanSetu в Telegram.
