Learnixo

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 history

All 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:

Python
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

Python
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

Python
manager = autogen.GroupChatManager(
    groupchat=group_chat,
    llm_config=llm_config,      # the manager needs an LLM to decide who speaks next
)

Running the GroupChat

Python
# 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:

Python
"""
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:

Python
# 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.

Python
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.

Python
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.

Python
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:

Python
# 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.

Python
# 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):

Python
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

  • GroupChat holds the agent pool, shared message history, and speaker selection configuration
  • GroupChatManager orchestrates who speaks next, using an LLM in auto mode
  • group_chat.messages gives 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_transitions to constrain valid speaker sequences
  • max_round is a hard limit on total turns — always set it

Next: we go inside the GroupChatManager and learn to write custom speaker selection functions.