Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Долговечное выполнение

Назначение

Почему агент должен записать, что он собирается сделать, до того как это сделает?

Долгоживущий агент переживает машину, на которой запустился. Хосты падают, воркеры вытесняются, развертывания перезапускают процессы, а траектория, приостановленная ради одобрения человеком, может ждать ответа несколько дней. Если это случилось на сороковом ходу, перезапуск с исходного промпта не возвращает задачу: каждый вызов модели дает новую выборку, повторный прогон идет по другому пути, а некоторые из первых сорока ходов уже изменили мир, отправив сообщение, слив ветку или создав сервер, и повторный прогон изменил бы их снова. Выживает только та работа, которую среда выполнения записала в правильном порядке до сбоя. Долговечное выполнение — это дисциплина ведения такой записи, чтобы траекторию можно было возобновить, повторить для отладки или перенести на другую машину, не повторяя эффект, который она уже вызвала. Та же запись позже становится свидетельством, которое читают оценка и обучение. Через призму H·S·A длинный горизонт превращает состояние в проблему долговечности, а уже потраченные полномочия — в долг, который восстановление не должно платить дважды.

Цели обучения

  • Спроектировать журнал траектории, события которого (сообщения, идентификаторы вызовов инструментов и их результаты, метаданные модели и сэмплирования, события сжатия, одобрения) достаточны для восстановления записи траектории.
  • Применить порядок «намерение до эффекта», чтобы после сбоя отнести каждое записанное действие к одному из состояний: не отправлено, завершено или под вопросом.
  • Выбрать частоту контрольных точек в ходах по стоимости контрольной точки и частоте сбоев и назвать то, что контрольная точка должна фиксировать, а журнал не может.
  • Объяснить, как возобновленная траектория восстанавливает свой контекст, во что обходится повторное предзаполнение и как при возобновлении учитываются ожидающие одобрения, поздние результаты инструментов и дрейф модели, промпта или схемы инструментов.
  • Построить режим повтора, который подменяет модель и инструменты заглушками, и использовать его для отладки с перемоткой во времени, контрфактических ветвей и регрессионных тестов обвязки.
  • Найти, почему повтор расходится со своей записью, отделив источники на стороне модели от источников на стороне обвязки и окружения.
  • Спроектировать политику хранения, которая сохраняет причинную запись траектории и выносит громоздкий вывод инструментов.

Журнал траектории

Горизонт растягивает состояние за пределы жизни одного хоста, поэтому эта глава делает запись долговечной.

Траектория находится на сороковом ходу миграции в облако. Обвязка только что отправила запрос на создание реплики базы данных, и хост теряет питание до прихода ответа. Когда траекторию подхватывает другой хост, от трех вопросов зависит, поможет восстановление или навредит. Что среда выполнения предложила, авторизовала и наблюдала к тридцать девятому ходу? Мог ли запрос на создание ресурса покинуть машину? Если мог, подействовал ли он? Запись траектории из раздела «Запись траектории» ни на один из этих вопросов не отвечает, потому что жила в памяти упавшего хоста. На длинном горизонте состояние, которое несет траектория, становится проблемой долговечности («Экспозиции H·S·A»), ведь траектории регулярно переживают машины, на которых стартовали («Растяжение времени: от наносекундных операций до килосекундных траекторий»).

Очевидное восстановление, при котором исходный промпт отправляют модели заново, не работает. Каждый вызов модели дает выборку, и даже при температуре ноль сервисная система не обещает одинаковых токенов дважды («Авторегрессионная генерация»). Поэтому повторный прогон может выбрать другой инструмент на третьем ходу и другой набор файлов на четвертом. Хуже того, повторный прогон не может знать, какие из первых сорока ходов уже изменили мир: второй запрос на создание даст вторую реплику. Значит, восстановлению нужна запись, которую среда выполнения сделала до сбоя и которая фиксирует происходившее ход за ходом.

Журнал как источник истины

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

Каждое событие фиксирует один факт о траектории: сборку контекста, ответ модели, авторизацию вызова инструмента средой выполнения или пришедший результат. Живая запись траектории из раздела «Обвязка агента» (статус, бюджеты, ожидающие одобрения, текущий план) больше не хранится отдельно. Это проекция, которую обвязка вычисляет, сворачивая журнал: начиная с исходной записи она применяет по одной детерминированной функции обновления на каждое событие:

Запись траектории как проекция журнала: долговечный журнал траектории (внизу) — это добавляемая только в конец последовательность типизированных событий, у каждого из которых есть порядковый номер записи (LSN) и обратная ссылка. Запись траектории в памяти среды выполнения (вверху) пересчитывается сверткой этих событий детерминированным кодом обвязки. Когда хост теряется на ходу 42, восстановление читает журнал по порядку и воссоздает ту же запись, не вызывая ни модель, ни инструменты.

На «Запись траектории как проекция журнала» видны обе половины. Функция обновления — обычный код обвязки без ввода-вывода, поэтому свертка одного и того же журнала всегда дает одну и ту же запись. После сбоя восстановление читает журнал и сворачивает его снова. Оно не вызывает ни модель, ни инструмент, потому что каждый нужный ответ модели и каждый результат инструмента уже есть в виде события (принцип «Намерение до действия»). Этот шаблон проектирования, событийное хранение (event sourcing), давно известен в системах данных (Kleppmann, 2016). Для агентов он подходит особенно хорошо: та часть системы, чей выход нельзя пересчитать, — модель, — как раз и есть та, чей выход журнал сохраняет.

Тот же разрез отделяет журнал от окна контекста. Контекст — это то, что читает следующий вызов модели. Он собирается из журнала и намеренно неполон: сжатие («Сжатие контекста») сворачивает старые ходы, а усечение («Усечение потока наблюдений») обрезает длинный вывод инструментов. Журнал сохраняет то, что контекст отбрасывает. Считать контекст записью траектории — самый частый способ ее потерять, потому что улики, которые нужны разбору после инцидента, — это как раз то, что удалило сжатие.

Что записывает журнал

Журнал полезен для восстановления, только если каждое событие несет то, что понадобится будущему читателю. В «Схеме журнала траектории» перечислены виды событий, которые нужны одноагентной траектории, и объяснено, зачем нужен каждый из них.

СобытиеКогда записываетсяКлючевые поляДля чего восстановление или повтор его используют
TaskStartedОдин раз, до первого ходаСпецификация задачи, идентификатор и версия модели, параметры сэмплирования, версия шаблона промпта, версии схем инструментов, идентификатор снимка рабочего пространстваФиксирует версии, которые должны совпасть при возобновлении или повторе, и задает стартовое рабочее пространство
ContextBuiltПеред каждым вызовом моделиУпорядоченные ссылки на сообщения, извлеченные элементы, хеш собранного контекстаПозволяет возобновлению заново вывести тот же контекст и обнаружить, когда повторная сборка дала бы другой результат
ModelRepliedПосле возврата каждого вызова моделиОтвет дословно, вызовы инструментов с их идентификаторами, причина остановки, счетчики токенов, версия модели, которая фактически обслужила запросЕдинственная копия выборки, которую нельзя сгенерировать заново
AuthorizedПосле проверок политики и бюджетаИдентификатор вызова, решение (разрешить, отклонить, удержать до одобрения), версия политики, оставшийся бюджетВосстанавливает бюджеты и показывает, почему действие было разрешено
ApprovalRequested, ApprovalResolvedКогда вызов удержан и когда человек отвечаетИдентификатор вызова, утверждающий, решение, срок действияПозволяет приостановленной траектории пережить ожидание и продолжиться с тем же решением
ToolIntentПосле авторизации, до отправки, долговечноИдентификатор вызова, имя инструмента, аргументы, ключ идемпотентностиОтмечает, что эффект мог произойти, поэтому восстановление его урегулирует, а не угадывает
ToolResultКогда инструмент вернул ответИдентификатор вызова, статус, вывод (или его хеш и указатель на полное тело), длительностьЗаменяет инструмент при повторе и закрывает намерение
CompactedКогда контекст сворачивается в сводкуОхваченные ходы, текст сводки в том виде, в каком он был полученПозволяет возобновлению переиспользовать записанную сводку, а не создавать другую
CheckpointПосле того как контрольная точка стала долговечнойОхваченная позиция в журнале, идентификатор снимка записи, идентификатор снимка рабочего пространстваОграничивает, сколько журнала должен прочитать перезапуск

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

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

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

Намерение до действия

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

Фантомный эффект
Внешнее изменение состояния, которое выполнение инструмента агентом произвело в мире, но которое ни одна долговечная запись журнала не описывает, потому что хост упал после отправки по сети, но до того, как барьер диска закрепил намерение.

Фундаментальный инвариант, который предотвращает этот режим отказа, — дисциплина журнала упреждающей записи (WAL), системная реализация принципа «Намерение до действия». Никакое внешнее изменение не может покинуть границу хоста, пока событие ToolIntent, которое авторизует действие и задает его, не будет долговечно зафиксировано на энергонезависимом носителе. Формально для каждого выполнения инструмента, которое меняет внешний мир, среда выполнения соблюдает строгую причинную последовательность:

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

Физическая последовательность барьеров и причинных переходов показана на «Последовательности выполнения агента с журналом упреждающей записи».

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

Ключевое ограничение в порядке операций WAL — барьер хранения . В программировании операционных систем вызов write() лишь копирует сериализованные байты из пространства пользователя в энергозависимый кеш страниц ядра. Если питание пропадает, пока байты находятся в энергозависимой DRAM, грязные страницы безвозвратно теряются. Долговечность требует явного аппаратного барьера через fdatasync() или прямой ввод-вывод, как показано в «Интерфейсах сохранности данных POSIX и гарантиях долговечности».

ИнтерфейсСемантика кешаУсловие возврата из ядраСостояние NVMe при возвратеБезопасно для ?Типичная задержка ()
write()Стандартный кеш страницСкопировано в DRAM ядраЭнергозависимое / не записано❌ Небезопасно (потеря данных при сбое)
write() + O_SYNCСинхронный кеш страницЗаписано в кеш контроллера дискаКеш контроллера DRAM / неизвестно⚠️ Условно (зависит от FUA)
write() + fdatasync()Явный сбросПодтверждено дискомЗафиксировано в флеш-ячейках✅ Безопасно (минимальные накладные расходы на метаданные)
write() + fsync()Явная полная синхронизацияПодтверждено дискомЗафиксировано в флеш-ячейках✅ Безопасно (обновляет все атрибуты inode)
Групповая фиксация (fdatasync)Пакетный векторный сбросПодтверждено для пакетаЗафиксировано в флеш-ячейках✅ Безопасно (амортизируется на событие)

Интерфейсы сохранности данных POSIX и гарантии долговечности: сравнение примитивов синхронизации файлов POSIX, семантики возврата из ядра, состояния энергонезависимого носителя и измеренных задержек на современных NVMe-дисках.

Три состояния после сбоя

Порядок из «порядка операций WAL» ограничивает комбинации записей, которые могут пережить сбой, и гарантирует, что восстановление находит каждое действие, меняющее внешний мир, в одном из трех детерминированных состояний («Состояния записанного действия»).

Записи в журналеСостояниеЧто делает восстановление
Нет долговечного ToolIntentНе отправленоВызов не покидал хост. Ход можно выполнить заново, с записанного ответа модели, если он есть.
ToolIntent и ToolResultЗавершеноИсход известен. Повтор и возобновление используют записанный результат и никогда не отправляют вызов повторно.
ToolIntent без ToolResultПод вопросомЗапрос мог не дойти, а мог быть выполнен с потерей только ответа. Урегулируйте до повтора.

Состояния записанного действия: порядок упреждающей записи гарантирует, что каждое действие, меняющее внешний мир, после сбоя находится ровно в одном из трех состояний. Только третье требует активной сверки.

Порядок упреждающей записи не может сам разрешить третье состояние, действие под вопросом. Он гарантирует, что это состояние явно видно. Урегулирование до повтора (принцип «Урегулирование до повтора») разрешает его механизмами, которые описаны в разделе «Выполнение идемпотентных действий»:

  1. Ключи идемпотентности: диспетчер восстановления повторно отправляет запрос инструменту с тем же одноразовым ключом, который записан в событии ToolIntent. Если сервер инструмента уже обработал прежний запрос, он возвращает закешированный исход и не повторяет побочный эффект.
  2. Зонды сверки: когда внешние API не поддерживают идемпотентность, диспетчер восстановления выполняет зонд чтения побочного эффекта (например, спрашивает, существует ли реплика базы данных), чтобы определить, произошло ли изменение.
  3. Эскалация супервизору: если среда остается неопределенной, супервизор останавливает выполнение и передает его человеку на проверку («Шлюзы одобрения»), а не рискует слепым повтором.

Цена долговечности: амортизация групповой фиксации

Требование синхронного барьера fdatasync() на критическом пути каждого выполнения инструмента добавляет задержку энергонезависимого носителя. Один сброс буфера NVMe на локальной флеш-памяти занимает от до , а сетевые блочные тома (например, AWS EBS или Google Persistent Disk) тратят от до на каждый барьер. В мультиагентных средах, где на общем узле работают десятки или сотни параллельных траекторий, последовательные сбросы создают серьезную очередь.

Стандартный механизм систем, который устраняет это узкое место, — групповая фиксация (DeWitt et al., 1984). Она описана в «Конвейере групповой фиксации». Вместо того чтобы каждый рабочий поток выполнял fdatasync() сам по себе, параллельные рабочие потоки агентов ставят авторизованные записи событий в кольцевой буфер с несколькими производителями без блокировок и усыпляют себя на futex завершения.

Этап конвейераПринцип работыПрофиль задержкиСтруктуры данных и примитивы ввода-вывода
1. ПриемПараллельные потоки агентов сериализуют предложения действий () и помещают их во временный буфер в памяти; потоки ждут промисы завершения.Почти нулевая ()Кольцевой буфер без блокировок с несколькими производителями (MPMC)
2. АгрегацияВыделенный координатор сброса забирает очередь, когда размер пакета достигает (например, 64 записи) или истекает таймаут (например, 2 мс).Векторный массив памяти для рассеивания-сбора (scatter-gather)
3. Векторная записьАгрегированные записи событий пишутся на устройство хранения одним системным вызовом рассеивания-сбора.POSIX writev() / отправка через Linux io_uring
4. Барьер храненияЯдро хоста заставляет контроллер диска перенести грязные блоки из кеша контроллера на энергонезависимую флеш-память.fdatasync() / команда сброса NVMe
5. Освобождение барьераКоординатор уведомляет все ожидающие промисы завершения; потоки агентов параллельно выходят из ожидания и диспетчеризуют инструменты.Почти нулевая ()Атомарная широковещательная рассылка futex / пробуждение по условной переменной

Архитектура конвейера групповой фиксации: амортизация барьеров энергонезависимого хранения между параллельными рабочими потоками агентов через буферизацию без блокировок и векторную синхронизацию.

Эффективную задержку синхронизации хранилища на ход мы моделируем при пуассоновском потоке авторизаций с суммарной интенсивностью . Если координатор групповой фиксации использует окно агрегации и максимальную емкость пакета , ожидаемое число событий, амортизируемых на один барьер, задается формулой:

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

По мере роста параллелизма траекторий () окно можно сокращать почти до нуля. Штраф синхронизации делится на траекторий, и накладные расходы на барьер на ход становятся пренебрежимо малыми, не нарушая причинного инварианта упреждающей записи ни для одного отдельного агента.

Контрольная точка: порядок упреждающей записи

  • Порядок: почему ToolIntent должен быть долговечным до отправки и почему запись ModelReplied после вызова модели безопасна?
  • Состояния восстановления: по журналу вы можете отнести каждое действие, меняющее внешний мир, к состоянию «не отправлено», «завершено» или «под вопросом»?
  • Урегулирование: что делает восстановление с вызовом под вопросом, если инструмент не принимает ключа идемпотентности?
  • Стоимость: почему групповая фиксация сохраняет причинный порядок каждой траектории и при этом снижает частоту барьеров хранения?

Контрольные точки

Программный агент находится на трехсотом ходу. Его журнал фиксирует, что на ходу 212 инструмент правки переписал parser.py, а на ходу 250 инструмент оболочки удалил каталог сборки. Свертка журнала восстанавливает запись траектории, но не восстанавливает parser.py. Журнал хранит вызовы инструментов и их результаты, а файлы, которые эти вызовы создали, живут в рабочем пространстве песочницы («Рабочее пространство агента»). Если хост с этим рабочим пространством сломался аппаратно, оно погибло вместе с ним. Повтор журнала тоже не воссоздаст измененные файлы, потому что повтор намеренно подменяет выполнение инструментов заглушками.

Что фиксирует контрольная точка: полный снимок и инкрементальное дельта-кодирование

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

  1. Снимок записи: сериализованная запись траектории на ходу , чтобы перезапуску не пришлось сворачивать весь журнал с .
  2. Снимок рабочего пространства: файловая система песочницы на ходу , снятая копированием при записи («Рабочее пространство агента»).
  3. Событие Checkpoint: добавляется в журнал только после того, как оба снимка стали долговечными на энергонезависимом носителе.

Архитектурный компромисс — в том, снимает ли супервизор полный снимок состояния или инкрементальный дельта-снимок. В долгих задачах буфер контекста растет монотонно: накапливаются диффы кода и трассы выполнения. Если контекст достигает структурированных токенов и метаданных, то полный снимок каждые пять ходов на траектории из 500 ходов заставит систему хранения записать:

Коэффициент усиления записи (): Для полных снимков на монотонных траекториях , тогда как для инкрементального дельта-кодирования .

Инкрементальное дельта-кодирование смягчает это усиление записи: оно сериализует только дифференциальные изменения, которые произошли с предыдущей контрольной точки, и формализуется формулой:

где — шаг контрольных точек. Вместо повторной сериализации всего буфера промпта среда выполнения записывает только вновь добавленные токены сообщений, обновленные регистры бюджета и измененные inode файловой системы («Компромиссы архитектуры контрольных точек состояния»).

Архитектурный аспектПолный снимок состоянияИнкрементальный дельта-снимокКопирование при записи (CoW)
Накладные расходы на сериализациюВысокие; растут линейно с размером контекста Низкие; строго ограничены изменением окна Почти нулевая пауза; дублирование таблицы страниц
Усиление храненияТяжелый квадратичный рост на длинных траекторияхМинимальное; хранит уникальные переходы состоянийУмеренное; пропорционально объему грязных страниц памяти
Время восстановления после сбояМинимальное ; одно чтение и мгновенная распаковка памятиУмеренное ; нужно применить цепочку дельт от базыМинимальное ; переотображает хранилище или файлы страниц
Влияние уплотнения журналаТривиальное; все исторические снимки до можно удалитьСложное; нужна периодическая консолидация базы, чтобы обрезать журналУмеренное; нужно фоновое слияние страниц и очистка
Барьер изоляцииВысокий; полная развязка с памятью живого процессаУмеренный; зависит от непрерывной проверки движком повтораПривязан к процессу; связан с семантикой памяти ядра локального хоста

Компромиссы архитектуры контрольных точек состояния: сравнение полных снимков состояния, инкрементального дельта-кодирования и форков памяти с копированием при записи.

Компромисс между накладными расходами на контрольные точки и временем восстановления через повторное выполнение показан на рисунке «Периодичность контрольных точек и восстановление повтором».

Периодичность контрольных точек и восстановление повтором: снимки состояния, взятые с частотой , ограничивают объем пересчета при восстановлении. Когда сбой происходит на ходу , менеджер восстановления загружает снимок и повторно применяет только суффикс событий из журнала упреждающей записи, не вызывая модель повторно.

Во что обходится сбой и как часто делать контрольные точки

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

Эта цена задает частоту. Пусть — время, на которое контрольная точка приостанавливает траекторию, а — среднее время между сбоями хоста или воркера. Если делать контрольную точку каждые секунд, доля времени на контрольные точки составит , а при каждом сбое в среднем теряется работы. Баланс дает классический оптимум первого порядка Янга (Young, 1974):

С учетом накладных расходов второго порядка на перезапуск формулировка Дейли высшего порядка (Daly, 2006) уточняет формулу Янга с помощью формулы:

Пример: выбор частоты контрольных точек в ходах

Задача: кодирующий агент работает на пуле прерываемых воркеров, среднее время между сбоями у которых составляет 4 часа. Ход занимает в среднем 12 с, а контрольная точка (сброс записи и снимок рабочего пространства копированием при записи на границе хода) приостанавливает траекторию на 0,5 с. В момент сбоя контекст содержит 40 000 токенов, каждый ответ содержит 350 выходных токенов, и тарифицируется это так же, как в примере бюджета из раздела «Бюджеты и потолки». Исходные данные условны. Как часто обвязке делать контрольные точки и во что обходится сбой при этой частоте и при гораздо более редкой?

Частота: при = 0,5 с и = 14 400 с формула Янга дает = 120 с, что соответствует 10 ходам.

Накладные расходы: доля времени, потраченного на паузы и потерянную работу, равна . В оптимуме она составляет 0,83 %. Контрольная точка на каждом ходу поднимает ее до 4,2 %, почти полностью из-за пауз, а контрольная точка каждые 100 ходов тоже дает 4,2 %, почти полностью из-за потерянной работы.

Цена сбоя: любое возобновление оплачивает одно холодное предзаполнение восстановленного контекста, примерно в 0,12 доллара. Сверх того сбой в среднем теряет половину интервала. В оптимуме это 5 ходов, около 1 минуты и 0,09 доллара токенов при теплом префиксе. Если же контрольная точка делается каждые 100 ходов, сбой теряет 50 ходов, около 10 минут и 0,86 доллара, а каждый переделанный ход — это новая выборка, которая может уйти с пути, записанного журналом.

Системный вывод: кривая накладных расходов плоская у минимума и крутая по краям, поэтому частота должна быть правильной лишь с точностью до множителя около двух. Сдвигает ее отношение стоимости контрольной точки к частоте сбоев. Более медленное хранилище снимков увеличивает и сдвигает оптимум вправо, а более нестабильный пул воркеров сокращает и сдвигает его влево. Денежная стоимость сбоя мала; более серьезная цена переделанных ходов в том, что они — новые выборки, поэтому журнал хранит каждый ответ, который модель уже выдала.

Четыре момента требуют контрольной точки независимо от формулы, потому что каждый из них — точка, к которой траектория будет возвращаться или которую она не должна потерять:

  • Перед необратимым действием, чтобы работа до поворота была в безопасности, что бы ни случилось после него («Восстановление после сбоев» рассматривает поворот).
  • Когда траектория приостанавливается ради одобрения, потому что ожидание, которое длится часами, делает сбой во время ожидания вероятным.
  • Перед сжатием, чтобы контекст до сжатия оставался достижимым.
  • Когда обвязка переносит траекторию на другого воркера («Возобновление траектории»).

Возобновление траектории

Траектория из начала главы теперь возобновляется на новом воркере. Предыдущий хост мог упасть, быть вытеснен или выведен из работы ради развертывания, либо траектория могла просто два дня ждать, пока человек одобрит изменение в продакшене («Шлюзы одобрения»). Во всех случаях воркер, который продолжает траекторию, начинает только с журналом, последней контрольной точкой и правилом не повторять эффект. Возобновление — это протокол, который превращает их в работающую траекторию:

  1. Захват владения. Получить аренду траектории с токеном ограждения, чтобы никакой другой воркер не мог действовать от ее имени (см. ниже).
  2. Восстановление. Загрузить снимки записи и рабочего пространства из последней контрольной точки, затем свернуть суффикс журнала в запись.
  3. Урегулирование. Разрешить каждое намерение под вопросом, как описано в разделе «Намерение до действия», до того как будет отправлено что-либо новое.
  4. Восстановление контекста из журнала и его предзаполнение.
  5. Продолжение цикла на следующей границе хода в том состоянии, которое записано в записи траектории.

Шаги 2 и 3 используют механизмы двух предыдущих разделов. Шаги 1 и 4, а также ситуации, которые их усложняют, относятся именно к возобновлению.

Восстановление контекста

Модель между вызовами ничего не хранит, поэтому возобновленный воркер должен передать ей контекст. Он заново выводит контекст из журнала теми же правилами сборки, которыми пользовалась траектория («Подготовка следующего вызова»): записанные сообщения, записанные результаты инструментов в том виде, в каком они были доставлены, и записанные сводки Compacted вместо ходов, которые они заменили. Хеш ContextBuilt позволяет обвязке проверить, что заново собранный контекст совпадает с последним, который модель видела до сбоя. Несовпадение означает, что правила сборки или их входы изменились. Это дрейф (см. ниже), а не восстановление.

Перестроенный контекст нужно предзаполнить заново. KV-состояние, которое держала старая сессия обслуживания, выводилось из префикса токенов и не пережило сбой (принцип «Состояние внимания выводится из префикса токенов»), а новый вызов может попасть на сервер, чей кеш префиксов никогда не видел эту траекторию («Кеширование префиксов между ходами»). Поэтому возобновление оплачивает одно холодное предзаполнение всего контекста. Для длинного контекста это секунды задержки и центы стоимости входных токенов («Задержка обслуживания на ускорителе»; «Размер состояния внимания»). Это немного по сравнению с переделкой ходов, которая стоит минут вызовов модели и долларов и ставит под угрозу каждый эффект под вопросом. Два приема помогают сохранить эту цену малой. Маршрутизация возобновленной траектории на сервер, который еще держит ее префикс, превращает холодное предзаполнение в попадание в кеш. Повторная сборка контекста побайтно как прежде сохраняет префикс идентичным, поэтому любая уцелевшая запись кеша остается пригодной.

Паузы, одобрения и поздние результаты

Пауза ради одобрения — это возобновление, которое запланировала обвязка. Когда вызов удержан («Шлюзы одобрения»), обвязка записывает ApprovalRequested, делает контрольную точку и полностью освобождает воркер и его состояние обслуживания модели, потому что держать и то и другое часами расточительно. Когда ответ приходит, среда выполнения записывает ApprovalResolved и возобновляет траекторию теми же пятью шагами. Собственные правила шлюза одобрения, например срок действия и повторная проверка предусловий действия перед отправкой, выполняются уже после возобновления, против мира в его нынешнем виде, а не в том, каким он был, когда запрос подавали.

Долгие инструменты создают противоположный случай. Задача, запущенная через асинхронный инструмент («Возобновление по событиям»), может закончиться после того, как воркер, который ее запустил, уже умер. Поэтому ее результат должен поступать в долговечный входящий буфер с ключом по идентификатору вызова, а не в память воркера. При возобновлении обвязка сопоставляет каждый ожидаемый результат с намерениями в журнале и записывает соответствующий ToolResult. Результат, у которого идентификатор вызова не имеет намерения в журнале или который пришел для траектории, уже отмененной, отклоняется защитами состояний записи («Состояния траектории») и не передается модели.

Перенос траектории и токены ограждения

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

Последовательные фазы передачи живой траектории показаны на «Архитектуре живого переноса траектории».

Архитектура живого переноса траектории: сквозная последовательность между уходящим узлом, распределенным координатором и приходящим узлом. Чистая приостановка на границе хода гарантирует отсутствие незавершенных действий инструментов во время передачи состояния. Токены ограждения не дают выполняться двум экземплярам одновременно (split-brain).

Критическая опасность при переносе — дилемма split-brain: воркер, испытывающий временный сетевой разрыв или зависание ядра, может не понять, что его аренда истекла, и продолжит выполнять шаги и выпускать мутации инструментов одновременно с только что пришедшим воркером. Среда выполнения предотвращает это через строго возрастающий токен ограждения :

  1. Когда служба распределенной аренды передает владение траекторией приходящему воркеру , она увеличивает счетчик эпохи: .
  2. Каждая полезная нагрузка вызова инструмента, отправленная воркером, должна нести назначенный ему токен ограждения: .
  3. Шлюз исполнения инструментов и внешние прокси ресурсов применяют монотонный барьер: любой вызов с безусловно отбрасывается с ошибкой авторизации.

Если устаревший воркер проснется после временной паузы и попытается выполнить внешний вызов инструмента, шлюз его отвергнет, потому что , предотвращая фантомные мутации и двойное выполнение.

Возобновление при дрейфе

Между сбоем и возобновлением или во время долгой паузы ради одобрения система вокруг траектории может измениться. Обслуживаемая модель может получить новую версию под тем же именем, шаблон промпта может быть отредактирован, а схема инструмента может получить новое поле или переименовать существующее. Поскольку TaskStarted и каждый ModelReplied записывают версии, которые использовались, обвязка может обнаружить дрейф, а не выяснять его по странному поведению, и у нее есть три варианта:

  • Закрепить записанные версии и возобновиться на них, что возможно, только пока эти версии еще доступны.
  • Перейти на новые версии на следующей границе хода, записав событие дрейфа со старой и новой версиями. Записанные ходы сохраняют свои записанные выходы, контекст пересобирается по новому шаблону, а кеш префиксов промахивается, начиная с первого измененного токена.
  • Отказаться и эскалировать — правильный ответ, когда измененная схема инструмента переинтерпретировала бы аргументы, уже записанные в намерении под вопросом.

Траектория, которая перешла на новые версии, начиная с хода , — уже другая система. Ее поведение до и после перехода не стоит объединять при оценке («Оценка агентов»), а событие дрейфа как раз и позволяет оценщику разделить их.

Возобновление использует журнал, чтобы продолжить траекторию. Тот же механизм может также восстановить прошлое траектории, не продолжая ее, и именно так инженеры выясняют, что пошло не так.

Повтор

Ночью агент удалил релизную ветку на ходу 23 задачи по очистке. Наутро инженеру нужно знать, что видела модель, когда выбирала это действие: какой список ей показали, что говорили инструкции после сжатия и проверялась ли авторизация по правильной политике. Повторный запуск задачи на эти вопросы не отвечает, потому что он сэмплирует новую траекторию и, направленный на настоящий репозиторий, может удалить что-то другое.

Повтор отвечает на них. Он запускает обвязку по журналу, при этом оба источника новой информации отключены. Клиент модели подменяется заглушкой, которая возвращает записанные события ModelReplied по порядку, а транспорт инструментов отключается и возвращает записанные события ToolResult. Код обвязки работает по-настоящему: собирает контексты, проверяет политики и обновляет запись, поэтому инженер видит то, что обвязка действительно сделала. На «Выполнении в реальном времени и повторе» сопоставлены два режима, а «Повтор и повторный запуск» ставит повтор рядом с повторным запуском, который он заменяет.

Выполнение в реальном времени и повтор: при выполнении в реальном времени (вверху) обвязка собирает контекст, модель отвечает вызовом инструмента, шлюз авторизации записывает решение и долговечное намерение, инструмент выполняется, а каждый шаг дописывается в журнал траектории. При повторе (внизу) клиент модели заглушен, а транспорт инструментов отключен. Обвязка сворачивает записанные события, получает записанные результаты вместо вызовов инструментов и воссоздает ту же запись без генерации токенов и без побочных эффектов. Любой вызов, которого журнал не учитывает, проваливает повтор.

ИзмерениеВыполнение в реальном времениПовторный запуск с промптаПовтор из журнала
МодельВызываетсяВызывается снова, новые выборкиЗаглушена, записанные ответы
ИнструментыВыполняютсяВыполняются снова, эффекты повторяютсяОтключены, записанные результаты
ИсходТраекторияДругая траекторияТа же траектория, шаг за шагом
СтоимостьТокены, доллары, эффектыТокены, доллары, повторные эффектыТолько вычисления обвязки
НазначениеВыполнение задачиИсследование альтернативВосстановление, отладка, аудит, тестирование

Повтор и повторный запуск: повтор восстанавливает траекторию из ее журнала без вызова модели и инструментов. Повторный запуск с исходного промпта — это новая траектория, которая снова тратит токены и повторяет эффекты.

Повтор строг. Если обвязка во время повтора пытается вызвать модель или инструмент, которых журнал не учитывает, повтор проваливается. Незаписанный вызов означает либо что код обвязки изменился после записи, либо что какой-то вход попал в живую траекторию, минуя журнал, и то и другое — находка. Идея пришла из детерминированного повтора целых машин, где для воспроизведения выполнения нужно записывать каждый недетерминированный вход (King et al., 2005). Для агента недетерминированных входов немного, и они хорошо определены: ответы модели, результаты инструментов и то, что сама обвязка читает из окружения («Почему повторы расходятся»).

Перемотка во времени и контрфактические ветви

Поскольку повтор может остановиться на любом ходу, журнал становится временной шкалой, по которой инженер может двигаться.

Определение: повтор с перемоткой во времени

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

  1. Значение: инженер может изучить контекст, который модель действительно получила, состояние бюджета и каждое решение авторизации на ходу, где траектория пошла не так, и платить только вычислениями обвязки.
  2. Отличие: в отличие от повторного запуска с промпта, он воспроизводит записанную траекторию, а не сэмплирует новую; в отличие от чтения сырого журнала, он показывает производное состояние обвязки (собранный контекст и запись) на этом ходу.
  3. Частая ошибка: позволить повтору дойти до живого инструмента, который повторит прошлый эффект в нынешнем мире.

Для удаленной ветки инженер повторяет до хода 23 и читает контекст, который получила модель. Допустим, список веток, который ей показали, был обрезан до строки, где релизная ветка помечена как защищенная. Тогда дефект в усечении наблюдений, а не в модели. Разделение ошибок модели и ошибок обвязки на множестве инцидентов — тема раздела «Криминалистический разбор инцидентов», и доказательства для него дает повтор.

Контрфактическая ветвь идет дальше. Инженер форкает траекторию на ходу 22, меняет одну вещь (правило усечения, шаблон промпта, схему инструмента или модель) и дает ей продолжиться в реальном времени с этой точки. Все до форка повторяется, поэтому ранние ходы ничего не стоят и остаются идентичными. Все после форка — новая траектория, и она должна работать в песочнице, где внешние инструменты заглушены или направлены на тестовые системы, потому что она живая и ее эффекты настоящие. Сравнение одной ветви с одной записью показывает лишь, что исправление может изменить исход. Чтобы показать, что оно делает это надежно, нужно прогнать много ветвей и измерить их, а этим занимается «Оценка агентов».

Регрессионные тесты для обвязки

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

Повтор архивных траекторий через измененную обвязку убирает все три проблемы. Записанные ответы модели служат фиксированными тестовыми входами, поэтому тест измеряет обвязку и ничего больше. Допустим, команда ужесточает парсер, который извлекает правки файлов из вызовов инструментов. Повтор нескольких сотен производственных журналов через новый парсер находит траекторию, где на ходу 14 старый парсер принял патч, обернутый в блок кода markdown, а новый его отвергает, и повтор останавливается на этом ходу, сохранив оба разбора. Тест стоил только вычислений обвязки, прошел за секунды и указал точный ход и полезную нагрузку.

Область этого теста намеренно узкая. Он показывает, что новая обвязка обрабатывает записанные выходы модели так же, как старая, или где она отличается. Он не может показать, что новая обвязка делает агента успешнее, потому что будущие ответы модели будут реагировать на новую обвязку. Для этого нужны свежие траектории и статистика («Оценка агентов»). Те же журналы, проверенные, становятся и сырьем для обучения («Курирование траекторий»).

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

Почему повторы расходятся

Инженер, разбирающий сбой агента на ходу 42, перезапускает траекторию той же моделью при температуре ноль на копии репозитория на том же коммите Git. Повтор расходится на ходу 6, выбирает совсем другой вызов инструмента и до сбоя не доходит. Ничего не сломано. Повторный прогон автономного агента имеет несколько независимых векторов расхождения.

Четыре источника расхождения

В «Источниках расхождения при повторе» перечислены четыре архитектурных слоя, на которых недетерминированность попадает в траекторию агента.

ИсточникКак он попадаетЧто делает среда выполнения
Вызов моделиСэмплирование при ненулевой температуре; неассоциативность сложения с плавающей точкой в параллельных редукционных ядрах при Записывать каждый ответ; никогда не ожидать, что повторный прогон его воспроизведет
Дрейф моделиНовая версия модели или конфигурация обслуживания под тем же именемЗаписывать обслуживаемую версию на каждом ответе; сравнивать версии, прежде чем сравнивать поведение
Входы обвязкиВременные метки, случайные идентификаторы, неупорядоченные списки каталогов и результаты поиска, попавшие в контекстПропускать каждый вход через обвязку, записывать его как событие и сортировать все, что не имеет внутреннего порядка
ОкружениеВнешние сервисы, репозитории и данные, которые изменились после записиЗаписывать каждый результат инструмента; повторять из журнала, а для контрфактических ветвей закреплять или снимать среду

Источники расхождения при повторе: четыре места, где повторный прогон может отклониться от своей записи, и то, что среда выполнения записывает или контролирует для каждого из них. Только первый нельзя устранить: его нужно виртуализировать и записывать.

Вызов модели сильнее всего удивляет инженеров, потому что обслуживание базовой модели недетерминировано даже при температуре ноль (). На GPU умножение плотных матриц (GEMM) и ядра FlashAttention выполняют параллельные редукции на тысячах потоковых мультипроцессоров. Поскольку сложение чисел с плавающей точкой неассоциативно:

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

Детерминированный повтор через перехват системных вызовов

Для побитово точного повтора в целях диагностической отладки среда выполнения применяет перехват окружения: между обвязкой и базовым хостом вставляется прослойка системных вызовов и ввода-вывода («Детерминированный повтор через перехват окружения»).

Детерминированный повтор через перехват окружения: прослойка системных вызовов перехватывает недетерминированные взаимодействия с хостом (часы, случайные зерна, вызовы инструментов) и отдает записанные события из журнала траектории, гарантируя побитово точное повторное выполнение логики супервизора.

Когда обвязка работает в режиме повтора:

  1. Виртуализация модели: вызовы API модели перехватываются и удовлетворяются возвратом записанной полезной нагрузки токенов ModelReplied из журнала.
  2. Перехват окружения: системные вызовы, которые запрашивают текущее время по часам (gettimeofday), идентификаторы процессов или криптографическую энтропию (/dev/urandom), возвращают записанные временные метки и зерна, сохраненные в событии ContextBuilt.
  3. Внедрение моков инструментов: диспетчеризация инструментов перехватывается; вместо выполнения команд оболочки или сетевых запросов прослойка внедряет записанные данные ToolResult.

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

Измерение расхождения

Когда инженер намеренно перезапускает модель, чтобы проверить устойчивость, главная диагностическая метрика — точка расхождения: самый ранний ход , на котором выбранное действие инструмента отклоняется от записанной базы. Сравнение хешей контекста на сразу изолирует первопричину: одинаковые хеши контекста подтверждают стохастичность модели или недетерминированность вычислений с плавающей точкой, тогда как различающиеся хеши указывают на дрейф окружения или неупорядоченные входы каталогов.

Контрольная точка: повтор и расхождение

  • Повтор против повторного запуска: почему повтор стоит только вычислений обвязки, тогда как повторный запуск с промпта тратит токены и рискует повторить эффекты?
  • Дрейф из-за плавающей точки: почему неассоциативность сложения в ядрах GPU вызывает расхождение декодирования при температуре ноль?
  • Строгость: что значит, когда повтор пытается вызвать модель или инструмент, которых журнал не учитывает?
  • Контрфактические ветви: почему контрфактическая ветвь должна восстановить рабочее пространство из контрольной точки, прежде чем продолжаться в реальном времени?

Хранение журнала

Движки хранения траекторий

Эксплуатация корпоративного парка, который производит десятки тысяч длинных траекторий в день, выявляет фундаментальную дилемму хранения: системе нужны синхронные барьеры записи менее миллисекунды для активного WAL на критическом пути выполнения, а также реляционные индексы для текущего мониторинга и плотная колоночная пакетная обработка для последующего дообучения с подкреплением (RL). Одна технология хранения не может удовлетворить эти конфликтующие требования («Парадигмы хранения траекторий»).

Архитектура храненияОсновная структура данныхЗадержка добавления (p99)Пропускная способность случайного сканированияТипичное усиление записиОптимальная роль в жизненном цикле агента
LSM-дерево (встроенное)MemTable + SSTablesВысокая (для последовательных диапазонов LSN)Горячий уровень: активный WAL, локальные барьеры ходов, мгновенный перезапуск после сбоя
B+ дерево (реляционное)Страничные дисковые блокиВысокая (сканирование по индексам нескольких атрибутов)Теплый уровень: координация парка в кластере, очереди одобрений HITL
Колоночное озеро данныхБлоки Parquet / ZstdПакетный импорт ()Сверхвысокая (векторизованные SIMD-сканы)Холодный уровень: курирование траекторий, аудит безопасности, обучение SFT и RLVR

Парадигмы хранения траекторий: сравнение физических архитектур хранения, паттернов доступа, коэффициентов усиления записи и размещения по жизненному циклу для агентных систем.

Многоуровневая пирамида хранения траекторий: организация хранения событий на NVMe-дисках для активных живых траекторий, распределенных хранилищ «ключ — значение» для недавних аналитических запросов и холодного объектного хранилища для долгосрочного аудита и обучающих данных.

Многоуровневый конвейер хранения

Производственные архитектуры снимают это напряжение, организуя хранение в трехуровневую пирамиду жизненного цикла («Многоуровневая пирамида хранения траекторий» и «Производственный многоуровневый движок хранения траекторий»):

  1. Горячий уровень (локальный NVMe / встроенная RocksDB): работает на воркере. Когда обвязка записывает ToolIntent или ToolResult, она дописывает его напрямую во встроенное LSM-дерево с fdatasync(). Поскольку запись строго последовательна и локальна, долговечная фиксация завершается менее чем за , что соблюдает порядок WAL без сетевых переходов.
  2. Теплый уровень (кластерная реляционная база / PostgreSQL): асинхронный потоковый демон-сайдкар следит за локальным журналом RocksDB и реплицирует зафиксированные пакеты событий в централизованную реляционную базу. Этот уровень дает индексированные мультиагентные запросы, административную отмену и живой мониторинг, не блокируя путь выполнения.
  3. Холодный уровень (колоночное объектное хранилище / Apache Parquet): офлайн-конвейер пакетной обработки сливает завершенные траектории старше 7 дней в сжатые файлы Apache Parquet в объектном хранилище (S3/GCS), разбитые по арендатору, дате и версии модели. Аналитические движки (DuckDB, Trino) и конвейеры обучения передают эти колоночные фрагменты прямо в память GPU для обучения политики («Курирование траекторий»).
Носитель храненияТехнология храненияПаттерн запроса и доступаЭксплуатационная функцияДиапазон производительности и стоимости
Горячий уровеньЛокальный NVMe SSD; встроенная RocksDB с журналом упреждающей записиПоследовательное добавление, точечный поиск по монотонному LSNЖурнал WAL в реальном времени, барьер авторизации действий, мгновенное восстановление после сбояЗадержка , WAF
Теплый уровеньКластерный PostgreSQL с индексами JSONB и секционированными таблицамиИндексированные диапазонные сканы, запросы статуса мультиагентов, реляционные соединенияМониторинг активных траекторий, проверка с участием человека, координация паркаЗадержка , доступность 99,99 %
Холодный уровеньРаспределенное облачное объектное хранилище (S3 / GCS, Parquet с Zstd)Векторизованные пакетные сканы (DuckDB, Trino, загрузчики PyTorch)Соответствие требованиям к хранению, аудит безопасности, офлайн SFT и RLVRСканы , стоимость

Производственный многоуровневый движок хранения траекторий: характеристики трех плоскостей жизненного цикла данных.

Идемпотентный прием и дедупликация данных

Чтобы сохранить причинную согласованность через сетевую границу между воркерами и теплым уровнем, схема реляционной базы обеспечивает семантику идемпотентного upsert через уникальные составные ключи:

INSERT INTO trajectory_events (
    trajectory_id, sequence_number, event_type,
    payload, cryptographic_hash, created_at
) VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT (trajectory_id, sequence_number) DO NOTHING;

Это гарантирует, что если сайдкар-сервис перезапустился после сбоя узла и повторно отправил пакет событий, дубликаты событий молча отбрасываются и не портят историческую последовательность.

Заблуждения и ловушки

Долговечное выполнение чаще всего ломается, когда инженеры переносят предположение, которое справедливо для обычных сервисов или одного вызова модели, но не для длинной траектории, меняющей мир.

Заблуждение и ловушка

Заблуждение: сохранить финальное окно контекста достаточно, чтобы аудировать или восстановить траекторию.

Окно контекста — это неполная проекция траектории. Сжатие свернуло ее ранние ходы, усечение обрезало вывод инструментов, и в нем нет записи о том, какие действия авторизовала среда выполнения, какие намерения были долговечны и какие результаты пришли. Если инструмент вернул учетные данные на ходу 7, а сводка удалила их к ходу 12, в финальном контексте не остается следа утечки. Восстановлению и аудиту нужен журнал из «Схемы журнала траектории»: по нему контекст можно восстановить, а сам контекст восстановить по нему нельзя.

Заблуждение и ловушка

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

Экономия мала, потому что локальный сброс добавляет к ходу меньше 1 процента. Цена — фантомный эффект всякий раз, когда хост падает в окне между отправкой и сбросом. Восстановленная обвязка не находит намерения, делает вывод, что вызов не случился, и повторяет платеж, сообщение или запрос на создание ресурса. Порядок упреждающей записи («Намерение до действия») закрывает это окно, а групповая фиксация возвращает большую часть задержки.

Заблуждение и ловушка

Заблуждение: повторный прогон модели при температуре ноль воспроизводит траекторию.

Обслуживание при температуре ноль все равно не побитово точно: состав пакета меняет порядок сумм с плавающей точкой, и почти равные токены переворачиваются. Версии модели меняются под стабильным именем, входы обвязки вроде временных меток попадают в контекст, а окружение меняется. Повторный прогон — это новая траектория. Воспроизведение дает повтор записанных ответов модели и результатов инструментов («Повтор»), а анализ расхождений («Почему повторы расходятся») объясняет остальное.

Заблуждение и ловушка

Ловушка: пересоздавать сводки и контексты при возобновлении вместо повторного использования записанных.

Возобновленная обвязка, которая заново суммирует старые ходы или пересобирает контекст по шаблону промпта, отредактированному после сбоя, дает модели другую память о ее собственном прошлом, чем та, по которой модель действовала. Траектория тогда продолжается из состояния, которого никогда не существовало, ее кеш префиксов промахивается, а поздние ходы нельзя сравнить с ранними. При возобновлении используются записанные события Compacted и записанная версия шаблона, а любое изменение считается явным дрейфом («Возобновление траектории»).

Заблуждение и ловушка

Ловушка: считать вызов под вопросом неудавшимся, потому что он истек по таймауту.

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

Заблуждение и ловушка

Ловушка: вечно хранить каждый байт сырого вывода инструментов в живом журнале.

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

Итоги

Глава отвечала на вопрос, что среда выполнения должна записать, чтобы траектория пережила сбой, паузу или перенос и не повторила эффект, который уже вызвала. Ответ — журнал траектории, который только дописывается, из которого вычисляется запись траектории и который устроен так, что каждому вызову, меняющему внешний мир, предшествует долговечное намерение. При таком порядке восстановление находит каждое действие как не отправленное, завершенное или под вопросом, а третьи урегулирует, прежде чем продолжить. Контрольные точки фиксируют то, что журнал не может восстановить, прежде всего рабочее пространство, в согласованных границах ходов. Возобновление объединяет оба механизма: восстанавливает контекст из записанных событий, платит за одно холодное предзаполнение и явно обрабатывает одобрения, поздние результаты и дрейф версий. Повтор запускает обвязку по журналу при заглушенных модели и инструментах, что превращает журнал в отладчик, источник контрфактических ветвей и регрессионный набор для обвязки, а также объясняет, почему повторные запуски расходятся.

Если этого нет в журнале, восстановление не может этому доверять

  • Журнал — это траектория, запись — ее представление: запись траектории пересчитывается сверткой журнала, в котором только дописываются ответы модели, решения, намерения и результаты. Восстановление читает журнал и никогда не вызывает модель, потому что ее выборки нельзя сгенерировать заново.
  • Записывайте намерение до эффекта: вызов, меняющий внешний мир, может покинуть хост только после того, как его намерение стало долговечным. Тогда восстановление находит каждое действие как не отправленное, завершенное или под вопросом, а действие под вопросом урегулируется ключом идемпотентности или зондом и никогда не отправляется повторно по предположению.
  • Контрольные точки существуют для того, что журнал не может восстановить: журнал не содержит файлов, которые записали инструменты. Контрольная точка снимает запись и рабочее пространство вместе на границе хода, а ее частота уравновешивает время паузы и ходы, которые стоит сбой.
  • Возобновление — это восстановление плюс перестройка контекста: возобновленная траектория использует записанные сводки и версии, платит за одно холодное предзаполнение своего контекста и считает изменение модели, промпта или схемы инструмента явным дрейфом, а не продолжает молча.
  • Воспроизводит траекторию повтор, а не повторный запуск: заглушка модели и инструментов превращает журнал в отладчик с перемоткой во времени и регрессионный набор для обвязки при стоимости только вычислений обвязки. Повторный запуск расходится через сэмплирование, обслуживание, дрейф модели, входы обвязки и окружение.

В этой главе восстановление никогда не вызывает модель. Запись траектории пересчитывается из журнала, упреждающая запись ставит каждый эффект после его долговечного намерения, а контрольные точки ограничивают то, что перезапуск должен переделать. Это принцип «Намерение до действия» с его издержками: одна долговечная запись на каждый вызов, меняющий внешний мир, и частота контрольных точек, заданная частотой сбоев. Здесь же проходит граница механической половины замыкания инвариантов («Принцип замыкания инвариантов») для долговечности. Среда выполнения может гарантировать, что ни один эффект не останется незаписанным и что ни один завершенный эффект не повторится, но по одному журналу она не может решить, произошло ли действие под вопросом и был ли записанный эффект правильным.

От долговечной истории к исправлению ее ошибок

Журнал говорит, что произошло. Когда произошедшее было ошибкой и часть его нельзя отменить, как траектория восстанавливается? Этим вопросом занимается «Восстановление после сбоев». Она использует журнал этой главы, чтобы знать, какие эффекты зафиксированы, компенсирует те, на которые можно ответить другим действием, исправляет ход вперед через модель там, где эффект можно оставить, останавливается на единственном действии, которое нельзя отменить, и обнаруживает, когда траектория вообще перестала продвигаться.