5 AutoGen Group Chat Patterns (Round-Robin, Speaker Selection)
Master AutoGen group chat patterns including round-robin, speaker selection, and custom orchestration for powerful multi-agent collaboration workflows.
Get more content like this on Telegram!
Daily AI tips, notes & resources — free
Multi-agent systems are only as good as their coordination. You can build ten specialized agents — a researcher, a coder, a critic, a planner — but if they all talk at once or never pass the baton correctly, the whole thing falls apart. That is where AutoGen's group chat system earns its keep.
AutoGen's GroupChat and GroupChatManager classes give you a structured way to coordinate multiple agents in a shared conversation. Depending on your task, you might want agents to take strict turns, let the LLM decide who speaks next, or write your own logic to route messages based on context. This guide covers five concrete patterns you can put to work today.
If you are new to the broader agent landscape, start with AI agents explained before diving into multi-agent orchestration.
What GroupChat Actually Does
Before jumping to patterns, it helps to understand what is happening under the hood.
A GroupChat object holds a list of agents and a conversation history. The GroupChatManager wraps that group chat, acts as a regular ConversableAgent, and does one special thing: after every message, it decides who speaks next and routes the conversation accordingly.
import autogen
config_list = [{"model": "gpt-4o", "api_key": "YOUR_API_KEY"}]
llm_config = {"config_list": config_list, "cache_seed": 42}
researcher = autogen.AssistantAgent(
name="Researcher",
system_message="You search for information and summarize findings.",
llm_config=llm_config,
)
analyst = autogen.AssistantAgent(
name="Analyst",
system_message="You analyze data and draw conclusions from research.",
llm_config=llm_config,
)
writer = autogen.AssistantAgent(
name="Writer",
system_message="You write clear, polished prose from the analyst's conclusions.",
llm_config=llm_config,
)
user_proxy = autogen.UserProxyAgent(
name="User",
human_input_mode="NEVER",
code_execution_config=False,
)
group_chat = autogen.GroupChat(
agents=[user_proxy, researcher, analyst, writer],
messages=[],
max_round=12,
)
manager = autogen.GroupChatManager(
groupchat=group_chat,
llm_config=llm_config,
)
user_proxy.initiate_chat(
manager,
message="Research the impact of LLMs on software development productivity.",
)
That is the baseline. Now let's look at the patterns that make group chats useful rather than chaotic.
Pattern 1: Round-Robin (Strict Turn Order)
The simplest pattern forces agents to speak in a fixed cycle. Agent A, then B, then C, then back to A. No LLM decision-making involved — just rotation.
group_chat = autogen.GroupChat(
agents=[user_proxy, researcher, analyst, writer],
messages=[],
max_round=12,
speaker_selection_method="round_robin",
)
When to use it: Pipeline workflows where each stage feeds the next. Research → Analyze → Write is a textbook example. The output of round N is naturally the input to round N+1.
Limitation: Round-robin breaks down when tasks are non-uniform. If the researcher needs two turns to gather enough information, the rigid rotation will cut them off or waste the analyst's turn on an empty pass.
Pattern 2: Auto Speaker Selection (LLM-Driven)
The default speaker_selection_method is "auto". The GroupChatManager calls the LLM with the full conversation history and asks it to pick the next speaker from the agent list. This is flexible but costs extra tokens on every turn.
group_chat = autogen.GroupChat(
agents=[user_proxy, researcher, analyst, writer],
messages=[],
max_round=15,
speaker_selection_method="auto", # default
speaker_transitions_type="allowed", # or "disallowed"
allowed_or_disallowed_speaker_transitions={
user_proxy: [researcher],
researcher: [analyst, researcher], # researcher can loop back to itself
analyst: [writer, researcher], # analyst can ask researcher for more
writer: [user_proxy],
},
)
The allowed_or_disallowed_speaker_transitions dictionary constrains which agents can follow which. This prevents nonsensical flows like the writer handing off to the researcher mid-draft.
When to use it: Tasks with variable depth. If some questions need three research passes and others need one, auto mode handles that naturally. Combined with transition constraints, you get flexibility without total chaos.
Pattern 3: Custom Speaker Selection Function
For production workflows, you often want deterministic routing logic that does not depend on an LLM call. You can pass a callable to speaker_selection_method.
def custom_speaker_selector(last_speaker, groupchat):
messages = groupchat.messages
# Start with researcher
if len(messages) <= 1:
return researcher
last_message = messages[-1]
last_name = last_speaker.name
# If researcher just spoke, check if research is complete
if last_name == "Researcher":
content = last_message.get("content", "")
if "RESEARCH_COMPLETE" in content:
return analyst
else:
return researcher # keep researching
# If analyst just spoke, move to writer
if last_name == "Analyst":
return writer
# If writer just spoke, end with user proxy
if last_name == "Writer":
return user_proxy
return researcher # fallback
group_chat = autogen.GroupChat(
agents=[user_proxy, researcher, analyst, writer],
messages=[],
max_round=20,
speaker_selection_method=custom_speaker_selector,
)
The function receives the last speaker agent object and the full GroupChat instance. You can inspect message content, count turns, check for keywords, or run any Python logic to decide who speaks next.
When to use it: Any workflow where you have clear business rules about routing. This is also significantly cheaper than auto mode since you skip the LLM speaker-selection call.
See how this pairs with planning logic in AI agent memory and planning.
Pattern 4: Random Speaker Selection for Diverse Exploration
Less common but occasionally useful, random selection has each agent picked randomly from the available pool.
group_chat = autogen.GroupChat(
agents=[critic_1, critic_2, critic_3, synthesizer],
messages=[],
max_round=16,
speaker_selection_method="random",
)
When to use it: Brainstorming or adversarial setups where you want diverse perspectives without a predetermined order. Running multiple critic agents randomly means no single critic dominates the first few turns.
A more controlled version combines random with constraints — you can restrict which agents are eligible to speak at each stage while still randomizing within that eligible set. AutoGen does not support this natively, but you can replicate it with a custom selector function that filters the candidate list and picks randomly from what remains.
Pattern 5: Nested Group Chats (Hierarchical Orchestration)
For complex tasks, a single flat group chat becomes unwieldy. The fifth pattern uses nested group chats, where one agent in a parent chat is itself a GroupChatManager for a sub-team.
# Sub-team: research specialists
research_lead = autogen.AssistantAgent(
name="ResearchLead",
system_message="You coordinate research tasks.",
llm_config=llm_config,
)
web_searcher = autogen.AssistantAgent(
name="WebSearcher",
system_message="You search the web for current information.",
llm_config=llm_config,
)
paper_reader = autogen.AssistantAgent(
name="PaperReader",
system_message="You read and extract key findings from academic papers.",
llm_config=llm_config,
)
research_proxy = autogen.UserProxyAgent(
name="ResearchProxy",
human_input_mode="NEVER",
code_execution_config=False,
)
research_group = autogen.GroupChat(
agents=[research_proxy, web_searcher, paper_reader],
messages=[],
max_round=8,
speaker_selection_method="auto",
)
research_manager = autogen.GroupChatManager(
groupchat=research_group,
llm_config=llm_config,
name="ResearchManager",
)
# Parent team uses research_manager as a single agent
synthesis_agent = autogen.AssistantAgent(
name="Synthesizer",
system_message="You synthesize research into actionable insights.",
llm_config=llm_config,
)
parent_proxy = autogen.UserProxyAgent(
name="ParentUser",
human_input_mode="NEVER",
code_execution_config=False,
)
parent_group = autogen.GroupChat(
agents=[parent_proxy, research_manager, synthesis_agent],
messages=[],
max_round=6,
speaker_selection_method="round_robin",
)
parent_manager = autogen.GroupChatManager(
groupchat=parent_group,
llm_config=llm_config,
)
parent_proxy.initiate_chat(
parent_manager,
message="Analyze current trends in agentic AI frameworks for enterprise use.",
)
The research_manager participates in the parent conversation like any other agent. When it is its turn, it spins up its own sub-conversation among the web searcher and paper reader, then returns a consolidated response to the parent chat.
When to use it: Enterprise-scale tasks that naturally decompose into sub-problems, each requiring its own specialist team. This mirrors how human organizations work — you have teams within teams, each with internal coordination.
For a full walkthrough of building research-oriented agents, see AI research agent build.
Comparison: Which Pattern for Which Task?
| Pattern | Speaker Selection | Token Cost | Best For |
|---|---|---|---|
| Round-Robin | Deterministic rotation | Low | Linear pipelines |
| Auto (LLM) | LLM decides | High | Variable-depth tasks |
| Custom Function | Your Python logic | Low | Production with rules |
| Random | Random from pool | Low | Brainstorming, diversity |
| Nested Chats | Hierarchical | Medium-High | Complex decomposed tasks |
A 2024 benchmark study on multi-agent frameworks found that LLM-driven speaker selection (auto mode) increases per-task token usage by roughly 15–25% compared to deterministic methods. For long-running tasks with many rounds, a custom selector function almost always makes sense from a cost perspective.
Controlling Termination
Group chats need a way to stop. AutoGen provides several mechanisms:
# Option 1: max_round (hard stop)
group_chat = autogen.GroupChat(
agents=[...],
messages=[],
max_round=20,
)
# Option 2: is_termination_msg on individual agents
researcher = autogen.AssistantAgent(
name="Researcher",
llm_config=llm_config,
is_termination_msg=lambda msg: "TASK_COMPLETE" in msg.get("content", ""),
)
# Option 3: UserProxyAgent with human input at key moments
user_proxy = autogen.UserProxyAgent(
name="User",
human_input_mode="TERMINATE", # ask human when agent says TERMINATE
code_execution_config=False,
)
Combining max_round with keyword-based termination gives you both a safety net and clean exits when tasks complete naturally.
Handling Agent Failures in Group Chats
Production group chats need error handling. An agent that fails mid-conversation can stall the entire workflow.
import autogen
from autogen import ConversableAgent
class ResilientAssistant(autogen.AssistantAgent):
def generate_reply(self, messages=None, sender=None, **kwargs):
try:
return super().generate_reply(messages=messages, sender=sender, **kwargs)
except Exception as e:
# Return a graceful fallback instead of crashing
return f"I encountered an error ({str(e)}). Passing to next agent."
Subclassing agents to add retry logic or fallback responses keeps your group chat alive even when individual components hiccup.
Practical Tips from Real Deployments
Keep system messages role-specific. Vague system messages lead to agents that try to do everything. The more precise the role definition, the cleaner the hand-offs.
Log conversation history. The group_chat.messages list is your audit trail. Persist it to a database or file for debugging.
import json
# After the chat completes
with open("chat_history.json", "w") as f:
json.dump(group_chat.messages, f, indent=2)
Limit max_round conservatively at first. It is easy to accidentally create loops where agents keep asking each other for clarification. Start with a low max_round, observe the patterns, then increase.
Test speaker selection logic independently. Your custom selector function is plain Python — unit test it before plugging it into a full agent run.
For broader context on deploying these patterns in production systems, check out Deploy AI model to production and Build AI agent with LangChain for comparison approaches.
Putting It All Together
Here is a production-ready template combining the best elements of the patterns above:
import autogen
import json
from datetime import datetime
config_list = autogen.config_list_from_json("OAI_CONFIG_LIST")
llm_config = {"config_list": config_list, "cache_seed": None} # disable cache in prod
def make_agent(name, role_description):
return autogen.AssistantAgent(
name=name,
system_message=role_description,
llm_config=llm_config,
is_termination_msg=lambda msg: "DONE" in msg.get("content", ""),
)
planner = make_agent("Planner", "Break down tasks into clear sub-tasks. Say DONE when plan is finalized.")
executor = make_agent("Executor", "Execute sub-tasks using available tools. Say DONE when all tasks complete.")
reviewer = make_agent("Reviewer", "Review outputs for accuracy. Say DONE when review passes.")
user = autogen.UserProxyAgent(
name="User",
human_input_mode="NEVER",
code_execution_config={"work_dir": "workspace", "use_docker": False},
max_consecutive_auto_reply=3,
)
def smart_selector(last_speaker, groupchat):
msgs = groupchat.messages
if not msgs:
return planner
last = last_speaker.name
if last == "User":
return planner
if last == "Planner":
return executor
if last == "Executor":
return reviewer
if last == "Reviewer":
content = msgs[-1].get("content", "")
return executor if "revise" in content.lower() else user
return planner
gc = autogen.GroupChat(
agents=[user, planner, executor, reviewer],
messages=[],
max_round=24,
speaker_selection_method=smart_selector,
)
manager = autogen.GroupChatManager(groupchat=gc, llm_config=llm_config)
result = user.initiate_chat(
manager,
message="Build a Python script that fetches weather data for 5 cities and saves it as CSV.",
)
# Persist results
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
with open(f"chat_{timestamp}.json", "w") as f:
json.dump(gc.messages, f, indent=2)
This template gives you deterministic routing with a clear Plan → Execute → Review loop, termination conditions on each agent, code execution for the executor, and automatic logging of the full conversation.
What Comes Next
Group chat patterns are one layer of a larger multi-agent architecture. The other pieces — tool use, memory, state persistence — all feed into how well your agents collaborate. If you are building something more autonomous, the AutoGPT vs BabyAGI comparison shows how different frameworks approach full autonomy, and CrewAI tutorial offers a different API for multi-agent coordination worth comparing.
The five patterns here cover most real-world needs. Start with round-robin for simple pipelines, move to custom selectors for production, and reach for nested chats when your tasks naturally decompose into sub-teams. The key is matching the coordination pattern to the structure of the problem — not the other way around.
Frequently Asked Questions
What is GroupChatManager in AutoGen? GroupChatManager is the orchestrator agent that manages the flow of conversation in a GroupChat. It decides which agent speaks next based on the speaker_selection_method you configure — auto, round_robin, or random — and routes messages accordingly.
Can I define my own speaker selection logic in AutoGen group chats? Yes. You can pass a callable function to speaker_selection_method instead of a string. The function receives the last speaker and the group chat object, and returns the next agent to speak. This gives you full control over conversation flow.
What is the max_round parameter in GroupChat? max_round sets the maximum number of conversation turns before the group chat terminates. Each time an agent sends a message counts as one round. Setting this too low can cut off incomplete tasks; setting it too high wastes tokens if agents loop unnecessarily.
Frequently Asked Questions
AiTechWorlds Team
✓ Verified WriterThe AiTechWorlds team is passionate about AI, technology, and education. We create high-quality, research-backed content to help you learn, grow, and succeed in the modern digital world.
Related Articles
5 AutoGen Agent Roles (Assistant, UserProxy, CodeExecutor)
Understand the 5 core AutoGen agent types — AssistantAgent, UserProxyAgent, CodeExecutorAgent, and more — with code examples and a comparison table for each role.
How to Deploy AutoGen Agents as APIs with FastAPI (2026)
Learn to serve AutoGen multi-agent systems as production REST APIs using FastAPI with async endpoints and real-time streaming responses.
How to Use AutoGen with Azure OpenAI (Enterprise Security)
Connect Microsoft AutoGen to Azure OpenAI for enterprise-grade AI agents. Step-by-step setup with private endpoints, OAI_CONFIG_LIST, and deployment config.
Build a Code Debugging Agent with AutoGen (Auto-Fix PRs)
Build an AutoGen agent that reviews code, analyzes PR diffs, suggests fixes, and automates code quality improvements with a full working implementation.