Короткий ответ
#Три часа ночи
Самая дорогая автоматизация выглядит скучно. Она не падает и не выдаёт ошибок. Она аккуратно доводит одно неверное предположение до конца.
Первый шаг разбирает входящие письма и помечает одно как просьбу закрыть аккаунт. Уверенность у этой оценки невысокая, но в следующий шаг она уходит обычной строкой: тип обращения, закрытие. Второй шаг снимает автопродление. Третий создаёт задачу в очереди на удаление данных. Четвёртый отправляет клиенту письмо с подтверждением закрытия. Пятый передаёт статус в бухгалтерскую систему.
В 03:10 всё это закончено. Клиент писал про смену тарифа. Шесть систем теперь согласованно считают, что он ушёл, и каждая из них уверена, потому что получила эти сведения от предыдущей.
Ошибка была одна и была мелкой. Инцидентом её сделали не она сама, а четыре доверчивых шага после неё. Важно, что каждый из этих шагов по отдельности написан правильно и делает именно то, что должен делать.
#Почему цепочка усиливает ошибку
У этого есть механика, и она простая. Первый шаг знал, что не уверен. Второй шаг этого не знал: к нему пришёл результат, а не оценка результата. Дальше уверенность только растёт, потому что каждый следующий шаг видит согласованные данные от нескольких источников, и это выглядит как подтверждение.
Останавливать дешевле всего в двух местах: перед шагом, который уходит наружу, и перед шагом, который нельзя отменить.
Из этого следует главный практический вывод. Оценивать риск нужно по цепочке целиком, а не по отдельному вызову инструмента. Вопрос «что делает этот шаг» безопасный. Вопрос, который стоит задавать, звучит иначе: что произойдёт со всей цепочкой, если первый шаг ошибся, и в какой момент это ещё можно остановить.
#Где цепочка рвётся
Точки остановки ставятся не после каждого шага, иначе автоматизация теряет смысл. Их ставят на переходах между классами действий, и таких переходов всего три.
- Из чтения в запись. Пока цепочка читает и считает, ошибка стоит электричества. Первая запись делает её видимой другим.
- Из внутреннего во внешнее. Письмо, сообщение, вебхук, запись в чужую систему. Отсюда отката не бывает, бывает извинение.
- Из обратимого в необратимое. Деньги, удаление, изменение прав, публикация. Здесь останавливаются всегда, даже если это замедляет.
Полезное упражнение: взять свою самую длинную автоматизацию и отметить на ней эти три перехода. Обычно выясняется, что они происходят раньше, чем кажется, и что между ними нет ни одной проверки.
#Глубина, повторы и бюджет
Три числа, которые должны быть заданы явно. Не потому, что они спасают от ошибки, а потому, что без них цепочка не имеет конца.
-
Максимальная глубина
Сколько шагов допускается в одном прогоне. Цепочка без предела рано или поздно зацикливается: агент видит результат своего предыдущего действия, считает его новым входом и продолжает.
Достигнутый предел это не ошибка, а сигнал. Он должен приводить к остановке и к записи, а не к молчаливому повтору.
-
Бюджет повторов
Сколько раз шаг может быть повторён и с каким интервалом. Бесконечные повторы это не надёжность, а способ превратить одну недоступность внешнего сервиса в тысячу запросов и в дублирующиеся операции.
После исчерпания бюджета задача уходит в разбор к человеку, а не растворяется.
-
Бюджет вызовов инструментов
Сколько всего действий агент может выполнить за один прогон и сколько из них изменяющих. Это то же ограничение объёма, что и лимит на массовую операцию, только по длине, а не по ширине.
Полезно логировать не только сам вызов, но и остаток бюджета: по этому графику видно, что цепочка удлиняется, задолго до инцидента.
#Один и тот же шаг дважды
Любая цепочка с повторами когда-нибудь выполнит один шаг два раза. Сеть отвалилась после отправки запроса, но до получения ответа. Задача была перезапущена. Человек нажал кнопку второй раз. Вопрос не в том, случится ли это, а в том, что получится в результате.
Идемпотентность означает, что повторение шага не создаёт второго следствия. Технически это обычно ключ операции: клиент действия сам придумывает уникальный идентификатор и передаёт его вместе с запросом, а получатель обещает, что второй запрос с тем же ключом не создаст вторую сущность, а вернёт результат первого. Платёжные провайдеры поддерживают это давно, и по той же причине.
Проверка на одну минуту
Возьмите шаг, который отправляет письмо или двигает деньги, и запустите его дважды с одними и теми же входными данными. Если получилось два письма или два списания, идемпотентности нет, и повторы в вашей цепочке работают против вас.
#Перепроверка перед необратимым
Самое дешёвое и самое редкое из всего списка. Перед необратимым шагом главный факт перепроверяется заново, и обязательно другим способом, чем он был получен.
В сцене из начала статьи достаточно было одной проверки: перед отправкой письма о закрытии посмотреть, есть ли в системе подтверждённое действие клиента на закрытие, а не вывод классификатора. Проверка стоит одного запроса, и она разрывает цепочку в том месте, где ущерб ещё не вышел за пределы компании.
Слово «другим способом» здесь не украшение. Перепроверка тем же классификатором с тем же промптом даёт тот же ответ и создаёт ложное ощущение подтверждения. Годится детерминированная проверка: поле в базе, состояние заказа, подпись, действие пользователя, ответ внешней системы.
#Тип шага и способ остановки
| Тип шага | Чем останавливается | Чем откатывается |
|---|---|---|
| Классификация или извлечение | Порог уверенности; при низкой уверенности маршрут к человеку, а не дальше по цепочке | Ничего не нужно откатывать, если следующий шаг не начался |
| Запись во внутреннюю систему | Ключ операции, короткая транзакция, ограничение числа объектов | Обратная запись с указанием причины и исходного значения |
| Постановка задачи в очередь | Отложенный запуск и окно отмены; задача читает состояние заново перед работой | Отмена, пока задача не начата |
| Сообщение наружу | Перепроверка факта другим способом, задержка на несколько минут, подтверждение | Отката нет: только второе письмо с исправлением |
| Деньги | Подтверждение человеком, лимит суммы за сутки | Компенсирующая операция и запись в журнале |
| Запись в чужую систему | Аварийный выключатель по числу аномалий за окно времени | Только компенсирующая операция, если она вообще предусмотрена их API |
Аварийный выключатель в последней строке стоит завести отдельно от всего остального. Правило может быть грубым: если за десять минут цепочка сделала больше N изменений или больше N процентов из них одного типа, она останавливается и ждёт человека. Грубое правило, работающее ночью, полезнее точного, которое кто-то обещал написать.
#Ревью длинной цепочки
Шесть вопросов к автоматизации перед запуском
Проходите по своей самой длинной цепочке, шаг за шагом, с ручкой.
- Где в этой цепочке появляется неуверенность и передаётся ли она дальше хоть в каком-то виде? Если следующий шаг получает только результат, вся цепочка считает его фактом.
- Сколько шагов и повторов допускается, и что происходит при достижении предела? Предел без обработки это тихая остановка, о которой никто не узнает.
- Какие шаги переживут повторный запуск без второго следствия? Это и есть проверка идемпотентности, и она обычно не пройдена.
- Какой факт перепроверяется перед первым необратимым шагом и каким способом? Перепроверка тем же способом не считается.
- Что из совершённого можно отменить и кто это сделает в три часа ночи? Компенсирующая операция должна быть написана заранее, а не придумана в момент.
- Какое правило остановит цепочку без участия человека и по какому признаку? Ночью решение принимает не команда, а порог, который кто-то догадался задать.
#Как это сделано у нас
1ADK сам устроен как цепочка: агент выполняет единицы работы, отправляет результаты, платформа проверяет их и сохраняет. Поэтому те же правила действуют у нас внутри. Повторная обработка одного и того же пакета безопасна и не создаёт вторую копию знаний. Незавершённые файлы живут отдельно и активируются только после проверки, а последняя известная рабочая версия не удаляется первой.
И ещё одна вещь, которая прямо относится к этой теме. Неизвестное у нас остаётся неизвестным: если агент не смог что-то установить, это записывается как пробел, а не превращается в уверенное утверждение. Охват анализа решает, что вообще можно назвать исчезнувшим. Это та же мысль, что и в статье, только применённая к знаниям: неуверенность обязана доезжать до следующего шага вместе с данными.
#Чего это не даёт
Чего это не делает
- Транзакции есть не везде. Во внешних сервисах часто доступна только компенсирующая операция, и иногда её нет вообще.
- Перепроверка стоит времени и денег, поэтому её ставят на необратимых шагах, а не на каждом.
- Точки остановки уменьшают автономность. Это настоящий компромисс, а не бесплатное улучшение.
- Ни одно из этих ограничений не исправляет неверную классификацию. Они ограничивают то, что из неё вырастет.
- Аварийный выключатель, который никто не настроил под реальный трафик, будет срабатывать в пиковый день и приучит команду его отключать.
Возьмите свою самую длинную автоматизацию и посчитайте, сколько необратимых действий она может совершить, пока никто не смотрит. Какое из них вы оставите автоматическим, если рядом поставить цену ошибки?