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:
Thorsten
2026-09-01 17:59:57 +02:00
parent 2b2af058b8
commit 81eaf667cd
7 changed files with 119 additions and 5 deletions
+47
View File
@@ -0,0 +1,47 @@
import uuid
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.world_state import WorldState
TOOL_SCHEMA = {
"name": "update_world_state",
"description": (
"Persist a compact, up-to-date recap of everything that matters long-term for this game: "
"key NPCs and their status, current location, open plot threads, important party/inventory "
"state. The raw chat history you see is only a recent window — this summary is what carries "
"everything older forward. Call it regularly (at least on every scene change or major "
"development), not just at the end. Send the full current picture each time, not a diff — "
"this replaces whatever was stored before."
),
"parameters": {
"type": "object",
"properties": {
"summary": {
"type": "string",
"description": (
"The complete current world-state recap, in the language you're playing in. "
"A few short paragraphs or a bullet list, not a full transcript."
),
},
},
"required": ["summary"],
},
}
async def update(session: AsyncSession, game_id: uuid.UUID, tool_input: dict) -> dict:
state = await session.get(WorldState, game_id)
if state is None:
state = WorldState(game_id=game_id, summary=tool_input["summary"])
session.add(state)
else:
state.summary = tool_input["summary"]
await session.commit()
return {"summary": state.summary}
async def get_summary(session: AsyncSession, game_id: uuid.UUID) -> str | None:
state = await session.get(WorldState, game_id)
return state.summary if state is not None and state.summary else None