Агенти LangGraph нарешті отримали надійний спосіб зберігання свого стану після тижнів безшумної втрати даних. Після трьох невдалих спроб реалізації чекпоінтів — SQLite, сирого об'єктного сховища та поламаної версії кожного з них — автор зупинився на патерні атомарного оновлення, який запобігає перезапуску агентів з нуля при кожному новому запиті.

Чому чекпоінтинг важливий для LangGraph

LangGraph дозволяє розробникам об'єднувати виклики LLM у багаторазові «агенти», які можуть пам'ятати, що відбувалося раніше в розмові. Ці агенти розбивають запит користувача на підзавдання, зберігають проміжні результати та продовжують роботу з того місця, де зупинилися, під час наступного виклику. Якщо збережений стан зникає, агент перераховує все заново, витрачаючи обчислювальні ресурси, збільшуючи затримку та погіршуючи досвід користувача. У роботі продуктового бота, що обробляє повідомлення в Telegram, втрата даних стерла тижні історії розмов.

Перше рішення: SQLite saver

Вбудований SqliteSaver працює добре, коли агент запускається в одному екземплярі. Він записує кожен чекпоінт як JSON-об'єкт у локальний файл SQLite. Проблеми почалися, коли розробник додав нове поле до типу AgentState і зробив редеплой. Існуючі чекпоінти, створені до зміни схеми, не мали нового поля. Оскільки SqliteSaver ніколи не запускає міграцію, LangGraph завантажував неповний JSON, видаляв відсутні дані, і агент перезапускався з самого початку.

Ключовий момент: сховище SQLite — це інструмент для демо-версій, а не готове до продакшену рішення, коли необхідна еволюція схеми.

Друге рішення: Об'єктне сховище

Щоб отримати контроль над форматом серіалізації, автор написав власний сейвер, який завантажував JSON-чекпоінт в Oracle Cloud Object Storage. Цей крок дав можливість вручну версіонувати схему, але спричинив новий тип помилки. Коли два запити одночасно надходили до одного потоку розмови, обидва намагалися перезаписати один і той самий об'єкт. Сервіси об'єктного сховища оптимізовані для патернів «запис один раз, читання багато разів»; вони не забезпечують семантики атомарного перезапису. Стан гонитви (race condition) призводив до створення пошкоджених або урізаних JSON-файлів, і агент знову втрачав свій контекст.

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

Третє рішення: Атомарні оновлення з версіонуванням

Остаточний стабільний дизайн поєднує дві ідеї: явні номери версій та умовні записи на основі ETag об'єкта (ідентифікатора контрольної суми сервісу зберігання).

  1. Зчитайте поточний чекпоінт і зафіксуйте його ETag.
  2. Збільште поле версії всередині оболонки чекпоінту.
  3. Запишіть оновлений чекпоінт за допомогою умовного запиту, який завершиться успішно лише в тому випадку, якщо ETag збігається з тим, що було зчитано раніше.
  4. Повторіть увесь цикл «читання-збільшення-запису», якщо умовний запис не вдався через те, що інший процес змінив об'єкт.

Оскільки запис завершується успішно лише тоді, коли жоден інший процес не змінив файл, лише один воркер може зафіксувати новий стан за один раз. Поле версії також дозволяє легко виявляти застарілі чекпоінти та мігрувати їх вперед при зміні схеми.

Цей патерн працює з об'єктними сховищами, які підтримують умовні записи на основі ETag.

Уроки для AI-інженерів

  • Використовуйте SQLite лише для прототипів. Продуктовим агентам потрібне сховище, яке може обробляти зміни схеми та паралельні записи.
  • Плануйте міграції схеми самостійно. Типізовані словники описують структуру для статичного аналізу, але не забезпечують структуру під час виконання.
  • Ставтеся до стану як до спільного ресурсу. Помилки паралелізму проявляються як безшумна втрата даних; їх важче відлагодити, ніж явні винятки (exceptions).
  • Використовуйте хмарні примітиви. Умовні записи на основі ETag забезпечують дешеве оптимістичне блокування без використання окремого сервісу блокувань.
  • Логуйте кожен крок. Безшумні помилки — наприклад, відсутнє поле, яке LangGraph ігнорує — найважче відстежити.

Що далі для чекпоінтингу в LangGraph?

Для команд, які вже стикнулися з подібними перешкодами, рецепт атомарного оновлення пропонує швидке та недороге рішення. Він показує, що надійний продуктовий конвеєр не потребує важковагового сховища станів — лише ретельного керування паралелізмом та версіонуванням.

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