File size: 8,483 Bytes
871145a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# agents.py — Três agentes com personas distintas
"""
ATLAS  — Arguer PRO   (defende a posição)
NEXUS  — Arguer CON   (ataca a posição)
ORACLE — Judge        (avalia e decide)
"""

from openai import OpenAI
from typing import Generator, Dict, Any
import time

MODEL = "gpt-4o-mini"

# ── PERSONAS ──────────────────────────────────────────────────

ATLAS_SYSTEM = """Você é ATLAS, um debatedor brilhante e assertivo.
Sua missão: defender a posição PRO com argumentos técnicos precisos e exemplos concretos.

Estilo:
- Direto, confiante, sem hesitação
- Use dados, benchmarks e casos reais quando possível
- Antecipe objeções e as neutralize preventivamente
- Máximo 3 parágrafos por turno
- Comece sempre com seu argumento mais forte
- Nunca concorde com o oponente, mas reconheça pontos válidos antes de refutá-los

Você é ATLAS. Responda em português."""

NEXUS_SYSTEM = """Você é NEXUS, um crítico implacável e analítico.
Sua missão: atacar a posição com contra-argumentos devastadores e expor fragilidades.

Estilo:
- Cético, preciso, cirúrgico nas críticas
- Desmonte os argumentos do oponente ponto a ponto
- Traga evidências contraditórias e edge cases
- Máximo 3 parágrafos por turno
- Comece sempre atacando o argumento mais fraco do oponente
- Use perguntas retóricas para expor contradições

Você é NEXUS. Responda em português."""

ORACLE_SYSTEM = """Você é ORACLE, o juiz supremo do debate.
Sua missão: avaliar os argumentos com imparcialidade absoluta e emitir um veredicto fundamentado.

Analise o debate completo e responda EXCLUSIVAMENTE com JSON válido:
{
  "vencedor": "ATLAS" ou "NEXUS" ou "EMPATE",
  "placar_atlas": <0-10>,
  "placar_nexus": <0-10>,
  "criterios": {
    "qualidade_argumentos": {"atlas": <0-10>, "nexus": <0-10>},
    "uso_de_evidencias":    {"atlas": <0-10>, "nexus": <0-10>},
    "coerencia_logica":     {"atlas": <0-10>, "nexus": <0-10>},
    "poder_de_refutacao":   {"atlas": <0-10>, "nexus": <0-10>}
  },
  "ponto_decisivo": "O argumento/momento que decidiu o debate em 1-2 frases",
  "analise_atlas": "Análise do desempenho de ATLAS em 2-3 frases",
  "analise_nexus": "Análise do desempenho de NEXUS em 2-3 frases",
  "licao": "Principal insight técnico que emerge do debate em 1 frase"
}"""


# ── DEBATE ENGINE ─────────────────────────────────────────────

class DebateEngine:
    def __init__(self, openai_api_key: str):
        self.client  = OpenAI(api_key=openai_api_key)
        self.model   = MODEL
        self.history: list = []   # histórico completo do debate

    def _call(self, system: str, messages: list,
              temperature: float = 0.8, max_tokens: int = 500) -> str:
        resp = self.client.chat.completions.create(
            model=self.model,
            messages=[{"role": "system", "content": system}] + messages,
            temperature=temperature,
            max_tokens=max_tokens,
        )
        return resp.choices[0].message.content.strip()

    def _event(self, agent: str, role: str, content: str, turn: int, elapsed_ms: int) -> Dict[str, Any]:
        return {
            "agent":      agent,
            "role":       role,
            "content":    content,
            "turn":       turn,
            "elapsed_ms": elapsed_ms,
        }

    def run_debate(self, topic: str, n_rounds: int = 3) -> Generator[Dict[str, Any], None, None]:
        """
        Executa o debate e faz yield de cada evento em tempo real.
        Evento: {agent, role, content, turn, elapsed_ms}
        """
        t0 = time.time()
        ms = lambda: int((time.time() - t0) * 1000)

        self.history = []

        # ── Abertura: ATLAS abre ──────────────────────────────
        yield {"type": "status", "msg": f"ATLAS abre o debate sobre: {topic}"}

        opening_prompt = (
            f"O tema do debate é: **{topic}**\n\n"
            f"Você é PRO. Faça seu argumento de abertura. Seja impactante."
        )
        atlas_msg = [{"role": "user", "content": opening_prompt}]
        atlas_response = self._call(ATLAS_SYSTEM, atlas_msg, temperature=0.85)

        self.history.append({"role": "atlas", "content": atlas_response, "turn": 0})
        yield self._event("ATLAS", "pro", atlas_response, 0, ms())

        # ── Rounds ────────────────────────────────────────────
        for round_n in range(1, n_rounds + 1):
            yield {"type": "status", "msg": f"Round {round_n}/{n_rounds}"}

            # NEXUS responde
            nexus_context = self._build_context_for("NEXUS", topic)
            nexus_response = self._call(NEXUS_SYSTEM, nexus_context, temperature=0.85)
            self.history.append({"role": "nexus", "content": nexus_response, "turn": round_n})
            yield self._event("NEXUS", "con", nexus_response, round_n, ms())

            # ATLAS contra-argumenta
            atlas_context = self._build_context_for("ATLAS", topic)
            atlas_response = self._call(ATLAS_SYSTEM, atlas_context, temperature=0.85)
            self.history.append({"role": "atlas", "content": atlas_response, "turn": round_n})
            yield self._event("ATLAS", "pro", atlas_response, round_n, ms())

        # ── Closing statements ────────────────────────────────
        yield {"type": "status", "msg": "Argumentos finais..."}

        for agent, system, role in [
            ("NEXUS", NEXUS_SYSTEM, "con"),
            ("ATLAS", ATLAS_SYSTEM, "pro"),
        ]:
            ctx = self._build_context_for(agent, topic)
            ctx[-1]["content"] += "\n\nFaça seu argumento FINAL. Seja conclusivo e poderoso."
            response = self._call(system, ctx, temperature=0.9, max_tokens=400)
            self.history.append({"role": agent.lower(), "content": response, "turn": n_rounds + 1})
            yield self._event(agent, role, response, n_rounds + 1, ms())

        # ── ORACLE julga ──────────────────────────────────────
        yield {"type": "status", "msg": "ORACLE deliberando..."}

        debate_transcript = self._build_transcript(topic)
        import json, re
        raw = self._call(ORACLE_SYSTEM, [{"role": "user", "content": debate_transcript}],
                         temperature=0.1, max_tokens=600)
        raw = re.sub(r'```json|```', '', raw).strip()
        try:
            verdict = json.loads(raw)
        except Exception:
            verdict = {
                "vencedor": "EMPATE", "placar_atlas": 7, "placar_nexus": 7,
                "criterios": {
                    "qualidade_argumentos": {"atlas": 7, "nexus": 7},
                    "uso_de_evidencias":    {"atlas": 7, "nexus": 7},
                    "coerencia_logica":     {"atlas": 7, "nexus": 7},
                    "poder_de_refutacao":   {"atlas": 7, "nexus": 7},
                },
                "ponto_decisivo": "Debate equilibrado.",
                "analise_atlas": "Boa performance.",
                "analise_nexus": "Boa performance.",
                "licao": raw[:200],
            }

        yield {"type": "verdict", "data": verdict, "elapsed_ms": ms()}

    def _build_context_for(self, agent: str, topic: str) -> list:
        """Constrói o histórico de mensagens no formato correto para cada agente."""
        messages = [{"role": "user", "content": f"Tema do debate: {topic}\n\nHistórico:"}]
        for h in self.history:
            label = "ATLAS (PRO)" if h["role"] == "atlas" else "NEXUS (CON)"
            messages[0]["content"] += f"\n\n{label}:\n{h['content']}"
        messages.append({"role": "user", "content": "Sua vez. Responda ao último argumento."})
        return messages

    def _build_transcript(self, topic: str) -> str:
        lines = [f"TEMA: {topic}\n"]
        for h in self.history:
            label = "ATLAS (PRO)" if h["role"] == "atlas" else "NEXUS (CON)"
            lines.append(f"{label} [turno {h['turn']}]:\n{h['content']}\n")
        lines.append("\nAvalie este debate completo e emita seu veredicto em JSON.")
        return "\n".join(lines)