Phase 4Multi Agent Systems·9 min read

Adversarial Debate: Agents Critiquing Each Other

Phase 4 of 8

What if agents argued with each other to find better answers? Adversarial debate uses the power of disagreement to improve quality.

Coming from Software Engineering? Think of adversarial debate as code review, automated. One agent writes the "PR," another agent reviews it and pushes back, and a third agent merges the best version. You've seen this dynamic in pair programming and design reviews — having a second set of eyes catches errors the author missed. The pattern is the same; you're just automating the reviewer role with a differently-prompted LLM.


The Debate Pattern

Key roles:

  • Proposer: Generates initial answer
  • Critic: Finds flaws and suggests improvements
  • Judge: Evaluates and decides final answer

Why Debate Works

Benefits:

  • Exposes blind spots
  • Forces justification
  • Catches errors
  • Improves reasoning quality

Choosing the Right Agent Topology

Before diving into code, here's a decision tree for picking the right multi-agent pattern. Refer back to this after you've seen all topologies (Days 57-59):

Topology Best For Tradeoff
Pipeline Document processing, ETL Simple but no error correction
Supervisor/Worker Decomposable tasks, research Flexible but supervisor is bottleneck
Adversarial Debate Accuracy-critical decisions Better quality but 2-3x cost
Parallel + Judge Creative tasks, brainstorming Fast but judge adds latency

Basic Debate Implementation

# script_id: day_052_adversarial_debate/debate_system
from openai import OpenAI
import json

client = OpenAI()

def proposer(question: str) -> str:
    """Generate an initial answer."""
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": """You are a knowledgeable expert.
Provide a well-reasoned answer to the question.
Be thorough and explain your reasoning."""},
            {"role": "user", "content": question}
        ]
    )
    return response.choices[0].message.content

def critic(question: str, answer: str) -> str:
    """Critique the proposed answer."""
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": """You are a critical reviewer.
Your job is to find flaws, gaps, and errors in the given answer.
Be thorough but fair. Point out:
1. Factual errors
2. Logical flaws
3. Missing information
4. Unclear explanations
5. Better alternatives"""},
            {"role": "user", "content": f"Question: {question}\n\nAnswer to critique:\n{answer}"}
        ]
    )
    return response.choices[0].message.content

def judge(question: str, answer: str, critique: str) -> str:
    """Judge and produce final answer."""
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": """You are an impartial judge.
Review the original answer and the critique.
Produce a final, improved answer that:
1. Addresses valid criticisms
2. Keeps correct parts of the original
3. Is clear and well-structured"""},
            {"role": "user", "content": f"""Question: {question}

Original Answer:
{answer}

Critique:
{critique}

Please provide the final, improved answer."""}
        ]
    )
    return response.choices[0].message.content

def debate(question: str) -> dict:
    """Run a full debate cycle."""

    print("=" * 60)
    print(f"Question: {question}")
    print("=" * 60)

    # Step 1: Proposer
    print("\n[PROPOSER]")
    answer = proposer(question)
    print(answer[:200] + "...")

    # Step 2: Critic
    print("\n[CRITIC]")
    critique = critic(question, answer)
    print(critique[:200] + "...")

    # Step 3: Judge
    print("\n[JUDGE - Final Answer]")
    final = judge(question, answer, critique)
    print(final)

    return {
        "question": question,
        "initial_answer": answer,
        "critique": critique,
        "final_answer": final
    }

# Usage
result = debate("Should companies adopt a 4-day work week?")

Multi-Round Debate

Allow multiple rounds of argument:

# script_id: day_052_adversarial_debate/debate_system
def multi_round_debate(question: str, rounds: int = 3) -> str:
    """Run multiple rounds of debate."""

    # Initial answer
    current_answer = proposer(question)
    debate_history = [{"role": "proposer", "content": current_answer}]

    for round_num in range(rounds):
        print(f"\n--- Round {round_num + 1} ---")

        # Critic responds
        critique = critic(question, current_answer)
        debate_history.append({"role": "critic", "content": critique})

        # Proposer defends/improves
        defense_response = client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": """You are defending your answer.
Address the critique by:
1. Accepting valid points and improving your answer
2. Defending correct parts with evidence
3. Providing a revised, stronger answer"""},
                {"role": "user", "content": f"""Question: {question}

Your previous answer:
{current_answer}

Critique to address:
{critique}

Provide your revised answer."""}
            ]
        )

        current_answer = defense_response.choices[0].message.content
        debate_history.append({"role": "proposer", "content": current_answer})

    # Final judgment
    final = judge(question, current_answer, debate_history[-2]["content"])

    return final

# Usage
result = multi_round_debate("Is remote work better than office work?", rounds=2)
print(result)

Red Team / Blue Team

One team attacks, one defends:

# script_id: day_052_adversarial_debate/debate_system
def red_blue_debate(topic: str, position: str) -> dict:
    """Red team attacks, blue team defends."""

    # Blue team: Defend the position
    def blue_team(position: str, attack: str = None) -> str:
        context = f"Position to defend: {position}"
        if attack:
            context += f"\n\nAttack to counter:\n{attack}"

        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": """You are the BLUE TEAM.
Your job is to DEFEND the given position with strong arguments.
Use facts, logic, and evidence to support your case."""},
                {"role": "user", "content": context}
            ]
        )
        return response.choices[0].message.content

    # Red team: Attack the position
    def red_team(position: str, defense: str) -> str:
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": """You are the RED TEAM.
Your job is to ATTACK the given position and find weaknesses.
Challenge assumptions, find counterexamples, and poke holes in the argument."""},
                {"role": "user", "content": f"""Position being defended: {position}

Current defense:
{defense}

Find weaknesses and attack!"""}
            ]
        )
        return response.choices[0].message.content

    # Run debate
    print(f"Topic: {topic}")
    print(f"Position: {position}")
    print("=" * 60)

    # Round 1
    defense1 = blue_team(position)
    print("\n[BLUE TEAM - Initial Defense]")
    print(defense1[:300] + "...")

    attack1 = red_team(position, defense1)
    print("\n[RED TEAM - Attack]")
    print(attack1[:300] + "...")

    # Round 2
    defense2 = blue_team(position, attack1)
    print("\n[BLUE TEAM - Counter]")
    print(defense2[:300] + "...")

    # Judgment
    verdict = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "You are a neutral judge. Evaluate the debate and determine the strength of the position after testing."},
            {"role": "user", "content": f"""Position: {position}

Final defense: {defense2}

Red team's best attack: {attack1}

Rate the position's strength (1-10) and explain."""}
        ]
    )

    return {
        "topic": topic,
        "position": position,
        "final_defense": defense2,
        "best_attack": attack1,
        "verdict": verdict.choices[0].message.content
    }

# Usage
result = red_blue_debate(
    "AI Safety",
    "AI development should be paused until safety is guaranteed"
)

Socratic Debate

Use questions to improve reasoning:

# script_id: day_052_adversarial_debate/debate_system
def socratic_debate(claim: str, max_questions: int = 5) -> dict:
    """Challenge a claim with Socratic questioning."""

    dialogue = []

    # Initial claim
    dialogue.append({"role": "claimant", "content": claim})

    for i in range(max_questions):
        # Questioner asks probing question
        question_response = client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": """You are a Socratic questioner.
Ask ONE probing question that:
- Challenges assumptions
- Requests evidence
- Explores implications
- Tests consistency

Be respectful but rigorous. Ask questions that lead to deeper understanding."""},
                {"role": "user", "content": f"Claim: {claim}\n\nPrevious dialogue:\n" +
                    "\n".join([f"{d['role']}: {d['content']}" for d in dialogue[-4:]])}
            ]
        )

        question = question_response.choices[0].message.content
        dialogue.append({"role": "questioner", "content": question})
        print(f"\n[Question {i+1}]: {question}")

        # Claimant responds
        answer_response = client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": """You are defending your claim.
Answer the question honestly and thoughtfully.
If the question reveals a flaw, acknowledge it and refine your position.
If your position is strong, explain why."""},
                {"role": "user", "content": f"Your claim: {claim}\n\nQuestion: {question}"}
            ]
        )

        answer = answer_response.choices[0].message.content
        dialogue.append({"role": "claimant", "content": answer})
        print(f"[Answer]: {answer[:200]}...")

    # Final refined position
    final = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "Based on the Socratic dialogue, provide a refined, well-examined version of the original claim."},
            {"role": "user", "content": f"Original claim: {claim}\n\nDialogue:\n" +
                "\n".join([f"{d['role']}: {d['content'][:100]}..." for d in dialogue])}
        ]
    )

    return {
        "original_claim": claim,
        "dialogue": dialogue,
        "refined_claim": final.choices[0].message.content
    }

# Usage
result = socratic_debate("Artificial General Intelligence will be achieved within 10 years")
print("\n[REFINED CLAIM]:", result["refined_claim"])

Consensus Building

Multiple agents debate until they agree:

# script_id: day_052_adversarial_debate/debate_system
def build_consensus(question: str, num_agents: int = 3, max_rounds: int = 5) -> str:
    """Multiple agents debate until consensus."""

    # Generate initial opinions
    opinions = []
    for i in range(num_agents):
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": f"You are Agent {i+1}. Provide your independent opinion."},
                {"role": "user", "content": question}
            ]
        )
        opinions.append(response.choices[0].message.content)

    print(f"Initial opinions from {num_agents} agents gathered.")

    for round_num in range(max_rounds):
        # Check for consensus
        consensus_check = client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": """Analyze these opinions and determine:
1. Do they agree on the main points? (yes/no)
2. What are the key disagreements?
3. What do they agree on?

Return JSON: {"consensus": true/false, "agreements": [...], "disagreements": [...]}"""},
                {"role": "user", "content": "\n\n".join([f"Agent {i+1}: {o}" for i, o in enumerate(opinions)])}
            ],
            response_format={"type": "json_object"}
        )

        check = json.loads(consensus_check.choices[0].message.content)

        if check.get("consensus"):
            print(f"Consensus reached in round {round_num + 1}!")
            break

        print(f"Round {round_num + 1}: No consensus. Disagreements: {check.get('disagreements', [])[:2]}")

        # Agents revise based on others' views
        new_opinions = []
        for i, opinion in enumerate(opinions):
            other_opinions = [o for j, o in enumerate(opinions) if j != i]

            revision = client.chat.completions.create(
                model="gpt-4o",
                messages=[
                    {"role": "system", "content": f"""You are Agent {i+1}. Consider other agents' views.
Where you agree, acknowledge it.
Where you disagree, explain why or find middle ground.
Try to move toward consensus while maintaining intellectual honesty."""},
                    {"role": "user", "content": f"""Your opinion: {opinion}

Other agents' opinions:
{chr(10).join(other_opinions)}

Provide your revised opinion."""}
                ]
            )
            new_opinions.append(revision.choices[0].message.content)

        opinions = new_opinions

    # Generate final consensus statement
    final = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "Synthesize these opinions into a final consensus statement."},
            {"role": "user", "content": f"Question: {question}\n\nFinal opinions:\n" +
                "\n\n".join([f"Agent {i+1}: {o}" for i, o in enumerate(opinions)])}
        ]
    )

    return final.choices[0].message.content

Summary


Quick Reference

# script_id: day_052_adversarial_debate/quick_reference
# Basic debate
answer = proposer(question)
critique = critic(question, answer)
final = judge(question, answer, critique)

# Multi-round
for round in range(3):
    critique = critic(question, answer)
    answer = defend(answer, critique)

# Red team / Blue team
defense = blue_team(position)
attack = red_team(position, defense)
verdict = judge(defense, attack)

What's Next?

Now let's explore CrewAI - a framework that makes building agent teams easy!