Перевод статьи о том, почему классические CI/CD-ворота не ловят тихие регрессии в LLM и как baseline-оценки, детектирование дрейфа, shadow-проверки и бюджеты предотвращают инциденты.— Читать дальше «Почему классический CI/CD не справляется с LLM (и какие release gates мы построили, чтобы это исправить)»
Перевод статьи Freddy Daniel Alvarez Pinto из The New Stack, оригинал: https://thenewstack.io/why-cicd-fails-llms/.
Эта статья объясняет, почему классических CI/CD-ворот недостаточно для production-систем на базе ИИ. Автор делится практическим подходом к release gates для LLM-пайплайнов с помощью baseline-оценок, детектирования дрейфа, shadow-проверок и ограничений по стоимости и латентности. Акцент — на профилактике: отлов тихих AI-регрессий до того, как они достигнут пользователей, на основе реальных уроков платформенной инженерии из production-инфраструктуры.
В пятницу днём я задеплоил обновлённый RAG-пайплайн. Все оценки прошли, оценки сходства выглядели отлично, а к утру понедельника система уверенно рекомендовала устаревшие цены, потому что embedding-модель дрейфовала настолько, что предпочитала старые чанки свежим. Ни один алерт не сработал, ни один тест не упал, дашборд был зелёным, а выходные данные — мусором.
В тот момент я перестал доверять зелёному цвету как сигналу к релизу. У нас не было проблемы с деплоем. У нас была проблема с release gates. Классический CI/CD создавался для детерминированного ПО. LLM — вероятностны. Наши ворота тоже должны быть вероятностными.
Классические CI/CD-ворота работают по принципу pass/fail, но LLM поставляют поведение, а не только код, поэтому нужны ворота, отслеживающие дрейф поведения.
Три режима отказа: eval drift (постепенная деградация оценок), distribution shift (реальные запросы отличаются от тестовых) и context poisoning (изменились извлечённые документы, а тесты спрашивают вчерашнее).
Четыре release gates: baseline eval suite, eval drift detection, shadow traffic validation, cost/latency guardrails.
Ворота должны быть простыми и понятными команде, иначе инженеры начнут их обходить.
Eval-датасеты нужно версионировать так же тщательно, как Terraform state: с бэкапами и параноей.
В обычной доставке ПО ворота достаточно просты: unit-тесты прошли, интеграционные тесты прошли, проверки безопасности прошли — деплой. Это бинарно. Сборка зелёная или красная. Эта модель ломается, когда вы поставляете не код, а поведение.
Классический CI/CD создавался для детерминированного ПО. LLM — вероятностны. Наши ворота тоже должны быть вероятностными.
Freddy Daniel Alvarez PintoThe New Stack
Production-система на базе ИИ может сегодня показывать relevance 0,82, завтра 0,79, а на следующей неделе 0,74. Поскольку ни один запуск не пересекает порог отказа, никого не пейджат, хотя пользователи уже получают худшие ответы.
Я больше 20 лет управлял production-инфраструктурой: приватными облаками OpenStack, миграциями баз данных, CI/CD-пайплайнами и mission-critical системами. В классическом DevOps мы усвоили: сервер, который сообщает о 99,9% аптайма при потере 0,1% финансовых транзакций, — не здоров. Он скрывает баг. Оценки LLM работают так же. Агрегированные метрики маскируют локальные отказы.
Первый режим отказа — eval drift: оценки деградируют постепенно, но недостаточно, чтобы упасть по жёсткому порогу. Второй — distribution shift: реальные пользователи задают короткие, беспорядочные, странные вопросы, о которых ваш чистый eval-датасет и не думал. Третий — context poisoning: ваши извлечённые документы изменились, а тесты всё ещё спрашивают вчерашние вопросы.
Традиционный CI/CD gate спрашивает: «Тест прошёл?» LLM release gate спрашивает: «Поведение осталось в допустимом диапазоне?» Обычный gate сравнивает ожидаемый вывод с фактическим. AI CI/CD gate сравнивает кандидатное поведение с историческим и production-поведением, а также со стоимостью, латентностью и риском. Традиционные ворота быстро отсеивают исключения. LLM release gates внимательно отсекают дрейф.
Традиционные ворота быстро отсеивают исключения. LLM release gates внимательно отсекают дрейф.
Freddy Daniel Alvarez PintoThe New Stack
Это различие важно, потому что production AI-системы гниют, а не взрываются. Я создал llm-eval-drift-release-gates-AGENT, потому что хотел ворота, которые относятся к AI-релизам как к инфраструктурным релизам: измеримым, повторяемым и, по возможности, скучным. Скука недооценена. Она позволяет инженерам спать.
llm-eval-drift-release-gates-AGENT
Первое ворото — baseline eval suite. Он прогоняет фиксированный датасет по кандидатному пайплайну и оценивает relevance, faithfulness, safety, groundedness и любые доменные проверки, которые вам важны. Цель не в том, чтобы доказать, что модель идеальна. Цель — поймать регрессии до того, как они станут историями от клиентов.
Вот упрощённая Python-структура из паттерна, который я использую:
from enum import StrEnum
from pydantic import BaseModel, Field
class GateStatus(StrEnum):
PASS = "pass"
WARN = "warn"
BLOCK = "block"
class EvalResult(BaseModel):
run_id: str
status: GateStatus
scores: dict[str, float] = Field(default_factory=dict)
drift_detected: bool = False
reason: str | None = None
Использование StrEnum сохраняет enum в виде строк без множественного наследования. Это важно в релизном инструментарии, потому что значения статусов часто логируются, сериализуются, сравниваются в CI или передаются в дашборды.
Это ловит очевидные отказы: коллапс relevance, падение faithfulness, небезопасные выходы или искажённые результаты оценщика. Если отказ жёсткий, пайплайн блокируется немедленно; если срабатывает предупреждение, требуется ручное одобрение. Мне не нравятся тихие предупреждения: это будущие инциденты в хорошей рубашке.
Второе ворото — детектирование дрейфа оценок. Фиксированного порога недостаточно. Если relevance падает с 0,91 до 0,86, ваш порог 0,80 говорит, что всё в порядке. Ваши пользователи могут не согласиться.
Поэтому я сравниваю текущие оценки с rolling baseline из недавних деплоев:
def detect_score_drift(
current: dict[str, float],
baseline: dict[str, float],
max_drop_pct: float = 5.0,
) -> tuple[bool, list[str]]:
failures: list[str] = []
for metric, base_value in baseline.items():
current_value = current.get(metric)
if current_value is None:
failures.append(f"Missing metric in current run: {metric}")
continue
drop_pct = ((base_value - current_value) / base_value) * 100
if drop_pct > max_drop_pct:
failures.append(f"{metric} dropped {drop_pct:.2f}%")
return bool(failures), failures
Eval suite обнаружил 6% падение relevance в 23:00 в четверг. Без него это падение достигло бы 200 пользователей к понедельнику. Ворото не знало бизнес-контекста. Ему это и не нужно. Оно знало, что кандидат хуже последнего известного хорошего релиза.
Третье ворото — shadow traffic validation. Перед полным раскатом я направляю небольшой процент реального трафика на кандидатный пайплайн. Пользователи всё ещё получают production-ответ, но система записывает кандидатный вывод для сравнения.
Это canary-deployment, применённый к ИИ. Я использовал тот же паттерн в инфраструктурных раскатах, включая идеи из моего репозитория eks-canary-deployment-pipeline. Разница в том, что для LLM вы сравниваете не только HTTP 200. Вы сравниваете качество ответа, извлечённый контекст, латентность и причины, по которым кандидат расходится с production подозрительным образом.
Judge-модель может быть полезна, но я не даю ей быть единственным авторитетом. Она даёт сигнал. Release policy принимает решение.
Четвёртое ворото — стоимость и латентность. Модель, которая идеально оценивается, но стоит в 3 раза дороже, — не валидный релиз. RAG-пайплайн, который добавляет две секунды латентности, тоже не готов. Эта же логика легла в основу моей работы enterprise-rag-guardrails-costops.
enterprise-rag-guardrails-costops
def enforce_runtime_budget(
total_tokens: int,
latency_ms: int,
max_tokens: int = 8000,
max_latency_ms: int = 2500,
) -> None:
if total_tokens > max_tokens:
raise RuntimeError(f"token budget exceeded: {total_tokens}")
if latency_ms > max_latency_ms:
raise RuntimeError(f"latency budget exceeded: {latency_ms}ms")
Когда это ворото не проходит, система блокирует релиз или направляет его на ручное одобрение. Я научился не торговаться о латентности во время деплоя. Она всегда побеждает позже.
Эти четыре ворота не делают деплой LLM идеальным. Они делают сложнее поставку бессмыслицы под зелёным бейджем.
Release gate не должен быть отдельным научным проектом. Если ваша платформенная команда уже использует GitHub Actions или GitLab CI, LLM-пайплайн деплоя должен вписаться в этот workflow.
Паттерн намеренно скучный:
YAML
pipeline:
- build
- unit-tests
- eval-baseline
- drift-check
- shadow-validate
- cost-guard
- deploy
- post-deploy-monitor
Такую форму я расширил из своей работы devsecops-pipeline-github-actions. Соберите приложение. Запустите детерминированные тесты. Запустите baseline eval suite. Сравните с rolling baselines. Провалидируйте на shadow-трафике. Проверьте бюджеты стоимости и латентности. Деплойте, только когда все ворота согласны.
Здесь guardrails MLOps становятся полезны платформенным инженерам. Вам не нужна отдельная религия деплоя. Вам нужен один дополнительный набор проверок внутри пайплайна, которому команда уже доверяет.
Вот небольшая Python-точка входа, которую можно вызывать из CI. Важная деталь: она падает аккуратно, когда нужные отчёты отсутствуют или искажены, потому что сырые stack trace — не стратегия релиза.
import json
import sys
from pathlib import Path
def load_scores(path: str) -> dict[str, float]:
payload = json.loads(Path(path).read_text(encoding="utf-8"))
return {key: float(value) for key, value in payload.items()}
current = load_scores("reports/current_eval.json")
baseline = load_scores("reports/baseline_eval.json")
drifted, reasons = detect_score_drift(current, baseline, max_drop_pct=5.0)
if drifted:
print("LLM release gate blocked:", "; ".join(reasons))
sys.exit(1)
print("LLM release gate passed")
Лучший release gate — тот, которым команда реально пользуется. Если для настройки нужна PhD по ML, это не ворота. Это стена. Я сейчас получаю степень PhD в области безопасности облачных вычислений, и даже я не хочу процесс деплоя, которому каждую пятницу нужна диссертация. Ворота должны быть достаточно простыми, чтобы запускаться в CI, достаточно строгими, чтобы блокировать плохие релизы, и достаточно гибкими, чтобы не стать офисным украшением.
Последняя часть далась мне дольше, чем хотелось бы признать.
Моя первая версия была болезненно строгой. Каждый деплой блокировался. Eval suite жаловался на мелкие изменения формулировок, безобидные различия форматирования и пограничные падения оценок. Команда начала обходить ворота полностью. Это была моя вина.
Ворота, которые блокируют всё, хуже, чем никаких ворот. По крайней мере, без ворот люди знают, что рискуют. С плохими воротами они учатся игнорировать систему.
Ворота, которые блокируют всё, хуже, чем никаких ворот. С плохими воротами люди учатся игнорировать систему.
Freddy Daniel Alvarez PintoThe New Stack
Моя вторая ошибка — оценивать только на синтетических запросах. Они были чистыми, полными и вежливыми. Реальные пользователи такими не бывают. Реальные пользователи печатают три слова, ошибаются в названиях продуктов, вставляют фрагменты и ожидают, что система поймёт контекст, который они не дали.
Оценки выглядели отлично. Production — нет.
Моя третья ошибка — не версионировал eval-датасет. Когда я добавлял новые крайние случаи, я терял возможность сравнивать старые релизы с новыми baselines. Теперь я версионирую eval-наборы так же, как версионирую Terraform state: внимательно, с бэкапами и с лёгкой паранойей.
Строить это из Кочабамбы, Боливия, тоже повлияло на дизайн. У меня не было неограниченных облачных кредитов на огромные eval-прогоны. Это заставило оптимизировать сэмплирование, кешировать вызовы judge-модели и разделять быстрые PR-проверки от более тяжёлых ночных.
Ограничения рождают лучшую инженерию. Раздражает, но правда.
В классическом ПО мы поставляем код и проверяем поведение. В ИИ мы поставляем поведение и проверяем соответствие. Release gates — это то, как мы закрываем этот разрыв.
LLM release gates не уберут неопределённость из production AI-систем. Ничто не уберёт. Но они дают платформенным командам практический способ поймать eval drift, distribution shift, context poisoning, скачки стоимости и регрессии латентности до пользователей.
Это важно для надёжности агентных workflow. Это важно для AI CI/CD. Это важно, потому что «все тесты прошли» больше не достаточно.
Я создал llm-eval-drift-release-gates-AGENT, потому что устал от зелёных пайплайнов, которые мне лгали. Репозиторий — open-source, offline-first референсная реализация этого паттерна release gates. Он намеренно достаточно мал, чтобы изучить, запустить, сломать и ужесточить в своём CI/CD.
Репозиторий: https://github.com/fdaniel-alvarez-dev/llm-eval-drift-release-gates-AGENT. Форкайте, ломайте, улучшайте. Худший release gate — тот, который вы никогда не построили.
Часто задаваемые вопросы
1
Чем LLM release gate отличается от обычного CI/CD gate?
Обычный CI/CD gate проверяет, прошли ли тесты. LLM release gate проверяет, осталось ли поведение модели в допустимом диапазоне: сравнивает кандидат с baseline, production и историческими релизами, а также учитывает стоимость, латентность и риск.
2
Что такое eval drift?
Это постепенное снижение оценок качества (relevance, faithfulness и других), которое не достигает жёсткого порога отказа, но со временем делает ответы хуже. Ворота детектирования дрейфа сравнивают текущие оценки с rolling baseline.
3
Почему shadow traffic важна для LLM?
Потому что реальные пользовательские запросы отличаются от синтетических тестов. Shadow-проверка позволяет сравнить кандидатный пайплайн с production на реальном трафике без риска показать пользователям плохой ответ.
4
Какие метрики отслеживать в cost/latency gate?
Количество токенов, латентность ответа, общую стоимость запроса. Модель с идеальными оценками, но в 3 раза дороже или медленнее на 2 секунды, — не готова к релизу.
5
Почему нельзя делать ворота слишком строгими?
Если ворота блокируют каждый деплой, команда начнёт их обходить. Ворота, которые блокируют всё, хуже отсутствия ворот, потому что создают ложное чувство контроля.
Классический CI/CD создавался для детерминированного ПО, а LLM — вероятностны. Поэтому release gates для ИИ должны отслеживать не только прохождение тестов, но и дрейф поведения: baseline-оценки, детектирование дрейфа, shadow-трафик, стоимость и латентность.
Ворота должны быть простыми, понятными и настраиваемыми. Их задача — не идеальная модель, а предотвращение тихих регрессий, которые иначе достигнут пользователей. Версионируйте eval-датасеты, проверяйте на реальном трафике и не забывайте про бюджеты. Так вы сможете доверять зелёному цвету пайплайна снова.
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | Будущее мошенничества уже здесь: как LLM превращают целевые атаки в массовый продукт | -2 | 7 | 25-06-2026 |
| 2 | Fine-tuning LLM в 2026: гид по LoRA, QLoRA и полному дообучению | 0 | 7 | 25-06-2026 |
| 3 | AI-агенты сломали управление жизненным циклом идентификации. Что с этим делать | 0 | 7 | 04-07-2026 |
| 4 | GitLab представила концепцию agentic infrastructure | 0 | 5 | 25-06-2026 |
| 5 | Как ограничить расходы на OpenAI API, чтобы ИИ-агенты не сожгли бюджет | 0 | 8 | 04-07-2026 |
| 6 | Как объединить инфраструктуру 35 продуктов в единую платформу данных | 0 | 7 | 26-06-2026 |
| 7 | Технический долг в деньгах: как считать ROI рефакторинга легаси-системы | 0 | 7 | 30-06-2026 |
| 8 | Deckhouse Conf 2026: зачем инженерам самописный SDN и виртуалки в Kubernetes | 0 | 5 | 02-07-2026 |
| 9 | Почему сеньоры не могут донести свою экспертизу | 0 | 5 | 03-07-2026 |
| 10 | Git в Telegram: как я избавился от JSON, победил Markdown и получил security by design | 5 | 7 | 02-07-2026 |