КЛАСТЕР 03 · ПАМЯТЬ И СОСТОЯНИЕ ИНВАРИАНТЫ ~15 МИНУТ
День 14 · из 35

Инварианты
и проверки

В Дне 13 — машина состояний агента. Состояния, переходы, явная структура. Но недостаточно описать, как агент должен ходить. Нужно описать, чего никогда не должно случаться. Это и есть инварианты — самый недооценённый инженерный артефакт.

Суть урока

Инвариант — это утверждение, которое должно быть истинным всегда, на любом шаге работы агента. «В состоянии "ожидание оплаты" платёж клиента не списан». «Если переписка получила тег "жалоба" — она не уйдёт в архив автоматически». Инварианты — это «защитные перила» вокруг состояний и переходов. Без них умная модель может вести агента «логически» — и нарушить правила, которые ты считал очевидными. Сегодня — как формулировать инварианты, где их проверять и что делать, если нарушены.

Почему недостаточно описать переходы

В Дне 13 мы построили машину состояний: список состояний, список переходов, условия каждого перехода. Это даёт агенту структуру. Кажется — достаточно?

Не совсем. Машина состояний описывает что разрешено: «из «собираю параметры» можно перейти в «подтверждение заказа», если все обязательные поля заполнены». А инвариант описывает что недопустимо никогда: «в состоянии «подтверждение заказа» сумма заказа не может быть отрицательной», «заказ не уходит в обработку, если клиент не подтвердил итоговую цену».

Это разные вещи. Машина состояний — это маршруты. Инварианты — это правила, действующие на всех маршрутах одновременно.

LLM-агент может найти неожиданный путь к цели — формально допустимый по схеме переходов, но нарушающий неявные правила. Инварианты — это способ сделать эти правила явными.

Пример: банковский ассистент

Состояния: «готов помочь»«уточняю операцию»«проверяю баланс»«выполняю перевод»«готово». Переходы выглядят логичными.

А теперь смотри: модель решила, что для выполнения перевода нужно сначала «облегчить пользователю задачу» — и в состоянии «уточняю операцию» сама догадалась, как зовут получателя, и предлагает «вы наверное хотели Ивану отправить?». Технически нарушения схемы нет. Но инвариант «получатель назван пользователем явно, агент не угадывает» — нарушен. И это в банковской системе — катастрофа.

Машина состояний об этом не «знает». Это неписаное правило, которое нужно сделать писаным.

Что такое инвариант

Инвариант — это предикат, который должен быть истинным всегда. Формально — булево выражение от состояния системы, проверяемое до и после каждого действия агента.

Хороший инвариант обладает тремя свойствами:

  • Сформулирован как утверждение, а не как процедура. «В корзине не больше 50 товаров» — да. «Проверять, что корзина меньше 50 товаров» — нет, это уже процедура.
  • Проверяемый автоматически. Без участия LLM. Программно, за миллисекунды. «Поле amount > 0» — да. «Ответ модели звучит вежливо» — нет, проверить программно невозможно (точнее, для проверки нужна другая LLM, и это уже другой класс).
  • Однозначный. Либо истинно, либо нет — без серой зоны. «Пользователь подтвердил действие словом «да», «yes», «подтверждаю» или «ок»» — да, чётко. «Пользователь согласен» — нет, размыто.

Где проверять

Инварианты проверяются в трёх местах:

  • Перед действием (precondition) — «я собираюсь сделать X. Можно ли его сейчас делать?». Если инвариант сломается после действия — действие не выполняется.
  • После действия (postcondition) — «я сделал X. Не сломалось ли что-то?». Если инвариант нарушен — это баг или попытка обхода: нужен откат или эскалация.
  • Постоянно (system invariants) — фоновые проверки, которые держат всё хозяйство в порядке. «Сумма всех активных заказов не превышает кредитного лимита пользователя».
i
Инвариант — это не «инструкция модели»
Самая частая ошибка: думать «я напишу в system prompt «не делай X» — и модель не будет». Это не инвариант, это надежда. Настоящий инвариант — это проверка в коде, которая срабатывает, даже если модель «забыла» инструкцию. Инструкция в промпте — это первая линия обороны. Инвариант — вторая, гарантирующая.

Три типа инвариантов

На практике инварианты делятся на три уровня — по строгости и последствиям нарушения:

Уровень 1 — Строгие (hard)

Если нарушены — действие не выполняется ни при каких обстоятельствах. Никаких «модель умная, она разберётся». Жёсткое предохранение.

Строгий
Перевод средств выполняется только после явного подтверждения пользователем суммы и получателя

Не подтвердил — операция не идёт, никакие «но я же уже спрашивал в прошлой реплике» не принимаются. Это первое, что появляется в банковском или платёжном агенте.

Строгий
Агент не отправляет сообщения от имени пользователя без его явной команды на отправку

Может составить черновик, показать его — но не нажмёт «отправить». Простой инвариант, который спасает email-агентов от катастрофы.

Строгий
Параметры функции, помеченной как «требует подтверждения», заполнены только из ввода пользователя

Модель не «догадывается» о значениях. Если пользователь не назвал — спрашивает, а не выдумывает. Защищает от ситуации «модель решила, что заказ нужен на 1000 единиц, потому что «обычно столько берут»».

Уровень 2 — Средние (medium)

Нарушение — это повод остановиться и спросить пользователя. Не катастрофа, но и не «прокатит автоматом».

Средний
Если стоимость операции превышает X — требуется явное подтверждение пользователем

Обычная покупка проходит без переподтверждения. Большая — останавливаем и спрашиваем. Защищает от ошибок в нолях и от случайных дорогих действий.

Средний
Если задача длится больше N итераций без видимого прогресса — эскалация к человеку

Защищает от «агент крутится по кругу» (см. День 6). Лучше показать «я не могу решить, помоги», чем сжечь $50 на бесконечных попытках.

Уровень 3 — Мягкие (soft)

Нарушение — это сигнал тревоги для тебя как разработчика. Действие может выполниться, но в логах появляется флаг. Это твоя «нервная система»: ты узнаёшь о странном поведении до того, как это станет проблемой пользователя.

Мягкий
Агент не должен использовать один инструмент более 5 раз подряд в одной задаче

Не катастрофа — может быть, задача правда требует много вызовов. Но 99% случаев это значит — модель буксует. Лог сигнала летит в мониторинг, ты замечаешь паттерн и тюнишь промпт.

Мягкий
Финальный ответ агента должен содержать упоминание ключевых данных, переданных пользователем

Если пользователь назвал имя, дату и сумму — финальный ответ их явно упоминает (не «я выполнил то, что ты просил»). Помогает ловить случаи, когда модель «сделала», но непонятно что именно.

Соотношение типов

В здоровой системе пропорция примерно такая:

~20%

Строгие

Связаны с деньгами, личными данными, необратимыми действиями. Их мало, но они спасают от катастроф.

~30%

Средние

Защита от ошибок и зацикливания. Эскалация в нужный момент.

~50%

Мягкие

Твои «датчики». Не блокируют, но дают видеть поведение. С них всё начинается — и часть из них потом «промотируется» в средние или строгие.

Решения AI-инженера

Развилка 1. Откуда брать инварианты

Не «придумывать в кабинете». Лучший источник инвариантов — это реальные ошибки. Когда агент сделал что-то не то — задаёшь себе вопрос: «какое правило я считал само собой разумеющимся, которое модель нарушила?». Это правило — и есть кандидат в инварианты.

Поэтому первый рабочий набор инвариантов появляется после того, как агент уже работает и допустил несколько ошибок. Преждевременная попытка перечислить все возможные инварианты заранее обычно даёт переусложнённую и неработающую систему.

Развилка 2. Кто проверяет инварианты

Два варианта:

  • Простой код (детерминированно) — для всего, что можно выразить как условие. «amount > 0», «state == "confirmed"». Быстро, надёжно, без сюрпризов.
  • Отдельная LLM (модель-судья) — для нечётких правил, где нужно понимание языка. «Ответ не содержит обещаний, которые система не может выполнить». Дорого, медленно, но иногда — единственный способ.

Правило: всё, что можно проверить кодом — проверяй кодом. LLM-судья — только когда правда без него не обойтись. У него есть свои недостатки (сам может ошибаться, дорог, требует своих инвариантов).

Концептуальные грабли

«В system prompt написал «никогда не отправляй email без подтверждения» — этого достаточно». Не достаточно. LLM статистическая: 99 раз из 100 будет делать как сказано, на 100-м — нарушит из-за необычного входа. Если 1% ошибок — это списанные деньги или отправленные не тем письма, это катастрофа.

Инструкция в промпте + инвариант в коде = эшелонированная оборона. Просто инструкция — это «надеюсь, всё будет хорошо».

«Я добавлю строгих инвариантов на всё — будет максимально безопасно». Получишь систему, в которой агент постоянно блокируется на разумных действиях. Пользователь видит «не могу выполнить» 30 раз в день — и уходит.

Строгий инвариант — это «стоп-кран». Стоп-кранов в поезде немного. Они для редких критических ситуаций. На большинство «странностей» хватит мягкого инварианта (запиши в лог) или среднего (спроси пользователя).

Инвариант сработал — действие заблокировано — пользователь увидел ошибку — на этом всё. Ты не знаешь, что инвариант сработал, как часто, при каких входах.

Каждое срабатывание инварианта — это сигнал. Может быть, модель пытается сделать недопустимое из-за плохого промпта. Может быть, инвариант слишком жёсткий и блокирует разумное действие. Может быть, реальная атака. Без логов — ничего из этого не разобрать.

«Поставлю отдельную модель, которая проверяет ответы основного агента, и буду полагаться на неё». Окей, но судья сам тоже модель. Если у него нет своих чётких правил — он будет ошибаться так же, как основная модель.

LLM-судья работает, только если у него: (а) узкая чётко описанная задача проверки; (б) свой набор правил, по которым он судит; (в) логирование и периодическая проверка точности — на размеченных кейсах.

Практика

Эксперимент 01 · Найди свои инварианты

Для агента из Дня 13

Возьми машину состояний, которую ты построил в Дне 13 (бот для бронирования или что-то своё). Задай себе вопрос: «что в этой системе никогда не должно произойти?». Запиши 5-7 инвариантов. Классифицируй каждый: строгий, средний, мягкий. Это даст тебе чувство, как смотрит на агента инженер с инвариантами в голове.

Эксперимент 02 · Реверс-инжиниринг продакшен-систем

Найди инварианты в чужом продукте

Открой банковское приложение или ChatGPT. Попробуй подсмотреть, какие инварианты у них есть. В банке: что приложение требует подтверждения? В ChatGPT: где появляется «я не могу это сделать»? У каждого продукта свой профиль инвариантов, и понимание чужих — отличный способ нарабатывать интуицию для своих.

Эксперимент 03 · Преврати ошибку в инвариант

Вспомни, как тебя «обмазала» AI-система

Вспомни случай, когда LLM-ассистент сделал что-то не то: что-то отправил без спроса, что-то напридумывал, что-то пропустил. Запиши, какое правило он нарушил. Сформулируй это как инвариант — проверяемое утверждение. Это самое реальное упражнение, потому что инварианты в проде именно так и появляются.

Что в Дне 15
Финал кластера. У нас есть машина состояний и инварианты. Дальше — контролируемые переходы: когда передавать управление человеку, как делать чек-листы перед опасными действиями, паттерны human-in-the-loop. Это то, что превращает «агента-эксперимент» в «систему, которой можно доверять».