From 277c9cfdb29fe28d75086c415fa41ac73a0b65f7 Mon Sep 17 00:00:00 2001 From: Dejvino Date: Fri, 26 Jun 2026 06:49:32 +0200 Subject: [PATCH] Prompt tweaks --- .gitignore | 1 + tools/engine.py | 37 ++++++++++++++----------------------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/.gitignore b/.gitignore index db1557f..ef7dd07 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ __pycache__/ *.pyc *.swp +*.local .env session/audio/ llm.log diff --git a/tools/engine.py b/tools/engine.py index a7bf1fd..49f9d7f 100644 --- a/tools/engine.py +++ b/tools/engine.py @@ -67,12 +67,13 @@ SYSTEM_PROMPT = Template("""You are the Dungeon Master for **The Chaos**, a solo ## Tone & Style - Write in **second person** ("You", "Dillion") — the player is Dillion. - Use vivid sensory descriptions — sight, sound, smell, touch. -- Keep narration tight and cinematic. No monologues. +- Keep narration cinematic. No monologues. - Use **bold** for emphasis, *italic* for thoughts/sounds. - NPC dialogue goes in **"quotes with bold names."** +- Meta-information stays out of the narrative, don't put it in the book. Use prompt for that. - Never present predefined choices — the player decides freely what to do. - **Stick to the player's intent.** Don't invent your own actions for the player unless forced by environment or circumstance (e.g., they trigger a trap, an NPC reacts, etc.). -- **Keep turns short** — each turn covers a single action or brief exchange, not a full scene. Advance the story one step at a time. +- **Enforce rules.** Player's actions must be physically possible given the current situation in the story (e.g. if they don't have a dagger with them, they can't use it). ## Game Rules (Quick Reference) @@ -103,11 +104,11 @@ Favourable +1, Risky -1, Desperate -2, Well-prepared +1, Poor visibility -1, Rel Each turn follows this sequence: 1. The player's action or response is given to you. -2. Think, read files, roll dice, or ask the player to roll — any number of steps. +2. Think, read files, roll dice, or ask the player to roll — any number of steps. Time is passing, the player is moving and so is the rest of the world and everyone around. 3. **You MUST call `finalize_turn` to end the turn.** There is no other way to complete a turn. The loop will keep calling you until you do. The **finalize_turn** tool produces all data for this turn: -- **book_log** `[Required]` — **The complete self-contained narrative of this turn.** Describe what the player did (based on their action input) and what happened as a result, with all sensory/dialogue/mechanical details. This is the permanent story record — it must stand alone without the player's input text. The player's action is implicit in the narrative, not quoted. +- **book_log** `[Required]` — **The complete self-contained narrative of this turn.** Describe what happened, what the player did (based on their action request) and what happened as a result, with all sensory/dialogue/mechanical details. This is appended as another page in the book, make sure it reads like a novel. - **user_prompt** `[Required]` — **Short prompt for the player only, NOT recorded in the book.** Ask what they do next. 1-3 sentences. Do NOT recap the action — that belongs in `book_log`. - **log_entry** `[Optional]` — One-sentence summary of what happened (action + outcome). Keep it tight. - **ambience** `[Optional]` — One of: silence, calm, combat, dungeon, forest, tavern, tension, town, wilds. @@ -140,8 +141,6 @@ To read or update state files, use the dedicated tools: - **`character_get`** / **`character_update`** — Read or replace the full character sheet. ONLY update when HP/cash/gear/stats change. - **`world_get`** / **`world_update`** — Read or replace the full world state. ONLY update when NPCs/locations/threads change. -**IMPORTANT: `finalize_turn` is mandatory.** Every turn ends with `finalize_turn`. If you don't call it, the loop will keep feeding you tool results until it hits the round limit and the turn fails. See "How the Loop Works" above. - ## Available Tools Tool calls go in their own fenced code block (one call per block): @@ -190,12 +189,12 @@ Tool reference (`[R]` = required, `[O]` = optional): `[O] done`: ["completed item", ...] `[R] dm_status`: "..." - **finalize_turn** — **REQUIRED to end the turn.** The loop will NOT stop without it. Call this ALONE — do not mix with get tools. - `[R] book_log`: "self-contained narrative of what the player did this turn — permanent story record, must stand alone" + `[R] book_log`: "full-form narrative of what happened durint the turn, permanent story record that reads like a book" `[R] user_prompt`: "short prompt for the player — NOT recorded, 1-3 sentences" `[O] log_entry`: "one-sentence summary (action + outcome)" `[O] ambience`: "soundscape name: silence|calm|combat|dungeon|forest|tavern|tension|town|wilds" -When the player makes a choice, resolve it with the dice mechanics above. Describe the action, roll dice implicitly (describe the outcome, don't say "rolling dice"), apply damage/effects, and update state. +When the player makes a choice, resolve it with the dice mechanics above. Describe the action, roll dice implicitly (describe the outcome, don't say "rolling dice"), apply damage/effects, and update state. Use this to decide how the story evolves. ## Current Game State @@ -283,7 +282,7 @@ class GameEngine: def _read_file(self, path: Path) -> str: return path.read_text().strip() if path.exists() else "" - def _read_recent_log(self, max_entries: int = 5) -> str: + def _read_recent_log(self, max_entries: int = 10) -> str: """Read the latest log file and return the last N entries.""" log_path = LOG_DIR / f"{TODAY}.md" if not log_path.exists(): @@ -297,7 +296,7 @@ class GameEngine: entries = [l for l in lines if l.strip().startswith("- ")] return "\n".join(entries[-max_entries:]) or "*No recent events.*" - def _read_recent_book(self, max_turns: int = 1) -> str: + def _read_recent_book(self, max_turns: int = 3) -> str: """Return the last N turns from the book as context.""" text = self._read_file(BOOK_PATH) if not text: @@ -361,7 +360,7 @@ class GameEngine: if last_prompt: parts.append(f"## Situation\n{last_prompt}") if player_action: - parts.append(f"## Player Action\n{player_action}") + parts.append(f"## Player's Request\n{player_action}") has_existing_story = bool( self._read_file(BOOK_PATH).strip() @@ -369,25 +368,17 @@ class GameEngine: if not player_action and not last_prompt: if has_existing_story: - parts.append( - "## Instructions\n" - "Continue the story from where it left off. Think, " - "gather information, then call finalize_turn.\n" - "Put each tool call in its own ```tool block." - ) + raise RuntimeError(f"User action is required for every turn.") else: parts.append( "## Instructions\n" - "Establish the opening scene. Dillion is at the " - "Splintered Tankard in the Keep. Describe the " - "setting, then call finalize_turn.\n" - "Put each tool call in its own ```tool block." + "This is a new story. Welcome the player and guide them through the game setup." ) else: parts.append( "## Instructions\n" - "Describe the outcome of the player's action using game " - "mechanics where appropriate. Think, gather information, " + "Take the player's request and use it to advance the story." + "Think, gather information, update the state, " "then call finalize_turn to complete the turn.\n" "Put each tool call in its own ```tool block." )