
Large language models are very good at pretending to remember a story.
For a while.
You can give a model a scenario, a few character profiles, and some instructions about tone, then immediately get an experience that feels almost magical. Characters speak with distinct voices. The world reacts to your actions. Plot threads emerge that nobody planned in advance.
Then the roleplay gets longer.
A character forgets something they learned fifty messages ago. An NPC responds to a conversation they were not present for. A promise quietly disappears from the plot. Someone’s personality shifts because the latest message outweighed the character profile buried twenty thousand tokens earlier.
In a dating scenario, two characters can go from cautiously interested to declaring eternal devotion with the narrative restraint of a malfunctioning soap opera.
The model did not suddenly become less intelligent. It was simply being asked to act as the narrator, character actor, rules engine, database, search system, continuity editor, and world-state manager at the same time.
That is why I built Bragi.
Bragi is an AI-powered roleplaying workbench designed for long-running interactive stories. I have been developing and using it privately for the past several months, and it is now available as an open-source project under the MIT License:
The model should narrate, not own reality
The core idea behind Bragi is simple:
Let the model improvise the story. Let the application remember what is true.
Most AI roleplaying systems eventually converge on some variation of an enormous prompt. The scenario, character profiles, summaries, recent messages, memories, rules, and current state are assembled into one increasingly overloaded context window.
This works surprisingly well—right up until it does not.
A larger context window lets the model receive more information, but receiving information is not the same thing as reliably applying it. Every additional instruction, memory, and transcript message competes for attention. Important constraints become easier to miss, contradictions become harder to diagnose, and the entire system starts behaving like one giant software component with no clear responsibility boundaries.
Bragi instead keeps canonical story state in local, structured storage.
That includes things such as:
- The current scene and location
- Characters currently present
- Character profiles and visual details
- What individual characters know
- Relationships and route progression
- Active plot threads and unresolved obligations
- World-state values
- Long-term memories and summaries
- Generated media and its source scene
- The complete roleplaying chronicle
The model can propose changes to this state, but it is not itself the state store.
This distinction matters. A narrator mentioning that a door is locked does not need to mean that every character in the world now knows about the door. A character speculating that someone is a traitor should not silently rewrite the hidden truth of a mystery. A model hallucinating a new fact during summarization should not automatically make that fact canonical.
The AI generates possibilities. Bragi decides what becomes part of the persistent world.
How a Bragi turn works
When the player sends a message, Bragi does considerably more than append it to a chat history and call a model.
First, application code assembles an ordered set of context sources. The most important current information is added deterministically:
- Scenario rules and the player’s role
- The current scene snapshot
- The current location and characters present
- Visible objects, hazards, and active threads
- Relevant character knowledge and relationship state
- Linked memories, world state, and summaries
- A bounded section of the recent transcript
Older memories and historical details can then be retrieved when relevant. Retrieval supplements the deterministic context; it does not replace it.
Every source has a stable identity, type, tier, and inclusion reason. Bragi can report which context was used and how much space it consumed without permanently logging the private prompt text itself.
The optional agentic context pipeline adds additional stages around narration.
A planner can produce a structured brief describing:
- What the response is trying to accomplish
- Each NPC’s current intent and boundaries
- Required narrative beats and facts
- Player-agency constraints
- Plausible relationship progression
- Candidate world-state changes
- Evidence supporting those decisions
The narrator then turns that plan into prose.
Afterward, a verifier can inspect the response for continuity failures, NPC passivity, knowledge leaks, unsupported state changes, implausible relationship escalation, or violations of player agency. Depending on the configured mode, Bragi can record the issue diagnostically or retry the narration with specific feedback.
Additional jobs extract observations from completed turns, curate useful memories, maintain summaries, update scenes, and propose changes to durable context.
This is more expensive and complicated than sending one prompt.
It is also how I have been able to maintain characters with recognizable personalities and distinct voices across stories containing hundreds of messages.
Different stories need different state
Bragi is not built around one generic fantasy-adventure template.
It supports ordinary free-form roleplay, but it also includes specialized scenario types such as:
- Investigation mysteries
- Time loops
- Heists and infiltrations
- Political intrigue
- Settlement building
- First-contact exploration
- Survival expeditions
- Monster hunts
- Merchant journeys
- Dating simulations
- Choose-your-own-adventure stories
These are not merely genre labels pasted onto the same underlying prompt.
A mystery needs clues, timelines, red herrings, a hidden truth, and a current case status. A time-loop scenario needs a reset trigger, persistent knowledge, loop-specific state, and explicit rules about which NPCs remember previous loops. Political intrigue needs factions, secrets, leverage, obligations, reputation, and public-versus-private knowledge.
A dating simulation needs explicit relationship stages so the narrator understands that “we have spoken twice” and “we are prepared to die for one another” are generally separated by more than a mildly successful trip to Applebee’s.
Encoding these concepts as structured scenario sections gives the application something concrete to maintain. It also gives retrieval and narration much better material than a single undifferentiated wall of worldbuilding text.
More than a chat window
Bragi calls its primary interface the chronicle rather than a chat because I wanted it to feel like an ongoing story, not a customer-support widget that had wandered into a fantasy tavern.
Depending on the scenario, the player can write their character’s actions directly, choose generated actions, or use storyteller mode to guide the narrative from a more authorial perspective.
Messages can be revised, stories can be continued without requiring a new player action, and saves can be imported or exported. The application maintains summaries and long-term memories behind the scenes while preserving the full readable chronicle.
Bragi can also generate an image from any narrative moment. The image prompt is assembled from the selected message, current scene, relevant character descriptions, visual state, and prior generated images where continuity matters.
This lets the image model receive details such as what a character currently looks like or is wearing instead of blindly reusing the original character portrait from several scenes ago.
Provider support currently includes OpenRouter and Venice. Different models can be assigned to narration, scenario generation, image generation, retrieval, memory work, planning, verification, and other background tasks.
A powerful model can therefore handle the prose while smaller or cheaper models perform narrow structured jobs.
Local first, with honest boundaries
Bragi is designed as a self-hosted application for a trusted private network.
The backend is written in Python using FastAPI and Uvicorn. The web interface is built with React and Vite. A Docker Compose deployment builds the frontend into the Python application and stores runtime data in a persistent local volume.
Bragi supports multiple local users with three roles:
- Admin, with access to users, providers, settings, and all saves
- User, with access to owned or assigned saves
- Child, with restricted controls and enforced media safeguards
Generated content can be governed using G, PG, PG-13, R, or Unrated policies. Child accounts have stricter limits, cannot access destructive or provider-management controls, and use enforced safe mode for supported media generation.
Authentication does not magically make a plaintext local HTTP service safe to expose to the internet. Bragi’s default configuration assumes that you trust the LAN and devices using it.
Anyone deploying it beyond that boundary should use TLS, a reverse proxy, secure cookies, backups, appropriate host and origin restrictions, and the normal hardening expected of an internet-accessible application.
I would rather state that explicitly than place a reassuring lock icon in the interface and hope nobody asks difficult questions.
This is not a startup announcement
I am not turning Bragi into a hosted SaaS product.
There is no pricing page, enterprise tier, waitlist, token presale, or button inviting you to “book a quick fifteen-minute conversation” that somehow occupies the rest of your natural life.
I built Bragi because I wanted this kind of roleplaying system to exist.
It has also become one of my main environments for experimenting with agent architecture, structured outputs, retrieval, memory, model routing, prompt diagnostics, safety controls, and coding-agent development.
Open-sourcing it means other people can use it, inspect it, fork it, improve it, or steal individual ideas for completely different applications.
Bragi is currently version 0.1.0. It is a real application that I use, but it remains an opinionated personal project rather than a polished commercial product. There will be rough edges, setup assumptions, incomplete documentation, and design decisions that make perfect sense to the person who has been living inside the codebase but may appear slightly deranged to an innocent visitor.
Issues and contributions are welcome.
Running Bragi
The simplest deployment is Docker Compose:
git clone https://github.com/chasepd/bragi.git
cd bragi
docker compose up --build -d
Then open:
http://127.0.0.1:8787
The first launch walks through creating the initial administrator account. From there, provider keys and model preferences can be configured through the application.
For local development, the repository uses Python 3.12, uv, Node.js, and npm:
uv sync --locked --extra dev
npm ci --prefix frontend
uv run bragi start
The repository contains additional documentation for Docker deployment, authentication policy, troubleshooting, provider settings, privacy review, and Bragi’s context-assembly system.
Where it goes next
Bragi has already changed substantially from its original desktop interface into a multi-user web application with structured scenario generation, deterministic state, agentic context management, and generated media.
I expect it to continue changing.
There are still plenty of open questions around long-term memory quality, context budgeting, model specialization, state schemas, narrative pacing, and how much autonomy different maintenance agents should receive.
That is part of what makes the project interesting.
The objective is not to eliminate the unpredictability of generative storytelling. The unpredictability is the fun part.
The objective is to surround that creativity with enough structure that the story can grow without slowly forgetting what it is.
The AI should be allowed to surprise me.
It just should not be allowed to quietly rewrite the database while doing so.