Multi-Agent Simulation · Google ADK · Deterministic Engine
AI Agents That Play Daggerheart Without Breaking the Rules
A Game Master agent and four player-character agents built with Google ADK play complete Daggerheart sessions autonomously—with zero human intervention, strict fog-of-war, and a deterministic Python engine that owns every die roll and state mutation.
- Google ADK Graph Workflows
- Gemini 3.1 Flash-Lite · Claude Haiku 5.5 · Ollama Gemma 4
- Daggerheart SRD 2.0
- Byte-for-Byte Decision Replay
Architecture
The Determinism Boundary: Make Drift Structurally Impossible
Left to roll its own dice and track its own hit points, an LLM table drifts within three turns.
tinker-daggerheart enforces a hard separation of powers: ADK decides who speaks next
and runs the models, while a pure Python engine owns every roll, resource track, and state write.
Stateless Schema Oracles
Every ADK LlmAgent runs with an output_schema (PlayerIntent,
GMMoveIntent, Narration). A player agent can declare
"I swing my mace at the bandit leader", never a roll or HP change.
Per-Viewer Fog of War
engine/fog.py projects the world through the eyes of whoever is acting: a PC sees
only revealed map features, public rumors, and their own private backstory and memory notes.
1:1 Audited Narration & Replay
ReplayPlugin records every model decision to decisions.jsonl so a whole
campaign replays offline byte for byte. tools/audit.py verifies that every engine
beat is answered 1:1 by one narration with zero orphans.
Orchestration
ADK Graph Workflows & Multi-Model Roles
The table is orchestrated by three nested Google ADK graph Workflows—one-shot / campaign,
scene, and combat—routing via Event.route between async function nodes
and a single model seam (oracle.ask(ctx, agent, prompt, schema)).
9 Playable Table Modes
Run a scripted one-shot ("The Sablewood Toll"), an auto-balanced bestiary skirmish
(--random), a multi-session campaign with level-ups (--campaign),
an emergent open table where the GM improvises scenes and fronts (--open-campaign),
AI party creation (--session-zero), or sit in a chair yourself with
DH_HUMAN_SEAT=kael while AI agents play the rest of the party.
Mix Gemini, Claude & Local Ollama Models
Assign different models per role with DH_MODEL_GM, DH_MODEL_PLAYER,
and DH_MODEL_TALK—for example, Claude Haiku 5.5 or Gemini 3.1 Flash-Lite for
tactical decisions and a local ollama/gemma4:26b model for table chatter and notes,
with live token & cost metering via MeterPlugin.
Evaluation
Measured Against a Real Human Table (DH_TABLE=real)
Beyond mechanical legality, the simulation measures how closely AI table dynamics match transcripts of real human tabletop sessions—tracking turn-taking, out-of-character banter, rules questions to the GM, reply chains, spotlight bidding, and per-player subjective memory recaps.
| Table Metric (3-Session Campaign, 4 Scenes/Session) | AI Table (DH_TABLE=real) |
Real Human Table Yardstick |
|---|---|---|
| GM Share of Talk (Distance) | 0.09 – 0.10 from target |
Baseline human GM share |
| GM Register Distance | 0.06 – 0.12 |
Baseline human GM register |
| Player Questions to GM | 12 – 13 / hour |
~25 / hour |
| Player-to-Player Reply Chains | Up to 5.4 / hour |
9.6 / hour |
| Out-of-Character Table Talk | 18% – 21% of turns |
13% of turns |
Mechanical Audit (tools/audit.py) |
100% (1:1 beat-to-narration, 0 orphans) | Deterministic invariant |