Human-in-the-loop interrupts
Scenario
A developer puts a standard Python `input("Approve this action?")` command in their AI agent to ask the user for permission. They deploy it to a cloud server.
What happens when the agent reaches this line in production?
Mental model
An interrupt is not a pause button; it's a save-and-quit button.
When you pause a video game, the console stays on and burns electricity. When you interrupt an agent, it saves the game and unplugs the console entirely. When the human finally returns to approve the action (even days later), the console plugs back in, loads the save, and continues.
Deep dive
Agents that execute destructive actions (like dropping a database, spending money, or sending emails) need **human-in-the-loop (HITL)** approval gates. We cannot trust the AI to make the final call on dangerous operations.
Dangerous agent actions require explicit human approval before execution.
You cannot use `input()` or `time.sleep()` to pause an agent. If you do, the server process stays alive waiting for an answer. In the cloud (like AWS Lambda), this will hit a timeout limit in 30 seconds, crash, and lose all memory of what the agent was doing.
Standard code pauses cause cloud timeouts and destroy agent state.
Instead, production frameworks use **interrupts**. An `interrupt()` suspends the graph and uses a checkpointer to save the state to a database. The server process then shuts down completely. Hours or days later, an external API call resumes the graph, injecting the human's response.
Interrupts save state to a database and shut down the process while waiting.
Prediction: A developer puts `send_email()` and then `interrupt()` inside the *same* agent node. The graph pauses. A day later, the human approves. The agent resumes... and sends the email again. Why? Because when a node resumes from an interrupt, the framework restarts that specific node from the top. Non-idempotent actions must be in separate nodes.
When an interrupted node resumes, all code before the interrupt re-executes.
Code examples
Safely interrupting and resuming an agent
from langgraph.types import interrupt, Command
def approval_node(state):
# 1. The graph suspends here, saves state, and dies cleanly.
user_decision = interrupt("Do you approve this $500 purchase?")
if user_decision == "yes":
return {"next_node": "buy_item"}
return {"next_node": "cancel"}
# --- DAYS LATER, in a different file/server request ---
# 2. The human clicked 'Yes' in a UI. We resume the graph.
# The 'resume' value is injected directly into 'user_decision' above.
graph.invoke(
Command(resume="yes"),
config={"configurable": {"thread_id": "123"}}
)
This is a durable pause. The server does not hang while waiting for the human. It completely stops. Later, `Command(resume=...)` wakes the graph back up and passes the human's answer exactly where the code left off.
Common mistakes
- Using blocking inputs instead of interrupts: Human-in-the-loop is not achieved by placing an `input()` prompt in the agent's code. Local CLI prototypes do this, but in production, this blocks the server thread and hits timeouts. An `interrupt()` safely suspends the graph and serializes state to a database, allowing the process to terminate.
- Assuming code before an interrupt only runs once: When a graph resumes after an `interrupt()`, execution picks up by restarting the node from the beginning. Any code preceding the `interrupt()` call inside that node re-executes. If you place a 'charge credit card' function right before the interrupt in the same node, the card will be charged a second time when the human approves.
Glossary
- human-in-the-loop (HITL)
- A system design where an AI must pause and ask a human for permission before taking a dangerous action.
- interrupt
- A specific command that suspends the agent graph, saves its state, and cleanly shuts down the server process while waiting.
Recall questions
- Why is it dangerous to use `time.sleep()` or `input()` to wait for human approval in a cloud-hosted agent?
- How does an agent know what the human decided after waking up from an interrupt?
- A developer puts an API call to a billing system and an `interrupt()` inside the exact same node. What bug will this cause when the graph resumes?
Questions & answers
Design an approval flow for an agent that drops a SQL table. How do you prevent the table from dropping while waiting for approval, without causing a server timeout?
Use a durable interrupt framework. The agent reaches an approval node, calls `interrupt()`, and halts. The state is saved to a checkpointer. A separate UI system alerts a human. Upon approval, the UI makes a new API call with `resume=True`, which loads the state and allows the graph to proceed to the actual execution node.
Approach: Demonstrate the difference between a blocking thread pause and a durable, stateless checkpoint interrupt.
Why can't you achieve human-in-the-loop by simply placing a `time.sleep()` or `input()` block inside a cloud-hosted agent?
Blocking the execution thread will cause the cloud function (e.g., AWS Lambda) to hit its timeout limit and terminate forcefully, resulting in the complete loss of the agent's in-memory state.
Approach: Highlight the difference between local blocking scripts and stateless, durable cloud architectures.
Continue learning
Previous: Agent checkpointing & resumability