Exercise: Brainstorm (Round-Robin)¶
Objective¶
Implement multi-agent brainstorm conversations with round-robin orchestration — agents take turns in a fixed, repeating cycle over a shared conversation thread.
Concepts Covered¶
- Round-robin orchestration — agents speak in a fixed, repeating order
- Shared conversation thread across agents
- Chat manager for speaking order and termination
- Context window management in multi-agent conversations
How It Works¶
Round-robin is the simplest orchestration strategy for multi-agent group chats: agents speak in a fixed, repeating order. Each agent gets exactly one turn per round, and the cycle repeats for a configured number of rounds. No agent can interrupt, skip, or speak out of turn.
Three agents — Product Manager, Designer, and Engineer — share a single shared_messages list. The orchestrator cycles through them in a predetermined order (SPEAKING_ORDER), prepends each agent's own system prompt before calling the API, and appends the reply back to the shared list.
# The round-robin orchestration loop
SPEAKING_ORDER = ["Product Manager", "Designer", "Engineer"]
MAX_ROUNDS = 3 # → 3 rounds × 3 agents = 9 total turns
for round_num in range(1, MAX_ROUNDS + 1):
for agent_name in SPEAKING_ORDER: # fixed order each round
# Prepend this agent's system prompt to shared context
messages = [
{"role": "system", "content": AGENTS[agent_name]},
*shared_messages,
]
# Call the LLM with this agent's persona + full history
response = client.chat.completions.create(model=model, messages=messages)
reply = response.choices[0].message.content
# Append reply to the SHARED list — all agents see it next turn
shared_messages.append({
"role": "assistant",
"content": f"[{agent_name}]: {reply}",
})
This creates predictable, democratic conversations where every perspective is heard equally. The trade-off is rigidity — agents cannot interrupt, respond out of order, or dynamically decide who speaks next. For more flexible conversations, you would need a manager-driven or priority-based speaking strategy.
What happens after the rounds finish?
In this exercise, nothing — the loop simply ends after MAX_ROUNDS and the raw conversation is the result. There is no final LLM call to extract consensus or synthesize a summary. In production systems you would typically add a synthesis step: one final API call with a prompt like "Summarize the key decisions and action items from this brainstorm" to distill the multi-agent discussion into actionable output. We omit this here to keep the focus on the round-robin orchestration pattern itself.
flowchart TB
subgraph SharedThread["Shared Messages List"]
M1["[user] 'Design a mobile health app'"]
M2["[assistant] '[PM]: We should focus on...'"]
M3["[assistant] '[Designer]: The UI should...'"]
M4["[assistant] '[Engineer]: For the backend...'"]
M5["[assistant] '[PM]: Building on that...'"]
M6["...continues for MAX_ROUNDS=3"]
M1 --> M2 --> M3 --> M4 --> M5 --> M6
end
subgraph Cycle["Round-Robin Order (per round)"]
direction LR
PM["Product<br/>Manager"] --> Des["Designer"] --> Eng["Engineer"] --> PM
end
Cycle -.- SharedThread
style SharedThread fill:#fef9e7,stroke:#f39c12
style Cycle fill:#e8f4fd,stroke:#4a90d9
sequenceDiagram
participant O as Orchestrator
participant PM as Product Manager
participant D as Designer
participant E as Engineer
participant LLM as LLM Provider
Note over O,LLM: Round 1 of 3
O->>PM: [PM system_prompt] + shared_messages
PM->>LLM: Full shared context
LLM-->>PM: "[PM]: We should focus on..."
PM->>O: Append reply to shared_messages
O->>D: [Designer system_prompt] + shared_messages
D->>LLM: Full shared context (includes PM reply)
LLM-->>D: "[Designer]: The UI should..."
D->>O: Append reply to shared_messages
O->>E: [Engineer system_prompt] + shared_messages
E->>LLM: Full shared context (includes PM + Designer)
LLM-->>E: "[Engineer]: For the backend..."
E->>O: Append reply to shared_messages
Note over O,LLM: Rounds 2-3 repeat same cycle
Context sharing: Fully shared. All agents read and write to the same shared_messages list. Each agent sees every previous turn from every other agent. The system prompt is swapped per agent by prepending it to the shared messages before each call.
Structured output: Not used. Agent replies are plain text strings.
Context window growth
In the brainstorm variant, the shared messages list grows with every turn. With 3 rounds × 3 agents = 9 turns, the context can become substantial. For production systems, consider summarizing or truncating older messages.
Interactive Message Flow¶
Message Flow: Practical Example¶
This example shows exactly how the shared messages list grows turn by turn. Unlike the concurrent pattern (where each agent has its own list), here all agents read and write to the same list.
Shared messages growing round by round¶
Start — the shared list has only the user prompt:
# Turn 0 — initial state
shared_messages = [
{
"role": "user",
"content": "Brainstorm session topic: A mobile app that helps people reduce food waste at home..."
}
]
Turn 1 — Product Manager speaks. The orchestrator prepends PM's system prompt before calling the LLM:
# What the LLM receives for the Product Manager:
api_call_messages = [
{"role": "system", "content": "You are an experienced Product Manager..."},
{"role": "user", "content": "Brainstorm session topic: A mobile app..."} # from shared_messages
]
# After PM responds → reply is appended to the SHARED list:
shared_messages = [
{"role": "user", "content": "Brainstorm session topic: A mobile app..."},
{"role": "assistant", "content": "[Product Manager]: The market for food waste apps is growing..."}
]
Turn 2 — Designer speaks. The Designer sees PM's contribution because it's in the shared list:
# What the LLM receives for the Designer:
api_call_messages = [
{"role": "system", "content": "You are a creative UX Designer..."},
{"role": "user", "content": "Brainstorm session topic: A mobile app..."}, # shared
{"role": "assistant", "content": "[Product Manager]: The market for food waste..."} # shared
]
# After Designer responds → appended to shared list:
shared_messages = [
{"role": "user", "content": "Brainstorm session topic: A mobile app..."},
{"role": "assistant", "content": "[Product Manager]: The market for food waste..."},
{"role": "assistant", "content": "[Designer]: We should focus on a camera-based UI..."}
]
Turn 3 — Engineer speaks. The Engineer sees both PM and Designer contributions:
# What the LLM receives for the Engineer:
api_call_messages = [
{"role": "system", "content": "You are a pragmatic Senior Engineer..."},
{"role": "user", "content": "Brainstorm session topic: A mobile app..."}, # shared
{"role": "assistant", "content": "[Product Manager]: The market for food waste..."}, # shared
{"role": "assistant", "content": "[Designer]: We should focus on a camera-based..."} # shared
]
# After Engineer responds → appended to shared list:
shared_messages = [
{"role": "user", "content": "Brainstorm session topic: A mobile app..."},
{"role": "assistant", "content": "[Product Manager]: The market for food waste..."},
{"role": "assistant", "content": "[Designer]: We should focus on a camera-based..."},
{"role": "assistant", "content": "[Engineer]: Image recognition for food is feasible with..."}
]
Rounds 2-3 continue the same way. By the end of 3 rounds × 3 agents = 9 turns, shared_messages has 10 entries (1 user + 9 assistant). Every agent in later turns sees the full accumulated history.
| After turn | shared_messages length |
Agents visible in history |
|---|---|---|
| Start | 1 (user only) | — |
| Turn 1 (PM) | 2 | PM |
| Turn 2 (Designer) | 3 | PM, Designer |
| Turn 3 (Engineer) | 4 | PM, Designer, Engineer |
| Turn 4 (PM, round 2) | 5 | All — PM sees Designer + Engineer's ideas |
| ... | ... | ... |
| Turn 9 (Engineer, round 3) | 10 | Complete group conversation |
File¶
01_brainstorm.py— Three agents debate a product idea in a shared thread
How to Run¶
Expected Output¶
Turn-by-turn logging showing which agent speaks, the shared conversation growing, and the round-robin cycle progressing through each round.