Урок 3 из 8
Состояние: общая память процесса
Цель урока: понять, что такое состояние (State), почему это сердце любого графа и как правильно решать, что в нём хранить.
Зачем нужно состояние
Вспомни аналогию из урока 1: отдел, работающий по регламенту. Если сотрудники передают работу друг другу на словах - информация теряется. Поэтому заводят общую карточку заказа: каждый читает её и дописывает своё.
Состояние в LangGraph - это и есть такая карточка. По документации: «общая структура данных, представляющая текущий снимок приложения». Каждый узел получает состояние на вход и возвращает обновление - что изменилось.
Состояние - это бланк с полями, который ты сам придумываешь под свой процесс. Для обработки заявки: «текст заявки», «категория», «черновик ответа», «вердикт проверки». Проектирование состояния - это ответ на вопрос: что должны знать все участники процесса?
Главное правило узла
Узел не переписывает всю карточку - он возвращает только те поля, которые изменил. LangGraph сам аккуратно вливает обновление в общее состояние. Так узлы не мешают друг другу и не затирают чужую работу.
Проектируя состояние, ты отвечаешь на два вопроса: какие поля нужны (бланк) и какое поле заполняет каждый шаг (зона ответственности). Это работа на уровне здравого смысла - код тут не нужен.
Как это выглядит в коде - заглянуть по желанию
Читать код не нужно: работу ты принимаешь по схемам и поведению (урок 8), а пишет его Claude Code. Это «внутренности» для любопытных - каждая строчка переведена на человеческий.
# Состояние - «бланк» процесса обработки заявки
class State(TypedDict):
zayavka: str # исходный текст от клиента
kategoriya: str # оплата / техподдержка / продажа
chernovik: str # черновик ответа
verdict: str # вердикт проверки: ok / dorabotat
# Узел - функция: получает состояние, возвращает ЧТО ИЗМЕНИЛОСЬ
def klassifikator(state: State):
otvet = llm.invoke(f"Определи категорию: {state['zayavka']}")
return {"kategoriya": otvet.content} # только своё поле!
Поле-копилка и поле-перезапись
По умолчанию новое значение поля заменяет старое. Но некоторым полям нужно накапливаться: например, истории сообщений диалога. Для этого у поля назначают правило слияния - редьюсер (reducer).
Самый ходовой готовый вариант - MessagesState: состояние с полем messages, где сообщения диалога накапливаются автоматически. Для чат-приложений его берут как основу.
Правило: храни сырьё, а не готовое блюдо
Официальная методичка LangGraph советует: в состоянии храни сырые данные (список найденных документов, цифры, факты), а не отформатированный текст («Вот что я нашёл: ...»). Причина: сырые данные каждый узел может использовать по-своему, а красивый текст пригоден только для показа.
Свалить всё в одно поле «context», куда узлы дописывают текст. Через пять шагов там каша, из которой ничего не достать. Правильно: отдельное поле под каждый смысл - как отдельные графы в CRM, а не одна заметка «всё обо всём».
Проверь себя
1. Что возвращает узел после своей работы?
2. Полю «история сообщений диалога» нужен редьюсер-копилка. Зачем?
3. Агент нашёл 10 отзывов клиентов для анализа. Что положить в состояние?
Потренируйся проектировать: «Я делаю [твой процесс]. Помоги спроектировать состояние: какие поля нужны, какие из них копилки?» Именно так начинается любой LangGraph-проект.