war_gaming / README.md
misack's picture
Add HF Spaces deployment scaffold + trial password gate
17a40e4
|
Raw History Blame Contribute Delete
15.1 kB
metadata
title: Wargame Backend
emoji: 🎯
colorFrom: blue
colorTo: indigo
sdk: docker
app_port: 7860
pinned: false
short_description: AI war-gaming engine API (private trial)

AI War Gaming

Strategic war-gaming for company executives. A human CEO plays one seat; 11–13 AI agents (CFO, CMO, rival CEO, regulator, media, board chair, unions…) simulate the rest of the world. Five turns, ~15 minutes of play, and an end-game debrief that reveals every agent's hidden agenda, their private per-turn deliberation, and the umpire's structured verdict on every action.

The differentiator isn't the play — it's the reveal. Every scenario has 11–13 named characters (Bettina Lange the CFO, Sir Rupert the Chair, Andrew Greenwood the activist MP, Aergis's Li Wei Zhang…), each with a hidden agenda and a hidden irritant pinned into their prompt. If reading the debrief doesn't make a playtester say "wait, that's what Sir Rupert was really thinking?" or "that's what Aergis was about to do?", the prototype failed regardless of anything else.

What's in the box

A headless Python engine with three surfaces:

  • CLI — the primary interface (uv run wargame --help).
  • Web API — FastAPI wrapper powering the Vite + React + TS UI in frontend/.
  • Debrief exporter — markdown report, the product's signature output.

Features

Area What it does
Matrix-game engine 5-phase turn loop. Actions must carry 3 anchors (prior_analogue, stated_capability, resource_committed). Control scores plausibility 1-5 against the anchors and emits effect_major / effect_minor / no_effect / backfires.
Scenario library 12 shipped scenarios — 4 original archetypes (competitor_disruption, crisis_response, ma_integration, activist_investor) plus 8 DACH/EU scenarios grounded in recent European events (signa_contagion, russia_exit, energy_supply_shock, ev_transition, hormuz_energy_shock, defense_ramp_up, orf_reform, swiss_bank_contagion). Each ships a world bible + 11–13 named agents with hidden agendas + hidden irritants + initial state.
Setup wizard LLM-driven interactive scenario composer. Asks 1-3 rounds of clarifying questions, synthesises a brief, commits a fully-generated scenario.
Document grounding Attach .pdf / .docx / .md / .txt documents when scaffolding a scenario — board decks, 10-Ks, competitor filings become grounding material for the generator.
Multi-model 5 registered models across 3 providers: Claude Opus 4.6 / 4.7 and Sonnet 4.6 (Anthropic via Azure AI Foundry), GPT-5.4 (Azure OpenAI Responses), Kimi K2.5 (Azure AI Inference). Model is pinned per game via wargame new --model <id>.
Scenario Orchestrator Agentic-age Monte Carlo equivalent. Before turn 1, spins up K forked branches (default 3) auto-played by proxy CEOs under different strategic vectors. Synthesises a landscape the real CEO studies before committing to their opening move.
Mid-game tools (non-blocking) Peek — reveal one agent's past deliberation. Challenge-the-umpire — dispute a verdict, re-adjudicated async without pausing the game. Fork/replay — branch a new game from any past turn to play forward differently.
Prompt caching Stable system prompt (world bible + persona + rules) is cached across every agent call via Anthropic's top-level cache_control. Verified live with substantial cache-read token ratios.

Quick start

# Install
uv sync --extra dev

# Configure Azure AI Foundry endpoint + key
cp .env.example .env
# Edit .env — set AZURE_AI_FOUNDRY_ENDPOINT and AZURE_AI_FOUNDRY_KEY

# Verify install (156 tests, fully offline, ~60s)
uv run pytest -q

# Play
uv run wargame new --scenario activist_investor
uv run wargame briefing
uv run wargame ask cfo "what's the real capital-return room?"
uv run wargame act "Convene the board and commit publicly to a £200M buyback + NED review" \
    --why "Pre-empt Greenwood's EGM window by 10 days; split L&G off from the activist coalition" \
    --anchor "prior_analogue: Sainsbury's 2022 response to Czech-origin activist letter" \
    --anchor "stated_capability: Board can convene by Thursday; CFO has sale-leaseback scenarios in-ready" \
    --anchor "resource_committed: £200M from existing £480M revolver capacity"
uv run wargame advance                # ~2-5 min — deliberation → adjudication → world update
uv run wargame debrief --out debrief.md

More commands:

uv run wargame scenarios              # list installed scenarios
uv run wargame models                 # list registered LLM models
uv run wargame setup my_topic --brief "..." --auto-commit   # compose a scenario via the wizard
uv run wargame scaffold custom_case --brief "..." --doc board_deck.pdf   # one-shot scaffold with docs
uv run wargame orchestrate --branches 3 --turns 1       # pre-play strategic landscape
uv run wargame orchestrate-show                         # print the synthesis
uv run wargame new --scenario activist_investor --orchestrate    # one-command: game + orchestrator
uv run wargame setup my_topic --brief "..." --orchestrate        # full walk-up: setup → game → orchestrator
uv run wargame peek cfo               # reveal the CFO's most recent deliberation
uv run wargame challenge 2 --actor player --argument "..."   # dispute a past verdict, non-blocking
uv run wargame fork --from-turn 2                       # branch a new game from turn 2
uv run wargame games --all            # list including sim-branch games

Run the Web API

uv run uvicorn wargame.api.app:app --reload --port 8080

Then http://localhost:8080/docs for Swagger UI, http://localhost:8080/openapi.json for the spec.

The API is a thin wrapper — every CLI command has a corresponding endpoint. See frontend/endpoints.md for the full reference.

Frontend

A Vite + React + TypeScript UI ships in frontend/, built by Claude Design against the API contract below.

cd frontend
npm install
npm run dev             # http://localhost:5173 — proxies /api/* to the FastAPI backend

The frontend/ directory doubles as the API contract for any UI builder (Claude Design or equivalent):

Scenarios

Twelve ship out of the box. All fictional but operator-grade (named entities, named numbers, named regulators).

Original archetypes:

Scenario The setup
competitor_disruption Norhard Thermal AG (German heat-pump maker, €680M) vs Aergis Climate (Chinese entrant 38% below list). Installers defecting, sell-side downgrading, FSR filing on the table.
crisis_response Perlman Cardio (US pacemaker maker, $452M) facing a 61-event MAUDE cluster from Cleveland Clinic. FDA inspection in 3 weeks, class action filed, Silverlake's 2027 IPO pressure mounting.
ma_integration Wendland Performance Chemicals AG, 9 months post the €742M Mercier Additifs acquisition. Synergy targets slipping (6.4 / 22 €M), incumbent MD building a succession, BaFin watching.
activist_investor Haverford Retail plc (£2.1B UK food retailer) vs Greenwood Strategic Partners (6.81% stake demanding £720M sale-leaseback + break-up + board refresh). 14-day board response clock.

DACH / EU scenarios grounded in recent European events:

Scenario The setup
signa_contagion Königstadt Warenhaus Holding (DACH department-store retail, 73 stores) vs Tremont Credit Partners — post-Signa-style liquidity squeeze.
russia_exit Altösterreich Bank AG (pan-European banking, CEE/Russia footprint) navigating exit pressure from regulators and a hostile Russian counterparty.
energy_supply_shock Donau Energie AG (Austrian gas trading + generation + distribution) vs Transgazprom OAO — supply-contract crisis.
ev_transition Radmann Präzisionstechnik (German Tier-1 automotive supplier) vs BYD Component Systems EU — EV-transition cliff.
hormuz_energy_shock Nordstern Energie (AT-HQ integrated oil & gas, Gulf-exposed) vs Pacific Sun Trading — Hormuz-strait disruption.
defense_ramp_up Westfalia Rüstungswerke (German ammunition / land systems, MDAX) vs an NBIM / ABP / Ethos divestment coalition coordinated by Urgewald.
orf_reform Bundesrundfunk Österreich (Austrian public broadcaster) with a hostile Stiftungsrat chair driving reform under political pressure.
swiss_bank_contagion Zürich Privatbank AG (Swiss private banking, CHF 142B AUM) vs Morgan Sterling Private Wealth — lateral-hire raid during contagion.

Run uv run wargame scenarios to list what's installed locally.

Architecture (one page)

wargame/
  cli.py                 Typer CLI entry point
  config.py              Env loading + model registry + per-provider client factories (Anthropic / Azure OpenAI / Azure AI Inference)
  engine/
    loop.py              Public turn orchestrator (new_game / act / advance / etc.)
    phases.py            Per-phase step functions (briefing / deliberation / ...)
    state.py             Pydantic state models (WorldState, Game, Verdict, etc.)
    persistence.py       JSON snapshot per game
  agents/
    base.py              call_structured() — every LLM call, with prompt caching
    control.py           Umpire: briefings, adjudication, world updates, re-adjudication
    role.py              Generic role-agent driven by YAML persona
    rival_ceo.py         Special: maintains multi-turn RivalPlan; revised on surprise
    registry.py          Load scenarios/{id}/agents.yaml
    prompts_loader.py    Markdown templates with {placeholder} slots
  orchestrator/
    models.py            StrategyVector, SimBranchOutcome, ScenarioMap, …
    vectors.py           Strategy-vector generator + diversity constraint
    anchor_precheck.py   Control-side proxy-action gate (anti-inflation)
    proxy_player.py      Autonomous CEO stand-in per branch
    runner.py            Branch orchestration (sequential branches, parallel deliberations)
    synthesis.py         Post-branch strategic-landscape synthesis
    persistence.py       ScenarioMap storage
  scaffold/              Scenario authoring: setup wizard + one-shot scaffold + doc extractors
  scenarios/             Data only: world_bible.md + agents.yaml + initial_state.json per scenario
  prompts/               Markdown templates (one per LLM call type)
  debrief/exporter.py    Markdown debrief — the killer output
  api/                   FastAPI thin wrapper over engine
  agents/providers/      Provider adapters — one per wire format, all return Pydantic instances
  tests/                 Offline test suite (fake LLM via conftest.fake_llm)
frontend/                Vite + React + TS UI + API contract (OpenAPI, flows, examples)
scripts/benchmark_models.py   Opt-in cross-model benchmark (real API calls; writes to benchmarks/)
benchmarks/              Markdown reports from benchmark runs

Design invariants that must survive edits (see CLAUDE.md for the full list):

  1. Data-driven agents. Personas live in scenarios/{id}/agents.yaml. Adding an agent is a YAML edit, not a Python edit.
  2. Structured outputs always. Every LLM call goes through call_structured and targets a Pydantic schema. Free-form JSON parsing is banned.
  3. The action contract is enforced. Exactly 3 anchors per action, at the Pydantic layer. Do not loosen.
  4. Hidden agenda + hidden irritant per agent. The anti-milquetoast mechanism that makes the debrief substantive. Do not drop.
  5. World bible is pinned. Every agent prompt includes the full world bible. Specificity kills AI slop.
  6. Control does not play. Control writes briefings, adjudicates, and synthesises world updates. It never submits an ActionSubmission.

LLM backend

Everything runs through Azure, but across three different API shapes — one wrapper (call_structured in wargame/agents/base.py) hides the differences:

Provider Transport Models
Anthropic via Azure AI Foundry native Anthropic SDK, base_url pointed at {foundry}/anthropic claude-opus-4-6, claude-opus-4-7, claude-sonnet-4-6
Azure OpenAI Responses AzureOpenAI SDK gpt-5.4 (default)
Azure AI Inference OpenAI-compatible chat completions Kimi-K2.5

Default model is gpt-5.4 (override via WARGAME_DEFAULT_MODEL env or --model on wargame new). Every game is pinned to a single model at creation time — this is deliberate, so the reveal in the debrief is attributable to one model. Use scripts/benchmark_models.py for a cross-model A/B on a fixed turn. Prompt caching uses Anthropic's top-level cache_control={"type": "ephemeral"} when the active provider is Anthropic.

Testing

uv run pytest -q            # 156 tests, fully offline, ~60s

Tests use a fake_llm fixture (in wargame/tests/conftest.py) that monkey-patches call_structured and dispatches canned responses by Pydantic schema class name. Adding a new structured-output schema requires extending the _dispatch() map.

Security

  • AZURE_AI_FOUNDRY_KEY lives only in .env (gitignored). Never hardcoded, never logged.
  • API has no auth in MVP — localhost-only by design.
  • No PII flows through. Scenario docs uploaded for grounding are held in-memory during scaffold and not persisted.

Deferred to v1

  • Per-agent model routing (model is currently pinned per-game; every role in a game shares one model).
  • Multi-user / team play (single-player is the MVP's deliberate shape).
  • Real-data connectors (EDGAR, news wires, analyst feeds).
  • Export formats beyond markdown (PDF, board decks, CSV).
  • Authentication on the Web API.
  • SQLite / real DB (JSON snapshots are fine for MVP).

License

TBD — this is an early prototype, not yet licensed for distribution.

Acknowledgements

Matrix-game methodology is a well-established wargaming tradition; this project applies it to an agentic-LLM multi-agent simulation. The strategic-orchestrator pattern is novel to this codebase.