Skip to content

Interrupts in Multi-Agent Systems

Interrupts work across multi-agent orchestration, so you can pause a swarm or graph for human approval or input the same way you pause a single agent. Raise an interrupt from a BeforeNodeCallEvent hook that runs before each node, or from within the nodes themselves. Session management works here too, so you can persist an interrupted multi-agent run and resume it later.

The interfaces mirror those used for single-agent interrupts. Read that page first if you have not built an interrupt/resume loop before.

A Swarm is a collaborative agent orchestration system where multiple agents work together as a team to solve complex tasks. The following example interrupts a swarm invocation through a BeforeNodeCallEvent hook.

import json
from strands import Agent
from strands.hooks import BeforeNodeCallEvent, HookProvider, HookRegistry
from strands.multiagent import Swarm, Status
class ApprovalHook(HookProvider):
def __init__(self, app_name: str) -> None:
self.app_name = app_name
def register_hooks(self, registry: HookRegistry) -> None:
registry.add_callback(BeforeNodeCallEvent, self.approve)
def approve(self, event: BeforeNodeCallEvent) -> None:
if event.node_id != "cleanup":
return
approval = event.interrupt(f"{self.app_name}-approval", reason={"resources": "example"})
if approval.lower() != "y":
event.cancel_node = "User denied permission to cleanup resources"
swarm = Swarm(
[
Agent(name="cleanup", system_prompt="You clean up resources older than 5 days.", callback_handler=None),
],
hooks=[ApprovalHook("myapp")],
)
result = swarm("Clean up my resources")
while result.status == Status.INTERRUPTED:
responses = []
for interrupt in result.interrupts:
if interrupt.name == "myapp-approval":
user_input = input(f"Do you want to cleanup {interrupt.reason['resources']} (y/N): ")
responses.append({
"interruptResponse": {
"interruptId": interrupt.id,
"response": user_input,
},
})
result = swarm(responses)
print(f"MESSAGE: {json.dumps(result.results['cleanup'].result.message, indent=2)}")

Swarms also support interrupts raised from within the nodes themselves, following any of the single-agent interrupt patterns.

  • event.interrupt - Raises an interrupt with a unique name and optional reason
    • The name must be unique across all interrupt calls configured on the BeforeNodeCallEvent. In the example above, we demonstrate using app_name to namespace the interrupt call. This is particularly helpful if you plan to vend your hooks to other users.
    • You can assign additional context for raising the interrupt to the reason field. Note, the reason must be JSON-serializable.
  • result.status - Check if the swarm stopped due to Status.INTERRUPTED
  • result.interrupts - List of interrupts that were raised
    • Each interrupt contains the user provided name and reason, along with an instance id.
  • interruptResponse - Content block type for configuring the interrupt responses.
    • Each response is uniquely identified by their interrupt’s id and will be returned from the associated interrupt call when invoked the second time around. Note, the response must be JSON-serializable.
  • event.cancel_node - Cancel node execution based on interrupt response
    • You can either set cancel_node to True or provide a custom cancellation message.

Strands enforces the following rules for interrupts in swarm:

  • All hooks configured on the interrupted event will execute
  • All hooks configured on the interrupted event are allowed to raise an interrupt
  • A single hook can raise multiple interrupts but only one at a time
    • In other words, within a single hook, you can interrupt, respond to that interrupt, and then proceed to interrupt again.
  • A single node can raise multiple interrupts following any of the single-agent interrupt patterns outlined above.

A Graph is a deterministic agent orchestration system based on a directed graph, where agents are nodes executed according to edge dependencies. The following example interrupts a graph invocation through a BeforeNodeCallEvent hook.

import json
from strands import Agent
from strands.hooks import BeforeNodeCallEvent, HookProvider, HookRegistry
from strands.multiagent import GraphBuilder, Status
class ApprovalHook(HookProvider):
def __init__(self, app_name: str) -> None:
self.app_name = app_name
def register_hooks(self, registry: HookRegistry) -> None:
registry.add_callback(BeforeNodeCallEvent, self.approve)
def approve(self, event: BeforeNodeCallEvent) -> None:
if event.node_id != "cleanup":
return
approval = event.interrupt(f"{self.app_name}-approval", reason={"resources": "example"})
if approval.lower() != "y":
event.cancel_node = "User denied permission to cleanup resources"
inspector_agent = Agent(name="inspector", system_prompt="You inspect resources.", callback_handler=None)
cleanup_agent = Agent(name="cleanup", system_prompt="You clean up resources older than 5 days.", callback_handler=None)
builder = GraphBuilder()
builder.add_node(inspector_agent, "inspector")
builder.add_node(cleanup_agent, "cleanup")
builder.add_edge("inspector", "cleanup")
builder.set_entry_point("inspector")
builder.set_hook_providers([ApprovalHook("myapp")])
graph = builder.build()
result = graph("Inspect and clean up my resources")
while result.status == Status.INTERRUPTED:
responses = []
for interrupt in result.interrupts:
if interrupt.name == "myapp-approval":
user_input = input(f"Do you want to cleanup {interrupt.reason['resources']} (y/N): ")
responses.append({
"interruptResponse": {
"interruptId": interrupt.id,
"response": user_input,
},
})
result = graph(responses)
print(f"MESSAGE: {json.dumps(result.results['cleanup'].result.message, indent=2)}")

Graphs also support interrupts raised from within the nodes themselves, following any of the single-agent interrupt patterns.

  • event.interrupt - Raises an interrupt with a unique name and optional reason
    • The name must be unique across all interrupt calls configured on the BeforeNodeCallEvent. In the example above, we demonstrate using app_name to namespace the interrupt call. This is particularly helpful if you plan to vend your hooks to other users.
    • You can assign additional context for raising the interrupt to the reason field. Note, the reason must be JSON-serializable.
  • result.status - Check if the graph stopped due to Status.INTERRUPTED
  • result.interrupts - List of interrupts that were raised
    • Each interrupt contains the user provided name and reason, along with an instance id.
  • interruptResponse - Content block type for configuring the interrupt responses
    • Each response is uniquely identified by their interrupt’s id and will be returned from the associated interrupt call when invoked the second time around. Note, the response must be JSON-serializable.
  • event.cancel_node - Cancel node execution based on interrupt response
    • You can either set cancel_node to True or provide a custom cancellation message.

Strands enforces the following rules for interrupts in graph:

  • All hooks configured on the interrupted event will execute
  • All hooks configured on the interrupted event are allowed to raise an interrupt
  • A single hook can raise multiple interrupts but only one at a time
    • In other words, within a single hook, you can interrupt, respond to that interrupt, and then proceed to interrupt again.
  • A single node can raise multiple interrupts following any of the single-agent interrupt patterns outlined above
  • All nodes running concurrently will execute
  • All nodes running concurrently are interruptible