MultiAgentDebate
from loomflow.architecture import MultiAgentDebateMultiAgentDebate has several agents answer the same question,
read each other’s answers, and argue over a few rounds. A judge agent
(or a majority vote) then picks the final answer. Use it for
contested, high-stakes questions where disagreement between
viewpoints tells you something. Based on Du et al. 2023, Improving
Factuality and Reasoning in Language Models through Multiagent
Debate, and Liang et al. 2023 (divergent thinking via debate);
AutoGen GroupChat and CAMEL use similar patterns.
For the conceptual page see Multi-Agent Debate.
Class signature
class MultiAgentDebate:
name: str = "debate"
def __init__(
self,
*,
debaters: list[Agent],
judge: Agent | None = None,
rounds: int = 2,
convergence_check: bool = True,
convergence_similarity: float = 0.85,
debater_instructions: str | None = None,
judge_instructions: str | None = None,
worker_registry: dict[str, Any] | None = None,
role_to_worker_id: dict[str, str] | None = None,
) -> None: ...Constructor parameters
debaters
| Type | list[Agent] |
| Default | required |
The debating agents. Must contain at least 2 (a debate with one
participant is just a single-agent run); raises ValueError
otherwise. For best results, use different models or prompts so
the debaters don’t share the same blind spots.
judge
| Type | Agent | None |
| Default | None |
Optional synthesising agent. Reads the full debate transcript and
produces the final answer. When None, the architecture falls back
to majority vote on the final round’s Final answer: lines. The
judge’s spend is charged to the coordinator’s budget.
rounds
| Type | int |
| Default | 2 |
Maximum debate rounds after the independent round 0, so each
debater answers up to rounds + 1 times. Du et al.’s rounds=2 (one
independent round plus one round of debate) is rounds=1 here. Each
round, all debaters run in parallel with full transcript context.
Must be >= 1.
convergence_check
| Type | bool |
| Default | True |
When True (default), the architecture checks for early termination
after each round: if every debater’s Final answer: line gives the
same answer, the loop terminates. Set to False for adversarial-only
debates where you want full rounds even if positions converge.
convergence_similarity
| Type | float |
| Default | 0.85 |
Jaccard similarity threshold in [0.0, 1.0] for the convergence
check’s fallback. It only applies when some reply in the round has no
Final answer: line; then every pair of whole replies must have a
word-level Jaccard similarity of at least this value. 0.85 means
roughly “the same reply, possibly in different words”. 1.0 requires
identical replies (after whitespace normalisation). Lower values stop
sooner (cheaper, but risk stopping before the debate has settled).
debater_instructions
| Type | str | None |
| Default | None (uses built-in default) |
Override the debaters’ prompt for the debate rounds (1 and later).
The default asks each debater to defend or update its position in
light of the prior transcript and to close with a
Final answer: <answer> line. Keep that request in a custom prompt:
the vote and the convergence check read it. Round 0 always sends the
question with a fixed request for an independent answer ending in a
Final answer: line.
judge_instructions
| Type | str | None |
| Default | None (uses built-in default) |
Override the judge’s prompt. The default reads the full transcript and synthesises the final answer with brief justification.
worker_registry / role_to_worker_id
Internal wiring set by Team.debate
when persistent_subagents=True (the default), so each debater keeps
one conversation per user across rounds. You normally don’t pass
these yourself.
Methods
declared_workers
def declared_workers(self) -> dict[str, Agent]:
workers = {f"debater_{i}": d for i, d in enumerate(self._debaters)}
if self._judge is not None:
workers["judge"] = self._judge
return workersEach debater is keyed debater_0, debater_1, …; the judge is
keyed judge when present.
run
- Round 0 (independent). All debaters answer the original
question simultaneously, with no awareness of each other. Run
in parallel. Emits
debate.round_started,debate.responseper debater. - Rounds 1..rounds (debate). Each debater receives the original question + full transcript. They defend or update their position. All debaters in a round run in parallel. The coordinator’s budget is checked before each round.
- Convergence check (when
convergence_check=True). Compares the debaters’ final answers (the lastFinal answer: ...line, or a\boxed{...}answer, normalised for case, whitespace, surrounding punctuation and**bold**). If they all match, stop early and emitdebate.converged. When a reply has no final answer, the check falls back to pairwise Jaccard similarity of whole replies againstconvergence_similarity. Agreement in round 0 ends the debate with the majority answer, without the judge. - Synthesise. Either
judge.run(synthesis_prompt)(eventdebate.judging) or a majority vote on the final round’s answers. The vote groups replies by final answer (whole text when a reply has none), returns the first reply of the largest group, and breaks ties by debater order. Emitsdebate.synthesizedwith the method used.
A debater that raises or comes back interrupted (its own max_turns,
a budget) sits that round out and the debate emits
debate.debater_failed with the error; the round continues with the
others. The debate raises only when no debater answers a round. Each
debater’s spend is charged to the coordinator’s budget.
When debate pays off
- High-stakes decisions: pricing, hiring, security policy, architecture choices.
- Adversarial review: red-team / blue-team style argument.
- When disagreement is informative: specialists with different priors should disagree, and the disagreement is the signal.
When NOT to use
- Tasks with a clear right answer (math, code), where debate is wasted effort.
- Time-sensitive interactive use (chatbots), where it is too slow.
- Cost-sensitive bulk processing: expect 3–5× single-agent cost.
Example
from loomflow import Agent
from loomflow.architecture import MultiAgentDebate
from loomflow.team import Team
optimist = Agent("Argue for the positive case.", model="claude-opus-4-7")
skeptic = Agent("Argue for the cautious case.", model="claude-opus-4-7")
analyst = Agent("Argue from the data.", model="gpt-4o")
cio = Agent(
"You are the CIO. Read the full debate transcript and pick the "
"best decision with one paragraph of justification.",
model="claude-opus-4-7",
)
team = Team.debate(
debaters=[optimist, skeptic, analyst],
judge=cio,
rounds=2,
convergence_similarity=0.85,
model="claude-opus-4-7",
)
result = await team.run(
"Should we adopt agent harnesses for our customer support stack?"
)Source
loomflow/architecture/debate.py
Convergence ≠ correctness. Debaters may converge on a wrong
answer if they share priors. Use different models and prompts so
their blind spots don’t overlap. For best-of-N with a
different-model critic,
ActorCritic is cheaper.