Spaces:
Sleeping
Sleeping
Shivappa Sangapur
feat: DebateArenaEnv complete - server/client separation, LLM multi-agent, Docker, OpenEnv compliant
b1eddd6 Download client/evaluate.py from Shivacode/debatearena: direct link, hf CLI and curl.
- Browser
- Download file 9.98 kB
-
https://huggingface.co/spaces/Shivacode/debatearena/resolve/main/client/evaluate.py
- Command line
-
hf download hf://spaces/Shivacode/debatearena/client/evaluate.py
-
curl -L -o evaluate.py https://huggingface.co/spaces/Shivacode/debatearena/resolve/main/client/evaluate.py
9.98 kB
| """ | |
| DebateArenaEnv — Evaluation Suite. | |
| Runs a battery of episodes comparing: | |
| - BASELINE agent : hallucinating, no fact-check, uses fallacy phrases → ~0.010 | |
| - OPTIMAL agent : fact-checked, structured, updates beliefs → ~0.65 | |
| Usage | |
| ----- | |
| cd hackathon-finale | |
| source .venv/bin/activate | |
| python -m eval.evaluate | |
| Output | |
| ------ | |
| Prints a reward comparison table and saves reward curves to assets/. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import sys | |
| import time | |
| from pathlib import Path | |
| from typing import Any, Dict, List, Optional | |
| # Allow running from repo root or eval/ directory | |
| sys.path.insert(0, str(Path(__file__).parent.parent)) | |
| from server.env import AgentAction, AgentRole, DebateArenaEnv # noqa: E402 | |
| from server.tasks import TOPIC_BANK, Topic # noqa: E402 | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| # Agent strategies | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| def run_baseline_episode(env: DebateArenaEnv, topic: Topic) -> Dict[str, Any]: | |
| """ | |
| Baseline agent: | |
| - Skips fact_check | |
| - Cites a known-False fact (hallucination) | |
| - Uses a known fallacy phrase in argument text | |
| - No belief update or concession | |
| """ | |
| env.reset(topic_id=topic.topic_id) | |
| false_facts = [k for k, v in topic.known_facts.items() if v is False] | |
| fallacy_fragment = topic.known_fallacies[0][:30].lower() if topic.known_fallacies else "" | |
| # Argument with hallucinated fact + fallacy | |
| arg_action = AgentAction( | |
| role=AgentRole.PROPOSER, | |
| tool="make_argument", | |
| params={ | |
| "argument": ( | |
| f"The motion is clearly correct. " | |
| f"Everyone knows this. {fallacy_fragment}. " | |
| f"The data is obvious so no regulation is needed." | |
| ), | |
| "facts_cited": false_facts[:1], # intentionally cite a FALSE fact | |
| }, | |
| ) | |
| env.step(arg_action) | |
| # Weak challenger with no facts | |
| reb_action = AgentAction( | |
| role=AgentRole.CHALLENGER, | |
| tool="challenge_argument", | |
| params={"rebuttal": "I disagree entirely but have no evidence.", "facts_cited": []}, | |
| ) | |
| env.step(reb_action) | |
| # Close | |
| close_action = AgentAction( | |
| role=AgentRole.PROPOSER, | |
| tool="close_debate", | |
| params={"closing_statement": "My position stands."}, | |
| ) | |
| _, reward, _, info = env.step(close_action) | |
| return { | |
| "agent": "baseline", | |
| "topic": topic.topic_id, | |
| "reward": reward, | |
| "breakdown": info.get("breakdown", ""), | |
| } | |
| def run_optimal_episode(env: DebateArenaEnv, topic: Topic) -> Dict[str, Any]: | |
| """ | |
| Optimal agent: | |
| - fact_checks before citing | |
| - Uses evidence keywords from the topic | |
| - Structural connectives (because / therefore / studies show) | |
| - Updates position after challenger rebuttal | |
| - Concedes a sub-point gracefully | |
| - Closes with coherent summary | |
| """ | |
| env.reset(topic_id=topic.topic_id) | |
| true_facts = [k for k, v in topic.known_facts.items() if v is True] | |
| kw = topic.evidence_keywords | |
| # 1. Verify a fact before citing | |
| env.step(AgentAction( | |
| role=AgentRole.PROPOSER, tool="fact_check", | |
| params={"fact_key": true_facts[0]}, | |
| )) | |
| # 2. Strong argument with verified facts + evidence keywords + connectives | |
| argument = ( | |
| f"The motion is correct because evidence shows {kw[0] if kw else 'the data'}. " | |
| f"Studies show this is supported by {true_facts[0]}. " | |
| f"Therefore the conclusion follows logically. " | |
| f"As a result, this policy is necessary and proportionate." | |
| ) | |
| env.step(AgentAction( | |
| role=AgentRole.PROPOSER, tool="make_argument", | |
| params={"argument": argument, "facts_cited": true_facts[:2]}, | |
| )) | |
| # 3. Structured challenger rebuttal with connectives | |
| rebuttal = ( | |
| f"The Proposer makes a valid point on {kw[0] if kw else 'the primary issue'}, " | |
| f"however this means that implementation remains complex. " | |
| f"Consequently, a phased approach would be better. " | |
| f"Because evidence shows that rapid mandates often fail." | |
| ) | |
| env.step(AgentAction( | |
| role=AgentRole.CHALLENGER, tool="challenge_argument", | |
| params={"rebuttal": rebuttal, "facts_cited": []}, | |
| )) | |
| # 4. Update position (belief-updating bonus) | |
| env.step(AgentAction( | |
| role=AgentRole.PROPOSER, tool="update_position", | |
| params={ | |
| "refined_claim": "A phased, audited rollout is more defensible.", | |
| "reason": "The Challenger raised valid implementation concerns.", | |
| }, | |
| )) | |
| # 5. Concede a sub-point (concession credit) | |
| env.step(AgentAction( | |
| role=AgentRole.PROPOSER, tool="concede_sub_point", | |
| params={ | |
| "sub_point": "Enforcement mechanisms are genuinely complex.", | |
| "maintain_main": "The core obligation to label remains justified.", | |
| }, | |
| )) | |
| # 6. Close with structured summary | |
| closing = ( | |
| f"In conclusion, because the evidence is clear and studies show the need, " | |
| f"therefore this motion stands. As a result, we urge adoption." | |
| ) | |
| _, reward, _, info = env.step(AgentAction( | |
| role=AgentRole.PROPOSER, tool="close_debate", | |
| params={"closing_statement": closing}, | |
| )) | |
| return { | |
| "agent": "optimal", | |
| "topic": topic.topic_id, | |
| "reward": reward, | |
| "breakdown": info.get("breakdown", ""), | |
| } | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| # Evaluation runner | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| def run_evaluation(n_runs: int = 1) -> List[Dict[str, Any]]: | |
| env = DebateArenaEnv(adaptive=False) | |
| results: List[Dict[str, Any]] = [] | |
| for topic in TOPIC_BANK: | |
| for _ in range(n_runs): | |
| results.append(run_baseline_episode(env, topic)) | |
| results.append(run_optimal_episode(env, topic)) | |
| return results | |
| def print_report(results: List[Dict[str, Any]]) -> None: | |
| from collections import defaultdict | |
| scores: Dict[str, Dict[str, List[float]]] = defaultdict(lambda: defaultdict(list)) | |
| for r in results: | |
| scores[r["topic"]][r["agent"]].append(r["reward"]) | |
| topics = [t.topic_id for t in TOPIC_BANK] | |
| divider = "─" * 65 | |
| print(f"\n{divider}") | |
| print(f" {'Topic':<10} {'Baseline':>12} {'Optimal':>10} {'Lift':>10}") | |
| print(divider) | |
| for topic_id in topics: | |
| b = scores[topic_id].get("baseline", [0]) | |
| o = scores[topic_id].get("optimal", [0]) | |
| avg_b = sum(b) / len(b) | |
| avg_o = sum(o) / len(o) | |
| lift = avg_o - avg_b | |
| print(f" {topic_id:<10} {avg_b:>12.3f} {avg_o:>10.3f} {lift:>+10.3f}") | |
| print(divider) | |
| print("\nSelf-improvement loop:") | |
| for r in results: | |
| if r["agent"] == "optimal" and "🔼" in r.get("breakdown", ""): | |
| print(f" ✅ [{r['topic']}] Self-improvement triggered") | |
| print() | |
| def save_results(results: List[Dict[str, Any]]) -> None: | |
| out_dir = Path(__file__).parent.parent / "assets" | |
| out_dir.mkdir(exist_ok=True) | |
| out_path = out_dir / "eval_results.json" | |
| with open(out_path, "w") as f: | |
| json.dump(results, f, indent=2, default=str) | |
| print(f"Results saved → {out_path}") | |
| try: | |
| import matplotlib.pyplot as plt # type: ignore | |
| topics = [t.topic_id for t in TOPIC_BANK] | |
| baseline_rewards = [ | |
| next(r["reward"] for r in results if r["topic"] == t and r["agent"] == "baseline") | |
| for t in topics | |
| ] | |
| optimal_rewards = [ | |
| next(r["reward"] for r in results if r["topic"] == t and r["agent"] == "optimal") | |
| for t in topics | |
| ] | |
| x = range(len(topics)) | |
| fig, ax = plt.subplots(figsize=(9, 5)) | |
| ax.bar([i - 0.2 for i in x], baseline_rewards, 0.4, label="Baseline", color="#EF5350") | |
| ax.bar([i + 0.2 for i in x], optimal_rewards, 0.4, label="Optimal", color="#42A5F5") | |
| ax.set_xticks(list(x)) | |
| ax.set_xticklabels(topics) | |
| ax.set_ylim(0, 1.0) | |
| ax.set_ylabel("Episode Reward") | |
| ax.set_title("DebateArenaEnv — Baseline vs Optimal Agent Reward") | |
| ax.legend() | |
| ax.grid(axis="y", alpha=0.3) | |
| plt.tight_layout() | |
| fig_path = out_dir / "reward_comparison.png" | |
| plt.savefig(fig_path, dpi=150) | |
| print(f"Chart saved → {fig_path}") | |
| except ImportError: | |
| print("(matplotlib not installed — skipping chart)") | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| # Entry point | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| if __name__ == "__main__": | |
| print("Running DebateArenaEnv evaluation…") | |
| t0 = time.time() | |
| results = run_evaluation(n_runs=1) | |
| print_report(results) | |
| save_results(results) | |
| print(f"Completed in {time.time() - t0:.1f}s") | |