Запись архива

Персистентное состояние в LangGraph: настройка SQLite и PostgreSQL checkpointer для российских AI-агентов

Как настроить MemorySaver, SQLite и PostgreSQL в LangGraph для сохранения состояния агентов. Разбор checkpointer, управление контекстом и отказоустойчивость в продакшене.

Схема архитектуры персистентного состояния в LangGraph с checkpointer на SQLite и PostgreSQL
Схема архитектуры персистентного состояния в LangGraph с checkpointer на SQLite и PostgreSQL
Butterfly Dreams | by Pink Poppy Photography | openverse | by

Разработка надежных AI-агентов на Python требует решения проблемы сохранения контекста. LLM по умолчанию не хранят состояние между вызовами, что делает невозможным восстановление после сбоев сети, превышения лимитов API или перезапуска сервера. LangGraph решает эту задачу через механизмы персистентности и контрольных точек. В этой статье мы разберем, как настроить checkpointer на SQLite и PostgreSQL, и рассмотрим практические аспекты их использования в российских проектах.

Проблема stateless LLM и решение LangGraph

В классических цепочках LangChain память реализуется через `ChatMessageHistory` — массив сообщений, передаваемый при каждом вызове модели. Это приводит к двум проблемам: превышение контекстного окна и отсутствие гранулярности шагов. Если агент ошибся на 15-м шаге, откатить выполнение к 12-му без потери всего сеанса невозможно.

LangGraph рассматривает приложение как граф состояний (StateGraph). Каждый узел принимает текущее состояние, выполняет логику и возвращает обновления. Checkpointer перехватывает эти обновления после завершения работы каждого узла и сохраняет их в хранилище. Это превращает граф в отказоустойчивую машину состояний, способную возобновлять работу с любой точки.

Настройка MemorySaver для локальной разработки

Для тестирования в изолированной среде используйте `MemorySaver` из пакета `langgraph-checkpoint-memory`. Он хранит историю в оперативной памяти процесса, что удобно для отладки, но данные исчезают при перезагрузке.

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

python
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
from langchain_core.messages import AnyMessage

class AgentState(TypedDict):
messages: list[AnyMessage]

def call_model(state: AgentState):
return {«messages»: [f»Обработано сообщений: {len(state[‘messages’])}»]}

workflow = StateGraph(AgentState)
workflow.add_node(«agent», call_model)
workflow.add_edge(START, «agent»)
workflow.add_edge(«agent», END)

memory = MemorySaver()
app = workflow.compile(checkpointer=memory)

При вызове графа передайте `thread_id` в конфигурации:

python
config = {«configurable»: {«thread_id»: «session_user_001»}}
response = app.invoke({«messages»: [«Привет, агент!»]}, config)

Повторный вызов с тем же `thread_id` извлечет предыдущее состояние и продолжит работу с накопленным контекстом.

Переход на SQLite для продакшен-прототипов

`MemorySaver` не подходит для продакшена из-за потери данных при перезапуске. LangGraph поддерживает бэкенды на базе реляционных баз данных через пакеты `langgraph-checkpoint-sqlite` и `langgraph-checkpoint-postgres`.

Подключение SQLite:

python
import sqlite3
from langgraph.checkpoint.sqlite import SqliteSaver

conn = sqlite3.connect(«checkpoints.db», check_same_thread=False)
memory_db = SqliteSaver(conn)

app_persistent = workflow.compile(checkpointer=memory_db)

Преимущества SQLite checkpointer:
— Отказоустойчивость: состояние сохраняется на диске.
— Инспекция сессий: можно запросить таблицы напрямую.
— Низкие накладные расходы для однопоточных приложений.

Недостаток: SQLite не поддерживает конкурентную запись из нескольких процессов. Для веб-серверов на FastAPI с несколькими воркерами используйте PostgreSQL.

Настройка PostgreSQL checkpointer для высоконагруженных систем

PostgreSQL обеспечивает масштабирование и конкурентный доступ. Установите пакет:

bash
pip install langgraph-checkpoint-postgres

Пример подключения:

python
from langgraph.checkpoint.postgres import PostgresSaver
import asyncpg

connection_string = «postgresql://user:password@localhost:5432/langgraph»
conn = await asyncpg.connect(connection_string)
checkpointer = PostgresSaver(conn)

app = workflow.compile(checkpointer=checkpointer)

Для синхронного использования применяйте `psycopg`:

python
import psycopg
from langgraph.checkpoint.postgres import PostgresSaver

conn = psycopg.connect(connection_string)
checkpointer = PostgresSaver(conn)

Таблица `checkpoints` создается автоматически. Вы можете настроить пул соединений через `psycopg_pool` для асинхронных веб-серверов.

Сравнение checkpointer: SQLite vs PostgreSQL vs MemorySaver

Ниже приведена таблица сравнения основных характеристик:

Параметр MemorySaver SQLite PostgreSQL
Хранение данных Оперативная память Файл на диске СУБД
Сохранение после перезапуска Нет Да Да
Конкурентный доступ Один поток Один процесс Много процессов
Производительность Высокая Средняя Средняя
Простота настройки Минимальная Простая Требует настройки СУБД
Подходит для Разработка, тесты Прототипы, малые проекты Продакшен, микросервисы

Выбор зависит от требований к отказоустойчивости и масштабированию. Для российских проектов с ограниченными ресурсами SQLite — оптимальный стартовый вариант.

Управление размером контекста и очистка старых checkpoints

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

Архитектурные подходы для предотвращения:
1. Ограничение глубины истории (TTL): настройте периодическую очистку записей для сессий, неактивных более 30 дней. Например, через SQL-запрос:
sql
DELETE FROM checkpoints WHERE created_at < NOW() - INTERVAL '30 days'; 2. Редукция состояния (State Reducers): используйте функции агрегации, например `add_messages` из LangChain, для суммаризации старых сообщений.
3. Изоляция баз данных: выделите отдельный инстанс PostgreSQL под персистентность агентов, чтобы сбои не затрагивали основную базу приложения.

Путешествие во времени и откат состояния

LangGraph поддерживает «путешествие во времени» через `get_state_history`. Это позволяет получить список всех контрольных точек и перезапустить выполнение из любой точки прошлого.

Пример получения истории:

python
config = {«configurable»: {«thread_id»: «session_user_001»}}
history = list(app_persistent.get_state_history(config))

for state in history:
print(f»Checkpoint ID: {state.config[‘configurable’][‘checkpoint_id’]}»)
print(f»Next nodes: {state.next}»)
print(f»State values: {state.values}\n»)

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

Практические рекомендации для российских разработчиков

Перед внедрением персистентности в рабочий контур проверьте:
— Убедитесь, что все объекты в состоянии графа корректно сериализуются. Избегайте передачи открытых сокетов, файловых дескрипторов или сложных лямбда-функций.
— Для многопоточных веб-серверов на FastAPI или Celery используйте пулы соединений с БД. Для PostgreSQL применяйте `psycopg_pool`, для SQLite — `check_same_thread=False`.
— Протестируйте поведение агента принудительным завершением процесса (SIGKILL) посреди выполнения шага. Транзакционность бэкенда должна защищать данные от повреждения.
— Настройте мониторинг размера таблиц `checkpoints` и `checkpoint_writes`. При превышении лимита в 1 ГБ для SQLite или 10 ГБ для PostgreSQL — запускайте очистку.

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

Источники:
— github.com/langchain-ai/langgraph
— langchain-ai.github.io/langgraph/
— langchain-ai.github.io/langgraph/how-tos/persistence/
— python.langchain.com/docs/concepts/langgraph/