Прерывания (interrupts), паттерны одобрения и редактирования, валидация ввода, правила прерываний.
Прерывания позволяют приостановить выполнение графа в любой точке и дождаться внешнего ввода — это основа паттерна «человек в цикле».
При вызове interrupt() внутри узла выполнение приостанавливается, состояние сохраняется через чекпоинтер. Граф ждёт неограниченное время до возобновления. Значение Command(resume=...) становится возвращаемым значением interrupt().
from langgraph.types import interrupt
def approval_node(state: State):
approved = interrupt("Вы одобряете это действие?")
return {"approved": approved}Для работы прерываний нужны: чекпоинтер для сохранения состояния, thread_id для идентификации потока и JSON-сериализуемое значение в interrupt().
Самый частый случай — пауза перед критическим действием. Узел вызывает interrupt с данными для отображения, а при возобновлении получает решение пользователя:
from langgraph.types import interrupt, Command
def approval_node(state: State) -> Command[Literal["proceed", "cancel"]]:
decision = interrupt({
"question": "Вы хотите продолжить?",
"details": state["action_details"],
})
return Command(goto="proceed" if decision else "cancel")Возобновление: graph.stream_events(Command(resume=True), config=config, version="v3") — для одобрения, Command(resume=False) — для отклонения.
Иногда нужно не просто одобрить, а отредактировать данные. Узел показывает текущее содержимое, а при возобновлении получает отредактированную версию.
Прерывания можно размещать внутри инструментов — инструмент приостанавливается перед выполнением. Это позволяет одобрять, редактировать или отменять вызовы инструментов.
Когда несколько параллельных веток вызывают interrupt, нужно возобновить все сразу через словарь {id_прерывания: значение}.
Для проверки ввода пользователя вызывайте interrupt() один раз за вызов узла. При некорректном вводе возвращайте обновлённое состояние с сообщением об ошибке и используйте условное ребро для повторного запроса. Не используйте while True с interrupt() — при каждом возобновлении узел выполняется заново, и цикл воспроизводит все предыдущие итерации.
def get_age_node(state: FormState):
question = state.get("pending_question") or "Какой вам возраст?"
answer = interrupt(question) # один раз за вызов
if isinstance(answer, int) and answer > 0:
return {"age": answer, "pending_question": None}
return {"pending_question": f"'{answer}' — некорректный возраст."}Не оборачивайте interrupt в try/except — он работает через специальное исключение. Не меняйте порядок вызовов interrupt между выполнениями — сопоставление строго индексное. Не пропускайте вызовы условно. Используйте идемпотентные операции до interrupt — узел выполняется заново при возобновлении.
Для пошаговой отладки графа можно задать interrupt_before и interrupt_after при компиляции — выполнение приостановится перед или после указанного узла.
Далее: Функциональный API