Пауза и возобновление работы агентов LangGraph с использованием функций interrupt() и Command
Как функции interrupt() и Command в LangGraph используют чекпоинты и потоки для приостановки работы агента в ожидании одобрения, изменений или ввода от человека, а затем безопасного возобновления его работы позже.
Агент, способный перемещать деньги, удалять ресурсы или запускаться в производственной среде, не должен всегда иметь возможность завершить начатое дело. Иногда граф должен намеренно остановиться, подождать несколько часов на действия человека, а затем продолжить ровно с того места, где остановился. В этой статье объясняется, как LangGraph реализует это с помощью interrupt() и Command, почему для этого необходимы чекпоинты и потоки, а также какие решения вы всё ещё можете принять перед запуском агентов с участием человека в производственной среде.
Основные компоненты
Модель HITL состоит из трёх слоев. Во-первых, выполнение графа: состояние попадает в узел, а ребро выбирает следующий узел.
STATE
↓
NODE
↓
EDGE
↓
NEXT NODE
Во-вторых, управление состоянием: узлы возвращают обновления, а редьюсеры объединяют их в новое состояние.
NODE
↓
STATE UPDATE
↓
REDUCER
↓
NEW STATE
В-третьих, сохранение данных: чекпоинтер сохраняет состояние в виде групп чекпоинтов, связанных с определённым потоком.
STATE
↓
CHECKPOINT
↓
CHECKPOINTER
↓
THREAD
↓
PERSISTENCE
Все это позволяет агенту работать, запоминать информацию и выживать после перезагрузки. Однако отсутствует способ определить, следует ли ему вообще продолжать работу.
Когда агент не должен просто продолжать
Пользователь говорит помощнику по оплатам: «Переведите 50 000 рупий на этот счет». Агент готовит операцию, и технически ничто не мешает её выполнению.
User
↓
Agent
↓
Prepare Transfer
↓
Transfer ₹50,000
↓
Execute
При настолько значимом действии человек должен сначала рассмотреть предложение, чтобы процесс разветвлялся в зависимости от его решения.
User
↓
Agent
↓
Prepare Transfer
↓
┌─────────────────────┐
│ HUMAN APPROVAL │
└──────────┬──────────┘
│
┌───┴───┐
↓ ↓
Approve Reject
│ │
↓ ↓
Execute Cancel
Теперь граф процессов должен намеренно остановиться: не из-за ошибки или сбоя, а потому что здесь требуется вмешательство человека.
Что означает участие человека в процессе
HITL включает решения человека в автоматизированный процесс. Вместо того чтобы сразу переходить от решения к действию,
Agent
↓
Decision
↓
Action
агент предлагает действие, делает паузу и ждет его рассмотрения.
Agent
↓
Proposed Action
↓
PAUSE
↓
Human Review
↓
Decision
↓
Continue
Рецензент может одобрить, отклонить предложение, внести изменения, предоставить недостающие данные, исправить агента или выбрать один из вариантов. Это подходит для любого шага с реальными последствиями.
Financial transactions
Sending emails
Deleting resources
Production deployments
Database modifications
Legal workflows
Publishing content
Access approvals
Sensitive tool calls
Что на самом деле требуется для приостановки графа
Возьмем цепочку с шагом одобрения перед последним узлом.
Node A
↓
Node B
↓
Human Approval
↓
Node C
Если рецензент отвечает через шесть часов, блокировка процесса — неправильное решение: это тратит ресурсы рабочего процесса и приводит к сбою при первой перезагрузке.
sleep(21600)
Вместо этого следует сохранить состояние и остановиться,
Node B
↓
SAVE STATE
↓
PAUSE EXECUTION
↓
затем восстановить его позже, возможно, в другом процессе, и продолжить работу с следующим узлом.
Application can stop──────── 6 HOURS LATER ────────Human responds
↓
RESTORE STATE
↓
CONTINUE EXECUTION
↓
Node C
Постоянство уже решает половину проблемы. Точки контроля, механизм их управления, потоки и thread_id — именно то, что необходимо для длительной приостановки.
Checkpoint
Checkpointer
Thread
thread_id
Persistence
Как функция interrupt() останавливает выполнение
interrupt() приостанавливает работу графа и запрашивает значение у внешнего источника. Его вызывают внутри узла с данных, сериализуемыми в JSON, которые описывают, что должен увидеть проверяющий. Приведённый фрагмент написан на Python, несмотря на первоначальную метку.
from langgraph.types import interrupt
def approval_node(state):
decision = interrupt({
"question": "Do you approve this transaction?",
"amount": state["amount"]
})
return {
"decision": decision
}
Когда выполнение достигает этого вызова,
interrupt(...)
узел не доходит до оператора возврата. Работа приостанавливается, а переданные данные возвращаются вызывающему для отображения.
approval_node
│
↓
interrupt()
│
↓
┌─────────────────┐
│ EXECUTION PAUSED│
└─────────────────┘
Важны два момента. interrupt() требует графа, скомпилированного с использованием чекпоинтера, поскольку состояние приостановки должно где-то храниться. При возобновлении работы LangGraph перезапускает прерванный узел с начала, причём interrupt() теперь возвращает значение, указанное человеком, поэтому код до вызова выполняется дважды. Избегайте побочных эффектов в этой части узла и проверьте поведение для вашей версии в документации LangGraph.
Что сохраняется во время прерывания
Рассмотрим этот график.
START
↓
Prepare Transfer
↓
Human Approval
↓
Execute Transfer
↓
END
Узел подготовки обновляет состояние, записывается контрольная точка, а узел одобрения вызывает interrupt().
START
↓
Prepare Transfer
↓
State Updated
↓
Checkpoint
↓
Approval Node
↓
interrupt()
Текущее состояние захватывается и записывается в хранилище.
Current State
│
↓
Checkpoint
│
↓
Persistent Storage
Теперь поток содержит детали передачи, статус ожидания и причину остановки.
THREAD: transaction-123
Checkpoint
────────────────────
amount ₹50,000
recipient Account X
status pending
────────────────────
Execution:
PAUSED
Reason:
Human approval required
Это всё, что необходимо для продолжения работы позже. Без сохранения информации процесс просто исчезнет после прерывания.
Agent
↓
interrupt
↓
process disappears
↓
???
Когда проверяющий указывает «Одобрено» на следующий день, никто не может сказать, что именно было одобрено, где остановился график или какой узел следует далее. С помощью контрольных точек состояние загружается из потока и восстанавливается перед возобновлением работы.
INTERRUPT
│
↓
CHECKPOINT
│
↓
THREAD
│
↓
PERSISTENT STORAGE
...time passes...
HUMAN INPUT
│
↓
THREAD
│
↓
LOAD CHECKPOINT
│
↓
RESTORE STATE
│
↓
RESUME
Возобновление с помощью команды
Приложение отображает содержимое прерывания в виде вопроса
Approve transfer of ₹50,000?
с двумя кнопками,
[ APPROVE ] [ REJECT ]
и рецензент выбирает одну из них.
APPROVE
Ответ возвращается через Command, который указывает графу, как действовать дальше. Чтобы возобновить работу, вызовите граф с Command(resume=...) вместо обычного ввода.
from langgraph.types import Command
graph.invoke(
Command(resume="approved"),
config=config
)
config должен использовать тот же поток, что и при приостановленной работе,
thread_id = transaction-123
потому что идентификатор потока — это то, по чему LangGraph определяет, какую приостановленную задачу имеется в виду.
Этапы жизненного цикла пошагово
Работа начинается с обычного вызова,
graph.invoke()
затем берется информация о потоке из конфигурации,
thread_id = transaction-123
после чего происходит вход в узел подготовки,
START
↓
Prepare Transfer
и информация о передаче записывается в состояние.
amount = ₹50,000
recipient = Account X
status = pending
Узел одобрения вызывает interrupt(),
Approval Node
↓
interrupt()
и выполнение приостанавливается.
EXECUTION PAUSED
Последний точка контроля остается в безопасности под потоком.
Thread
↓
Checkpoint
↓
Persistent Storage
Человек видит запрос
Approve ₹50,000 transfer?
и отвечает на него.
YES
Приложение отправляет команду возобновления
Command(resume="approved")
в тот же поток.
transaction-123
LangGraph восстанавливает выполнение, передаёт решение узлу одобрения и продолжает до конца.
Restore Execution
↓
Human Decision = approved
↓
Continue Graph
↓
Execute Transfer
↓
END
Каждый из приведённых ниже вариантов является модификацией этого жизненного цикла.
Человеческие решения, выходящие за рамки «да» или «нет»
Ответ рецензента не обязательно должен быть двоичным.
YES / NO
Одобрение приводит к реализации предложения.
Agent Proposal
↓
Human
↓
APPROVE
↓
Execute
Отклонение аннулирует его.
Agent Proposal
↓
Human
↓
REJECT
↓
Cancel
Редактирование изменяет часть предложения, например сумму, прежде чем граф продолжит работу.
Agent:
Send ₹50,000
↓
Human:
Change to ₹40,000
↓
Graph continues with
updated information
Предоставление недостающей информации превращает перерыв в вопрос.
Agent
↓
Missing Account Number
↓
INTERRUPT
↓
Human provides account
↓
RESUME
Выбор позволяет агенту предложить альтернативы и оставить решение за человеком.
Agent generates:
Option A
Option B
Option C
↓
Human chooses B
↓
Graph continues
HITL — это общий способ ввода данных от человека в работающую графику, а не только диалог подтверждения.
Команда как поток данных и поток управления
Команда также может обновлять состояние и выбирать следующее направление.
Resume Execution
Update State
Choose Next Destination
Одна команда может установить статус и отправить выполнение к конкретному узлу; в LangGraph такая форма обычно возвращается из узла.
Command(
update={
"status": "approved"
},
goto="execute"
)
Это объединяет два аспекта,
DATA FLOW
+
CONTROL FLOW
поскольку маршрутизация раньше относилась только к ребрам.
Edges control where we go.
Теперь один объект может содержать как изменение состояния, так и следующий шаг.
Command can participate in
state updates + control decisions.
Исправление состояния перед продолжением
Предположим, что в состоянии хранится эта сумма,
amount = 50000
и рецензент одобряет её с внесением корректировок.
Approve, but change amount to 40000.
Поток данных идёт от первоначального значения, через процедуру рецензирования, к обновлённому значению, и только затем возобновляется (второй фрагмент кода выводится на одну строку).
BEFORE
amount = 50000 ↓Human Review ↓UPDATEamount = 40000 ↓RESUME
Вопрос меняется с «Могу ли я продолжить?» на «Проверить, при необходимости исправить, затем продолжить».
Контроль вызовов чувствительных инструментов
HITL наиболее эффективен перед использованием рискованных инструментов.
search_web()
read_database()
send_email()
delete_resource()
transfer_money()
Поиск в интернете может выполняться автоматически,
search_web()
↓
Execute Automatically
в то время как удаление ресурса должно ждать одобрения.
delete_resource()
↓
INTERRUPT
↓
Human Approval
Каждый вызов инструмента проходит проверку на чувствительность: безопасные вызовы выполняются, а чувствительные прерываются для получения одобрения или отмены.
Agent
│
↓
Tool Call
│
↓
Sensitive Tool?
/ \
NO YES
│ │
↓ ↓
Execute INTERRUPT
│ │
│ ↓
│ Human
│ / \
│ Approve Reject
│ │ │
└─────────┘ ↓
Cancel
Для более полной реализации с использованием хранилища см. агентов с контролем одобрения в LangGraph.
Проверка как целенаправленная граница контроля
Проверка человеком — это не способ исправить агента, который недостаточно умён. Это граница, которую вы устанавливаете намеренно. Работы с низким риском выполняются автоматически,
LOW RISK
Search
Read
Summarize
↓
Automatic
а работы с высоким риском проходят через человека.
HIGH RISKSend
Delete
Purchase
Transfer
Deploy
↓
Human Approval
Цель — определить, где целесообразна автономия, а не уменьшать её везде.
Обработка отклонений и молчания
Отклонения требуют тщательного проектирования.
Agent
↓
Prepare Action
↓
Interrupt
↓
Human
↓
REJECT
Записывайте результат в состояние,
status = "rejected"
затем направляйте выполнение с использованием условного ребра.
Decision
/ \
approved rejected
│ │
↓ ↓
Execute Cancel
│ │
↓ ↓
END END
Все предыдущие концепции участвуют в процессе, а ввод человека — это просто дополнительная информация, влияющая на выполнение.
State
Reducers
Conditional Routing
Checkpoints
Threads
Commands
Рецензенты также могут никогда не ответить.
Interrupt
↓
Waiting for approval
↓
1 hour
↓
1 day
↓
7 days
Задержанный поток может ожидать бесконечно, поэтому вашему приложению нужны правила управления.
Expiration
Timeout
Cancellation
Reminder
Escalation
Распространенным решением является ограничение времени ожидания, обычно реализуемое расписателем, который возобновляет устаревшие потоки при истечении времени ожидания.
Approval Requested
│
↓
Wait up to 24 hours
│
┌───┴────┐
↓ ↓
Response Timeout
│ │
↓ ↓
Resume Cancel
LangGraph предоставляет примитивы; бизнес-правила определяются вами.
Множественные контрольно-пропускные пункты и потоковая обработка
Агент развертывания может запросить одобрение своего плана, а затем снова — перед запуском в производство.
START
↓
Analyze Code
↓
Generate Deployment Plan
↓
Human Approval #1
↓
Deploy to Staging
↓
Run Tests
↓
Human Approval #2
↓
Deploy Production
↓
END
Каждый контрольно-пропускной пункт приостанавливает и возобновляет тот же поток, оставляя историю точек контроля и прерываний.
Thread
│
├── CP1
│
├── CP2
│
├── INTERRUPT
│
├── RESUME
│
├── CP3
│
├── INTERRUPT
│
├── RESUME
│
└── END
Это долговечный рабочий процесс, используемый как людьми, так и агентом. До сих пор во всех примерах использовались блокирующие вызовы,
graph.invoke(...)
которые возвращаются только при завершении выполнения или приостановке.
START → A → B → C → END
↓
RESULT
Интерактивные приложения требуют отображения прогресса в реальном времени, до момента подачи заявки на утверждение.
START
↓
Planner Update ─────────→ UI
↓
Tool Call ──────────────→ UI
↓
Tool Result ────────────→ UI
↓
Approval Required ──────→ UI
Стриминг передает эти события, что позволяет интерфейсу отображать завершённые шаги и ожидаемое решение.
┌──────────────────────────────────┐
│ Deployment Agent │
├──────────────────────────────────┤
│ │
│ ✓ Analyzed repository │
│ │
│ ✓ Generated deployment plan │
│ │
│ ✓ Staging tests passed │
│ │
│ ⚠ Production deployment ready │
│ │
│ Human approval required │
│ │
│ [ APPROVE ] [ REJECT ] │
│ │
└──────────────────────────────────┘
За этим экраном события поступают на фронтенд, а решение возвращается через Command(resume=...).
LangGraph
│
↓
Streaming
│
↓
Frontend
│
↓
Human Decision
│
↓
Command(resume=...)
│
↓
LangGraph
Полная модель выполнения
На одном диаграмматическом изображении: пользователь запускает процесс на определённом потоке, состояние перемещается между узлами и редюсерами, записываются контрольные точки, маршрутизация выбирает следующий шаг, а чувствительные действия перенаправляются на прерывание, которое решает человек.
USER
│
↓
graph.invoke()
│
↓
THREAD
│
↓
CHECKPOINTER
│
↓
STATE
│
↓
NODE
│
↓
STATE UPDATE
│
↓
REDUCER
│
↓
NEW STATE
│
↓
CHECKPOINT
│
↓
EDGE / ROUTING
│
↓
SENSITIVE ACTION?
/ \
NO YES
│ │
↓ ↓
NEXT NODE interrupt()
│
↓
PAUSED
│
↓
HUMAN
/ | \
/ | \
Approve Edit Reject
│ │ │
└────────┼────────┘
↓
Command(resume)
│
↓
SAME thread_id
│
↓
RESTORE CHECKPOINT
│
↓
CONTINUE GRAPH
│
↓
END
С точки зрения сюжета, поступает запрос.
User Input
и начинается выполнение.
START
Узлы считывают состояние и возвращают обновления, редьюсеры объединяют их, чекпоинты сохраняют их в рамках определенной нити, а ребра выбирают, что будет выполняться далее. Граф может разветвляться, содержать циклы или вызывать инструменты.
BRANCH
LOOP
CALL TOOLS
Когда требуется участие человека, прерывание приостанавливает выполнение.
interrupt()
Человек отвечает,
APPROVE
REJECT
EDIT
PROVIDE INPUT
команда возобновления продолжает работу с той нити, где данные были сохранены,
Command(resume=...)
и тогда граф завершается.
END
Большинство проектов начинаются с одного вызова модели,
LLM
↓
Response
и заканчиваются графом, объединяющим узлы LLM и инструментов, редьюсеры, чекпоинты, механизмы маршрутизации, прерывания и команды.
USER
│
↓
START
│
↓
STATE
│
↓
NODE
│
┌──────┴──────┐
│ │
LLM TOOL
│ │
└──────┬──────┘
↓
STATE UPDATE
│
↓
REDUCER
│
↓
NEW STATE
│
↓
CHECKPOINT
│
↓
THREAD
│
↓
ROUTING
/ | \
/ | \
NODE LOOP INTERRUPT
│
↓
HUMAN
/ | \
Approve Edit Reject
\ | /
\ | /
↓
COMMAND
│
↓
RESUME
│
↓
NEXT NODE
│
↓
END
Чек-лист для производственной среды
Сохраняйте фокус узлов
Маленькие узлы с одной функцией легче тестировать, перепробовать и безопасно прерывать,
Plan Node
Search Node
Validate Node
Execute Node
в отличие от узла, который выполняет всё.
do_everything_agent()
Целенаправленный дизайн состояния и редюсеров
Сохраняйте только то, что необходимо узлам, поскольку всё сохраняется в формате чекпоинтов и может быть показано ревизорам. Тщательно выбирайте редюсеры для полей, в которых накапливаются данные.
messages
events
results
observations
Сделайте побочные эффекты идемпотентными
Операции, которые нельзя откатить, требуют особого внимания.
Send Email
Charge Card
Delete Resource
Deploy
Transfer Money
Повторные попытки и возобновление работы никогда не должны повторять эти операции. Поскольку прерванный узел запускается заново при возобновлении, размещайте непервичные вызовы после точки утверждения или в отдельном узле, а также используйте ключи идемпотентности там, где это поддерживают API.
Используйте надежное хранение
Чекпоинты в памяти подходят для обучения; в производственных условиях требуется надежное хранилище, соответствующее вашим требованиям к надежности.
Запишите границы и ограничения
Явно определите их для каждого рабочего процесса,
What can AI do automatically?
What requires approval?
What can humans modify?
When does approval expire?
и укажите условия завершения циклов.
Maximum attempts
Timeouts
Validation
Failure paths
Escalation
Когда HITL оправдан
Ручная проверка приносит пользу в определенных ситуациях.
Cost of wrong action is high
Action is irreversible
Policy requires approval
Agent confidence is insufficient
Sensitive information is involved
Human judgment adds value
Расположенные повсюду, они препятствуют автоматизации и заставляют проверяющих переходить дальше, не читая содержимое.
Agent
↓
Ask Human
↓
Agent
↓
Ask Human
↓
Agent
↓
Ask Human
Лучший дизайн предоставляет агенту автономную зону для планирования и рассуждений с одним границей перед выполнением действия.
AUTONOMOUS ZONE
┌───────────────────────┐
│ │
│ Plan → Search → Think │
│ │
└───────────┬───────────┘
│
↓
CONTROL BOUNDARY
│
↓
HUMAN
│
↓
APPROVED ACTION
Как соединяются слои
Выполнение графа включает узлы, ребра и маршрутизацию;
Graph
Nodes
Edges
START
END
Loops
Conditional Routing
Управление состоянием включает обновления и редьюсеры;
State
State Updates
Reducers
Messages
State Evolution
Постоянство данных включает точки контроля, потоки и восстановление;
Checkpoints
Checkpointer
Threads
thread_id
State History
Recovery
А HITL добавляет прерывания, команды и шаблоны проверки.
interrupt()
Command
Pause
Resume
Approve
Reject
Edit
Human Control
Сложенные вместе, они образуют одну цепочку обработки.
GRAPH
↓
STATE
↓
NODE
↓
UPDATE
↓
REDUCER
↓
CHECKPOINT
↓
THREAD
↓
ROUTING
↓
INTERRUPT
↓
HUMAN
↓
COMMAND
↓
RESUME
↓
END
Основные выводы
Лексика может показаться слишком обширной,
StateGraph
MessagesState
Reducers
Checkpointers
Threads
Commands
Interrupts
Но каждый компонент решает одну часть единой цели: агент, который знает информацию, действует, обновляется, принимает решения, сохраняет прогресс, останавливается в нужный момент, запрашивает у человека и безопасно возобновляет работу.
Remember what it knows
↓
Perform some work
↓
Update what it knows
↓
Decide what to do next
↓
Persist its progress
↓
Pause when necessary
↓
Ask a human
↓
Resume safely
↓
Continue until complete
- Состояние — это то, что знает агент; узлы — это действия, которые он выполняет; рёбра — направления его движения; редюсеры — способы изменения состояния.
- Чекпоинты фиксируют момент, в котором агент находился; потоки указывают, к какому историческому состоянию относится выполнение.
- Функция
interrupt()определяет момент остановки и запроса информации; объектCommandуказывает, как продолжить работу, всегда в том же потоке. - Перезапускаемые узлы возобновляют работу после прерывания, поэтому необходимо сохранять необратимые побочные эффекты после точки утверждения.
- Проводите проверку на реальных границах управления с чёткими правилами истечения времени и эскалации.