Handling Handoffs Between Agents in Swarm Group Chat: A Complete Guide
In AutoGen's Swarm group chat, agents transfer control exclusively through HandoffMessage objects, with the SwarmGroupChatManager selecting the next speaker by scanning the message thread backwards for the most recent handoff target.
The Swarm team implementation in the microsoft/autogen repository provides a lightweight, deterministic approach to handling handoffs between agents in Swarm group chat. Unlike other group chat patterns that rely on LLM-based speaker selection, Swarm routes conversations exclusively via explicit HandoffMessage declarations, making it ideal for deterministic workflows and human-in-the-loop scenarios.
Understanding the Swarm Handoff Architecture
Core Components for Agent Handoffs
-
Swarm: The public API class that validates the first participant can emit handoffs and wires the team toSwarmGroupChatManager. Located inpython/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_swarm_group_chat.py(lines 26-45). -
SwarmGroupChatManager: The runtime engine that validates startup handoffs, selects speakers by scanning forHandoffMessagetargets, and manages state persistence. Implementation spans lines 47-113 in the same file. -
HandoffMessage: The exclusive routing mechanism carrying atargetfield (next agent name) and optionalcontext. Defined inpython/packages/autogen-agentchat/src/autogen_agentchat/messages.py(lines 21-30). -
SwarmManagerState: Pydantic model for serializing the manager's internal state includingmessage_thread,current_turn, andcurrent_speaker. Found inpython/packages/autogen-agentchat/src/autogen_agentchat/state/_states.py(lines 57-58).
The Handoff Lifecycle in Swarm Group Chat
-
Initialization: The first agent in the
participantslist becomes the initial speaker. TheSwarmconstructor asserts this agent can emitHandoffMessageobjects. -
Handoff Emission: An agent emits a
HandoffMessagewithtarget=<next_agent_name>. The manager validates the target exists invalidate_group_state(lines 47-73). -
Speaker Selection: The manager scans the message thread backwards to find the latest
HandoffMessageand sets_current_speakeraccordingly inselect_speaker(lines 82-98). If no handoff is present, the current speaker continues. -
State Persistence:
save_state(lines 100-107) serializes the thread and speaker, whileload_state(lines 108-113) restores execution context.
Implementing Agent Handoffs in Swarm Group Chat
Basic Automatic Handoffs Between Two Agents
The following example demonstrates a two-agent Swarm where Alice automatically hands off to Bob:
import asyncio
from autogen_ext.models.openai import OpenAIChatCompletionClient
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.teams import Swarm
from autogen_agentchat.conditions import MaxMessageTermination
async def main() -> None:
client = OpenAIChatCompletionClient(model="gpt-4o")
alice = AssistantAgent(
"Alice",
model_client=client,
handoffs=["Bob"], # Alice must be able to hand off to Bob
system_message="You are Alice; answer only about yourself."
)
bob = AssistantAgent(
"Bob",
model_client=client,
system_message="You are Bob; your birthday is 1 Jan."
)
termination = MaxMessageTermination(3) # stop after three messages
swarm = Swarm([alice, bob], termination_condition=termination)
async for msg in swarm.run_stream(task="When is Bob's birthday?"):
print(msg)
asyncio.run(main())
Key implementation details:
Swarm([alice, bob])creates a hand-off-driven team.- The first agent (
alice) must havehandoffsconfigured so it can emit aHandoffMessage. - The conversation ends when
MaxMessageTerminationfires.
Human-in-the-Loop Handoffs with HandoffTermination
For scenarios requiring human intervention, use HandoffTermination to pause execution when control transfers to a user:
import asyncio
from autogen_ext.models.openai import OpenAIChatCompletionClient
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.teams import Swarm
from autogen_agentchat.conditions import HandoffTermination, MaxMessageTermination
from autogen_agentchat.ui import Console
from autogen_agentchat.messages import HandoffMessage
async def main() -> None:
client = OpenAIChatCompletionClient(model="gpt-4o")
alice = AssistantAgent(
"Alice",
model_client=client,
handoffs=["user"], # declares a human handoff target
system_message="You are Alice; ask the user for help when needed."
)
termination = HandoffTermination(target="user") | MaxMessageTermination(3)
swarm = Swarm([alice], termination_condition=termination)
# 1️⃣ Alice asks the user a clarification question
await Console(swarm.run_stream(task="Explain the concept of recursion."))
# 2️⃣ Human replies via a hand‑off message
await Console(
swarm.run_stream(
task=HandoffMessage(source="user", target="Alice",
content="Recursion is a function calling itself.")
)
)
asyncio.run(main())
Key points:
HandoffTermination(target="user")stops the AI loop when aHandoffMessagetargeting"user"appears.- The
ConsoleUI streams messages; the second call resumes the sameSwarminstance with a handcraftedHandoffMessage.
Persisting and Restoring Swarm State
Swarm supports full serialization of conversation state, enabling pause-and-resume workflows:
# Assume `swarm` is a running Swarm instance
state = await swarm.save_state() # serialises the whole chat manager
# …persist `state` to a DB or file…
# Later, recreate the same agents and load the saved state
new_swarm = Swarm([alice, bob], termination_condition=termination)
await new_swarm.load_state(state) # resumes exactly where we left off
The saved JSON contains the message thread, the current turn index, and the name of the agent that will speak next (_current_speaker).
Core Source Files and Implementation Details
| File | Purpose | Direct link |
|---|---|---|
python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_swarm_group_chat.py |
Swarm class, manager implementation, speaker selection, validation, state persistence |
Swarm implementation |
python/packages/autogen-agentchat/src/autogen_agentchat/messages.py |
Definition of HandoffMessage (target, optional context) |
HandoffMessage definition |
python/packages/autogen-agentchat/src/autogen_agentchat/state/_states.py |
SwarmManagerState model used for checkpointing |
SwarmManagerState |
python/packages/autogen-agentchat/tests/test_group_chat.py |
Unit tests that illustrate hand‑off flows, state round‑tripping and optional team‑event emission | Swarm hand‑off tests |
python/packages/autogen-agentchat/src/autogen_agentchat/teams/__init__.py |
Exposes Swarm in the public package namespace |
Team package init |
These files together form the complete implementation of how Swarm orchestrates hand‑offs between agents, validates targets, and preserves conversational state.
Summary
- Swarm routes conversations exclusively via
HandoffMessageobjects, not LLM-based selection. - The
SwarmGroupChatManagerselects the next speaker by scanning backwards for the most recent handoff target inselect_speaker. - Agents must declare their handoff capabilities via the
handoffsparameter inAssistantAgent. - State persistence is handled through
SwarmManagerState, enabling conversation checkpointing and resumption viasave_stateandload_state. - Human-in-the-loop workflows are supported via
HandoffTerminationtargeting a user agent.
Frequently Asked Questions
How does Swarm differ from other AutoGen group chat implementations?
Unlike RoundRobinGroupChat or SelectorGroupChat, Swarm does not use an LLM to choose the next speaker. Instead, it relies exclusively on explicit HandoffMessage declarations from agents, making it deterministic and ideal for structured workflows where predictable routing is required.
Can an agent hand off to multiple targets in a single turn?
An agent emits a single HandoffMessage with one target field. However, the agent can be configured with multiple potential handoff targets in its handoffs list, and conditional logic within the agent can determine which specific target to specify for each turn based on conversation context.
What happens if an agent emits a HandoffMessage targeting an unregistered agent?
The SwarmGroupChatManager validates all handoff targets in validate_group_state. If a HandoffMessage targets an agent not present in the participants list, the validation will fail and raise an appropriate error before the next speaker selection occurs, preventing invalid routing.
How do I resume a Swarm conversation after a system restart?
Use await swarm.save_state() to serialize the current state (including the message thread and current speaker) to a JSON-compatible dictionary. Persist this state to a database or file. After recreating the agent instances, instantiate a new Swarm and call await new_swarm.load_state(state) to resume execution from the exact point of interruption.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →