TL;DR:
- Single LLMs are overconfident; multi-agent debate forces explicit justification and catches errors the first agent missed
- The three-agent pattern (proposer, critic, synthesiser) is the most practical starting point for production workflows
- Debate adds latency and cost — use it selectively on high-stakes outputs, not routine tasks
Language models are confident. They produce fluent, well-structured answers even when those answers are wrong. They don’t naturally hedge, and they don’t check their own work the way a careful human analyst would. Multi-agent debate addresses this directly: instead of asking one agent for an answer, you have multiple agents argue positions, critique each other’s reasoning, and converge on a better answer through structured disagreement.
This isn’t a hypothetical approach. Research from MIT and Stanford has demonstrated that structured debate between language models outperforms single-agent baselines on complex reasoning tasks. The intuition is simple — the same mechanism that makes peer review and adversarial collaboration valuable in human knowledge work applies to AI systems.
When to Use Multi-Agent Debate
Debate adds latency and cost. It’s not appropriate for every workflow. Use it when:
- The stakes of being wrong are high: contract review, medical decision support, financial analysis
- The problem has multiple defensible answers: strategic planning, risk assessment, design decisions
- You need to surface assumptions: policy analysis, compliance interpretation
- You want to catch model-specific blind spots: combining different model providers in the debate panel catches errors caused by shared training biases
Don’t use it for routine tasks where speed matters and errors are cheap to fix. Summarising a document, drafting a first-pass email, or extracting structured data rarely benefit from debate overhead.
The Three-Agent Pattern
The most practical starting structure is proposer → critic → synthesiser.
Proposer: Generates the initial answer or recommendation, with explicit reasoning.
Critic: Receives the proposer’s output and is instructed to find weaknesses, unstated assumptions, factual errors, and missing considerations. The critic is explicitly told not to agree unless the reasoning is genuinely sound.
Synthesiser: Receives both the proposal and the critique, then produces a final answer that incorporates valid criticisms and resolves the disagreement.
This maps naturally to human review workflows — you’re essentially automating the process of writing a draft, having it reviewed, and producing a revised version.
Implementation in LangGraph
LangGraph’s graph structure makes debate flows clean to express. Each agent is a node; the graph defines the debate sequence.
from langgraph.graph import StateGraph, END
from typing import TypedDict, List
class DebateState(TypedDict):
topic: str
proposal: str
critique: str
final_answer: str
def proposer_node(state: DebateState) -> DebateState:
response = llm.invoke(
f"Analyse the following and provide your recommendation with explicit reasoning:\n\n{state['topic']}"
)
return {"proposal": response.content}
def critic_node(state: DebateState) -> DebateState:
response = llm.invoke(
f"Review this analysis critically. Find weaknesses, unsupported assumptions, "
f"and missing considerations. Be specific and direct:\n\n{state['proposal']}"
)
return {"critique": response.content}
def synthesiser_node(state: DebateState) -> DebateState:
response = llm.invoke(
f"Original analysis:\n{state['proposal']}\n\n"
f"Critical review:\n{state['critique']}\n\n"
f"Incorporate valid criticisms into a revised final answer. "
f"Note which criticisms you disagree with and why."
)
return {"final_answer": response.content}
builder = StateGraph(DebateState)
builder.add_node("proposer", proposer_node)
builder.add_node("critic", critic_node)
builder.add_node("synthesiser", synthesiser_node)
builder.set_entry_point("proposer")
builder.add_edge("proposer", "critic")
builder.add_edge("critic", "synthesiser")
builder.add_edge("synthesiser", END)
graph = builder.compile()
result = graph.invoke({"topic": "Should we migrate our database to a managed cloud service?"})
Multi-Round Debate
For harder problems, run multiple debate rounds before synthesis. Each round gives the critic another pass at revised proposals.
def should_continue(state: DebateState) -> str:
# Run up to 3 rounds, stop early if critique scores the proposal highly
if state.get("rounds", 0) >= 3:
return "synthesise"
if "no significant issues" in state.get("critique", "").lower():
return "synthesise"
return "propose_again"
Two or three rounds is typically sufficient. Beyond three, debate tends to converge or loop rather than improve.
Using Different Models Per Role
One underused technique is assigning different model families to proposer and critic roles. A Claude-based proposer and a Gemini-based critic (or vice versa) will have different failure modes and training emphases. Each model is more likely to catch errors the other makes.
from anthropic import Anthropic
from google import genai
anthropic_client = Anthropic()
google_client = genai.Client()
def proposer_node(state):
# Claude as proposer
message = anthropic_client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": f"Analyse: {state['topic']}"}]
)
return {"proposal": message.content[0].text}
def critic_node(state):
# Gemini as critic — different model, different blind spots
response = google_client.models.generate_content(
model="gemini-2.5-pro",
contents=f"Critique this analysis:\n\n{state['proposal']}"
)
return {"critique": response.text}
This cross-model debate is particularly valuable for high-stakes domains where model-specific biases (like over-confidence on certain question types, or training-data gaps) are a real concern.
CrewAI Implementation
CrewAI’s role-based agent model is a natural fit for debate. Define agents with explicit role instructions that create productive conflict:
from crewai import Agent, Task, Crew
proposer = Agent(
role="Senior Analyst",
goal="Produce well-reasoned analysis with explicit assumptions stated",
backstory="You are thorough and confident but always show your working."
)
critic = Agent(
role="Devil's Advocate",
goal="Find every weakness in analyses presented to you",
backstory="Your job is to stress-test reasoning. Approval should be rare and earned."
)
synthesis_task = Task(
description="Produce a final analysis incorporating the valid points from the critical review.",
agent=proposer, # Proposer revises based on critique
context=[propose_task, critique_task]
)
What Debate Doesn’t Fix
Multi-agent debate won’t catch factual errors that both agents share because of common training data. If both the proposer and critic were trained on the same incorrect information, neither will flag it. For factual claims, debate improves reasoning quality and catches logical errors — but ground the output in retrieved documents for factual accuracy. Combine debate with retrieval, not instead of it.
The practical verdict: multi-agent debate is a high-value pattern for reasoning-heavy, high-stakes workflows. For most teams, starting with the three-agent proposer-critic-synthesiser structure and graduating to multi-round or cross-model debate as needed covers 80% of the use cases where it genuinely helps.