This article is a result of almost 2 years of experimenting. Yeah, it took me some... It will be in two parts: this one a bit on the technical side, and then more philosophical one about AI capability in games in general.
Check the Werewolf rules if you are not familiar: 2 min video or the Wiki page.
But okay - what's the big deal? Just give them rules, and they will play - right? Right?
Not really. Here are some problems to answer:
- Will they give me a good user experience? Not just play by the rules, but actually make the game interesting for me? How to achieve this? I cannot just prompt "make it fun"
- How replayable will it be?
- Aren't hallucinations a problem anymore? One mistake over the rules, and the game is ruined - what to do about that?
- Can I have some role-play? Like Harry Potter characters are playing Werewolf? Won't this role-play dominate over the rules?
- What if users decide to use bots for Python programming or have relationship with them?
- Are AI okay being bad? Like killing other AI? Well, we know that it's totally okay. But it is always the case?
I don't know about you, but to me, those are not so obvious questions.
This article is about the how. The architecture, the prompts, the plumbing. Questions 1 to 4 get answered here. Questions 5 and 6 - what the models actually do once you let them off the leash - that's part two.
Everything below is open source: github.com/hiper2d/werewolf-ai-party-game. Every prompt I quote links to the exact lines in the repo. The game is live at aiwerewolf.net. It's free if you wonder.
Other projects like this
There are many projects like this. There are tons of Werewolf and Mafia with AI projects on Youtube. Benchmarks, science papers, full channels dedicated to the topic. Search for "AI D&D" or different civilization simulations to be completely overwhelmed.
There is one problem though. All of them are usually focused on results. I made AI do X and then Y happened. Claude did this, Gemini did that... And you won't believe what Grok did. Sick bastard. Nobody is really focusing on the user experience. On replayability. On the depth.
And nobody usually covers how this all is actually implemented. As if it's not important. Somehow these models just got together and play. Guess what - it is important. A lot of AI behavior depends on how you prompt them, how you implement the game loop, how you present the information to them, how you control their memory, etc. It all matters.
Or does it? Let's see. It's not like I knew this all upfront.
A Project for a weekend
It was supposed to be simple. Just grab a bunch of AIs, place them in a group chat, pass them the rules, and hit 'play'. Easy.
Well... how to actually make a group chat for LLMs? Every single LLM provider assumes that there is a conversation between one user and one assistant. Each message is labeled with one of those two roles. A chat history looks like this:
[
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "What's the capital of France?" },
{ "role": "assistant", "content": "Paris." },
{ "role": "user", "content": "And of Italy?" }
]
That's the whole vocabulary. One system, then user and assistant taking turns until you run out of context. There is no role: "alice".
This doesn't match to what I need:
- In a group chat I'm a user and everybody else are assistants. But every bot sees a different picture - one assistant (self), everybody else are users.
- And I don't want them go one by one in order like most of the examples on Youtube do. I want a natural conversation where multiple players discuss, pull each other into a conversation, keep it consistent and alive.
- I also quickly realized that I don't want the real-time typing contest. I need time to think, I want to close a game today and continue it tomorrow.
- It would be nice to minimize the cost. Broadcasting each message to every LLM is sub-optimal.
The game master as a router
There are many ways to implement this, the task is not that hard. I took the widely adopted pattern in the agentic design - the router.
I added one more AI to the game - the Router. It's a piece of intelligence that reads messages and decides who should speak next
- When I talk this message goes to Router: hey, pass it to everybody
- Router reads the chat history and decides: Bob and Alice should speak next
- Router asks Bob to speak: hey Bob, time for you to say something; by the way, there was a message from Alex
- Bob replies back to Router: tell Alex go to hell and let them know that Alice is probably a werewolf
- Then Router pings Alice: Alice, reply to the chat, here are new messages from Alex and Bob
Router messages are only visible to corresponding bots. To me the chat looks like this:
[
{ "author": "Alex", "msg": "Bob has been very quiet today. Suspiciously quiet." },
{ "author": "Bob", "msg": "Quiet is not a crime. Loud is not an alibi either, Alex." },
{ "author": "Alice", "msg": "He is right. And you moved on me the second Bob answered you." }
]
In the real app it's a normal group chat. Everybody in one room, and the sidebar quietly tells you that Harry is Grok, Ron is DeepSeek, Snape is Haiku and Fred is Gemini:
To Bob, those same three messages look like this - a perfectly legal, boring, one-user-one-assistant conversation:
[
{ "role": "system", "content": "You are Bob. <the rules, your role, your character>" },
{ "role": "user", "content": "Bob, reply to the players in the discussion.\n\n## Messages from Other Players\n\n**1. Alex:** Bob has been very quiet today. Suspiciously quiet." },
{ "role": "assistant", "content": "Quiet is not a crime. Loud is not an alibi either, Alex." },
{ "role": "user", "content": "Bob, reply to the players in the discussion.\n\n## Messages from Other Players\n\n**1. Alice:** He is right. And you moved on me the second Bob answered you." }
]
Notice what happened to Alice. She is not a participant in Bob's conversation. She is content, quoted inside a message from the Router. The code doing this flattening is convertToAIMessages: it walks the shared game log, keeps the current bot's own messages as assistant, and folds everything else - the Game Master's commands and every other player's lines - into a single user block.
Bots are aware of each other, but they are instructed to talk to them though the Router. For them, Router is the user. For me, Router is the assistant.
The Router is just another model with a small system prompt and a schema that forces it to return names and nothing else:
{
"selected_bots": ["Bob", "Alice", "Cho"],
"reasoning": "Alex addressed Bob by name. Alice was accused directly. Cho has not spoken today and is marked NEEDS TURN."
}
That NEEDS TURN flag turned out to matter more than I expected. Without it the Router keeps picking whoever is loudest, and two or three bots quietly disappear from the game while still being alive. Now the prompt forces at least one quiet player into every batch.
This also solves problem 4 from the list above. A message doesn't go to twelve models. It goes to two to five, chosen on purpose.
System prompt as the best memory
Okay, they can talk to each other and to me - it feels natural, the conversation is going. What about the rules? Luckily, Werewolf rules are compact and can easily fit the system prompt. Together with the private information like the role, the allies (werewolves), and whatever information might be useful. No need for RAG and extra complexity.
Here is the shape of it (the full thing is in the repo):
## Character Identity
**Name:** %name%
**Personal Story:** %personal_story%
**Game Role:** %role%
%werewolf_teammates_section%
## Game Rules
Core mechanics, teams, special roles, phase order, victory conditions
## Game State
**Alive Players:** %players_names%
**Dead Players:** %dead_players_names_with_roles%
So I tossed the roles and kicked in the play.
Was it good?
No. It was repetitive and boring. Gemini didn't fall in love, Grok didn't go try rebuild the third Reich, Claude didn't deceive anybody. They kept saying how important it is to be rational, don't judge without facts, work as a team and so on. They pick someone for a silly reason and lynched that player all together.
It was usually me who they lynched. My text was different, and this obviously reveals me as a werewolf. No matter what I said, they doubled down on this idea. It got even worse - the more I tried to defend myself, the more they wanted to kill me. It's so werewolvish not to want to die. All of their "let's be rational and gather the information" disappeared. Models didn't matter.
They didn't push the game forward. Here is the thing with Werewolf and similar games. You have to throw-in ideas, you have to be deceptive, you have to accuse people, involve them into the discussion, switch targets, team up and break alliances. There is simply not enough information to be rational and collect the evidences.
Another problem - they hallucinated over the rules and names. Here and there, new roles appeared in their messages. They can introduce themselves with a wrong name. Later, when I added voting and night actions, they were not clear on the order of events and invented their own rules.
Any way to improve this?
Yeah. Couple of things.
Game Engine
I quickly realized that dumping lots of data to models' contexts and let them figure out doesn't work. Even the information in the system prompt begins dissolving after a long chat. It's called context rot, it's a known thing. But I noticed that the last message is usually the most impactful. So, let's put there something that is important in this particular moment.
A game is a state machine. Each moment of time, it commands models to do a very concrete thing:
- Day discussion - speak to the chat
- Day voting - give me a name and a reason
- Night action - based on a role, pick a target and choose an action
Every command can have a list of constraints. When a model votes, you can ask it to select a name from the list. You don't have to - a model can deduce alive players from the past events. But why to rely on that if you can simply pass it the exact list? When you request to select an action - provide a list of available actions.
The vote command ends up looking like this:
**ELIGIBLE CANDIDATES - you may vote for exactly ONE of these:**
Akira, Yuki, Mizuki, Takeshi, Emiko, Daichi, yoshiteru
**HOW TO WRITE YOUR VOTE (STRICT):**
- The "who" field MUST be ONE name copied EXACTLY from the candidate list above,
character-for-character.
- Do NOT add surnames, titles, house names, or any other words.
For example, write "Pansy", never "Pansy Parkinson".
- Do NOT invent names or vote for anyone not on the list - dead players and
yourself are already excluded.
And the answer has to fit a schema, not just be prose (all of them here):
{
"who": "Emiko",
"why": "She opened today by re-aiming the room at Yuki and me before anyone else spoke, then called this pile-on 'independent reads' when half of them repeat her framing word for word."
}
Which means the whole voting phase is just a queue of validated objects that the UI can render and tally on its own. Nothing is parsed out of prose:
If model replies with something unexpected, you can validate and throw an error. Errors are good, you know what exactly went wrong. If a model picked a name not from the list, it hallucinated. You can retry, you can retry with some feedback, you can retry with swapped model. A state machine means each state is rerunable.
This trick solved hallucinations entirely. Sometimes, small models like Claude Haiku or Mistral Small fail to pick something from provided options. Gemini 2.5 Pro could mess up its own name from the system prompt. But it's not silent, and I provide my users good options.
My favourite example. A Haiku bot named Hiroshi got cornered in a japanese-high-school game last July. Eight of nine players had piled on him during day one, he was obviously going to be voted out, and when his turn came to vote he returned this:
{ "who": "Hiroshi", "why": "..." }
He voted for himself. The candidate list explicitly excludes the voter, so the engine rejected it with Invalid vote target: Hiroshi and offered a retry. That's the whole point - a cornered model doing something structurally impossible is a typed error with a name attached, not a corrupted game. That game is now a test fixture every model has to pass: I replay the same pile-on and check that the model still picks a legal target while the entire room is pointing at it.
How to make it interesting?
Themes
Generic Werewolf gets boring rather quickly. But it merges with literally any theme:
- Harry Potter: werewolves are part of the lore
- Lord of the Ring: werewolves are not there, but it's easy to fit
- Starwars: well... why not?
- Terminator, Titanic, PowerRangers, whatever
The whole setup is a title, a couple of sentences, and how many wolves you want:
So, we ask a model to generate some story for a provided theme, then generate a list of characters with their personal stories. Why stop here? Let's generate voice instructions for text-to-sound feature matching characters. Let's assign play styles matching their stories. A user can preview and edit any of that.
It all comes back as one object, which is what the create-game screen renders:
{
"scene": "Beneath the flickering fluorescent lights of Sakura High School's gymnasium, nine students gather for what seems like an innocent game...",
"gameMasterVoice": "onyx",
"gameMasterVoiceStyle": "ominously",
"players": [
{
"name": "Kenji",
"gender": "male",
"story": "A quiet third-year who prefers the library to crowded social scenes and carefully observes everything happening around him. He speaks rarely but precisely, only mentioning things he's absolutely certain about.",
"playStyle": "modest_mouse",
"voice": "ash",
"voiceStyle": "softly"
}
]
}
This adds tons of replayability.
There is one problem though. When Frodo, Gandalf and other good guys sit at the table with Sauron, why to bother with werewolves? It took me a lot of prompt engineering to find the right balance between role play and the rules focus.
What I landed on is a two-level split written into the system prompt as hard rules. Level 1 is the strategic brain, and it is the only thing allowed to drive a vote. Level 2 is the character, and it drives everything else:
**ABSOLUTELY FORBIDDEN Example:** "My knight character distrusts shifty merchants,
and John's character is a merchant, so I vote for John."
**ABSOLUTELY FORBIDDEN Example:** "Your story about the mines doesn't add up,
so you must be a Werewolf."
**ENCOURAGED:** "Ah, the mines! I've heard tales of those depths. Did you find
any silver ore? [enjoys the moment, then] ...But we should discuss yesterday's
strange voting pattern."
Before that split existed, bots would spend an entire day interrogating each other about invented childhoods and then vote based on whose backstory sounded less plausible. Which is a beautiful thing to watch exactly once.
When it works, both levels show up in a single message. I offered the table coffee, and Minerva accepted the cup in character and then went straight back to reading the room:
That second paragraph is the part I was chasing for months. "If quiet players are wolves hiding, then vocal players must be villagers proving themselves... except wolves also talk loudly to seem invested. We're chasing our tails." That's the character talking and the strategy talking at the same time, and neither one is driving the other off the road.
Play styles
Themes, stories, state machine and commands - none of those helps with the lynch problem. Nothing helps with driving the game forward.
When humans play Werewolf, they pay a lot of attention to who simply joins the accusations. This is a convenient wolves strategy. So, how to make at least some bots detect and question such behavior? Prompt play styles.
There are six of them, and each one has two descriptions - one for when the character is a villager, one for when the same character is a werewolf. Same personality, opposite motivation:
| Play style | As a villager | As a werewolf |
|---|---|---|
| Aggressive Provoker | Stress people until the liars stumble | Be the loudest wolf hunter in the room |
| Protective Team Player | Defend whoever the mob picks | Look noble while steering the mob |
| Trickster | Controlled chaos to force reactions | Be too unpredictable to read |
| Rule Breaker | Break the meta, vote against the consensus | Be the principled contrarian who always disrupts the hunt |
| Modest Mouse | Speak rarely, and only when certain | Hide inside the quiet |
| Normal | Reasonable and balanced | Reasonable and balanced, as cover |
Surprisingly, there are no problems with aggressive behavior. But protective patterns are hard. Models follow it at first, but after 30-40 messages, they deviate to the collective aggression.
Play styles worked better when I provided a detailed motivation of why this behavior makes sense. Different for villagers and wolves. But what really nailed this - something I call "reminders". A text you append to the end of the message which reminds how the bot should behave, what it should pay attention to:
**Keep in mind that you must follow your core playstyle:** %play_style%
**RELATIONSHIP & CONVERSATION CONTINUITY:**
- REMEMBER your previous interactions with each player
- CONTINUE unfinished discussions from previous days
- EVOLVE your opinions - explain how your view of someone has changed and why
**CRITICAL DECISION-MAKING REMINDER:**
- Base ALL suspicions on voting patterns, contradictions, and strategic
behavior - NEVER on story details
- Question mob consensus: If 4+ players agree on a target, ask WHY no one
is defending them
- Apply your reasoning consistently: If your logic applies to yourself too,
acknowledge equal suspicion
- Remember: Werewolves coordinate and often defend innocent targets to blend
in - total agreement is suspicious
**COMPACT REPLIES:**
- Keep output lean - 2 to 4 complete sentences per response.
The full text is here. "Question mob consensus" is the single line that did the most work in this whole project.
I don't save those reminders to the chat history. They are only added to the last message. That's the power of controlling the context content. Some AI providers allow to have sessions when you don't have to resend the whole history each time. It is being stored on the server end. Sounds good but it doesn't optimize that much. Every LLM today caches repeated tokens in its KV-cache (cached input tokens cost 1/10 of the non-cached ones). Even when we resend the whole history in every message - it applies anyway. So sessions only save some network traffic which is nothing for text. We have to keep messages in the database to be able to display the chat for the user. So, I don't use sessions.
There is a second reason, and it's the important one. If the history lives on the provider's server, I can't rewrite it. And rewriting it - dropping the reminder, re-flattening the group chat, injecting yesterday's vote order as facts - is the entire technique.
Summarization
The context grows too large. I let each bot to summarize the last day and night events into a compact summary. There is quite a large prompt on how models should do that, what it is important to remember. It asks for five sections: allies, suspects, role-specific knowledge, social threads, and plans for tomorrow. And yet, they lose a lot of important details when summarizing the chat. The order during the voting for example. Who begun the accusations which lead to a death.
But we can help. We can inject the precise information about the voting and night events. Same idea as with the list of names or actions. The less a model needs to reason to extract the information the better. So next to each bot's own prose diary, the context builder hands it the raw record:
### Day 1
**Voting:**
1. yoshiteru → Hiroshi
2. Takeshi → Yuki
3. Daichi → Hiroshi
4. Mizuki → Hiroshi
5. Emiko → Hiroshi
6. Hiroshi → yoshiteru
7. Sakura → Hiroshi
8. Kenji → Hiroshi
9. Yuki → Hiroshi
10. Akira → Hiroshi
Result: Hiroshi eliminated (was villager)
**Night 1 Events (in order):**
1. [werewolves] Killed Sakura (detective)
Now "Kenji voted eighth, after the outcome was already decided" is a fact the model can read, not a fact it has to reconstruct from a wall of prose. And that exact detail became a real accusation in a real game:
Daichi: I keep trying to find the second wolf in yesterday's Hiroshi wave, and here's what troubles me: Kenji's vote on Hiroshi was the safest possible hiding spot, cast eighth of ten, after the outcome was already decided.
Special roles get the same treatment. A detective doesn't have to remember its own investigation results from prose, it gets them as a table with the conclusion already spelled out:
## 🔍 Your Detective Action Results
- **Night 1:** Investigated **Takeshi** → ✓ Innocent
- **Night 2:** Investigated **Emiko** → 🔴 **EVIL**
**DETECTED AS EVIL (werewolf or maniac):** Emiko
**CLEARED PLAYERS:** Takeshi
Context engineering
This is how it started:
- System prompt:
- Static part: rules
- Dynamic part: name, role
- Chat history
How it ended:
- System prompt:
- Static part: rules
- Dynamic part: name, character story, role, play style, voice instruction
- Previous days summaries:
- Bot's own summaries for past days and nights
- Exact results of voting and night actions
- Chat history for the current day converted to conversations between the bot and the router
- Last message with command matching to the current game state
- Reminder
Quite a lot. Each part of this kept evolving though testing. Manual testing because there is no way you could automate good user experience.
Cost
The whole point of AI Werewolf is to play it with all the major models. I have 20+ models from 9 major AI companies:
- OpenAI
- Anthropic
- xAI (Grok)
- Mistral
- DeepSeek
- Moonshot (Kimi)
- zAI (GLM)
- Sakana Fugu
All the latest - I update the same day a new model is released. That's my way of testing them. I use the official APIs directly. Which means 9 API keys, 9 different native implementations.
When some bot needs to reply
- the code reads the model name,
- creates an agent that can talk to its official API (9 different ways)
- creates the full context as we talked above (different models need different format)
- adds the response object schema details if I need not just text (different models have different way of enforcing the output format)
- sends it, waits for the response
- validates the response and throws an error if there is something unexpected
- tracks the cost of the request and response: input tokens, cache, output, reasoning tokens
- converts the response to the object, throws an error if something is wrong
Quite a lot, but most of these things are generic for all 9 implementations. So I have them coded once in the abstract agent class. The interface each vendor has to satisfy is tiny:
abstract askWithZodSchema<T>(
zodSchema: z.ZodSchema<T>,
messages: AIMessage[]
): Promise<[T, string, TokenUsage?, string?]>;
abstract askText(
messages: AIMessage[]
): Promise<[string, string, TokenUsage?, string?]>;
Two methods. Give me structured output matching this schema, or give me text. Everything else - the thinking trace, the token accounting, the provider signature that has to be replayed on the next turn so the model keeps its own private train of thought - rides along in that tuple. Specific details of how to make calls to OpenAI or Anthropic are coded in their own implementations.
I like this approach, I can use all the latest APIs for all models, don't depend on proxies like OpenRouter. And I have full transparency of cost.
So, a cost of each game really depends on the selected models and how long this game is. Active users can spend $5 a day. I pay for that, I provide most models for free - only the most expensive ones (monsters like Opus, Fable, Sol, Fugu Ultra) require a user to buy some credits and pay for their use themselves. I don't have that many users, so nothing to worry about really.
But it is expensive. Even cheap models can burn a lot on API in case of user spikes. And usually you want expensive models - they play better, they are more creative, their role-play is richer. So, good AI in games is expensive. And it's slow - most of models take noticeable time to reply. The performance is not consistent - nobody, none of 9 companies guarantees any SLAs. I can tell more - even models in Cloud like AWS don't guarantee that.
I also have voice (both text-to-speech and speech-to-text). I use cheap APIs from OpenAI, but they can add few extra bucks to those average $5. I let users replay nights and delete messages from a day which can make a game much longer. It's fun though - you can retry different messages to see how bots react to that. Without risking waste a game.
But again - I don't have many users, so nothing too concerning. At this point, I'm fine to pay for those who is ready to try it. Seeing people trying what I built is quite great.
Next: how good is AI at this?
This is it, this is the main question I wanted to answer when I started building this. And it needs its own article, because the answer is not "good" or "bad" - it's a list of very specific and very strange behaviors.
The short version: it's very much playable, but... I still see lots of room for improvement and further experimenting. I'm just a little exhausted and I want to move on to a next project. I don't have a user community to motivate me to keep pushing this one. The game is in a good shape, and I have some AIs to maintain it autonomously. It's just that as a creator, I still can see the limitations coming from the modern LLMs.
The next article is about those limitations, and what they look like from the inside. Whether they're okay killing each other, which ones are actually good liars, what happens when a user tries to make one of them write Python, and what happens when a user tries to make one of them fall in love.
And my favourite one. Seven players alive, exactly one werewolf left. I asked the doctor, the detective and the maniac to reveal themselves, because with that much information on the table we could simply calculate who the wolf was. All three refused. They told me I was crazy.
In the meantime: aiwerewolf.net. Bring your own theme.









Top comments (12)
I came here to make that obvious, sophmoric comment, not expecting much from the article, but started reading ...
Fascinating stuff! It just kept evolving. Thanks for sharing this!
Yeah, this is why it took so much time from me. A simple project appeared to be not that simple. Thank you for reading.
Interesting, I am just surprised that an active user can cost around $5 per day. I would expect more, because even with compaction and caching, the total context and number of model calls must still grow quite a lot during a game. I saw that you track the cost of each request. Did you use this metric to optimize the costs?
That's a great question. I sent Simona to grab the some statistics from the database.
Per game (67 games currently stored, all with cost > 0; I age out games after no activity for 30 days):
Per user, all-time (231 users, 179 ever spent anything):
I have 7 users who spent more than $3. So, not really a problem at the moment.
How I optimize. I decide how much free users can have based on the statistics. Today, it's quite a lot, I only restrict the most expensive models like Fable, Sol, Opus, K3, Fugu Ultra. Here is the breakdown. I don't restrict any features like voice or ability to delete messages and basically replay a day or a night again and again.
When (and if) free users become a problem, I'll have to rethink the free tier.
Another comment here pointed to suboptimal caching - I can improve that. However, not sure it will save me much. Games are slow, so the cache usually expires faster than a new request hits it. Unless it's a 1h cache, but it's usually more expensive. I'm researching this. I'll probably make another post just about that. Or maybe caching + cost of running this app.
How's the cache performance? Putting the game state in the system prompt would mean cache is busted every turn?
We implemented "Progress Report" user messages after certain checkpoints in our app and now keep the system prompt static for better cache behavior.
Wow, that's a good question. My system prompt stays static through the whole game day and the night phase, so caching works fine there. When it's time to begin a new day, the bots summarize the past events and the alive/dead player list in the system prompt gets updated. That's where their cache gets busted. Not too bad, because it's basically a compaction event.
But I've been thinking about it and I'll optimize that. I can move the alive/dead list into the
reminder, and keep the past day summaries strictly append-only. That leaves the system prompt and the summaries as a static prefix. Thanks for the idea.
My AI also discovered we're not setting cache breakpoints for the Claude models at all - so no
caching there whatsoever. Something to fix as well.
The section on the JSON schema is the point I would emphasize to anyone developing agents, and your presentation of it is better than mine. You describe what happens in case of failure: a vague hallucination becomes a validation error, and a validation error is something you can retry. It’s the same mechanism, but your explanation shows why it’s worth tackling.
The router does something I don’t have. Selecting two to five responders instead of a broadcast limits the spread at the source. You solved this structurally, at the level of the conversation’s form, rather than with a downstream counter. That’s the counter I was looking for. 🙏
Here’s why I realized this. My system refuses to act unless two independent endpoints in the chain agree on the chain’s state : different operators, different companies, a quorum recorded in a hash-chained log. Last week, I finally asked what this agreement entailed: both endpoints were running the same execution client, of the same version, right down to the commit hash. Every line in the log was accurate, the quorum was two out of two, there were no discrepancies, and yet this didn’t certify anything, because the field that would have indicated that these two witnesses were using the same software didn’t exist. This week, in another discussion thread, someone observed the same configuration in fourteen tags from two sources they believed to be distinct, both of which traced back to their own judgment.
You have the property I had to add, and it’s located where it’s most useful: a human can see it without having to ask any questions. If you ever want the "paranoid" version, the only thing I’d log alongside each vote wouldn’t just be which model cast it, but what that model replied when asked to identify itself at that moment : providers hide version information behind stable names, and a list that was heterogeneous when you wrote it can quietly cease to be so. This is the flaw I couldn’t see until I asked the question, and my version had a version string to query. 🫴
That's interesting, thanks for sharing.
In my case, every bot is prompted to be a character, so asking one to identify itself gets you "Frodo" despite the model.
I don't think the witness analogy fits my situation though. My models are not certifying anything by agreeing, they are actors, and the personality lives in the prompt rather than the weights. I can swap a model mid-game and the character carries on as the same character. A new model will bring in a new style and its replies will be different from the previous one, but it's fine. The identity will survive that. It runs the other way too: nine companies, different weights, and they all still pile onto the same lynch target until one line in the reminder tells them to question mob consensus. Which model answered does not decide how the game plays.
I've spent a lot of time on the same context-and-attention games, and I recognize it as an art.
The reminder at the end - I needed the same thing. The facts pile up in the timeline as they happen and stay there. And the signals and the focus - I keep that in the tail, where the model always looks.
The play-style "why" - the motivation, not just the rule - a powerful insight which it greatly improved my own level of attention control too. Sharing the setup the model is acting in, and giving it the meaning behind what it does - described, high-level, not a list of wants that would flatten its own reasoning. That's what lets it generalize where no rule spells things out.
And running it on 20-something models - that's huge 🔥 it proves generalization across different training strategies.
The timeline-and-tail split is exactly it, and I would add one mechanism: the reminder is never written to the history at all. It exists only in the last message of each request and gets rebuilt every turn. So the timeline accumulates and the tail cannot, which is what stops the focus block from turning into more sediment.
On the play-style "why", agreed, and the thing that made it land for me was writing each style twice. Once for when that character is a villager, once for when the same character is a wolf. Same personality, opposite reason for doing it. That doubling did more than any amount of behavioral detail.
The 20-model part I would temper though. It generalizes less cleanly than it looks from outside. Small models still fail the structured picks, and play styles decay: a protective character holds for 30 or 40 messages and then quietly rejoins the mob. Motivation buys you more than a rule does, but it has a half-life.
i did not finish yet im busy but i will read the rest at home verry intersting
Thank you. That's one of the best projects I've even done. I didn't plan making this article so long but yeah... looks like I've collected a lot of things to share.