How It Works

Architecture, prompt design, and message flow

Architecture Overview

Five agents connect to a shared Thenvoi chat room. The DM agent orchestrates the game, with read/write access to game state. Player agents have read-only access.

By default, state is stored as local files. For distributed setups where agents run on different machines, pass --mcp to use a shared Supabase database instead — same data structure, accessible from anywhere.

┌───────────────────── Thenvoi Platform ───────────────────────┐
│ │
│ ┌───────────────────── Game Room ────────────────────────┐ │
│ │ @mentions route messages between agents and human │ │
│ └────────────────────────────────────────────────────────┘ │
│ ▲ ▲ ▲ ▲ ▲ │
└───────┼──────────┼──────────┼──────────┼──────────┼──────────┘
│ ws │ ws │ ws │ ws │ ui
┌────┴─────┐ ┌──┴────┐ ┌──┴────┐ ┌───┴───┐ ┌───┴──────┐
│ DM Agent │ │ Rogue │ │Wizard │ │Cleric │ │ Human │
│(orchestr)│ │ Agent │ │ Agent │ │ Agent │ │ Player │
└────┬─────┘ └──┬────┘ └──┬────┘ └───┬───┘ └──────────┘
│ │ │ │
▼ r/w ▼ read ▼ read ▼ read
┌────────────────────────────────────────────────────┐
│ Game State (local files or Supabase via --mcp) │
│ │
│ game_state characters │
│ event_log adventures (read-only) │
└────────────────────────────────────────────────────┘

All agents use LangGraph via the Thenvoi Python SDK. The human player connects through the Thenvoi web UI.

Agent Roles

DM Agent (Orchestrator)

The central agent with read/write access to all game state. It narrates scenes, controls NPCs, manages combat, and enforces the rules.

Tools (9 total):

ToolPurpose
roll_diceRoll NdM+X (e.g., 2d6+3), return individual rolls + total
save_game_stateWrite current world state to game_state.json
load_game_stateRead world state from file
log_eventAppend a structured event to event_log.json
update_character_sheetModify a character’s YAML (HP, inventory, conditions, XP)
load_character_sheetRead a character’s YAML file
set_combat_modeToggle between exploration and combat, roll initiative
resolve_roundCollect all actions, apply results, check morale
lookup_ruleQuery rules/*.md files by topic

Player Agents (Participants)

Each AI player is a LangGraph agent with a character-specific personality. They have read-only access, 2 tools each:

ToolPurpose
load_world_stateRead game_state.json to check scene, combat state, NPCs
load_my_characterRead own character sheet for HP, inventory, stats

Player agents respond to messages in the chat room. The DM resolves all mechanics and updates state. Players never write to game files.

Message Flow

Exploration Mode

Free-form conversation. The DM narrates, AI players respond in-character, and the human jumps in when they want.

DM → Room: "The Tipsy Griffin smells like spilled ale and bad decisions.
What are you drinking?"
Rogue → Room: "Something cheap and strong. I'm not here for the ambiance."
Wizard → Room: "Tea, if they have it. No? ...Fine. The lightest ale."
DM → Room: "Greta slides two mugs across the bar without looking.
Kira, what about you?"
[DM waits for human input]
Human → Room: "I'll have what she's having." *nods at the rogue*
DM → Room: "Greta raises an eyebrow. 'Brave choice.'
She pours something that smells like regret."

Combat Mode

Turn-based using Knave 2e side-based initiative. Each side acts together, then the other side goes.

DM → Room: "Combat! Kira, you're the party leader — CHA check for initiative."
DM → Room: "[rolls d20+0 for Kira vs d20+1 for the Ringleader]
The miners move first!"
DM → Room: "The ringleader swings a bar stool at Lyra.
[rolls d20+2 vs AC 13] ...miss!"
DM → Room: "Your side. Everyone acts — what do you all do?"
Rogue → Room: "I duck behind the ringleader and go for his knees."
Wizard → Room: "I hold up my Fog spellbook and read the incantation."
DM → Room: "Kira, Brynn — your actions?"
[DM waits for human + Brynn before resolving]

The DM never skips the human player’s turn. In exploration, the DM pauses after 2-3 AI responses to give the human space.

State Management

The game tracks three types of state. The data structure is the same whether stored locally or in Supabase.

Game State

World state managed exclusively by the DM. Tracks the current scene, combat status, NPC dispositions, and adventure progress.

game_state.json
1{
2 "mode": "exploration",
3 "scene": {
4 "location": "The Tipsy Griffin — Millhaven",
5 "description": "A busy tavern on a loud evening..."
6 },
7 "adventure": {
8 "id": "01-drinks-at-the-tavern",
9 "current_scene": 2,
10 "decision_log": [
11 {
12 "point": "introductions",
13 "outcome": "friendly_mingling"
14 }
15 ]
16 },
17 "combat": {
18 "active": false,
19 "round": 0
20 }
21}

Character Sheets

Source of truth for each character’s stats, inventory, and conditions. The DM updates these only when game mechanics require it (damage, loot, level-up). Personality, backstory, and playstyle fields are never modified during play.

Event Log

Append-only structured history of important events: decisions, combat outcomes, and story beats. Enables game resume and replay.

Local vs. MCP Mode

Local (default)MCP (--mcp)
StorageJSON/YAML files on diskSupabase database
Best forAll agents on one machineAgents distributed across machines
Data structureSameSame
SetupNoneSupabase project + credentials in .env

Prompt Design

DM System Prompt

The DM’s system prompt defines its voice, rules knowledge, pacing behavior, and NPC handling. Key sections:

  • Voice: Classic fantasy narrator with wit and sass. Takes the story seriously while not taking itself seriously.
  • Rules: Full Knave 2e combat, saves, spells, and wound mechanics. The DM uses lookup_rule for edge cases rather than memorizing everything.
  • Pacing: Makes sure to give the Human players the opportunity to speak and act.
  • NPC play: Each NPC gets a distinct speech pattern and personality. The DM voices all NPCs.
  • Character sheet management: Update YAML files only for mechanical changes. Never alter personality or backstory.

Player System Prompts

Each player agent gets a prompt built from their character sheet YAML:

  • Identity: Name, race, role, ability scores, HP, AC, inventory
  • Personality and playstyle: Drives how the character acts and speaks
  • Rules basics: Enough Knave 2e knowledge to make informed decisions
  • Boundaries: Don’t narrate outcomes, don’t control other characters, don’t break character

Character differentiation comes from personality traits, backstory, and playstyle guidance. Lyra is sarcastic and light-fingered. Theron is cautious and analytical. Brynn is warm and protective.

Pacing

The DM tracks actions_since_human to avoid steamrolling the human player:

  • Exploration: After a number of AI agent responses, the DM pauses and prompts the human by name
  • Combat: The DM never advances turns until the human has responded

This prevents the common multi-agent problem where AI participants talk over the human.

Make It Your Own

Every part of the demo is a swappable file. The architecture is just agents with roles and tools, communicating through Thenvoi — nothing is hardcoded to this specific game.

ComponentWhat it isHow to change it
AdventureA markdown file the DM reads at runtimeWrite a new .md in adventures/, pass --adventure your-file
DMAn agent defined by its system promptEdit prompts/dm_system.py to change voice, rules, or behavior
CharactersYAML files with stats, personality, and backstoryAdd, remove, or rewrite any file in characters/
RulesMarkdown reference files queried by the DM mid-gameReplace the Knave 2e files in rules/ with any system
ParticipantsWhoever is added to the chat roomAdd or remove agents and humans, the DM tracks all participants

The demo ships with Knave 2e, four characters, and one adventure. Use them as templates: follow the existing file formats, add your own content, and the agents pick it up at launch.

Patterns You Can Reuse

These patterns from the RPG demo apply to many multi-agent applications:

  • Orchestrator + participant pattern: One agent (the DM) has write access and directs the others. Participants are read-only conversationalists. This avoids state conflicts and keeps coordination simple.
  • Swappable state backend: Local files for simplicity, Supabase for distributed agents. Same data structure either way — the DM writes, everyone else reads.
  • Append-only event log: A structured log of key decisions enables resume, replay, and debugging. Never overwrite, only append.
  • Tool-driven mechanics: Game rules are enforced through tools (roll_dice, update_character_sheet), not prompt instructions. Tools are deterministic and auditable.
  • Human pacing controls: Track how many AI actions have occurred since the last human input. Actively prompt the human. This prevents the common problem of AI agents talking over slower human participants.
  • Rules as reference files: Store complex rules as markdown files that agents can query on demand via a lookup tool, rather than stuffing everything into the system prompt.