Interrupting and Resuming an Agent Loop
Most agents can't be paused — kill one mid-task and its work is gone. Building a pause resume agent loop takes one design change. Here's the fragile version and the resumable fix.
def run(task):
messages = [system, user(task)]
plan = None
for step in range(max_steps):
reply = model.call(messages) # all state is local
messages.append(reply.as_message())
if reply.tool_calls:
for c in reply.tool_calls:
messages.append(tool_result(c.id, run_tool(c)))
elif reply.finished:
return reply.answer
# if the process dies here, `messages` and `plan` die with itHere's something most people don't realize until it costs them: the typical agent can't be paused. Stop it mid-task — a crash, a deploy, a user closing the tab — and everything it had done evaporates. Not slowed, not queued: gone. The agent had made ten tool calls, gathered real results, and formed a plan, and killing it threw all of that away because the entire state lived in memory that died with the process. A pause resume agent loop treats that as unacceptable, and the fix is a single design change. Let's tear apart the fragile version and build the resumable one.
Being able to interrupt and resume isn't a luxury feature. It's what lets an agent survive a deployment, wait for a human approval, respect a rate limit, or recover from a crash without redoing hours of work. And the difference between an agent that can and one that can't comes down to where its state lives.
That last point is the entire lesson compressed to a sentence, and it's worth sitting with. Nothing about resumability is about the model's intelligence, the quality of the prompts, or the cleverness of the tools. It's a pure architecture question: is the agent's working state trapped inside a running process, or does it live somewhere the process can die without taking it along? Two agents identical in every other respect — same model, same prompt, same tools — differ completely on whether an interruption is a catastrophe or a shrug, based only on that one storage decision.
Before: The Weak Prompt
Here's the fragile loop — the shape almost every agent starts as:
[object Object], ,[object Object],(,[object Object],):
messages = [system, user(task)]
plan = ,[object Object],
,[object Object], step ,[object Object], ,[object Object],(max_steps):
reply = model.call(messages) ,[object Object],
messages.append(reply.as_message())
,[object Object], reply.tool_calls:
,[object Object], c ,[object Object], reply.tool_calls:
messages.append(tool_result(c.,[object Object],, run_tool(c)))
,[object Object], reply.finished:
,[object Object], reply.answer
,[object Object],What this does: holds the entire agent state — transcript, plan, progress — in local variables that exist only inside the running function, so any interruption erases all of it with no way to pick back up.
Kill this at step ten and you restart at step zero. The ten tool calls, the accumulated context, the half-formed answer — all unrecoverable, because none of it was ever written anywhere but volatile memory.
Why It Fails
The state is trapped in the process. Everything the agent knows lives in local variables, which means the agent's memory and the process's lifetime are the same thing. When the process ends — for any reason — the agent's entire working state ends with it. There's no seam to stop at and nothing to resume from.
This isn't a rare-crash problem. It shows up constantly: you can't deploy new code without killing in-flight agents and losing their work; you can't pause an agent for a human to approve a risky step; you can't stop and requeue an agent that's hit a rate limit. All of these need the same thing — the ability to freeze state and thaw it later — and the in-memory loop offers no way to do it.
That's the reframe worth holding onto: a pause resume agent loop isn't a feature you add for a specific scenario, it's a capability that unlocks a whole category of scenarios at once. Deploys, approvals, rate-limit backoff, crash recovery — they look like four different requirements, but they're one requirement wearing four hats. Solve "freeze and thaw state" once and all four fall out for free. Which is exactly why it's worth building into the loop's foundation rather than bolting onto whichever scenario forces the issue first.
⚠️ Common mistake: Assuming you'll add resumability later if you need it. Retrofitting it means untangling state from a loop that assumed state lived in local variables — touching every place the loop reads or writes progress. Building the state as an explicit, serializable object from the start costs little; bolting it on after the loop is woven around local variables is a rewrite. This is a decision to make early.
⚡ Pro tip: Ask of any agent, "if this process died right now, what would I lose?" If the answer is "everything," your state lives in the wrong place. A resumable agent can answer "nothing since the last checkpoint," and that difference is entirely about where state is stored, not how smart the agent is.
After: The Improved Prompt
The fix is to make state an explicit, serializable object that lives outside the loop's local scope — saved after each step, loaded to resume.
[object Object], ,[object Object],(,[object Object],):
state = load_state(run_id) ,[object Object], AgentState.new(task) ,[object Object],
,[object Object], ,[object Object], state.done ,[object Object], state.step < max_steps:
,[object Object], state.should_pause(): ,[object Object],
save_state(run_id, state)
,[object Object], Paused(run_id)
reply = model.call(state.messages)
state.apply(reply) ,[object Object],
,[object Object], reply.tool_calls:
,[object Object], c ,[object Object], reply.tool_calls:
state.add_observation(c.,[object Object],, run_tool(c))
save_state(run_id, state) ,[object Object],
,[object Object], state.resultWhat this does: keeps all progress in an explicit
AgentStaterun_id[object Object], ,[object Object],(,[object Object],):
,[object Object], run(state.task, run_id) ,[object Object],What this does: restarts the loop for a given run, which loads the persisted state and continues from the last checkpoint instead of starting over — turning interruption from data loss into a pause.
Breaking Down Each Element
Three changes make the loop resumable.
Explicit state — an
AgentStateTogether they move the agent's memory out of the process and into durable storage. The process becomes disposable; the state persists. That's the whole shift.
And it's a shift in how you think about the process, not just where you put a variable. In the fragile design the process is the agent — kill it and the agent is gone. In the resumable design the process is merely a worker that happens to be advancing the agent right now; the agent itself lives in the checkpoint and can be picked up by any worker, later, elsewhere. That decoupling is what makes everything else — deploys, approvals, backoff, recovery — possible, because none of them care which process does the work as long as the state outlives any single one.
⚡ Pro tip: Checkpoint between steps, at a clean boundary, never mid-tool-call. Resuming is only safe if every saved state is consistent — a checkpoint taken while a tool was half-executed resumes into a corrupt state. Save after a step fully completes, so every checkpoint represents a coherent moment you can safely restart from.
Variations for Different Contexts
Resumability enables different capabilities per context.
A platform engineer running long agents makes them survive deploys — in-flight agents checkpoint, the process restarts on new code, and they resume mid-task instead of dying. A compliance-sensitive workflow uses the pause seam for human approval — the agent pauses before a risky action, waits for a person to approve, and resumes on their signal. A batch-processing team uses resumability to respect rate limits — an agent that hits a limit saves state, requeues, and resumes later, rather than failing and restarting from scratch.
Same resumable-state mechanism, three capabilities — durability, human-in-the-loop, and graceful backoff — that the in-memory loop simply can't offer.
⚡ Pro tip: Give every run a stable, external
run_id⚡ Pro tip: Version your state schema. When you change what
AgentStateSave and Reuse This
The explicit-state pattern — a serializable state object, per-step checkpointing, and a pause seam — is the same for any agent regardless of what it does. Swap what
AgentStateI keep the
AgentStateContinue Reading
Save the prompts from this post
PromptABCD is a free prompt manager. Paste, organize, and reuse your best AI prompts — no more hunting through chat history.
