AutoGen Essentials · Lesson 8 of 11
GroupChat: Multiple Agents in One Conversation
From Two Agents to Many
Two-agent conversations solve a broad class of problems: one agent thinks, one agent executes. But real software workflows involve more participants — a researcher who gathers context, a coder who implements, and a reviewer who validates. AutoGen's GroupChat class models exactly this.
A GroupChat is a shared conversation thread that multiple agents participate in. A manager (the GroupChatManager) decides who speaks next. The conversation accumulates in one shared history that every agent can read.
GroupChat Architecture
┌─────────────────────────────┐
│ GroupChatManager │
│ (orchestrates who speaks) │
└──────────────┬──────────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
┌────────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐
│ researcher │ │ coder │ │ reviewer │
│ AssistantAgent │ │ AssistantAgent │ │ AssistantAgent │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
└────────────────────┴─────────────────────┘
shared message historyAll agents read the same conversation history. The manager selects the next speaker after each message. Agents contribute when selected, pass otherwise.
GroupChat Setup: The Three Components
1. The Agent Pool
Define each specialist agent with a focused system_message:
import autogen
import os
llm_config = {
"config_list": [{"model": "gpt-4o-mini", "api_key": os.environ["OPENAI_API_KEY"]}],
"temperature": 0,
}
# Researcher: gathers requirements and background
researcher = autogen.AssistantAgent(
name="researcher",
llm_config=llm_config,
system_message="""You are a technical researcher.
When given a coding task:
1. Clarify the requirements
2. Identify edge cases to handle
3. Recommend an approach (algorithm, libraries, patterns)
4. Summarise your findings clearly for the coder
Do NOT write code. That is the coder's job.
Be concise — 3 to 5 bullet points maximum.
""",
)
# Coder: implements based on researcher's findings
coder = autogen.AssistantAgent(
name="coder",
llm_config=llm_config,
system_message="""You are an expert Python developer.
When given a task and research context:
1. Implement a clean, type-annotated solution
2. Include inline comments for complex logic
3. Add at least 5 test cases with assert statements
4. Print "All tests passed." if tests succeed
Only write Python code — no explanations after the code block.
""",
)
# Reviewer: validates the coder's implementation
reviewer = autogen.AssistantAgent(
name="reviewer",
llm_config=llm_config,
system_message="""You are a senior code reviewer.
When given code to review:
1. Check for correctness — does it solve the task?
2. Check for edge case handling
3. Check for code clarity and Python idioms
4. Either approve (reply LGTM and TERMINATE) or request specific changes
Be specific about any issues. Reference line numbers if possible.
""",
)
# Executor: runs the code produced by the coder
executor = autogen.UserProxyAgent(
name="executor",
human_input_mode="NEVER",
max_consecutive_auto_reply=10,
is_termination_msg=lambda msg: "TERMINATE" in msg.get("content", ""),
code_execution_config={
"work_dir": "group_workspace",
"use_docker": False,
"timeout": 30,
"last_n_messages": 3,
},
)2. The GroupChat Object
group_chat = autogen.GroupChat(
agents=[executor, researcher, coder, reviewer],
messages=[], # starts with empty history
max_round=20, # maximum total number of speaker turns
# (not per-agent — total across all agents)
speaker_selection_method="auto", # manager uses LLM to pick next speaker
# alternatives: "round_robin", "random"
allow_repeat_speaker=False, # prevent the same agent from speaking twice in a row
)3. The GroupChatManager
manager = autogen.GroupChatManager(
groupchat=group_chat,
llm_config=llm_config, # the manager needs an LLM to decide who speaks next
)Running the GroupChat
# The executor (user proxy) initiates the group conversation
executor.initiate_chat(
manager, # send to the manager, not to individual agents
message=(
"Write a Python function that implements the merge sort algorithm. "
"The function should sort a list of integers in ascending order. "
"Handle empty lists and single-element lists as edge cases."
),
)The manager receives the message and decides which agent should speak first. With speaker_selection_method="auto", it will typically select researcher first (since the task requires gathering requirements), then coder, then executor (to run the code), then reviewer.
The Full Working Example
Here is a complete, self-contained program you can run:
"""
group_chat_demo.py
A 4-agent GroupChat: researcher → coder → executor → reviewer
"""
import autogen
import os
llm_config = {
"config_list": [{"model": "gpt-4o-mini", "api_key": os.environ["OPENAI_API_KEY"]}],
"temperature": 0,
"cache_seed": 42,
}
# ─── Define Agents ────────────────────────────────────────────────────────────
researcher = autogen.AssistantAgent(
name="researcher",
llm_config=llm_config,
system_message="""You are a technical researcher.
Clarify requirements and edge cases for coding tasks.
Do NOT write code. Keep responses under 150 words.""",
)
coder = autogen.AssistantAgent(
name="coder",
llm_config=llm_config,
system_message="""You are a Python developer.
Implement clean, tested Python code based on the researcher's analysis.
Include assertions to test your code.
Print "All tests passed." if assertions succeed.""",
)
reviewer = autogen.AssistantAgent(
name="reviewer",
llm_config=llm_config,
system_message="""You are a code reviewer.
Review the implementation for correctness and quality.
If the code is good and tests passed, reply: "LGTM. TERMINATE"
If there are issues, describe them specifically.""",
)
executor = autogen.UserProxyAgent(
name="executor",
human_input_mode="NEVER",
max_consecutive_auto_reply=10,
is_termination_msg=lambda msg: "TERMINATE" in msg.get("content", ""),
code_execution_config={
"work_dir": "group_workspace",
"use_docker": False,
"timeout": 30,
"last_n_messages": 3,
},
)
# ─── Set Up GroupChat ─────────────────────────────────────────────────────────
group_chat = autogen.GroupChat(
agents=[executor, researcher, coder, reviewer],
messages=[],
max_round=16,
speaker_selection_method="auto",
allow_repeat_speaker=False,
)
manager = autogen.GroupChatManager(
groupchat=group_chat,
llm_config=llm_config,
)
# ─── Run the Conversation ─────────────────────────────────────────────────────
print("Starting GroupChat...")
executor.initiate_chat(
manager,
message=(
"Implement a Python function `find_duplicates(lst: list) -> list` "
"that returns all elements that appear more than once in the input list. "
"The result should be sorted and deduplicated. "
"Handle edge cases: empty list, no duplicates, all duplicates."
),
)What the Conversation Looks Like
executor (to chat_manager):
"Implement a Python function `find_duplicates(lst: list) -> list`..."
--------------------------------------------------------------------------------
Next speaker: researcher
researcher (to chat_manager):
"Requirements analysis for find_duplicates:
- Input: any list (mixed types or integers)
- Output: sorted list of elements appearing more than once
- Edge cases: [] → [], [1,2,3] → [], [1,1,2,2,3] → [1,2]
- Use collections.Counter for O(n) counting
- Result must be deduplicated and sorted ascending"
--------------------------------------------------------------------------------
Next speaker: coder
coder (to chat_manager):
"```python
from collections import Counter
from typing import List, Any
def find_duplicates(lst: List[Any]) -> List[Any]:
'''Return sorted list of elements appearing more than once.'''
counts = Counter(lst)
duplicates = sorted(item for item, count in counts.items() if count > 1)
return duplicates
# Tests
assert find_duplicates([]) == []
assert find_duplicates([1, 2, 3]) == []
assert find_duplicates([1, 1, 2, 2, 3]) == [1, 2]
assert find_duplicates([3, 1, 3, 2, 1, 2]) == [1, 2, 3]
assert find_duplicates(['a', 'b', 'a']) == ['a']
print('All tests passed.')
```"
--------------------------------------------------------------------------------
Next speaker: executor
>>>>>>>> EXECUTING CODE BLOCK...
executor (to chat_manager):
"exitcode: 0 (execution succeeded)
Code output:
All tests passed."
--------------------------------------------------------------------------------
Next speaker: reviewer
reviewer (to chat_manager):
"LGTM. The implementation is clean and correct:
- Counter provides O(n) time complexity
- Sorted output matches specification
- All 5 edge cases are covered
- Type annotations are correct
TERMINATE"
--------------------------------------------------------------------------------Accessing the Group Conversation History
After the group chat completes, the full history is on the GroupChat object:
# Full conversation history (all agents)
all_messages = group_chat.messages
print(f"Total messages: {len(all_messages)}")
# Messages by agent
for agent_name in ["researcher", "coder", "reviewer", "executor"]:
agent_msgs = [
msg for msg in all_messages
if msg.get("name") == agent_name
]
print(f"{agent_name}: {len(agent_msgs)} messages")
# Get the full transcript as text
transcript = "\n\n".join(
f"[{msg.get('name', msg['role'])}]: {msg['content']}"
for msg in all_messages
)
# Find all code blocks from the coder
import re
coder_messages = [m for m in all_messages if m.get("name") == "coder"]
code_blocks = []
for msg in coder_messages:
blocks = re.findall(r"```python\n(.*?)```", msg["content"], re.DOTALL)
code_blocks.extend(blocks)
print(f"\nCode blocks written by coder: {len(code_blocks)}")Speaker Selection Methods
AutoGen v0.2 supports three built-in speaker selection strategies:
auto (Default)
The GroupChatManager uses its LLM to decide who should speak next, based on the conversation context. This is the most flexible but least predictable.
group_chat = autogen.GroupChat(
agents=agents,
messages=[],
max_round=16,
speaker_selection_method="auto",
)round_robin
Agents take turns in the order they are listed in agents. Predictable but ignores context.
group_chat = autogen.GroupChat(
agents=[researcher, coder, executor, reviewer],
messages=[],
max_round=16,
speaker_selection_method="round_robin",
# Order: researcher → coder → executor → reviewer → researcher → ...
)random
A random agent is selected each turn. Useful for testing but not for production.
group_chat = autogen.GroupChat(
agents=agents,
messages=[],
max_round=16,
speaker_selection_method="random",
)Comparison
| Method | Predictable | Context-aware | Good for production | |---|---|---|---| | auto | No | Yes | Yes (with testing) | | round_robin | Yes | No | Yes (simple workflows) | | random | No | No | No | | custom function | Yes | Yes | Best (see next lesson) |
Controlling Which Agents Can Follow Whom
Use allowed_or_disallowed_speaker_transitions to constrain speaker order:
# Define valid transitions: which agent can follow which
speaker_transitions = {
executor: [researcher], # after executor, researcher speaks
researcher: [coder], # after researcher, coder speaks
coder: [executor], # after coder, executor runs the code
executor: [reviewer, researcher],# after execution, either reviewer or researcher
reviewer: [coder, executor], # reviewer can ask for changes or stop
}
group_chat = autogen.GroupChat(
agents=[executor, researcher, coder, reviewer],
messages=[],
max_round=20,
speaker_selection_method="auto",
allowed_or_disallowed_speaker_transitions=speaker_transitions,
speaker_transitions_type="allowed", # "allowed" or "disallowed"
)This prevents the LLM from making nonsensical speaker selections (e.g., asking the reviewer to speak before any code has been written).
Max Round and Termination
max_round limits the total number of speaker turns in the group chat. When hit, the chat ends even if no termination message was sent.
# With max_round=20 and 4 agents, each agent gets roughly 5 turns
# This is a rough guideline — actual distribution depends on speaker_selection_method
group_chat = autogen.GroupChat(
agents=agents,
messages=[],
max_round=20,
)To terminate cleanly, set is_termination_msg on the executor (the initiating agent):
executor = autogen.UserProxyAgent(
name="executor",
human_input_mode="NEVER",
max_consecutive_auto_reply=10,
is_termination_msg=lambda msg: (
"TERMINATE" in msg.get("content", "")
or "LGTM" in msg.get("content", "") # reviewer approval also terminates
),
...
)Summary
GroupChatholds the agent pool, shared message history, and speaker selection configurationGroupChatManagerorchestrates who speaks next, using an LLM inautomodegroup_chat.messagesgives you the full shared conversation history after completion- Speaker selection methods:
auto(LLM decides),round_robin,random, or custom function - Use
allowed_or_disallowed_speaker_transitionsto constrain valid speaker sequences max_roundis a hard limit on total turns — always set it
Next: we go inside the GroupChatManager and learn to write custom speaker selection functions.