> ## Documentation Index
> Fetch the complete documentation index at: https://docs.swarms.world/llms.txt
> Use this file to discover all available pages before exploring further.

# GroupChat

> A turn-based groupchat where every agent bids each turn and the single highest-scoring bidder above threshold speaks

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

<Note>
  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.
</Note>

## Import

```python theme={null}
from swarms import Agent, GroupChat, RESPOND_TOOL
```

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

```python theme={null}
GroupChat(
    name: str = "dynamic-groupchat",
    description: str = "Agents take turns; one speaker per turn.",
    agents: Optional[List[Agent]] = None,
    max_loops: int = 20,
    threshold: float = 0.5,
    recency_penalty: float = 0.3,
    recency_window: int = 1,
    idle_timeout: float = 8.0,
    output_type: str = "str-all-except-first",
    verbose: bool = False,
    auto_equip: bool = True,
)
```

<ParamField path="name" type="str" default="dynamic-groupchat">
  Human-readable name used in logs and serialized state.
</ParamField>

<ParamField path="description" type="str" default="Agents take turns; one speaker per turn.">
  Short description of the chat.
</ParamField>

<ParamField path="agents" type="Optional[List[Agent]]" required>
  Agents participating in the conversation. **At least two are required** for a meaningful discussion. Fewer than two raises `ValueError`.
</ParamField>

<ParamField path="max_loops" type="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.
</ParamField>

<ParamField path="threshold" type="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.
</ParamField>

<ParamField path="recency_penalty" type="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.
</ParamField>

<ParamField path="recency_window" type="int" default="1">
  How many of the most recent speakers are subject to `recency_penalty`.
</ParamField>

<ParamField path="idle_timeout" type="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.
</ParamField>

<ParamField path="output_type" type="str" default="str-all-except-first">
  Format passed to `history_output_formatter`. Common values: `"str-all-except-first"`, `"str"`, `"list"`, `"dict"`, `"json"`.
</ParamField>

<ParamField path="verbose" type="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).
</ParamField>

<ParamField path="auto_equip" type="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.
</ParamField>

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

```python theme={null}
from swarms import Agent, RESPOND_TOOL

agent = Agent(
    agent_name="Researcher",
    model_name="gpt-5.4",
    max_loops=1,
    persistent_memory=False,
    tools_list_dictionary=[RESPOND_TOOL],
)
```

`RESPOND_TOOL` forces a call to `respond(score, message)`:

| Field     | Type            | Meaning                                                                        |
| --------- | --------------- | ------------------------------------------------------------------------------ |
| `score`   | number (`0..1`) | How much the agent wants to speak. `0` = stay silent, `1` = strongly wants to. |
| `message` | string          | The reply to broadcast, or an empty string to stay silent.                     |

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.

```python theme={null}
def run(
    self,
    task: str,
    streaming_callback: Optional[Callable[[str, str, bool], None]] = None,
) -> Any
```

<ParamField path="task" type="str" required>
  The initial user message that seeds the conversation.
</ParamField>

<ParamField path="streaming_callback" type="Optional[Callable[[str, str, bool], None]]" optional>
  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`).
</ParamField>

**Returns:** the conversation formatted per `output_type`.

```python theme={null}
result = chat.run("Should we adopt AI for medical diagnosis?")
```

***

### `run_batch(tasks)`

Run several independent groupchats sequentially, one per task.

```python theme={null}
def run_batch(self, tasks: List[str]) -> List[Any]
```

<ParamField path="tasks" type="List[str]" required>
  Tasks to run, each as its own fresh groupchat.
</ParamField>

**Returns:** a list of formatted outputs, one per task.

## Usage Examples

### Basic dynamic groupchat

```python theme={null}
from swarms import Agent, GroupChat

optimist = Agent(
    agent_name="Optimist",
    system_prompt="You argue for the benefits.",
    model_name="gpt-5.4",
    max_loops=1,
    persistent_memory=False,
)
pessimist = Agent(
    agent_name="Pessimist",
    system_prompt="You argue for the risks.",
    model_name="gpt-5.4",
    max_loops=1,
    persistent_memory=False,
)
realist = Agent(
    agent_name="Realist",
    system_prompt="You seek a balanced analysis.",
    model_name="gpt-5.4",
    max_loops=1,
    persistent_memory=False,
)

chat = GroupChat(
    agents=[optimist, pessimist, realist],
    max_loops=10,      # stop after 10 total messages
    threshold=0.5,     # only the single highest bid above 0.5 wins each turn
)

result = chat.run("Should we adopt AI for medical diagnosis?")
print(result)
```

`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

```python theme={null}
# Raise the threshold so only a strongly-motivated bid wins the floor each turn,
# and widen the recency window so agents rotate speaking turns more aggressively.
chat = GroupChat(
    agents=[optimist, pessimist, realist],
    max_loops=20,
    threshold=0.7,
    recency_penalty=0.4,
    recency_window=2,
)

result = chat.run("Design the architecture for a real-time fraud-detection system.")
```

### Equipping the tool yourself

```python theme={null}
from swarms import Agent, GroupChat, RESPOND_TOOL

agents = [
    Agent(
        agent_name=name,
        system_prompt=prompt,
        model_name="gpt-5.4",
        max_loops=1,
        persistent_memory=False,
        tools_list_dictionary=[RESPOND_TOOL],
    )
    for name, prompt in [
        ("Researcher", "You contribute research and evidence."),
        ("Critic", "You stress-test claims and find weaknesses."),
    ]
]

chat = GroupChat(agents=agents, auto_equip=False, max_loops=8)
result = chat.run("Discuss the tradeoffs of autonomous multi-agent systems.")
```

## 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](https://github.com/kyegomez/swarms/blob/master/swarms/structs/groupchat.py).
