Skip to main content

Overview

GroupChat runs a turn-based, self-selecting conversation. There is no fixed speaking order and no speaker-selection function, but exactly one agent speaks per turn. Each turn, every agent is asked concurrently (via a forced respond(score, message) function call) how strongly it wants to speak, on a 0..1 scale, together with the reply it would give. The single highest bidder whose score — after a recency adjustment — clears threshold takes the floor; only that agent’s reply is posted to the shared conversation, and every other bid from that turn is discarded. A recency_penalty is subtracted from the score of any agent that spoke within the last recency_window turns, so the floor rotates around the room instead of one agent monologuing. The chat ends when either:
  • max_loops total messages have been posted (the initial user task counts as the first), or
  • no agent’s bid clears threshold on a turn — a conversational lull.
This replaces the older speaker-function design. Parameters and methods such as speaker_function, speaker_state, set_speaker_function, start_interactive_session, @mention routing, and the round-robin-speaker / random-speaker / priority-speaker selectors no longer exist. The current API is the turn-based, bid/threshold model documented below. Note also that idle_timeout is accepted for backward compatibility but is not used to end the chat — see below.

Import

Both GroupChat and RESPOND_TOOL are exported from the top level (the submodule path from swarms.structs.groupchat import GroupChat, RESPOND_TOOL also works).

Constructor

str
default:"dynamic-groupchat"
Human-readable name used in logs and serialized state.
str
default:"Agents take turns; one speaker per turn."
Short description of the chat.
Optional[List[Agent]]
required
Agents participating in the conversation. At least two are required for a meaningful discussion. Fewer than two raises ValueError.
int
default:"20"
Hard cap on the total number of messages posted, including the initial user task. When this many messages have been published the chat stops.
float
default:"0.5"
Minimum (recency-adjusted) bid a reply must exceed to take the floor. Raise it for a more selective room; lower it for livelier chatter. A turn where no agent clears it ends the chat.
float
default:"0.3"
Amount subtracted from the bid of any agent that spoke within the last recency_window turns, so the floor rotates instead of one agent monologuing. Set to 0.0 to disable.
int
default:"1"
How many of the most recent speakers are subject to recency_penalty.
float
default:"8.0"
Accepted for backward compatibility only. Not used to end the chat in the current implementation — the chat now ends on a bidding lull (no agent clears threshold on a turn) rather than a wall-clock timeout.
str
default:"str-all-except-first"
Format passed to history_output_formatter. Common values: "str-all-except-first", "str", "list", "dict", "json".
bool
default:"False"
Emit internal log messages (bids, scores, stop events) and print each posted message to the terminal as a styled panel (the user task in green, agent replies in blue with their score).
bool
default:"True"
When True (default), the RESPOND_TOOL schema is automatically injected into any agent that does not already carry it, and that agent’s LLM client is rebuilt so the forced tool call works. Set to False only if you equip every agent with RESPOND_TOOL yourself.

The respond tool

Every agent must carry RESPOND_TOOL so the chat can force a structured speaking decision instead of parsing free-form text. With auto_equip=True this is handled for you; otherwise add it explicitly:
RESPOND_TOOL forces a call to respond(score, message): A bid only wins the floor when its (recency-adjusted) score clears threshold and message is non-empty — and only the single highest such bid each turn is posted; every other agent’s bid for that turn is discarded.

Methods

run(task, streaming_callback=None)

Synchronously run the turn-based groupchat until a lull (no agent’s bid clears threshold) or until max_loops messages have been posted. Posts task as the first message, then on each turn collects a concurrent (score, message) bid from every agent, lets the single highest (recency-adjusted) bidder speak, and returns the formatted conversation.
str
required
The initial user message that seeds the conversation.
Optional[Callable[[str, str, bool], None]]
Optional (agent_name, chunk, is_final) callback. Each posted message — the initial task and every winning speaker’s reply — is replayed to it as whitespace-chunked tokens, with is_final=True marking the end of that speaker’s turn. Matches the streaming signature used elsewhere in the framework (ConcurrentWorkflow, HierarchicalSwarm).
Returns: the conversation formatted per output_type.

run_batch(tasks)

Run several independent groupchats sequentially, one per task.
List[str]
required
Tasks to run, each as its own fresh groupchat.
Returns: a list of formatted outputs, one per task.

Usage Examples

Basic dynamic groupchat

auto_equip=True injects the respond tool into each agent automatically, so you do not need to pass tools_list_dictionary=[RESPOND_TOOL] yourself.

A more selective room

Equipping the tool yourself

How It Works

  1. Seed — the user task is posted to the single shared conversation as the first message.
  2. Bid — each turn, every agent is run concurrently (via asyncio.gather + asyncio.to_thread, so one slow model call never stalls the turn) and forced through the respond tool to return a (score, message) bid against a snapshot of the current transcript.
  3. Select — the highest bid wins, after subtracting recency_penalty from any agent that spoke within the last recency_window turns. Bids with an empty message never win, and the turn ends with no winner if no adjusted score clears threshold.
  4. Post — only the winning agent’s reply is appended to the shared conversation; every other bid from that turn is discarded. All agents see the new message as context on the next turn.
  5. Stop — the chat ends when max_loops messages have been posted, or a turn passes with no bid clearing threshold (a conversational lull), and the formatted transcript is returned. idle_timeout is accepted for backward compatibility but no longer used to end the chat.

Tuning

  • threshold — higher means fewer turns get a winning bid (more lulls, shorter chats); lower means livelier back-and-forth.
  • recency_penalty / recency_window — control how strongly a recent speaker’s next bid is discounted so the floor rotates instead of one agent dominating every turn. Set recency_penalty=0.0 to disable.
  • max_loops — the hard ceiling on total messages; the primary cost control.
  • idle_timeout — accepted for backward compatibility only; it does not end the chat in the current implementation (the chat stops on a bidding lull instead of a wall-clock timeout).
  • max_loops=1 per agent — give each participating Agent max_loops=1 and persistent_memory=False so each speaking decision is a clean, single-shot call.

Notes

  • GroupChat inherits SerializableMixin; agents and conversation are excluded from serialized state.
  • Exactly one agent speaks per turn — the highest recency-adjusted bidder above threshold — not every agent that clears the bar. This is a turn-based design, not a free-for-all broadcast.
  • There is no human-in-the-loop REPL in this implementation — run is fully autonomous and returns when the chat concludes.

Source Code

View the source code on GitHub.