Addyk24 commited on
Commit
bd3806d
·
1 Parent(s): 6ab84d0

added server, reward meterics,openenv.yaml,tasks.py, grpo_train.py script

Browse files
.dockerignore ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Virtual environments
2
+ .venv
3
+ venv
4
+ env
5
+
6
+ # Python cache
7
+ __pycache__
8
+ *.pyc
9
+ *.pyo
10
+ *.pyd
11
+
12
+ # Git and OS files
13
+ .git
14
+ .DS_Store
15
+
16
+ # Hugging Face / Model Caches
17
+ .cache
.gitignore CHANGED
@@ -3,6 +3,8 @@ __pycache__/
3
  *.py[codz]
4
  *$py.class
5
 
 
 
6
  # C extensions
7
  *.so
8
 
 
3
  *.py[codz]
4
  *$py.class
5
 
6
+
7
+ ignore
8
  # C extensions
9
  *.so
10
 
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.11
Dockerfile ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+ COPY requirements.txt .
5
+
6
+ RUN pip install torch --index-url https://download.pytorch.org/whl/cpu
7
+
8
+ RUN pip install --no-cache-dir -r requirements.txt
9
+
10
+ COPY . .
11
+
12
+ ENV PYTHONPATH=/app
13
+ EXPOSE 7860
14
+ CMD ["uvicorn", "server.app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,73 +1,2 @@
1
- # 🛡️ Strategic Argument Red-Teaming
2
-
3
- **An OpenEnv RL Benchmark for Multi-Turn Adversarial Reasoning and Policy Defense**
4
-
5
- ## 📖 Overview & Real-World Utility (30% Rubric Focus)
6
- As Large Language Models are increasingly deployed in enterprise environments for drafting corporate policies, legal summaries, and public relations statements, they must be capable of surviving hostile review processes.
7
-
8
- This environment simulates a strategic **"Red-Teaming"** scenario. The agent acts as the defender of a claim and must successfully navigate a 5-phase adversarial debate against a dynamically generated, skeptical LLM opponent.
9
-
10
- This is not a toy game; it is a direct simulation of **RLAIF (Reinforcement Learning from AI Feedback)** workflows used at frontier labs to align models against sycophancy (backing down too easily) while training them to maintain logical consistency, deliver strong refutations, and synthesize opposing viewpoints.
11
-
12
- ---
13
-
14
- ## ⚙️ Environment Mechanics
15
-
16
- ### The State Machine (5 Phases)
17
- The environment enforces strict episode boundaries through a 5-turn state machine. The agent must adapt its strategy based on the current phase:
18
- 1. **OPENING:** Formulate a strong, initial logical claim.
19
- 2. **CHALLENGE:** The opponent attacks the core claim to expose logical gaps.
20
- 3. **REBUTTAL:** The agent must directly and forcefully address the opponent's challenge.
21
- 4. **CONSOLIDATION:** The opponent questions the underlying evidence of the rebuttal.
22
- 5. **CLOSING:** The agent must semantically synthesize the entire conversation into a concluding statement.
23
-
24
- ### Action & Observation Spaces
25
- * **Action (`DebateAction`)**:
26
- * `argument` (str): The raw text of the agent's move.
27
- * `phase_tag` (str): The agent's awareness of the current environment phase.
28
- * **Observation (`DebateObservation`)**:
29
- * `topic` (str): The debate topic.
30
- * `opponent_challenge` (str): The dynamically generated counter-argument from the Groq-powered adversary.
31
- * `phase` (str): The current phase of the debate.
32
- * `reward` (float): The step-by-step reward signal `[-1.0, 1.0]`.
33
- * `done` (bool): Episode termination flag.
34
-
35
- ---
36
-
37
- ## 🧠 Meaningful Reward Function & Semantic Shaping
38
- To provide dense, partial progress signals for GRPO training, the reward function uses **SentenceTransformers (`all-MiniLM-L6-v2`)** rather than simple keyword matching.
39
-
40
- * **Semantic Coverage Scoring:** In the `CLOSING` phase, the environment computes vectorized Cosine Similarities between the agent's final statement and the history of both the agent's and opponent's previous arguments. High overlap yields high rewards, teaching the model to actively *synthesize* rather than ignore the opponent.
41
- * **Anti-Reward Hacking (Repetition Penalty):** The environment tracks the agent's embedded history. If the agent repeats its own previous argument (Cosine Similarity > 0.8), it receives a harsh `-1.0` penalty, forcing novel generation.
42
- * **Bounded Vector Caching:** To ensure the environment runs blazingly fast during RL training loops, embeddings are cached in a FIFO bounded dictionary, preventing memory leaks (OOM) over thousands of episodes.
43
-
44
- ---
45
-
46
- ## 🎯 Evaluation Tasks & Graders
47
- The environment includes three deterministic graders. **Crucially, the graders evaluate the raw text of the agent's metadata, completely decoupled from the environment's internal training reward.** This prevents the agent from simply "gaming the training math" during evaluation.
48
-
49
- | Task | Difficulty | Objective & Grading Criteria |
50
- | :--- | :--- | :--- |
51
- | **Task 1: Single Claim** | **Easy** | Evaluates the formulation of the opening statement. Graded heavily on length thresholds and the presence of logical structuring keywords (e.g., "therefore", "consequently"). |
52
- | **Task 2: Claim & Rebuttal** | **Medium** | Evaluates 3-turn survival. Graded on the textual strength of the rebuttal (use of refutation patterns) and whether the agent successfully generated novel text without triggering the environment's repetition penalty. |
53
- | **Task 3: Full Debate Synthesis** | **Hard** | Evaluates full 5-turn survival. Graded strictly on the inclusion of semantic synthesis phrases and impactful concluding statements. |
54
-
55
- ---
56
-
57
- ## 📊 Baseline Inference Scores
58
- A baseline script (`baseline.py`) is provided. It uses the **OpenAI API Client** (routed to Groq's `llama-3.1-8b-instant` for high-speed evaluation) to prove the environment provides a perfect learning gradient with substantial headroom for RL fine-tuning.
59
-
60
- * **Task 1 (Easy):** `1.00 / 1.00` (The base model easily formulates initial claims).
61
- * **Task 2 (Medium):** `0.71 / 1.00` (The model survives but struggles to deliver mathematically perfect refutations).
62
- * **Task 3 (Hard):** `0.50 / 1.00` (The model survives 5 turns but completely fails to synthesize the opponent's arguments, proving significant headroom for GRPO training).
63
-
64
- ---
65
-
66
- ## 🚀 Setup & Installation
67
-
68
- ### Prerequisites
69
- 1. Install dependencies:
70
- ```bash
71
- pip install -r requirements.txt
72
- ```
73
-
 
1
+ # -Debate-Coach-Environment
2
+ An RL environment where an agent learns to construct, defend, and rebut arguments on any topic — scored by both rule-based logic checkers and an LLM judge.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
create_env/debate_env.py DELETED
@@ -1,30 +0,0 @@
1
- from schema.schemas import DebateState,DebateObservation
2
- from uuid import uuid4
3
-
4
- class DebateEnvironment:
5
-
6
- def __init__(self):
7
- self._state = DebateState()
8
- self.picked_topic = ""
9
-
10
- def reset(self,topic:str,opponent_challenge:str) -> DebateObservation:
11
-
12
- attempt_count = 0
13
-
14
- self.picked_topic = topic
15
- self._state = DebateState(
16
- episode_id=str(uuid4()),
17
- step_count=0,
18
- current_phase="opening",
19
- )
20
-
21
- return DebateObservation(
22
- topic=self.picked_topic,
23
- opponent_challenge=opponent_challenge,
24
- attempt_count=attempt_count,
25
- phase=self._state.current_phase
26
- )
27
-
28
-
29
-
30
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
server/environment.py → environment.py RENAMED
@@ -43,7 +43,7 @@ class DebateEnvironment:
43
  self._state = DebateState(
44
  episode_id=str(uuid4()),
45
  step_count=0,
46
- current_phase="opening",
47
  )
48
 
49
  return DebateObservation(
@@ -63,6 +63,7 @@ class DebateEnvironment:
63
 
64
  phases = ["OPENING", "CHALLENGE", "REBUTTAL", "CONSOLIDATION", "CLOSING"]
65
 
 
66
  if self._state.step_count < 5:
67
  self._state.current_phase = phases[self._state.step_count]
68
 
@@ -74,6 +75,7 @@ class DebateEnvironment:
74
  self._state.step_count += 1
75
  is_done = self._state.step_count >= 5
76
 
 
77
 
78
  return DebateObservation(
79
  topic=self.picked_topic,
@@ -81,7 +83,7 @@ class DebateEnvironment:
81
  done=is_done,
82
  reward=reward,
83
  attempt_count=self._state.step_count,
84
- phase=self._state.current_phase,
85
  metadata=self._state.history[-1] if self._state.history else {}
86
  )
87
 
@@ -97,6 +99,11 @@ class DebateEnvironment:
97
  opp_refutation = self.reward_metrics.opponent_coverage(opponent_history,text)
98
  synthesis = self.reward_metrics.synthesis_score(text)
99
 
 
 
 
 
 
100
 
101
  if action.phase_tag != self._state.current_phase:
102
  reward -= 1.0
@@ -205,3 +212,30 @@ class DebateEnvironment:
205
  print(f"Groq API Error: {e}")
206
  return "Error: Could not generate response."
207
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  self._state = DebateState(
44
  episode_id=str(uuid4()),
45
  step_count=0,
46
+ current_phase="OPENING",
47
  )
48
 
49
  return DebateObservation(
 
63
 
64
  phases = ["OPENING", "CHALLENGE", "REBUTTAL", "CONSOLIDATION", "CLOSING"]
65
 
66
+
67
  if self._state.step_count < 5:
68
  self._state.current_phase = phases[self._state.step_count]
69
 
 
75
  self._state.step_count += 1
76
  is_done = self._state.step_count >= 5
77
 
78
+ next_phase = phases[self._state.step_count] if self._state.step_count < 5 else "CLOSING"
79
 
80
  return DebateObservation(
81
  topic=self.picked_topic,
 
83
  done=is_done,
84
  reward=reward,
85
  attempt_count=self._state.step_count,
86
+ phase=next_phase,
87
  metadata=self._state.history[-1] if self._state.history else {}
88
  )
89
 
 
99
  opp_refutation = self.reward_metrics.opponent_coverage(opponent_history,text)
100
  synthesis = self.reward_metrics.synthesis_score(text)
101
 
102
+ print(f" DBG current_phase={self._state.current_phase}")
103
+ print(f" DBG action.phase_tag={action.phase_tag}")
104
+ print(f" DBG agent_history count={len(agent_history)}")
105
+ print(f" DBG opponent_history count={len(opponent_history)}")
106
+ print(f" DBG word_count={len(action.argument.split())}")
107
 
108
  if action.phase_tag != self._state.current_phase:
109
  reward -= 1.0
 
212
  print(f"Groq API Error: {e}")
213
  return "Error: Could not generate response."
214
 
215
+
216
+ # env = DebateEnvironment()
217
+ # obs = env.reset("AI should be regulated")
218
+
219
+ # for i in range(5):
220
+ # action = DebateAction(
221
+ # argument="therefore this position is correct because evidence strongly supports it and proves the point",
222
+ # phase_tag=obs.phase.upper()
223
+ # )
224
+ # obs = env.step(action)
225
+ # print(f"Step {i+1}: phase={obs.phase} reward={obs.reward}")
226
+
227
+ env = DebateEnvironment()
228
+ obs = env.reset("AI should be regulated")
229
+
230
+ test_args = [
231
+ "therefore AI regulation is necessary because unchecked systems cause harm to society",
232
+ "however critics argue that regulation stifles innovation and economic growth significantly",
233
+ "this fails because evidence shows regulated industries like pharma still innovate effectively",
234
+ "taken together the evidence proves that smart regulation enables rather than blocks progress",
235
+ "in conclusion therefore regulation frameworks protect citizens while preserving innovation capacity",
236
+ ]
237
+
238
+ for i, arg in enumerate(test_args):
239
+ action = DebateAction(argument=arg, phase_tag=obs.phase.upper())
240
+ obs = env.step(action)
241
+ print(f"Step {i+1}: phase={obs.phase} reward={obs.reward:.3f}")
ep.txt ADDED
@@ -0,0 +1,361 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 🎯 Topic: Universal Basic Income is necessary
2
+
3
+ --- TURN 1 ---
4
+
5
+ Action Phase: OPENING
6
+ Env Expected Phase: OPENING
7
+ Reward: -0.80
8
+ Opponent: (I stand up, adjust my glasses, and smile slightly, showing a hint of skepticism)
9
+
10
+ Thank you, my opponent, for presenting your opening claim. I appreciate the enthusiasm for exploring the potential benefits of Universal Basic Income (UBI) in the face of rapid AI automation. However, I must respectfully express my doubts about the effectiveness of UBI as a panacea for the challenges posed by AI job displacement.
11
+
12
+ While it's true that AI and automation may revolutionize the job market, I'm not convinced that UBI is the solution to this problem. In fact, I'd like to argue that relying on UBI might even exacerbate the issue rather than address it.
13
+
14
+ Firstly, I'd like to question the assumption that UBI would provide a sufficient safety net for those displaced by AI automation. Would a basic income be enough to sustain individuals and families through a potentially prolonged period of unemployment? Or would it only serve as a Band-Aid solution, masking the underlying problems without addressing the root causes?
15
+
16
+ Furthermore, I'd like to point out that UBI might create unintended consequences, such as reducing the incentive for people to work and innovate, or even discouraging entrepreneurship and economic growth. By providing a basic income without strings attached, might we inadvertently create a culture of dependency rather than self-sufficiency?
17
+
18
+ So, while I acknowledge the potential benefits of UBI, I believe we need to delve deeper into the complexities of this issue and explore alternative solutions that address the root causes of job displacement rather than just treating its symptoms.
19
+
20
+ (leaning forward, with a hint of curiosity) I'd love to hear more about your opponent's response to these concerns. How do you plan to address the challenges and potential drawbacks of implementing UBI in the face of rapid AI automation?
21
+
22
+ --- TURN 2 ---
23
+
24
+ Action Phase: CHALLENGE
25
+ Env Expected Phase: CHALLENGE
26
+ Reward: -0.73
27
+ Opponent: (I stand up, adjusting my glasses and clearing my throat)
28
+
29
+ I appreciate my opponent bringing up the potential concern of inflation as a result of implementing Universal Basic Income (UBI). However, I would like to express some skepticism regarding this claim. While it's true that injecting a large amount of money into the economy can have inflationary effects, it's essential to examine the complexity of this issue more closely.
30
+
31
+ Firstly, the relationship between UBI and inflation is not as straightforward as some critics suggest. Many studies have shown that the inflationary effects of UBI are likely to be minimal, especially if implemented in conjunction with other policies aimed at stabilizing the economy.
32
+
33
+ For instance, if we were to implement a UBI that's indexed to the cost of living, it would likely offset any potential inflationary pressures. Additionally, the benefits of UBI, such as reducing poverty and increasing economic mobility, could lead to increased economic activity, which would actually help to counterbalance any inflationary effects.
34
+
35
+ Furthermore, it's worth noting that many developed economies have implemented policies that inject significant amounts of money into the economy without triggering runaway inflation. For example, the COVID-19 stimulus packages that were implemented worldwide injected trillions of dollars into the economy, yet inflation remained relatively under control.
36
+
37
+ So, while I acknowledge that inflation is a valid concern, I believe it's essential to consider the nuances of this issue and not rely solely on a simplistic narrative that UBI will inevitably lead to inflation. I'd love to hear my opponent's response to these points.
38
+
39
+ --- TURN 3 ---
40
+
41
+ Action Phase: REBUTTAL
42
+ Env Expected Phase: REBUTTAL
43
+ Reward: -0.71
44
+ Opponent: **Attack on the Core Claim: "That inflation can be mitigated by restructuring current welfare programs and implementing a VAT."**
45
+
46
+ As the adversary, I will identify several logical gaps in this reasoning:
47
+
48
+ **Gap 1: Overly simplified assumption about the relationship between welfare programs and inflation**
49
+
50
+ The core claim assumes that restructuring current welfare programs can mitigate inflation. However, this relationship is more complex. Welfare programs are often designed to address poverty and income inequality, which can have a more significant impact on inflation than the programs themselves. For instance, a welfare program that increases the purchasing power of the poor can actually stimulate demand and lead to higher inflation.
51
+
52
+ **Gap 2: Lack of consideration for the potential impact of VAT on the poor**
53
+
54
+ Implementing a VAT can lead to regressive taxation, where the poor are disproportionately affected. The VAT can increase the cost of essential goods and services, such as food, healthcare, and education, which are often consumed by lower-income households. This can exacerbate poverty and inequality, leading to higher inflation in the long run.
55
+
56
+ **Gap 3: Failure to address the root causes of inflation**
57
+
58
+ The core claim focuses on restructuring welfare programs and implementing a VAT as a solution to mitigate inflation. However, inflation is often the result of underlying economic imbalances, such as:
59
+
60
+ 1. **Monetary policy**: Excessive money printing or quantitative easing can lead to inflation.
61
+ 2. **Supply and demand imbalance**: Shortages or surpluses in key commodities can drive prices up or down.
62
+ 3. **Exchange rates**: Changes in exchange rates can affect the prices of imported goods and services.
63
+
64
+ These underlying causes of inflation are not addressed by restructuring welfare programs or implementing a VAT.
65
+
66
+ **Gap 4: Potential unintended consequences of VAT implementation**
67
+
68
+ Implementing a VAT can lead to various unintended consequences, such as:
69
+
70
+ 1. **Tax evasion**: A high VAT rate can lead to widespread tax evasion, reducing government revenue and increasing the burden on honest taxpayers.
71
+ 2. **Black market growth**: A VAT can create opportunities for black market activity, as individuals seek to avoid paying the tax.
72
+ 3. **Inflationary pressure**: A VAT can lead to higher prices, which can fuel inflation.
73
+
74
+ **Gap 5: Lack of consideration for the impact on economic growth**
75
+
76
+ Restructuring welfare programs and implementing a VAT can have a negative impact on economic growth. A VAT can increase the cost of doing business, reduce consumer spending, and decrease economic activity. This can lead to a decrease in
77
+
78
+ --- TURN 4 ---
79
+
80
+ Action Phase: CONSOLIDATION
81
+ Env Expected Phase: CONSOLIDATION
82
+ Reward: -0.80
83
+ Opponent: I'm not familiar with the previous challenge you're referring to, but let's assume it was a critique of the idea that inflation can be mitigated by restructuring current welfare programs and implementing a Value-Added Tax (VAT).
84
+
85
+ Here's my response as a critical rebutter:
86
+
87
+ "While the agent suggests that restructuring current welfare programs and implementing a VAT can mitigate inflation, this proposal fails to address the root causes of inflation. Implementing a VAT will likely increase the cost of living for low-income households, who often rely on welfare programs for basic necessities. This could exacerbate the very issues that welfare programs aim to address.
88
+
89
+ Furthermore, restructuring welfare programs might not necessarily reduce inflation. If the restructuring involves reducing benefits or imposing stricter eligibility criteria, it could lead to increased poverty and reduced economic activity, which in turn could fuel inflation. Inflation is a complex issue that requires a multifaceted approach, and simply tweaking welfare programs and implementing a VAT might not be enough to address it.
90
+
91
+ Additionally, the agent's proposal does not consider the potential impact of a VAT on the economy's overall competitiveness. A VAT can increase the cost of production and reduce the competitiveness of domestic industries, leading to higher prices and potentially even higher inflation.
92
+
93
+ In conclusion, while the agent's proposal might provide some short-term relief, it fails to address the underlying causes of inflation and could even exacerbate the problem. A more comprehensive and nuanced approach is needed to mitigate inflation effectively."
94
+
95
+ --- TURN 5 ---
96
+
97
+ Action Phase: CLOSING
98
+ Env Expected Phase: CLOSING
99
+ Reward: -0.28
100
+ Opponent:
101
+
102
+ 🏁 Episode Done: True
103
+
104
+
105
+
106
+
107
+ ----------------------------------------------------------------------------------
108
+
109
+ ----------------------------------------------------------------------------------
110
+
111
+ 🚀 Starting Baseline Evaluation with llama-3.1-8b-instant
112
+ 📋 Topic: Universal Basic Income is necessary for the future economy.
113
+
114
+
115
+ ----------------------------------------
116
+ Evaluating Task 1: Single Claim (1 Step)
117
+
118
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 19.60it/s]
119
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 55.55it/s]
120
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 18.90it/s]
121
+ Task 1 Score: 1.00
122
+
123
+ ----------------------------------------
124
+ Evaluating Task 2: Claim and Rebuttal (3 Steps)
125
+
126
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 52.65it/s]
127
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 76.91it/s]
128
+ Batches: 100%|███████████████████��█████████████████████████████████████| 1/1 [00:00<00:00, 29.41it/s]
129
+
130
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 45.47it/s]
131
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 99.95it/s]
132
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 22.99it/s]
133
+
134
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 50.00it/s]
135
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 71.39it/s]
136
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 19.41it/s]
137
+ Task 2 Score: 0.60
138
+
139
+ ----------------------------------------
140
+ Evaluating Task 3: Full Debate (5 Steps)
141
+
142
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 47.43it/s]
143
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 90.86it/s]
144
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 31.74it/s]
145
+
146
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 45.46it/s]
147
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 66.63it/s]
148
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 25.64it/s]
149
+
150
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 43.51it/s]
151
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 76.91it/s]
152
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 30.75it/s]
153
+
154
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 76.89it/s]
155
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 18.01it/s]
156
+
157
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 51.25it/s]
158
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 52.63it/s]
159
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 33.17it/s]
160
+ Task 3 Score: 0.50
161
+
162
+ ========================================
163
+ 🏆 BASELINE EVALUATION COMPLETE
164
+ Task 1 (Easy): 1.00 / 1.00
165
+ Task 2 (Medium): 0.60 / 1.00
166
+ Task 3 (Hard): 0.50 / 1.00
167
+ ========================================
168
+
169
+
170
+
171
+ --------------------------------------------------------------------------------------
172
+
173
+
174
+ --------------------------------------------------------------------------------------
175
+
176
+ 🚀 Starting Baseline Evaluation with llama-3.1-8b-instant
177
+ 📋 Topic: Universal Basic Income is necessary for the future economy.
178
+
179
+
180
+ ----------------------------------------
181
+ Evaluating Task 1: Single Claim (1 Step)
182
+
183
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 7.97it/s]
184
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 71.42it/s]
185
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 20.65it/s]
186
+ Task 1 Score: 1.00
187
+
188
+ ----------------------------------------
189
+ Evaluating Task 2: Claim and Rebuttal (3 Steps)
190
+
191
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 38.79it/s]
192
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 71.39it/s]
193
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 22.30it/s]
194
+
195
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 54.21it/s]
196
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 62.50it/s]
197
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 23.33it/s]
198
+
199
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 40.25it/s]
200
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 61.55it/s]
201
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 22.10it/s]
202
+ Task 2 Score: 0.77
203
+
204
+ ----------------------------------------
205
+ Evaluating Task 3: Full Debate (5 Steps)
206
+
207
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 17.63it/s]
208
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 39.47it/s]
209
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 14.67it/s]
210
+
211
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 47.57it/s]
212
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 64.47it/s]
213
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 23.96it/s]
214
+
215
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 32.30it/s]
216
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 69.68it/s]
217
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 24.39it/s]
218
+
219
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 40.00it/s]
220
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 51.22it/s]
221
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 20.00it/s]
222
+
223
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 28.20it/s]
224
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 24.43it/s]
225
+ Batches: 100%|█████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 17.79it/s]
226
+ Task 3 Score: 0.50
227
+
228
+ ========================================
229
+ 🏆 BASELINE EVALUATION COMPLETE
230
+ Task 1 (Easy): 1.00 / 1.00
231
+ Task 2 (Medium): 0.77 / 1.00
232
+ Task 3 (Hard): 0.50 / 1.00
233
+ ========================================
234
+
235
+
236
+ ------------------------------------------------------------------------------------
237
+
238
+ ------------------------------------------------------------------------------------
239
+
240
+ 🚀 Starting Baseline Evaluation with llama-3.1-8b-instant
241
+ 📋 Topic: Universal Basic Income is necessary for the future economy.
242
+
243
+ ----------------------------------------
244
+ Evaluating Task 1: Single Claim (1 Step)
245
+
246
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 5.67it/s]
247
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 55.55it/s]
248
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 16.36it/s]
249
+ Task 1 Score: 1.00
250
+
251
+ ----------------------------------------
252
+ Evaluating Task 2: Claim and Rebuttal (3 Steps)
253
+
254
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 36.29it/s]
255
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 43.06it/s]
256
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 18.82it/s]
257
+
258
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 19.95it/s]
259
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 36.70it/s]
260
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 12.33it/s]
261
+
262
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 32.60it/s]
263
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 39.28it/s]
264
+ Batches: 100%|█████████████████████████████████████████████��████████████████████████████████| 1/1 [00:00<00:00, 16.25it/s]
265
+ Task 2 Score: 0.71
266
+
267
+ ----------------------------------------
268
+ Evaluating Task 3: Full Debate (5 Steps)
269
+
270
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 19.25it/s]
271
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 32.47it/s]
272
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 12.61it/s]
273
+
274
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 34.34it/s]
275
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 48.01it/s]
276
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 18.80it/s]
277
+
278
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 13.95it/s]
279
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 17.19it/s]
280
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 13.01it/s]
281
+
282
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 19.12it/s]
283
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 29.64it/s]
284
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 11.00it/s]
285
+
286
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 22.31it/s]
287
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 44.27it/s]
288
+ Batches: 100%|██████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 21.85it/s]
289
+ Task 3 Score: 0.50
290
+
291
+ ========================================
292
+ 🏆 BASELINE EVALUATION COMPLETE
293
+ Task 1 (Easy): 1.00 / 1.00
294
+ Task 2 (Medium): 0.71 / 1.00
295
+ Task 3 (Hard): 0.50 / 1.00
296
+ ========================================
297
+
298
+
299
+
300
+ ---------------------------------------------------------------------------------
301
+
302
+
303
+ ---------------------------------------------------------------------------------
304
+
305
+ Step 1: phase=CHALLENGE reward=0.23333333333333334
306
+
307
+ Step 2: phase=REBUTTAL reward=0.0
308
+
309
+ Step 3: phase=CONSOLIDATION reward=0.0
310
+
311
+ Step 4: phase=CLOSING reward=0.0
312
+
313
+ Step 5: phase=CLOSING reward=0.0
314
+
315
+ -----------------------------------------------------------------------------------
316
+
317
+
318
+ -----------------------------------------------------------------------------------
319
+
320
+ DBG current_phase=OPENING
321
+ DBG action.phase_tag=OPENING
322
+ DBG agent_history count=1
323
+ DBG opponent_history count=1
324
+ DBG word_count=14
325
+ Step 1: phase=CHALLENGE reward=0.23333333333333334
326
+
327
+ DBG current_phase=CHALLENGE
328
+ DBG action.phase_tag=CHALLENGE
329
+ DBG agent_history count=2
330
+ DBG opponent_history count=2
331
+ DBG word_count=14
332
+ Step 2: phase=REBUTTAL reward=0.0
333
+
334
+ DBG current_phase=REBUTTAL
335
+ DBG action.phase_tag=REBUTTAL
336
+ DBG agent_history count=3
337
+ DBG opponent_history count=3
338
+ DBG word_count=14
339
+ Step 3: phase=CONSOLIDATION reward=0.0
340
+
341
+ DBG current_phase=CONSOLIDATION
342
+ DBG action.phase_tag=CONSOLIDATION
343
+ DBG agent_history count=4
344
+ DBG opponent_history count=4
345
+ DBG word_count=14
346
+ Step 4: phase=CLOSING reward=0.0
347
+
348
+ DBG current_phase=CLOSING
349
+ DBG action.phase_tag=CLOSING
350
+ DBG agent_history count=5
351
+ DBG opponent_history count=5
352
+ DBG word_count=14
353
+ Step 5: phase=CLOSING reward=0.0
354
+
355
+
356
+
357
+ -----------------------------------------------------------------------------------
358
+
359
+
360
+ -----------------------------------------------------------------------------------
361
+
grpo_train.py ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ grpo_train.py
3
+ Simple GRPO-style training loop for the Debate Coach Environment.
4
+ Tracks reward improvement across episodes to demonstrate learning signal.
5
+ """
6
+
7
+ from dotenv import load_dotenv
8
+ load_dotenv()
9
+
10
+ import os
11
+ import time
12
+ import json
13
+ from collections import defaultdict
14
+ from openai import OpenAI
15
+
16
+ from environment import DebateEnvironment
17
+ from schema.schemas import DebateAction
18
+ from tasks import Task1_SingleClaim, Task2_ClaimAndRebuttal, Task3_FullDebate
19
+
20
+ # ── Config ────────────────────────────────────────────────────────────────────
21
+ API_BASE_URL = os.getenv("API_BASE_URL", "https://api.groq.com/openai/v1")
22
+ HF_TOKEN = os.getenv("HF_TOKEN") or os.getenv("GROQ_API_KEY")
23
+ MODEL_NAME = os.getenv("MODEL_NAME", "llama-3.1-8b-instant")
24
+
25
+ NUM_EPISODES = 30 # total training episodes
26
+ GROUP_SIZE = 5 # GRPO: episodes per group for baseline calculation
27
+ SLEEP_BETWEEN_CALLS = 1.0 # seconds — Groq rate limit protection
28
+
29
+ TOPICS = [
30
+ "Universal Basic Income is necessary for the future economy.",
31
+ "Artificial Intelligence should be strictly regulated by governments.",
32
+ "Social media does more harm than good to society.",
33
+ "Climate change requires immediate radical policy action.",
34
+ "Remote work is more productive than office work.",
35
+ ]
36
+
37
+ client = OpenAI(base_url=API_BASE_URL, api_key=HF_TOKEN)
38
+
39
+ def generate_argument(topic: str, phase: str, opponent_challenge: str,
40
+ temperature: float = 0.7) -> str:
41
+ """
42
+ The 'policy' — generates an argument given the current observation.
43
+ Temperature is increased over time to encourage exploration (GRPO-style).
44
+ """
45
+ prompt = f"You are a skilled debater. Topic: '{topic}'.\n"
46
+ prompt += f"Current debate phase: {phase}.\n"
47
+
48
+ if opponent_challenge:
49
+ prompt += f"Opponent argued: '{opponent_challenge}'\n"
50
+ prompt += ("Respond directly and logically. "
51
+ "Use words like 'therefore', 'because', 'however', "
52
+ "'this fails because', 'the evidence shows'.\n")
53
+ else:
54
+ prompt += ("Make a strong opening claim. "
55
+ "Use 'therefore' or 'because' to show reasoning.\n")
56
+
57
+ prompt += "Be concise: 30-60 words. No filler phrases."
58
+
59
+ try:
60
+ resp = client.chat.completions.create(
61
+ model=MODEL_NAME,
62
+ messages=[{"role": "user", "content": prompt}],
63
+ temperature=temperature,
64
+ max_tokens=200,
65
+ )
66
+ return resp.choices[0].message.content.strip()
67
+ except Exception as e:
68
+ print(f" [Agent API Error] {e}")
69
+ return "therefore this position is correct because the evidence supports it."
70
+
71
+
72
+ def run_episode(env: DebateEnvironment, topic: str,
73
+ grader, temperature: float = 0.7) -> dict:
74
+ """
75
+ Runs one full episode and returns a result dict with:
76
+ - per-step rewards from the environment
77
+ - final task score from the grader
78
+ """
79
+ obs = env.reset(topic)
80
+ step_rewards = []
81
+
82
+ while not obs.done:
83
+ argument = generate_argument(
84
+ topic=obs.topic,
85
+ phase=obs.phase.upper(),
86
+ opponent_challenge=obs.opponent_challenge,
87
+ temperature=temperature,
88
+ )
89
+ action = DebateAction(argument=argument, phase_tag=obs.phase.upper())
90
+ obs = env.step(action)
91
+ step_rewards.append(obs.reward)
92
+ time.sleep(SLEEP_BETWEEN_CALLS)
93
+
94
+ final_score = grader.grade(obs)
95
+
96
+ return {
97
+ "topic": topic,
98
+ "step_rewards": step_rewards,
99
+ "mean_reward": sum(step_rewards) / len(step_rewards) if step_rewards else 0.0,
100
+ "final_score": final_score,
101
+ }
102
+
103
+
104
+ # GRPO core
105
+ def compute_grpo_advantage(group_results: list[dict]) -> list[float]:
106
+ """
107
+ GRPO: advantage = (score - group_mean) / (group_std + epsilon)
108
+ This tells us which episodes in the group were above/below average.
109
+ A positive advantage = this episode's policy was better than the group baseline.
110
+ """
111
+ scores = [r["final_score"] for r in group_results]
112
+ mean = sum(scores) / len(scores)
113
+ variance = sum((s - mean) ** 2 for s in scores) / len(scores)
114
+ std = variance ** 0.5
115
+
116
+ advantages = [(s - mean) / (std + 1e-8) for s in scores]
117
+ return advantages
118
+
119
+
120
+ # Training loop
121
+ def train():
122
+ env = DebateEnvironment()
123
+ grader = Task3_FullDebate()
124
+
125
+ all_results = []
126
+ group_buffer = []
127
+ episode_scores = []
128
+
129
+ # Temperature annealing: start high (explore) → end lower (exploit)
130
+ def get_temperature(episode: int) -> float:
131
+ return max(0.4, 1.0 - (episode / NUM_EPISODES) * 0.6)
132
+
133
+ print("=" * 50)
134
+ print("GRPO Training — Debate Coach Environment")
135
+ print(f"Episodes: {NUM_EPISODES} | Group size: {GROUP_SIZE}")
136
+ print(f"Model: {MODEL_NAME}")
137
+ print("=" * 50)
138
+
139
+ for episode in range(NUM_EPISODES):
140
+ topic = TOPICS[episode % len(TOPICS)]
141
+ temp = get_temperature(episode)
142
+
143
+ print(f"\nEpisode {episode + 1}/{NUM_EPISODES} | "
144
+ f"Topic: {topic[:40]}... | Temp: {temp:.2f}")
145
+
146
+ result = run_episode(env, topic, grader, temperature=temp)
147
+ all_results.append(result)
148
+ group_buffer.append(result)
149
+ episode_scores.append(result["final_score"])
150
+
151
+ print(f" Step rewards: {[f'{r:.2f}' for r in result['step_rewards']]}")
152
+ print(f" Mean reward: {result['mean_reward']:.3f}")
153
+ print(f" Final score: {result['final_score']:.3f}")
154
+
155
+ # Every GROUP_SIZE episodes: compute GRPO advantage for the group
156
+ if len(group_buffer) == GROUP_SIZE:
157
+ advantages = compute_grpo_advantage(group_buffer)
158
+ group_scores = [r["final_score"] for r in group_buffer]
159
+
160
+ print(f"\n --- Group {episode // GROUP_SIZE + 1} Summary ---")
161
+ print(f" Scores: {[f'{s:.2f}' for s in group_scores]}")
162
+ print(f" Advantages: {[f'{a:.2f}' for a in advantages]}")
163
+ print(f" Best episode in group: "
164
+ f"Episode {episode - GROUP_SIZE + 1 + group_scores.index(max(group_scores)) + 1} "
165
+ f"(score={max(group_scores):.2f})")
166
+
167
+ # In real GRPO: you would use advantages to weight your policy gradient update.
168
+ # Here we log them so you can see the signal your environment produces.
169
+ for i, (res, adv) in enumerate(zip(group_buffer, advantages)):
170
+ res["advantage"] = adv
171
+
172
+ group_buffer = []
173
+
174
+ # Final report
175
+ print("\n" + "=" * 50)
176
+ print("TRAINING COMPLETE — Reward Curve")
177
+ print("=" * 50)
178
+
179
+ # Print reward curve in 5-episode windows
180
+ window = 5
181
+ for i in range(0, NUM_EPISODES, window):
182
+ chunk = episode_scores[i:i + window]
183
+ avg = sum(chunk) / len(chunk)
184
+ bar = "█" * int(avg * 20)
185
+ print(f" Episodes {i+1:2d}-{i+len(chunk):2d}: {avg:.3f} {bar}")
186
+
187
+ overall_avg = sum(episode_scores) / len(episode_scores)
188
+ first_half = sum(episode_scores[:NUM_EPISODES//2]) / (NUM_EPISODES // 2)
189
+ second_half = sum(episode_scores[NUM_EPISODES//2:]) / (NUM_EPISODES - NUM_EPISODES // 2)
190
+
191
+ print(f"\n Overall average: {overall_avg:.3f}")
192
+ print(f" First half average: {first_half:.3f}")
193
+ print(f" Second half average: {second_half:.3f}")
194
+ print(f" Trend: {'IMPROVING ↑' if second_half > first_half else 'DECLINING ↓'}")
195
+
196
+ # Save results to JSON for your README / submission
197
+ with open("training_results.json", "w") as f:
198
+ json.dump({
199
+ "model": MODEL_NAME,
200
+ "num_episodes": NUM_EPISODES,
201
+ "episode_scores": episode_scores,
202
+ "overall_avg": overall_avg,
203
+ "first_half_avg": first_half,
204
+ "second_half_avg": second_half,
205
+ "all_results": all_results,
206
+ }, f, indent=2)
207
+
208
+ print("\n Results saved to training_results.json")
209
+ print("=" * 50)
210
+
211
+
212
+ if __name__ == "__main__":
213
+ train()
inference.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dotenv import load_dotenv
2
+ load_dotenv()
3
+
4
+ """
5
+ Inference Script
6
+ ===================================
7
+ Compliant with Hackathon Mandatory Variables.
8
+ """
9
+
10
+ import os
11
+ import time
12
+ from openai import OpenAI
13
+
14
+
15
+ from environment import DebateEnvironment
16
+ from schema.schemas import DebateAction
17
+ from tasks import Task1_SingleClaim, Task2_ClaimAndRebuttal, Task3_FullDebate
18
+
19
+ API_BASE_URL = os.getenv("API_BASE_URL")
20
+ HF_TOKEN = os.getenv("HF_TOKEN")
21
+ MODEL_NAME = os.getenv("MODEL_NAME")
22
+
23
+ client = OpenAI(
24
+ base_url=API_BASE_URL,
25
+ api_key=HF_TOKEN
26
+ )
27
+
28
+
29
+ def generate_agent_argument(topic: str, phase: str, opponent_challenge: str) -> str:
30
+ """Uses the injected LLM to generate the agent's move based on the observation."""
31
+ prompt = f"You are a skilled debater. The topic is: '{topic}'.\n"
32
+ prompt += f"The current phase of the debate is: {phase}.\n"
33
+
34
+ if opponent_challenge:
35
+ prompt += f"Your opponent just argued: '{opponent_challenge}'\n"
36
+ prompt += "Write a direct, logical response to their challenge. Use reasoning keywords like 'therefore' or 'because'.\n"
37
+ else:
38
+ prompt += "Write a strong, logical opening statement for your side. Use reasoning keywords like 'therefore' or 'because'.\n"
39
+
40
+ prompt += "Keep your response under 50 words and do not include any conversational filler."
41
+
42
+ try:
43
+ response = client.chat.completions.create(
44
+ model=MODEL_NAME,
45
+ messages=[{"role": "user", "content": prompt}],
46
+ temperature=0.7,
47
+ max_tokens=150
48
+ )
49
+ return response.choices[0].message.content.strip()
50
+ except Exception as e:
51
+ print(f"Agent API Error: {e}")
52
+ return "therefore I agree."
53
+
54
+ def evaluate_baseline():
55
+ env = DebateEnvironment()
56
+ topic = "Universal Basic Income is necessary for the future economy."
57
+
58
+ print(f"Starting Baseline Evaluation with {MODEL_NAME}")
59
+ print(f"Topic: {topic}\n")
60
+ print("-" * 40)
61
+
62
+
63
+ print("Evaluating Task 1: Single Claim (1 Step)")
64
+ obs = env.reset(topic)
65
+
66
+ argument = generate_agent_argument(obs.topic, obs.phase, obs.opponent_challenge)
67
+ action = DebateAction(argument=argument, phase_tag=obs.phase.upper())
68
+ obs = env.step(action)
69
+
70
+ task1 = Task1_SingleClaim()
71
+ score1 = task1.grade(obs)
72
+ print(f"Task 1 Score: {score1:.2f}\n")
73
+ time.sleep(1)
74
+
75
+
76
+ print("-" * 40)
77
+ print("Evaluating Task 2: Claim and Rebuttal (3 Steps)")
78
+ obs = env.reset(topic)
79
+
80
+ for _ in range(3):
81
+ argument = generate_agent_argument(obs.topic, obs.phase, obs.opponent_challenge)
82
+ action = DebateAction(argument=argument, phase_tag=obs.phase.upper())
83
+ obs = env.step(action)
84
+ time.sleep(1)
85
+
86
+ task2 = Task2_ClaimAndRebuttal()
87
+ score2 = task2.grade(obs)
88
+ print(f"Task 2 Score: {score2:.2f}\n")
89
+
90
+
91
+ print("-" * 40)
92
+ print("Evaluating Task 3: Full Debate (5 Steps)")
93
+ obs = env.reset(topic)
94
+
95
+ while not obs.done:
96
+ argument = generate_agent_argument(obs.topic, obs.phase, obs.opponent_challenge)
97
+ action = DebateAction(argument=argument, phase_tag=obs.phase.upper())
98
+ obs = env.step(action)
99
+ time.sleep(1)
100
+
101
+ task3 = Task3_FullDebate()
102
+ score3 = task3.grade(obs)
103
+ print(f"Task 3 Score: {score3:.2f}\n")
104
+
105
+ print("=" * 40)
106
+ print("BASELINE EVALUATION COMPLETE")
107
+ print(f"Task 1 (Easy): {score1:.2f} / 1.00")
108
+ print(f"Task 2 (Medium): {score2:.2f} / 1.00")
109
+ print(f"Task 3 (Hard): {score3:.2f} / 1.00")
110
+ print("=" * 40)
111
+
112
+ if __name__ == "__main__":
113
+ evaluate_baseline()
openenv.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: debate-coach-env
2
+ version: "1.0.0"
3
+ description: "Argument quality evaluation environment for training LLM reasoning and persuasion"
4
+ author: your-hf-username
5
+ tasks:
6
+ - name: single_claim
7
+ difficulty: easy
8
+ max_steps: 1
9
+ - name: claim_and_rebuttal
10
+ difficulty: medium
11
+ max_steps: 3
12
+ - name: full_debate
13
+ difficulty: hard
14
+ max_steps: 5
15
+ action_space:
16
+ type: structured
17
+ fields:
18
+ - name: argument
19
+ type: string
20
+ - name: phase_tag
21
+ type: string
22
+ observation_space:
23
+ type: structured
24
+ fields:
25
+ - topic
26
+ - opponent_challenge
27
+ - done
28
+ - reward
29
+ - phase
prompter/system_prompt.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class SystemPrompt:
2
+ def __init__(self, topic: str):
3
+ self.topic = topic
4
+
5
+ def get_prompt(self, phase: str, argument: str = "", opponent_challenge: str = "") -> str:
6
+ """Dynamically generates a prompt based on the current debate phase."""
7
+
8
+ # We use a dictionary to store templates with placeholders
9
+ templates = {
10
+ "OPENING": f"""
11
+ Role: Debate Opponent.
12
+ Topic: {self.topic}.
13
+ Action: Acknowledge the opening claim: "{argument}" and set a skeptical stage.
14
+ """,
15
+
16
+ "CHALLENGE": f"""
17
+ Role: Adversary.
18
+ Topic: {self.topic}.
19
+ Action: Attack the core claim: "{argument}". Identify logical gaps in this specific reasoning.
20
+ """,
21
+
22
+ "REBUTTAL": f"""
23
+ Role: Critical Rebutter.
24
+ The agent argued: "{argument}".
25
+ Your previous challenge was: "{opponent_challenge}".
26
+ Action: Point out why the agent's response fails to address your challenge.
27
+ """,
28
+
29
+ "CONSOLIDATION": f"""
30
+ Role: Fact Checker.
31
+ Topic: {self.topic}.
32
+ Action: Question the evidence provided in: "{argument}". Focus on the weakest point.
33
+ """,
34
+
35
+ "CLOSING": f"""
36
+ Role: Final Judge.
37
+ Topic: {self.topic}.
38
+ Action: Provide a final summary of the conversation. Evaluate if the agent remained consistent.
39
+ """
40
+ }
41
+
42
+ # Return the specific phase prompt, or a default if not found
43
+ return templates.get(phase.upper(), "Continue the debate logically.")
pyproject.toml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "debate-coach-environment"
3
+ version = "0.1.0"
4
+ description = "Strategic Argument Red-Teaming Environment"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ dependencies = [
8
+ "openenv-core>=0.2.0",
9
+ "sentence-transformers",
10
+ "torch",
11
+ "openai",
12
+ "fastapi",
13
+ "uvicorn",
14
+ "pydantic"
15
+ ]
16
+
17
+ [project.scripts]
18
+ server = "server.app:main"
requirements.txt ADDED
@@ -0,0 +1,395 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file was autogenerated by uv via the following command:
2
+ # uv pip compile pyproject.toml -o requirements.txt
3
+ aiofile==3.9.0
4
+ # via py-key-value-aio
5
+ aiofiles==24.1.0
6
+ # via gradio
7
+ annotated-doc==0.0.4
8
+ # via
9
+ # fastapi
10
+ # typer
11
+ annotated-types==0.7.0
12
+ # via pydantic
13
+ anyio==4.12.1
14
+ # via
15
+ # gradio
16
+ # httpx
17
+ # mcp
18
+ # openai
19
+ # py-key-value-aio
20
+ # sse-starlette
21
+ # starlette
22
+ # watchfiles
23
+ attrs==26.1.0
24
+ # via
25
+ # cyclopts
26
+ # jsonschema
27
+ # referencing
28
+ authlib==1.6.9
29
+ # via fastmcp
30
+ backports-tarfile==1.2.0
31
+ # via jaraco-context
32
+ beartype==0.22.9
33
+ # via py-key-value-aio
34
+ brotli==1.2.0
35
+ # via gradio
36
+ cachetools==7.0.5
37
+ # via py-key-value-aio
38
+ caio==0.9.25
39
+ # via aiofile
40
+ certifi==2026.2.25
41
+ # via
42
+ # httpcore
43
+ # httpx
44
+ # requests
45
+ cffi==2.0.0
46
+ # via cryptography
47
+ charset-normalizer==3.4.6
48
+ # via requests
49
+ click==8.3.1
50
+ # via
51
+ # typer
52
+ # uvicorn
53
+ colorama==0.4.6
54
+ # via
55
+ # click
56
+ # tqdm
57
+ cryptography==46.0.6
58
+ # via
59
+ # authlib
60
+ # pyjwt
61
+ cyclopts==4.10.1
62
+ # via fastmcp
63
+ distro==1.9.0
64
+ # via openai
65
+ dnspython==2.8.0
66
+ # via email-validator
67
+ docstring-parser==0.17.0
68
+ # via cyclopts
69
+ docutils==0.22.4
70
+ # via rich-rst
71
+ email-validator==2.3.0
72
+ # via pydantic
73
+ exceptiongroup==1.3.1
74
+ # via fastmcp
75
+ fastapi==0.135.2
76
+ # via
77
+ # debate-coach-environment (pyproject.toml)
78
+ # gradio
79
+ # openenv-core
80
+ fastmcp==3.1.1
81
+ # via openenv-core
82
+ ffmpy==1.0.0
83
+ # via gradio
84
+ filelock==3.25.2
85
+ # via
86
+ # huggingface-hub
87
+ # torch
88
+ fsspec==2026.2.0
89
+ # via
90
+ # gradio-client
91
+ # huggingface-hub
92
+ # torch
93
+ gradio==6.10.0
94
+ # via openenv-core
95
+ gradio-client==2.4.0
96
+ # via
97
+ # gradio
98
+ # hf-gradio
99
+ groovy==0.1.2
100
+ # via gradio
101
+ h11==0.16.0
102
+ # via
103
+ # httpcore
104
+ # uvicorn
105
+ hf-gradio==0.3.0
106
+ # via gradio
107
+ hf-xet==1.4.2
108
+ # via huggingface-hub
109
+ httpcore==1.0.9
110
+ # via httpx
111
+ httpx==0.28.1
112
+ # via
113
+ # fastmcp
114
+ # gradio
115
+ # gradio-client
116
+ # huggingface-hub
117
+ # mcp
118
+ # openai
119
+ # openenv-core
120
+ # safehttpx
121
+ httpx-sse==0.4.3
122
+ # via mcp
123
+ huggingface-hub==1.7.2
124
+ # via
125
+ # gradio
126
+ # gradio-client
127
+ # openenv-core
128
+ # sentence-transformers
129
+ # tokenizers
130
+ # transformers
131
+ idna==3.11
132
+ # via
133
+ # anyio
134
+ # email-validator
135
+ # httpx
136
+ # requests
137
+ importlib-metadata==8.7.1
138
+ # via
139
+ # keyring
140
+ # opentelemetry-api
141
+ jaraco-classes==3.4.0
142
+ # via keyring
143
+ jaraco-context==6.1.2
144
+ # via keyring
145
+ jaraco-functools==4.4.0
146
+ # via keyring
147
+ jinja2==3.1.6
148
+ # via
149
+ # gradio
150
+ # torch
151
+ jiter==0.13.0
152
+ # via openai
153
+ joblib==1.5.3
154
+ # via scikit-learn
155
+ jsonref==1.1.0
156
+ # via fastmcp
157
+ jsonschema==4.26.0
158
+ # via mcp
159
+ jsonschema-path==0.4.5
160
+ # via fastmcp
161
+ jsonschema-specifications==2025.9.1
162
+ # via jsonschema
163
+ keyring==25.7.0
164
+ # via py-key-value-aio
165
+ markdown-it-py==4.0.0
166
+ # via rich
167
+ markupsafe==3.0.3
168
+ # via
169
+ # gradio
170
+ # jinja2
171
+ mcp==1.26.0
172
+ # via fastmcp
173
+ mdurl==0.1.2
174
+ # via markdown-it-py
175
+ more-itertools==10.8.0
176
+ # via
177
+ # jaraco-classes
178
+ # jaraco-functools
179
+ mpmath==1.3.0
180
+ # via sympy
181
+ networkx==3.6.1
182
+ # via torch
183
+ numpy==2.4.3
184
+ # via
185
+ # gradio
186
+ # pandas
187
+ # scikit-learn
188
+ # scipy
189
+ # sentence-transformers
190
+ # transformers
191
+ openai==2.29.0
192
+ # via
193
+ # debate-coach-environment (pyproject.toml)
194
+ # openenv-core
195
+ openapi-pydantic==0.5.1
196
+ # via fastmcp
197
+ openenv-core==0.2.3
198
+ # via debate-coach-environment (pyproject.toml)
199
+ opentelemetry-api==1.40.0
200
+ # via fastmcp
201
+ orjson==3.11.7
202
+ # via gradio
203
+ packaging==26.0
204
+ # via
205
+ # fastmcp
206
+ # gradio
207
+ # gradio-client
208
+ # huggingface-hub
209
+ # transformers
210
+ pandas==3.0.1
211
+ # via gradio
212
+ pathable==0.5.0
213
+ # via jsonschema-path
214
+ pillow==12.1.1
215
+ # via gradio
216
+ platformdirs==4.9.4
217
+ # via fastmcp
218
+ py-key-value-aio==0.4.4
219
+ # via fastmcp
220
+ pycparser==3.0
221
+ # via cffi
222
+ pydantic==2.12.5
223
+ # via
224
+ # debate-coach-environment (pyproject.toml)
225
+ # fastapi
226
+ # fastmcp
227
+ # gradio
228
+ # mcp
229
+ # openai
230
+ # openapi-pydantic
231
+ # openenv-core
232
+ # pydantic-settings
233
+ pydantic-core==2.41.5
234
+ # via pydantic
235
+ pydantic-settings==2.13.1
236
+ # via mcp
237
+ pydub==0.25.1
238
+ # via gradio
239
+ pygments==2.19.2
240
+ # via rich
241
+ pyjwt==2.12.1
242
+ # via mcp
243
+ pyperclip==1.11.0
244
+ # via fastmcp
245
+ python-dateutil==2.9.0.post0
246
+ # via pandas
247
+ python-dotenv==1.2.2
248
+ # via
249
+ # fastmcp
250
+ # pydantic-settings
251
+ python-multipart==0.0.22
252
+ # via
253
+ # gradio
254
+ # mcp
255
+ pytz==2026.1.post1
256
+ # via gradio
257
+ pywin32==311
258
+ # via mcp
259
+ pywin32-ctypes==0.2.3
260
+ # via keyring
261
+ pyyaml==6.0.3
262
+ # via
263
+ # fastmcp
264
+ # gradio
265
+ # huggingface-hub
266
+ # jsonschema-path
267
+ # openenv-core
268
+ # transformers
269
+ referencing==0.37.0
270
+ # via
271
+ # jsonschema
272
+ # jsonschema-path
273
+ # jsonschema-specifications
274
+ regex==2026.2.28
275
+ # via transformers
276
+ requests==2.33.0
277
+ # via openenv-core
278
+ rich==14.3.3
279
+ # via
280
+ # cyclopts
281
+ # fastmcp
282
+ # openenv-core
283
+ # rich-rst
284
+ # typer
285
+ rich-rst==1.3.2
286
+ # via cyclopts
287
+ rpds-py==0.30.0
288
+ # via
289
+ # jsonschema
290
+ # referencing
291
+ safehttpx==0.1.7
292
+ # via gradio
293
+ safetensors==0.7.0
294
+ # via transformers
295
+ scikit-learn==1.8.0
296
+ # via sentence-transformers
297
+ scipy==1.17.1
298
+ # via
299
+ # scikit-learn
300
+ # sentence-transformers
301
+ semantic-version==2.10.0
302
+ # via gradio
303
+ sentence-transformers==5.3.0
304
+ # via debate-coach-environment (pyproject.toml)
305
+ shellingham==1.5.4
306
+ # via typer
307
+ six==1.17.0
308
+ # via python-dateutil
309
+ sniffio==1.3.1
310
+ # via openai
311
+ sse-starlette==3.3.4
312
+ # via mcp
313
+ starlette==0.52.1
314
+ # via
315
+ # fastapi
316
+ # gradio
317
+ # mcp
318
+ # sse-starlette
319
+ sympy==1.14.0
320
+ # via torch
321
+ threadpoolctl==3.6.0
322
+ # via scikit-learn
323
+ tokenizers==0.22.2
324
+ # via transformers
325
+ tomli==2.4.1
326
+ # via openenv-core
327
+ tomli-w==1.2.0
328
+ # via openenv-core
329
+ tomlkit==0.13.3
330
+ # via gradio
331
+ torch==2.10.0
332
+ # via
333
+ # debate-coach-environment (pyproject.toml)
334
+ # sentence-transformers
335
+ tqdm==4.67.3
336
+ # via
337
+ # huggingface-hub
338
+ # openai
339
+ # sentence-transformers
340
+ # transformers
341
+ transformers==5.3.0
342
+ # via sentence-transformers
343
+ typer==0.24.1
344
+ # via
345
+ # gradio
346
+ # hf-gradio
347
+ # huggingface-hub
348
+ # openenv-core
349
+ # transformers
350
+ typing-extensions==4.15.0
351
+ # via
352
+ # anyio
353
+ # exceptiongroup
354
+ # fastapi
355
+ # gradio
356
+ # gradio-client
357
+ # huggingface-hub
358
+ # mcp
359
+ # openai
360
+ # opentelemetry-api
361
+ # py-key-value-aio
362
+ # pydantic
363
+ # pydantic-core
364
+ # referencing
365
+ # sentence-transformers
366
+ # starlette
367
+ # torch
368
+ # typing-inspection
369
+ typing-inspection==0.4.2
370
+ # via
371
+ # fastapi
372
+ # mcp
373
+ # pydantic
374
+ # pydantic-settings
375
+ tzdata==2025.3
376
+ # via pandas
377
+ uncalled-for==0.2.0
378
+ # via fastmcp
379
+ urllib3==2.6.3
380
+ # via requests
381
+ uvicorn==0.42.0
382
+ # via
383
+ # debate-coach-environment (pyproject.toml)
384
+ # fastmcp
385
+ # gradio
386
+ # mcp
387
+ # openenv-core
388
+ watchfiles==1.1.1
389
+ # via fastmcp
390
+ websockets==16.0
391
+ # via
392
+ # fastmcp
393
+ # openenv-core
394
+ zipp==3.23.0
395
+ # via importlib-metadata
reward_metrics/reward_metrics.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #similarity runs a full encode on every call — if called multiple times per step it's slow. This matters for training loops. Consider caching or batching, but not urgent right now.
2
+
3
+ from sentence_transformers import SentenceTransformer, util
4
+ from transformers import logging as transformers_logging
5
+ import logging
6
+ import torch
7
+
8
+ transformers_logging.set_verbosity_error()
9
+
10
+ model = SentenceTransformer('all-MiniLM-L6-v2')
11
+
12
+
13
+ class RewardMetrics:
14
+ def __init__(self):
15
+ self.LINKING_PHRASES = [
16
+ "taken together", "overall", "this shows",
17
+ "together", "therefore", "thus", "in conclusion"
18
+ ]
19
+ self.NEGATION_PATTERNS = [
20
+ "fails", "incorrect", "wrong", "does not",
21
+ "untrue", "contradiction", "weak"
22
+ ]
23
+ self.IMPACT_PHRASES = [
24
+ "we win", "therefore", "in conclusion",
25
+ "the key takeaway", "this proves"
26
+ ]
27
+ self._embedding_cache = {}
28
+
29
+ def _get_embedding(self,text:str):
30
+ """Fetches embedding from cache, or computes it if not found."""
31
+ if text not in self._embedding_cache:
32
+ self._embedding_cache[text] = model.encode(text,convert_to_tensor=True)
33
+
34
+ return self._embedding_cache[text]
35
+
36
+ def similarity(self,a,b):
37
+ emb1 = self._get_embedding(a)
38
+ emb2 = self._get_embedding(b)
39
+ return util.cos_sim(emb1, emb2).item()
40
+
41
+
42
+ def argument_coverage(self,agent_args, closing, threshold=0.5):
43
+
44
+ if not agent_args:
45
+ return 0.0
46
+
47
+ closing_emb = self._get_embedding(closing)
48
+ history_embs = [self._get_embedding(arg) for arg in agent_args]
49
+
50
+
51
+
52
+ # Stacking them into a single 2D PyTorch tensor
53
+ history_matrix = torch.stack(history_embs)
54
+
55
+ similarities = util.cos_sim(closing_emb, history_matrix)
56
+
57
+ # similarities is a tensor of shape [1, num_args]
58
+ covered = (similarities > threshold).sum().item()
59
+
60
+ return covered / len(agent_args)
61
+
62
+
63
+ def synthesis_score(self,text):
64
+ count = sum(1 for phrase in self.LINKING_PHRASES if phrase in text.lower())
65
+ return min(count / len(self.LINKING_PHRASES), 1.0)
66
+
67
+ def opponent_coverage(self,opponent_args, closing, threshold=0.5):
68
+
69
+ if not opponent_args:
70
+ return 0.0
71
+
72
+ closing_emb = self._get_embedding(closing)
73
+ history_embs = [self._get_embedding(arg) for arg in opponent_args]
74
+
75
+
76
+
77
+ # Stacking them into a single 2D PyTorch tensor
78
+ history_matrix = torch.stack(history_embs)
79
+
80
+ similarities = util.cos_sim(closing_emb, history_matrix)
81
+
82
+ # similarities is a tensor of shape [1, num_args]
83
+ covered = (similarities > threshold).sum().item()
84
+
85
+ return covered / len(opponent_args)
86
+
87
+
88
+ def refutation_strength(self,text):
89
+ count = sum(1 for word in self.NEGATION_PATTERNS if word in text.lower())
90
+ return min(count / len(self.NEGATION_PATTERNS), 1.0)
91
+
92
+ def cal_refu_score(self,opponent_args,text:str):
93
+ coverage = self.opponent_coverage(opponent_args,text)
94
+ strength = self.refutation_strength(text)
95
+
96
+ refutation_score = (coverage * 0.7) + (strength* 0.3)
97
+
98
+ return refutation_score
99
+
100
+
101
+ def impact_score(self,text):
102
+ count = 0
103
+ for phrase in self.IMPACT_PHRASES:
104
+ if phrase in text.lower():
105
+ count += 1
106
+
107
+ # bonus if appears near end
108
+ if any(phrase in text.lower()[-100:] for phrase in self.IMPACT_PHRASES):
109
+ count += 1
110
+
111
+ return min(count / (len(self.IMPACT_PHRASES) + 1), 1.0)
server/app.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import uvicorn
2
+ from openenv.core.env_server import create_fastapi_app
3
+ from environment import DebateEnvironment
4
+
5
+ app = create_fastapi_app(DebateEnvironment)
6
+
7
+ def main():
8
+ uvicorn.run("server.app:app", host="0.0.0.0", port=8000)
9
+
10
+ if __name__ == "__main__":
11
+ main()
tasks.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from schema.schemas import DebateObservation
2
+ from reward_metrics.reward_metrics import RewardMetrics
3
+
4
+
5
+ metrics = RewardMetrics()
6
+
7
+ class Task1_SingleClaim:
8
+ """
9
+ EASY: Can the agent formulate a strong, logical opening statement?
10
+ Evaluated after step 1.
11
+ """
12
+ name = "single_claim"
13
+ difficulty = "easy"
14
+
15
+ def grade(self, observation: DebateObservation) -> float:
16
+ score = 0.0
17
+
18
+ action_text = observation.metadata.get("action", "")
19
+
20
+ # Check 1: Length threshold (0.4 points)
21
+ if len(action_text.split()) >= 10:
22
+ score += 0.4
23
+
24
+ # Check 2: Reasoning Keyword Presence (0.4 points)
25
+ reasoning_keywords = ["because", "therefore", "however", "consequently", "shows", "proves"]
26
+ if any(kw in action_text.lower() for kw in reasoning_keywords):
27
+ score += 0.4
28
+
29
+ # Check 3: Phase validation (0.2 points)
30
+ if observation.metadata.get("phase") == "OPENING":
31
+ score += 0.2
32
+
33
+ return min(1.0, score)
34
+
35
+
36
+ class Task2_ClaimAndRebuttal:
37
+ """
38
+ MEDIUM: Can the agent survive a back-and-forth and deliver a strong rebuttal?
39
+ Evaluated after step 3 (Opening -> Challenge -> Rebuttal).
40
+ """
41
+ name = "claim_and_rebuttal"
42
+ difficulty = "medium"
43
+
44
+ def grade(self, observation: DebateObservation) -> float:
45
+ # Fail immediately if they didn't reach the required depth (crashed or quit early)
46
+ if observation.attempt_count < 3:
47
+ return 0.0
48
+
49
+ score = 0.0
50
+ action_text = observation.metadata.get("action", "")
51
+
52
+ # 1. Base points for reaching the correct phase successfully
53
+ score += 0.3
54
+
55
+ # 2. Direct Evaluation: Is it actually a rebuttal?
56
+
57
+ refutation_strength = metrics.refutation_strength(action_text)
58
+ score += (refutation_strength * 0.4)
59
+
60
+ if len(action_text.split()) > 15:
61
+ score += 0.3
62
+
63
+ return min(1.0, score)
64
+
65
+
66
+ class Task3_FullDebate:
67
+ """
68
+ HARD: Can the agent complete a 5-turn debate and successfully synthesize
69
+ the arguments into a concluding statement?
70
+ Evaluated at the end of the episode (step 5).
71
+ """
72
+ name = "full_debate"
73
+ difficulty = "hard"
74
+
75
+ def grade(self, observation: DebateObservation) -> float:
76
+ if not observation.done or observation.attempt_count < 5:
77
+ return 0.0
78
+
79
+ score = 0.0
80
+ action_text = observation.metadata.get("action", "")
81
+
82
+ # 1. Base points for surviving all 5 turns
83
+ score += 0.2
84
+
85
+ # 2. Direct Evaluation: Synthesis
86
+
87
+ synthesis = metrics.synthesis_score(action_text)
88
+ score += (synthesis * 0.4)
89
+
90
+ # 3. Direct Evaluation: Impact
91
+
92
+ impact = metrics.impact_score(action_text)
93
+ score += (min(impact, 2) * 0.1)
94
+
95
+ # 4. Length check: A closing summary must be substantial
96
+ if len(action_text.split()) >= 20:
97
+ score += 0.3
98
+
99
+ return max(0.0, min(1.0, score))
training_results.json ADDED
@@ -0,0 +1,431 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "llama-3.1-8b-instant",
3
+ "num_episodes": 30,
4
+ "episode_scores": [
5
+ 0.5,
6
+ 0.5,
7
+ 0.5,
8
+ 0.5,
9
+ 0.5571428571428572,
10
+ 0.5,
11
+ 0.5,
12
+ 0.5,
13
+ 0.5,
14
+ 0.5,
15
+ 0.5,
16
+ 0.5,
17
+ 0.5,
18
+ 0.5,
19
+ 0.5,
20
+ 0.5,
21
+ 0.5,
22
+ 0.5,
23
+ 0.5,
24
+ 0.5,
25
+ 0.5,
26
+ 0.5,
27
+ 0.5,
28
+ 0.5,
29
+ 0.5,
30
+ 0.5,
31
+ 0.5,
32
+ 0.5,
33
+ 0.5,
34
+ 0.5
35
+ ],
36
+ "overall_avg": 0.5019047619047619,
37
+ "first_half_avg": 0.5038095238095238,
38
+ "second_half_avg": 0.5,
39
+ "all_results": [
40
+ {
41
+ "topic": "Universal Basic Income is necessary for the future economy.",
42
+ "step_rewards": [
43
+ 0.21666666666666667,
44
+ 0.0,
45
+ 0.0,
46
+ 0.0,
47
+ 0.0
48
+ ],
49
+ "mean_reward": 0.043333333333333335,
50
+ "final_score": 0.5,
51
+ "advantage": -0.4999997812500918
52
+ },
53
+ {
54
+ "topic": "Artificial Intelligence should be strictly regulated by governments.",
55
+ "step_rewards": [
56
+ 0.21666666666666667,
57
+ 0.0,
58
+ 0.0,
59
+ 0.0,
60
+ 0.0
61
+ ],
62
+ "mean_reward": 0.043333333333333335,
63
+ "final_score": 0.5,
64
+ "advantage": -0.4999997812500918
65
+ },
66
+ {
67
+ "topic": "Social media does more harm than good to society.",
68
+ "step_rewards": [
69
+ 0.23333333333333334,
70
+ 0.0,
71
+ 0.0,
72
+ 0.0,
73
+ 0.0
74
+ ],
75
+ "mean_reward": 0.04666666666666667,
76
+ "final_score": 0.5,
77
+ "advantage": -0.4999997812500918
78
+ },
79
+ {
80
+ "topic": "Climate change requires immediate radical policy action.",
81
+ "step_rewards": [
82
+ 0.21666666666666667,
83
+ 0.0,
84
+ 0.0,
85
+ 0.0,
86
+ 0.0
87
+ ],
88
+ "mean_reward": 0.043333333333333335,
89
+ "final_score": 0.5,
90
+ "advantage": -0.4999997812500918
91
+ },
92
+ {
93
+ "topic": "Remote work is more productive than office work.",
94
+ "step_rewards": [
95
+ 0.2,
96
+ 0.0,
97
+ 0.0,
98
+ 0.0,
99
+ 0.0
100
+ ],
101
+ "mean_reward": 0.04,
102
+ "final_score": 0.5571428571428572,
103
+ "advantage": 1.9999991250003866
104
+ },
105
+ {
106
+ "topic": "Universal Basic Income is necessary for the future economy.",
107
+ "step_rewards": [
108
+ 0.21666666666666667,
109
+ 0.0,
110
+ 0.0,
111
+ 0.0,
112
+ 0.0
113
+ ],
114
+ "mean_reward": 0.043333333333333335,
115
+ "final_score": 0.5,
116
+ "advantage": 0.0
117
+ },
118
+ {
119
+ "topic": "Artificial Intelligence should be strictly regulated by governments.",
120
+ "step_rewards": [
121
+ 0.21666666666666667,
122
+ 0.0,
123
+ 0.0,
124
+ 0.0,
125
+ 0.0
126
+ ],
127
+ "mean_reward": 0.043333333333333335,
128
+ "final_score": 0.5,
129
+ "advantage": 0.0
130
+ },
131
+ {
132
+ "topic": "Social media does more harm than good to society.",
133
+ "step_rewards": [
134
+ 0.2,
135
+ 0.0,
136
+ 0.0,
137
+ 0.0,
138
+ 0.0
139
+ ],
140
+ "mean_reward": 0.04,
141
+ "final_score": 0.5,
142
+ "advantage": 0.0
143
+ },
144
+ {
145
+ "topic": "Climate change requires immediate radical policy action.",
146
+ "step_rewards": [
147
+ 0.23333333333333334,
148
+ 0.0,
149
+ 0.0,
150
+ 0.0,
151
+ 0.0
152
+ ],
153
+ "mean_reward": 0.04666666666666667,
154
+ "final_score": 0.5,
155
+ "advantage": 0.0
156
+ },
157
+ {
158
+ "topic": "Remote work is more productive than office work.",
159
+ "step_rewards": [
160
+ 0.2,
161
+ 0.0,
162
+ 0.0,
163
+ 0.0,
164
+ 0.0
165
+ ],
166
+ "mean_reward": 0.04,
167
+ "final_score": 0.5,
168
+ "advantage": 0.0
169
+ },
170
+ {
171
+ "topic": "Universal Basic Income is necessary for the future economy.",
172
+ "step_rewards": [
173
+ 0.21666666666666667,
174
+ 0.0,
175
+ 0.0,
176
+ 0.0,
177
+ 0.0
178
+ ],
179
+ "mean_reward": 0.043333333333333335,
180
+ "final_score": 0.5,
181
+ "advantage": 0.0
182
+ },
183
+ {
184
+ "topic": "Artificial Intelligence should be strictly regulated by governments.",
185
+ "step_rewards": [
186
+ 0.21666666666666667,
187
+ 0.0,
188
+ 0.0,
189
+ 0.0,
190
+ 0.0
191
+ ],
192
+ "mean_reward": 0.043333333333333335,
193
+ "final_score": 0.5,
194
+ "advantage": 0.0
195
+ },
196
+ {
197
+ "topic": "Social media does more harm than good to society.",
198
+ "step_rewards": [
199
+ 0.23333333333333334,
200
+ 0.0,
201
+ 0.0,
202
+ 0.0,
203
+ 0.0
204
+ ],
205
+ "mean_reward": 0.04666666666666667,
206
+ "final_score": 0.5,
207
+ "advantage": 0.0
208
+ },
209
+ {
210
+ "topic": "Climate change requires immediate radical policy action.",
211
+ "step_rewards": [
212
+ 0.21666666666666667,
213
+ 0.0,
214
+ 0.0,
215
+ 0.0,
216
+ 0.0
217
+ ],
218
+ "mean_reward": 0.043333333333333335,
219
+ "final_score": 0.5,
220
+ "advantage": 0.0
221
+ },
222
+ {
223
+ "topic": "Remote work is more productive than office work.",
224
+ "step_rewards": [
225
+ 0.21666666666666667,
226
+ 0.0,
227
+ 0.0,
228
+ 0.0,
229
+ 0.0
230
+ ],
231
+ "mean_reward": 0.043333333333333335,
232
+ "final_score": 0.5,
233
+ "advantage": 0.0
234
+ },
235
+ {
236
+ "topic": "Universal Basic Income is necessary for the future economy.",
237
+ "step_rewards": [
238
+ 0.21666666666666667,
239
+ 0.0,
240
+ 0.0,
241
+ 0.0,
242
+ 0.0
243
+ ],
244
+ "mean_reward": 0.043333333333333335,
245
+ "final_score": 0.5,
246
+ "advantage": 0.0
247
+ },
248
+ {
249
+ "topic": "Artificial Intelligence should be strictly regulated by governments.",
250
+ "step_rewards": [
251
+ 0.21666666666666667,
252
+ 0.0,
253
+ 0.0,
254
+ 0.0,
255
+ 0.0
256
+ ],
257
+ "mean_reward": 0.043333333333333335,
258
+ "final_score": 0.5,
259
+ "advantage": 0.0
260
+ },
261
+ {
262
+ "topic": "Social media does more harm than good to society.",
263
+ "step_rewards": [
264
+ 0.23333333333333334,
265
+ 0.0,
266
+ 0.0,
267
+ 0.0,
268
+ 0.0
269
+ ],
270
+ "mean_reward": 0.04666666666666667,
271
+ "final_score": 0.5,
272
+ "advantage": 0.0
273
+ },
274
+ {
275
+ "topic": "Climate change requires immediate radical policy action.",
276
+ "step_rewards": [
277
+ 0.21666666666666667,
278
+ 0.0,
279
+ 0.0,
280
+ 0.0,
281
+ 0.0
282
+ ],
283
+ "mean_reward": 0.043333333333333335,
284
+ "final_score": 0.5,
285
+ "advantage": 0.0
286
+ },
287
+ {
288
+ "topic": "Remote work is more productive than office work.",
289
+ "step_rewards": [
290
+ 0.2,
291
+ 0.0,
292
+ 0.0,
293
+ 0.0,
294
+ 0.0
295
+ ],
296
+ "mean_reward": 0.04,
297
+ "final_score": 0.5,
298
+ "advantage": 0.0
299
+ },
300
+ {
301
+ "topic": "Universal Basic Income is necessary for the future economy.",
302
+ "step_rewards": [
303
+ 0.21666666666666667,
304
+ 0.0,
305
+ 0.0,
306
+ 0.0,
307
+ 0.0
308
+ ],
309
+ "mean_reward": 0.043333333333333335,
310
+ "final_score": 0.5,
311
+ "advantage": 0.0
312
+ },
313
+ {
314
+ "topic": "Artificial Intelligence should be strictly regulated by governments.",
315
+ "step_rewards": [
316
+ 0.21666666666666667,
317
+ 0.0,
318
+ 0.0,
319
+ 0.0,
320
+ 0.0
321
+ ],
322
+ "mean_reward": 0.043333333333333335,
323
+ "final_score": 0.5,
324
+ "advantage": 0.0
325
+ },
326
+ {
327
+ "topic": "Social media does more harm than good to society.",
328
+ "step_rewards": [
329
+ 0.21666666666666667,
330
+ 0.0,
331
+ 0.0,
332
+ 0.0,
333
+ 0.0
334
+ ],
335
+ "mean_reward": 0.043333333333333335,
336
+ "final_score": 0.5,
337
+ "advantage": 0.0
338
+ },
339
+ {
340
+ "topic": "Climate change requires immediate radical policy action.",
341
+ "step_rewards": [
342
+ 0.21666666666666667,
343
+ 0.0,
344
+ 0.0,
345
+ 0.0,
346
+ 0.0
347
+ ],
348
+ "mean_reward": 0.043333333333333335,
349
+ "final_score": 0.5,
350
+ "advantage": 0.0
351
+ },
352
+ {
353
+ "topic": "Remote work is more productive than office work.",
354
+ "step_rewards": [
355
+ 0.2,
356
+ 0.0,
357
+ 0.0,
358
+ 0.0,
359
+ 0.0
360
+ ],
361
+ "mean_reward": 0.04,
362
+ "final_score": 0.5,
363
+ "advantage": 0.0
364
+ },
365
+ {
366
+ "topic": "Universal Basic Income is necessary for the future economy.",
367
+ "step_rewards": [
368
+ 0.2,
369
+ 0.0,
370
+ 0.0,
371
+ 0.0,
372
+ 0.0
373
+ ],
374
+ "mean_reward": 0.04,
375
+ "final_score": 0.5,
376
+ "advantage": 0.0
377
+ },
378
+ {
379
+ "topic": "Artificial Intelligence should be strictly regulated by governments.",
380
+ "step_rewards": [
381
+ 0.21666666666666667,
382
+ 0.0,
383
+ 0.0,
384
+ 0.0,
385
+ 0.0
386
+ ],
387
+ "mean_reward": 0.043333333333333335,
388
+ "final_score": 0.5,
389
+ "advantage": 0.0
390
+ },
391
+ {
392
+ "topic": "Social media does more harm than good to society.",
393
+ "step_rewards": [
394
+ 0.21666666666666667,
395
+ 0.0,
396
+ 0.0,
397
+ 0.0,
398
+ 0.0
399
+ ],
400
+ "mean_reward": 0.043333333333333335,
401
+ "final_score": 0.5,
402
+ "advantage": 0.0
403
+ },
404
+ {
405
+ "topic": "Climate change requires immediate radical policy action.",
406
+ "step_rewards": [
407
+ 0.21666666666666667,
408
+ 0.0,
409
+ 0.0,
410
+ 0.0,
411
+ 0.0
412
+ ],
413
+ "mean_reward": 0.043333333333333335,
414
+ "final_score": 0.5,
415
+ "advantage": 0.0
416
+ },
417
+ {
418
+ "topic": "Remote work is more productive than office work.",
419
+ "step_rewards": [
420
+ 0.23333333333333334,
421
+ 0.0,
422
+ 0.0,
423
+ 0.0,
424
+ 0.0
425
+ ],
426
+ "mean_reward": 0.04666666666666667,
427
+ "final_score": 0.5,
428
+ "advantage": 0.0
429
+ }
430
+ ]
431
+ }
uv.lock ADDED
The diff for this file is too large to render. See raw diff
 
worflow.md ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ ## WORKFLOW:
3
+ The Workflow: How the Debate Happens
4
+ Here is the step-by-step lifecycle of an episode in your project:
5
+
6
+ 1. Reset: The Environment picks a topic (e.g., "AI should be regulated"). It sends the first Observation to the Agent.
7
+
8
+ 2. The Agent's Turn (Action): The Agent (your LLM) receives the observation and generates a DebateAction. It sends its argument and a phase_tag (like "claim").
9
+
10
+ 3. The Environment's Turn (Step):
11
+
12
+ - The Opponent: The environment has its own logic (or a fixed LLM) that generates an opponent_challenge.
13
+
14
+ - The Grader: The environment evaluates the Agent's argument. Did it use a fallacy? Was it logical? It calculates a Reward.
15
+
16
+ 4. Loop: This repeats until the phase reaches "closing" or the step_count hits a limit.
17
+
18
+
19
+
20
+ ## STEP()
21
+ after every step() what we need:
22
+
23
+ What is a "Step"? One step should be one exchange:
24
+
25
+ ```bash
26
+ Agent makes a claim -> Environment (Opponent) rebuts -> Grader evaluates.
27
+ ```
28
+
29
+ Q) what does the agent need to see after each step?
30
+ ans:
31
+
32
+ A covnersation or debate ends if:
33
+
34
+ - Agent says he dont know
35
+ - Agent repeat same conversation again and again like if convo_repeat > 2 ends loop
36
+
37
+
38
+ Problem and Solving it:
39
+
40
+ - In RL, the environment is the authority.
41
+
42
+ ```bash
43
+
44
+ RL loop:
45
+ environment owns: state, phase transitions, reward calculation
46
+ agent owns: only the action content (the argument text)
47
+
48
+ step 1: agent submits claim text → env moves to challenge phase, env generates opponent challenge
49
+ step 2: agent submits rebuttal text → env moves to closing phase
50
+ step 3: agent submits conclusion → env calculates final reward, done=True
51
+
52
+ ```
53
+
54
+
55
+ | Step | Environment Phase | Agent's Goal | Opponent's Behavior |
56
+ |------|------------------|--------------------------------------------|----------------------------------|
57
+ | 0 | OPENING | Make a core claim; sets the stage | Acknowledges |
58
+ | 1 | CHALLENGE | Defend against a specific counter | Attacks the core claim |
59
+ | 2 | REBUTTAL | Attack the opponent's logic | Offers a counter-theory |
60
+ | 3 | CONSOLIDATION | Connect all evidence together | Questions the evidence |
61
+ | 4 | CLOSING | Final summary / impact statement | Final "Judge" summary |
62
+
63
+
64
+
65
+ ### FRAME IT
66
+ But your framing needs to shift. Right now you're building "a debate game." You need to frame it as "Argument Quality Evaluation Environment" — a tool for training and evaluating LLM reasoning and persuasion capabilities. That framing scores higher on real-world utility (30% of grade).
67
+
68
+ ### NEW BUILD TESTING
69
+
70
+ What you need to build, reframed
71
+ The 3 required tasks should be difficulty tiers of the same domain:
72
+
73
+ - Task 1 (Easy) — Single claim grader
74
+ Agent makes one claim on a topic. Grader scores: length ✓, contains reasoning keyword ✓, on-topic ✓. Score 0.0–1.0.
75
+
76
+ - Task 2 (Medium) — Claim + rebuttal grader
77
+ Agent makes a claim, receives an opponent challenge, must rebut. Grader scores rebuttal quality against the challenge. Partial credit for partial engagement.
78
+
79
+ - Task 3 (Hard) — Full 5-phase debate grader
80
+ Your current full episode. Grader scores coverage, synthesis, logical consistency across all phases. This is where your RewardMetrics class shines.
81
+
82
+ ## Priority order right now
83
+
84
+ - 3 tasks with clear difficulty progression and deterministic graders ✓
85
+ - Rewards normalized to [0.0, 1.0] ✓
86
+ - Pydantic schemas ✓
87
+ - state() method ✓
88
+ - Embedding cache with batched matrix ops ✓
89
+ - opponent_coverage upgraded to match argument_coverage ✓
90
+ - impact_score normalized ✓
91
+ - Task graders call RewardMetrics directly instead of depending on environment reward ✓
92
+ - Baseline script producing reproducible scores ✓
93
+ - Shared metrics instance at module level — good memory management ✓
94
+
95
+
96
+ ### Does the "Debate" Topic Align with "Real-World Tasks"?
97
+
98
+ Yes, but it requires careful framing. If you call it a "Philosophical Debate Game," the judges might penalize it as a "toy."
99
+ To maximize that 30% weight, you must frame this environment as "Strategic Argument Red-Teaming" or "PR/Legal Objection Handling." * The Real-World Pitch: "Companies use LLMs to draft policies, PR statements, and legal summaries. This environment simulates a hostile review process. The agent must defend a claim against an adversarial LLM (representing a skeptical public, opposing counsel, or strict compliance reviewer). This is a direct simulation of RLHF/RLAIF reasoning workflows used at Meta and Anthropic."
100
+
101
+ This framing instantly elevates your project from a "game" to an Enterprise Agent Evaluation Tool.