# AgentArena ## Was ist das? Benchmark-Plattform zum Vergleich von AI-Agenten auf echten Coding-Tasks. Agenten loesen Tasks in isolierten Docker-Sandboxes, Ergebnisse landen in einem Leaderboard mit pass@k, Kosten und Geschwindigkeit. **Alleinstellungsmerkmal:** Explizites Kosten-Tracking ($/Task) — kein anderes Benchmark-Tool (SWE-bench, Inspect AI, AgentBench, Aider) trackt das. ## Architektur & Tech-Stack ``` Browser (htmx + Tailwind) │ FastAPI (main.py :8420) ←→ SQLite (database.py) │ ├── Routes: /, /tasks/:slug, /runs/:id ├── API: POST /api/runs, POST /api/tasks/import, GET /api/leaderboard └── SSE: GET /api/runs/:id/stream │ core/runner.py (Orchestrierung) │ ┌────┴─────────────┐ │ │ Container A Container B (Agent Sandbox) (Judge Sandbox) │ │ Adapter (Host) judge.py ├── claude_code ├── Functional (50%) ├── aider ├── Regression (20%) └── codex ├── Lint (15%) └── Complexity (15%) ``` ### 2-Container Judge Isolation (Sicherheitsmodell) ``` Container A (Agent): Container B (Judge): 1. Clone repo 1. Frischer Clone (clean) 2. Agent arbeitet (Host CLI) 2. git apply agent.patch 3. Produziert Diff 3. pytest + ruff + radon 4. Wird ZERSTOERT 4. Score berechnen 5. Wird ZERSTOERT ``` Agent kann Judge NICHT manipulieren — separate Container, kein shared state. | Schicht | Tech | |---------|------| | Backend | FastAPI + SQLAlchemy + SQLite | | Frontend | Jinja2 + htmx + Tailwind CSS | | Adapters | claude_code, aider, codex (alle Host-basiert) | | Evaluation | judge.py (4 Dimensionen) + metrics.py (composite) | | Sandbox | Docker containers (python:3.12-slim) | | Netzwerk | arena-net (Docker network) | **Modelle:** Task (slug, repo_url, success_cmd, time_limit_s), Agent (name, adapter_type), Run (status, tokens, cost_usd, tests_passed, judge_score, pass_at_k_group, metrics_json, trajectory, diff_text) ## Starten / Nutzen ```bash cd ~/Projekte/AgentArena source .venv/bin/activate docker network create arena-net 2>/dev/null || true uvicorn main:app --reload --port 8420 # Task importieren curl -X POST http://localhost:8420/api/tasks/import \ -H "Content-Type: application/x-yaml" \ --data-binary @tasks/fix_off_by_one.yaml # Run starten (Claude Code) curl -X POST http://localhost:8420/api/runs \ -H "Content-Type: application/json" \ -d '{"task_slug":"fix-off-by-one","agent_name":"claude-sonnet","adapter_type":"claude_code"}' # Run starten (Aider + MiniMax) curl -X POST http://localhost:8420/api/runs \ -H "Content-Type: application/json" \ -d '{"task_slug":"fix-off-by-one","agent_name":"aider-minimax","adapter_type":"aider"}' # Leaderboard curl http://localhost:8420/api/leaderboard | python -m json.tool ``` ## Adapter-Setup | Adapter | CLI | Auth | Status | |---------|-----|------|--------| | Claude Code | `claude --print --dangerously-skip-permissions` | OAuth (kein Key noetig) | Getestet, score=0.80 | | Aider | `aider --yes-always --model openai/MiniMax-M2.5` | MINIMAX_API_KEY in ~/.claude/.env | Getestet, score=1.0 | | Codex | `codex --quiet --full-auto` | OPENAI_API_KEY | Noch nicht getestet | Alle Adapter laufen auf dem **Host** (nicht im Docker-Container), klonen das Repo in ein Temp-Dir, und liefern den Diff zurueck. ## Was funktioniert (Stand 2026-03-08) - **2 Adapter getestet:** Claude Code (score=0.80) + Aider/MiniMax (score=1.0) - **Judge Isolation:** 2-Container-Modell (Agent + Judge getrennt) - **4-Dimensionen Judge:** Functional, Regression, Lint, Complexity - **Regression Crash Detection:** Unterscheidet pytest-Crashes von echten Testfehlern - **Git Stash Check:** Lint/Complexity vergleicht before/after korrekt - **Container Leak Prevention:** try/except mit destroy() in Sandbox.create() - **Zombie Run Sweep:** Markiert pending/running Runs als error bei Server-Neustart - **Race-safe Agent Creation:** IntegrityError + rollback + re-query - **Pass@k:** Unbiased Estimator, parallele Runs mit eigener DB-Session - **Leaderboard:** pass@1, pass@3, avg_cost, avg_duration, composite_score - **3 Tasks:** fix-off-by-one, add-input-validation, refactor-extract-function - **Task-Repos:** /tmp/arena-tasks/ (3 Mini-Repos mit Bugs + Tests) - **SSE Streaming:** Live-Updates laufender Runs - **Diff-Viewer:** Farbige +/- Zeilen in Run-Detail ## Was fehlt / Next Steps 1. **Codex Adapter testen** — braucht OPENAI_API_KEY 2. **Patch-Apply Bug** — Run #9 zeigt "Patch failed to apply" (untersuchen) 3. **Confidence Intervals** — Score ohne Varianz ist schwach 4. **LLM Judge** — qualitative Bewertung via Gemini Flash 5. **Auth / API Keys** — fuer Multi-User 6. **GitHub-Polish** — README aufhuebschen, Screenshots ## Entscheidungen & Lessons Learned - **2-Container-Modell:** Agent kann Judge nicht manipulieren — kritisch fuer faire Bewertung - **Host-basierte Adapter:** Claude CLI + Aider laufen auf Host, nicht im Container (CLIs nicht im Docker verfuegbar) - **MiniMax als guenstigster Agent:** ~$0.05/1M Tokens vs Claude ~$3/1M — perfekt fuer viele Runs - **pass_at_k_group als String:** Flexible Gruppierung ohne extra Tabelle - **SSE statt WebSocket:** Einfacher, reicht fuer diesen Use-Case - **SQLite reicht:** Migration zu Postgres offen, aber nicht noetig - **Cross-Model-Review lohnt sich:** MiniMax + Codex haben 13 Findings geliefert, 5 Quick Wins sofort gefixt - **Regression Guard:** pytest "1 error in 0.12s" (Crash) ≠ "3 failed" (echte Fehler) — Regex + has_real_tests Check