LangGraph Pending Writes

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

Суть

Модель супершагов даёт атомарность коммита снапшота состояния — следующий шаг не увидит частичную картину. Отсюда легко сделать неверный вывод, будто при падении узла «откатывается вся итерация» и вся работа шага пропадает. Это не так, и различие практически важно (Pregel Model).

Сохранение идёт на уровне отдельного узла, а не только шага. Как только узел внутри супершага завершился, его вывод записывается отдельной записью. Если сосед по шагу падает, результаты успешных уже сохранены, и переигрывается только упавший.

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

Чем это ограничено

Режимом устойчивости. Момент сохранения задаётся параметром durability на вызове: "sync", "async" или "exit". В режиме "exit" промежуточное состояние не сохраняется вовсе — записи происходят только на выходе из прогона, и тогда вся конструкция с pending writes для этого прогона не работает. Выбор режима — это выбор между стоимостью записи и живучестью (Durable Execution).

Границей возобновления. Возобновление всё равно происходит с границы супершага, а не с произвольной точки внутри узла. Узел, упавший на середине, начинается с первой строки функции.

Что из этого следует для внешнего мира

Ровно ничего. Pending writes защищают состояние графа, а не побочные эффекты: вызов, который узел успел сделать наружу до падения, уже произошёл. Штатный обход внутри узла — обёртка @task, результат которой чекпоинтится отдельно, так что при возобновлении завершённая подзадача не выполняется заново.

Но и она не отменяет главного правила: ключ идемпотентности лежит на инструменте, а не на оркестраторе (Idempotency For Agents).

Связано с