Implement the world-state summarization from the Phase 2 plan
Context was pure full-text replay of every message, capped at 40k chars with oldest-dropped-first — no summarization/world-state layer, as called out as still-missing in an earlier conversation. Add a new update_world_state DM tool that persists a compact, DM-authored recap (key NPCs, current location, open plot threads, party/inventory state) to a new world_state table, one row per game. It's injected into the system prompt every turn, ahead of the raw message window. The DM is instructed to call it regularly — at every scene change or major event, not just at the end — sending the full current picture each time (matches the replace-not-merge pattern already used for combat_stats/abilities/equipment). Since the summary now backs up everything older, shrink the raw message window from 40k to 16k chars — it's recent continuity now, not the sole memory of the session. Full history remains available via "Volltext laden" regardless, since that reads the messages table directly rather than through this context builder. Simplified from the original plan sketch (state JSONB) to a single free-text summary field — natural-language recaps are something an LLM authors well; a structured world model would need a schema the DM would have to conform to for no real benefit here. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@ from app.config import settings
|
||||
from app.llm.client import get_dm_system_prompt, get_llm_client
|
||||
from app.llm.context import build_context
|
||||
from app.llm.json_utils import fix_double_escaped_unicode
|
||||
from app.llm.tools import character_sheet, dice, end_game, monster
|
||||
from app.llm.tools import character_sheet, dice, end_game, monster, world_state
|
||||
from app.models.game import Game
|
||||
from app.models.message import Message
|
||||
from app.rag.retrieval import build_rag_block
|
||||
@@ -24,6 +24,7 @@ TOOLS = [
|
||||
{"type": "function", "function": character_sheet.TOOL_SCHEMA},
|
||||
{"type": "function", "function": end_game.TOOL_SCHEMA},
|
||||
{"type": "function", "function": monster.TOOL_SCHEMA},
|
||||
{"type": "function", "function": world_state.TOOL_SCHEMA},
|
||||
]
|
||||
|
||||
|
||||
@@ -41,6 +42,8 @@ async def _execute_tool_call(
|
||||
return await end_game.end(session, game_id, tool_input)
|
||||
if tool_name == "update_monster_hp":
|
||||
return await monster.update(session, game_id, tool_input)
|
||||
if tool_name == "update_world_state":
|
||||
return await world_state.update(session, game_id, tool_input)
|
||||
return {"error": f"Unknown tool {tool_name!r}"}
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.warning("Tool call %s failed: %s", tool_name, exc)
|
||||
@@ -85,6 +88,14 @@ async def run_dm_turn(
|
||||
f"Name: {game.name}\nBeschreibung: {game.description}"
|
||||
)
|
||||
|
||||
world_summary = await world_state.get_summary(session, game_id)
|
||||
if world_summary:
|
||||
system_prompt = (
|
||||
f"{system_prompt}\n\nBisheriger Weltzustand (von dir zuletzt über update_world_state "
|
||||
f"zusammengefasst — der Chatverlauf unten zeigt nur ein aktuelles Fenster, das hier "
|
||||
f"trägt alles Ältere):\n{world_summary}"
|
||||
)
|
||||
|
||||
if latest_player_message:
|
||||
rag_block = await build_rag_block(session, latest_player_message)
|
||||
if rag_block:
|
||||
|
||||
Reference in New Issue
Block a user