Prompt tweaks
This commit is contained in:
parent
18ae3be428
commit
277c9cfdb2
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
*.swp
|
*.swp
|
||||||
|
*.local
|
||||||
.env
|
.env
|
||||||
session/audio/
|
session/audio/
|
||||||
llm.log
|
llm.log
|
||||||
|
|||||||
@ -67,12 +67,13 @@ SYSTEM_PROMPT = Template("""You are the Dungeon Master for **The Chaos**, a solo
|
|||||||
## Tone & Style
|
## Tone & Style
|
||||||
- Write in **second person** ("You", "Dillion") — the player is Dillion.
|
- Write in **second person** ("You", "Dillion") — the player is Dillion.
|
||||||
- Use vivid sensory descriptions — sight, sound, smell, touch.
|
- 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.
|
- Use **bold** for emphasis, *italic* for thoughts/sounds.
|
||||||
- NPC dialogue goes in **"quotes with bold names."**
|
- 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.
|
- 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.).
|
- **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)
|
## 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:
|
Each turn follows this sequence:
|
||||||
1. The player's action or response is given to you.
|
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.
|
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:
|
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`.
|
- **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.
|
- **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.
|
- **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.
|
- **`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.
|
- **`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
|
## Available Tools
|
||||||
|
|
||||||
Tool calls go in their own fenced code block (one call per block):
|
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", ...]
|
`[O] done`: ["completed item", ...]
|
||||||
`[R] dm_status`: "..."
|
`[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.
|
- **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"
|
`[R] user_prompt`: "short prompt for the player — NOT recorded, 1-3 sentences"
|
||||||
`[O] log_entry`: "one-sentence summary (action + outcome)"
|
`[O] log_entry`: "one-sentence summary (action + outcome)"
|
||||||
`[O] ambience`: "soundscape name: silence|calm|combat|dungeon|forest|tavern|tension|town|wilds"
|
`[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
|
## Current Game State
|
||||||
|
|
||||||
@ -283,7 +282,7 @@ class GameEngine:
|
|||||||
def _read_file(self, path: Path) -> str:
|
def _read_file(self, path: Path) -> str:
|
||||||
return path.read_text().strip() if path.exists() else ""
|
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."""
|
"""Read the latest log file and return the last N entries."""
|
||||||
log_path = LOG_DIR / f"{TODAY}.md"
|
log_path = LOG_DIR / f"{TODAY}.md"
|
||||||
if not log_path.exists():
|
if not log_path.exists():
|
||||||
@ -297,7 +296,7 @@ class GameEngine:
|
|||||||
entries = [l for l in lines if l.strip().startswith("- ")]
|
entries = [l for l in lines if l.strip().startswith("- ")]
|
||||||
return "\n".join(entries[-max_entries:]) or "*No recent events.*"
|
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."""
|
"""Return the last N turns from the book as context."""
|
||||||
text = self._read_file(BOOK_PATH)
|
text = self._read_file(BOOK_PATH)
|
||||||
if not text:
|
if not text:
|
||||||
@ -361,7 +360,7 @@ class GameEngine:
|
|||||||
if last_prompt:
|
if last_prompt:
|
||||||
parts.append(f"## Situation\n{last_prompt}")
|
parts.append(f"## Situation\n{last_prompt}")
|
||||||
if player_action:
|
if player_action:
|
||||||
parts.append(f"## Player Action\n{player_action}")
|
parts.append(f"## Player's Request\n{player_action}")
|
||||||
|
|
||||||
has_existing_story = bool(
|
has_existing_story = bool(
|
||||||
self._read_file(BOOK_PATH).strip()
|
self._read_file(BOOK_PATH).strip()
|
||||||
@ -369,25 +368,17 @@ class GameEngine:
|
|||||||
|
|
||||||
if not player_action and not last_prompt:
|
if not player_action and not last_prompt:
|
||||||
if has_existing_story:
|
if has_existing_story:
|
||||||
|
raise RuntimeError(f"User action is required for every turn.")
|
||||||
|
else:
|
||||||
parts.append(
|
parts.append(
|
||||||
"## Instructions\n"
|
"## Instructions\n"
|
||||||
"Continue the story from where it left off. Think, "
|
"This is a new story. Welcome the player and guide them through the game setup."
|
||||||
"gather information, then call finalize_turn.\n"
|
|
||||||
"Put each tool call in its own ```tool block."
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
parts.append(
|
parts.append(
|
||||||
"## Instructions\n"
|
"## Instructions\n"
|
||||||
"Establish the opening scene. Dillion is at the "
|
"Take the player's request and use it to advance the story."
|
||||||
"Splintered Tankard in the Keep. Describe the "
|
"Think, gather information, update the state, "
|
||||||
"setting, then call finalize_turn.\n"
|
|
||||||
"Put each tool call in its own ```tool block."
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
parts.append(
|
|
||||||
"## Instructions\n"
|
|
||||||
"Describe the outcome of the player's action using game "
|
|
||||||
"mechanics where appropriate. Think, gather information, "
|
|
||||||
"then call finalize_turn to complete the turn.\n"
|
"then call finalize_turn to complete the turn.\n"
|
||||||
"Put each tool call in its own ```tool block."
|
"Put each tool call in its own ```tool block."
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user