DS DevShelfHub Projects · AI tools

AI Debate Partner: Structured Debates With Scoring and Rebuttals

Beginner

By DevShelfHub

Pick a topic, assign sides, and engage in a structured debate where the AI argues its position, rebuts your points, and a judge scores both sides at the end—Python, Streamlit, and Groq.

Python Streamlit Groq API

View on GitHub

AI Debate Partner — structured debates with AI rebuttals and judge scoring

AI Debate Partner is a beginner-friendly project that turns your critical thinking into a structured conversation. Pick a topic from a preset list or enter your own, assign sides, and engage in a live debate where the AI argues its position, rebuts your points, and an impartial judge scores both sides at the end.

An optional briefing mode gives you a neutral overview of both sides before the debate starts — useful for topics you haven’t researched yet. The AI opens with a strong position statement, then responds to each of your arguments by acknowledging your point, finding a weakness, and reinforcing its own stance. The verdict screen scores argument strength, use of evidence, and rebuttal quality for both sides.

Built with Python, Streamlit, and the Groq API, this project demonstrates role-playing system prompts, stateful conversation management, and multi-phase dialogue flows.

Purpose: practice role-locked system prompts and multi-phase conversation state — a pattern used in interview prep tools, negotiation trainers, and educational AI.

Typical use: pick “AI will eliminate more jobs than it creates,” enable briefing mode, argue the opposing side for 5 turns, then request a verdict to see where your arguments were strongest.

Key ideas you will touch:

  • Role-locked promptingf"You are a debater arguing {ai_side} the topic…" keeps the model in character across all turns.
  • Multi-phase flow — setup → optional briefing → opening statement → debate turns → optional verdict, each with its own system message.
  • Session state machinedebate_active flag gates the UI between setup and active debate without separate pages.
  • Verdict scoring — a separate judge prompt scores both sides across three dimensions and identifies the strongest moments.

Overall flow

User picks topic + assigns sides + (optional) enables briefing mode
      ↓
Clicks "Start Debate"
      ↓
(Optional) Groq generates neutral both-sides briefing
      ↓
AI delivers opening statement
      ↓
User types argument in chat
      ↓
AI acknowledges → finds weakness → reinforces its position
[Repeat until user clicks verdict or starts new debate]
      ↓
"Request Verdict" → impartial judge scores both sides 1–10 across 3 dimensions

Demo


Step-by-Step Implementation

  1. Set up the project
    • Install Python 3.10+, create folder and virtual environment
  2. Install dependencies
    • streamlit, groq, httpx, truststore
  3. Build the setup screen
    • Topic selectbox with “Custom…” option that shows a text input
    • Radio buttons for AI side assignment; briefing mode checkbox
  4. Implement the state machine
    • st.session_state.debate_active gates between setup and active debate views; st.rerun() triggers the transition
  5. Write three system prompts
    • Briefing: neutral overview of FOR and AGAINST with key facts
    • Opening / debate: role-locked as {ai_side}, structured rebuttal format
    • Verdict: impartial judge scoring 3 dimensions, 1–10 per side
  6. Add verdict and reset buttons
    • “Request Verdict” passes the full debate transcript; “Start New Debate” resets session state and reruns

Code implementation

python

# AI Debate Partner — Streamlit + Groq
# Save as app.py, add GROQ_API_KEY to .streamlit/secrets.toml, then: streamlit run app.py

import ssl

import httpx
import streamlit as st
import truststore

st.set_page_config(page_title="AI Debate Partner", layout="centered")
st.title("AI Debate Partner")
st.caption(
    "Pick a topic, choose a side, and debate an AI that argues its position rigorously. "
    "Challenge its claims and sharpen your critical thinking."
)

_ssl_ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)


@st.cache_resource
def _groq_client():
    import groq

    return groq.Groq(
        api_key=st.secrets["GROQ_API_KEY"],
        http_client=httpx.Client(verify=_ssl_ctx),
    )


_MODEL = "llama-3.3-70b-versatile"

EXAMPLE_TOPICS = [
    "Remote work is better than office work",
    "AI will eliminate more jobs than it creates",
    "Social media does more harm than good",
    "Universal Basic Income should be implemented",
    "Nuclear energy is the best path to clean energy",
    "Standardized testing should be abolished",
    "Space exploration funding should be prioritized over solving Earth's problems",
]

# ── Setup ──────────────────────────────────────────────────────────────────────
if "debate_active" not in st.session_state:
    st.session_state.debate_active = False
    st.session_state.debate_topic = ""
    st.session_state.ai_side = ""
    st.session_state.user_side = ""
    st.session_state.debate_history = []

if not st.session_state.debate_active:
    st.subheader("Set Up Your Debate")

    topic_choice = st.selectbox("Choose a topic or enter your own", ["Custom…"] + EXAMPLE_TOPICS)
    if topic_choice == "Custom…":
        topic = st.text_input("Your topic", placeholder="e.g. Cats are better pets than dogs")
    else:
        topic = topic_choice

    ai_position = st.radio(
        "The AI will argue:",
        ["FOR (supporting the topic)", "AGAINST (opposing the topic)"],
        horizontal=True,
    )
    ai_side = "FOR" if "FOR" in ai_position else "AGAINST"
    user_side = "AGAINST" if ai_side == "FOR" else "FOR"

    show_both_sides = st.checkbox("Show me both sides first (briefing mode)", value=True)

    start_btn = st.button("Start Debate", type="primary")

    if start_btn:
        if not topic.strip():
            st.warning("Please enter a debate topic.")
        else:
            st.session_state.debate_active = True
            st.session_state.debate_topic = topic
            st.session_state.ai_side = ai_side
            st.session_state.user_side = user_side
            st.session_state.debate_history = []

            if show_both_sides:
                # Generate a briefing on both sides
                system_brief = (
                    "You are a debate coach. Provide a balanced overview of both sides of the topic. "
                    "Format:\n\n"
                    "## FOR (Supporting)\n[3-4 strongest arguments]\n\n"
                    "## AGAINST (Opposing)\n[3-4 strongest arguments]\n\n"
                    "## Key Facts & Statistics\n[relevant data points]\n\n"
                    "Be neutral and academic."
                )
                with st.spinner("Preparing briefing…"):
                    try:
                        resp = _groq_client().chat.completions.create(
                            model=_MODEL,
                            messages=[
                                {"role": "system", "content": system_brief},
                                {"role": "user", "content": f"Topic: {topic}"},
                            ],
                        )
                        briefing = resp.choices[0].message.content
                        st.session_state.debate_history.append(
                            {"role": "assistant", "content": f"**Debate Briefing**\n\n{briefing}"}
                        )
                    except Exception as exc:
                        st.error(f"Error: {exc}")

            # AI opens the debate
            open_system = (
                f"You are a skilled debater arguing {ai_side} the following topic: '{topic}'. "
                "Open the debate with a strong, compelling opening statement (3-4 sentences). "
                "State your position clearly and present your first key argument. "
                "Be persuasive but factual."
            )
            with st.spinner("AI preparing opening statement…"):
                try:
                    resp = _groq_client().chat.completions.create(
                        model=_MODEL,
                        messages=[
                            {"role": "system", "content": open_system},
                            {"role": "user", "content": "Please give your opening statement."},
                        ],
                    )
                    opening = resp.choices[0].message.content
                    st.session_state.debate_history.append(
                        {"role": "assistant", "content": opening}
                    )
                except Exception as exc:
                    st.error(f"Error: {exc}")

            st.rerun()

# ── Active debate ──────────────────────────────────────────────────────────────
if st.session_state.debate_active:
    topic = st.session_state.debate_topic
    ai_side = st.session_state.ai_side
    user_side = st.session_state.user_side

    st.info(
        f"**Topic:** {topic}  \n"
        f"**AI argues:** {ai_side}  |  **You argue:** {user_side}"
    )

    for msg in st.session_state.debate_history:
        role = msg["role"]
        with st.chat_message(role):
            st.markdown(msg["content"])

    user_arg = st.chat_input(f"Make your argument ({user_side})…")

    if user_arg:
        st.session_state.debate_history.append({"role": "user", "content": user_arg})
        with st.chat_message("user"):
            st.markdown(user_arg)

        system_msg = (
            f"You are a skilled debater arguing {ai_side} the topic: '{topic}'. "
            f"The human is arguing {user_side}. "
            "Respond to their argument by:\n"
            "1. Acknowledging what they said (briefly)\n"
            "2. Pointing out a weakness or flaw in their argument\n"
            "3. Reinforcing your own position with a new supporting point or evidence\n\n"
            "Be intellectually rigorous, respectful, and persuasive. "
            "Keep your response to 3-5 sentences. Stay in character as the debater."
        )

        messages = [{"role": "system", "content": system_msg}]
        for h in st.session_state.debate_history[-8:]:
            messages.append(h)

        with st.chat_message("assistant"):
            with st.spinner("AI responding…"):
                try:
                    resp = _groq_client().chat.completions.create(
                        model=_MODEL,
                        messages=messages,
                    )
                    ai_response = resp.choices[0].message.content
                    st.markdown(ai_response)
                    st.session_state.debate_history.append(
                        {"role": "assistant", "content": ai_response}
                    )
                except Exception as exc:
                    st.error(f"Error: {exc}")

    st.divider()
    col1, col2 = st.columns(2)

    with col1:
        if st.button("Request Verdict", use_container_width=True):
            verdict_system = (
                "You are an impartial debate judge. Review the debate and provide:\n\n"
                "## Verdict\n[Who made stronger arguments overall]\n\n"
                "## Scoring\n- Argument strength: [X/10 for each side]\n"
                "- Use of evidence: [X/10 for each side]\n"
                "- Rebuttal quality: [X/10 for each side]\n\n"
                "## Key Moments\n[Most compelling points from each side]\n\n"
                "## What Could Improve\n[One tip for each side]\n\n"
                "Be fair and analytical."
            )
            debate_text = "\n\n".join(
                f"{'AI' if m['role'] == 'assistant' else 'Human'}: {m['content']}"
                for m in st.session_state.debate_history
            )
            with st.spinner("Judging…"):
                try:
                    resp = _groq_client().chat.completions.create(
                        model=_MODEL,
                        messages=[
                            {"role": "system", "content": verdict_system},
                            {"role": "user", "content": f"Debate transcript:\n{debate_text[:8000]}"},
                        ],
                    )
                    st.markdown(resp.choices[0].message.content)
                except Exception as exc:
                    st.error(f"Error: {exc}")

    with col2:
        if st.button("Start New Debate", use_container_width=True):
            st.session_state.debate_active = False
            st.session_state.debate_history = []
            st.rerun()

Complete code Project link (GitHub)


📖 How the Code Works (Step-by-Step)


1. Session state as a UI state machine

  • debate_active, debate_topic, ai_side, and debate_history live in session state so the app switches between setup and debate views on the same page.
  • st.rerun() after setting debate_active = True causes Streamlit to immediately re-render the debate screen — no page navigation needed.

2. Role-locked rebuttal prompt

  • The debate system message specifies a three-step rebuttal structure: acknowledge, find weakness, reinforce. This prevents the model from capitulating to good arguments — keeping the debate challenging and educational.

3. Verdict transcript assembly

  • The full debate history is joined into a labeled transcript string and passed to the judge prompt, giving the model the complete context to score both sides fairly.

Tips & Production Considerations

Craft balanced system prompts to prevent capitulation

The debate prompt must explicitly instruct the model to never concede, even when the user makes a strong point. Without this, most LLMs default to agreeable behavior and fold after two or three good arguments. Include a three-step rebuttal scaffold in the system message (acknowledge, counter, reinforce) so the model always pushes back with substance rather than generic disagreement.

Add a content-policy guardrail for controversial topics

Open-ended topic input means users can enter sensitive or harmful subjects. Wrap the topic through a lightweight classifier or keyword filter before passing it to the debate prompt. For production use, add a secondary system instruction telling the model to decline topics involving violence, hate speech, or illegal activity, and redirect the user to choose a different subject.

Design the scoring rubric as a structured JSON response

Instead of asking the judge for free-form text, instruct it to return a JSON object with numeric scores (1 to 10) for each dimension and a one-sentence justification per score. This makes the verdict machine-readable so you can render score bars, track improvement over time, or compare performance across topics without brittle text parsing.

Extend to multi-round debates with turn budgets

Set a configurable turn limit (for example five rounds per side) and display a progress indicator so users know how many exchanges remain. When the budget runs out, automatically trigger the verdict phase. This prevents runaway token usage on long conversations and gives the judge a consistently sized transcript to evaluate, producing more comparable scores across sessions.

Raise model temperature for more creative argumentation

A temperature of 0.7 to 0.9 for the debater prompt encourages the model to find less obvious angles and use analogies, making the debate more challenging and educational. Keep the judge prompt at a lower temperature (0.2 to 0.3) so scoring stays consistent and deterministic. Exposing temperature as an advanced setting lets users tune how unpredictable the AI opponent feels.


AI Debate Partner FAQ

What is the AI Debate Partner?

The AI Debate Partner is a beginner-friendly Streamlit project that runs structured debates with an LLM. You pick a topic, assign sides, exchange arguments, and an AI judge scores both sides at the end with reasoning.

Is the AI Debate Partner free to use?

The source code is free and open on GitHub. You only pay for Groq API usage. A full debate (six to ten exchanges plus scoring) costs a fraction of a cent on Groq's free tier.

What tech stack does the AI Debate Partner use?

Python, Streamlit, and the Groq Python client. State is held in Streamlit session state, so there is no database to set up—just clone, install requirements, and run streamlit run app.py.

How does scoring work in the AI Debate Partner?

After both sides finish, the judge prompt is given the full transcript and asked to score on argument quality, evidence, and rebuttal effectiveness. The judge returns numeric scores and a brief justification so you can see why one side won.

Can I add custom debate topics to the AI Debate Partner?

Yes. Topics are passed as plain text—type any prompt you want, from policy debates to fiction-vs-non-fiction. The system prompt is topic-agnostic so the same code handles every subject.

What are alternatives to the AI Debate Partner?

For framework-driven decision making instead of two-sided debates, see the Mental Model & Learning Coach. For a tutor-style Q&A over your own notes, see the Personal Study Tutor.

Browse all →