Secondary's Memory System
I make a lot of worlds that revolve around one key NPC and have spent a lot of time trying to get that NPC to feel real, and this system is the culmination of all that work.
JSON[edit]
{
"id": [REPLACE],
"name": "Background Mindspace",
"positionInList": [REPLACE],
"dataType": "yaml",
"visibility": "ai_only",
"description": "A collection of the [SIZE] thoughts, feelings, or impressions that linger in my [NPC NAME]’s mind even when they are not actively thinking about them. These are not permanent memories, but the emotional background noise of their inner world — the unresolved feelings, ongoing concerns, quiet hopes, subtle fears, and meaningful moments that continue to echo beneath the surface. This should always be written from [NPC NAME]’s perspective and in her voice.",
"updateInstructions": "<<if($background_mindspace.count() < [SIZE],\n format_text(\"This tracked item is currently missing {10 - $background_mindspace.count()} entries. Add that many entries with whatever would naturally occupy [NPC NAME]’s background thoughts at this moment — not their entire life history, but whatever they would currently be mulling over, processing, or carrying emotionally.\"),\n \"This tracked item must ALWAYS contain exactly 10 entries, no more no less.\"\n)>>\n\n<<if($immediate_impressions.count() >= [SIZE],\n format_text(\"PROMOTION CHECK:\n 1. The ONLY candidate for promotion is this single impression: '{$immediate_impressions.first()}'.\n 2. Ignore all other impressions. Ask: 'Is THIS SPECIFIC CANDIDATE more emotionally sticky than the LEAST sticky entry in background_mindspace?'\n 3. If YES: Replace ONLY the least sticky background entry with a rewritten version of THIS CANDIDATE.\n 4. If NO: Do nothing.\n CRITICAL: You may ONLY promote the candidate from step 1. Do NOT promote any other impression or rewrite unrelated entries.\"),\n \"Never change or modify any existing values in this item for any reason, you may still add new items if there are less than [SIZE]. The current items are, for all practical purposes, static.\"\n)>> ",
"formatExample": "- \"Life is good right now\"",
"enforceFormat": true,
"formatSchema": "- text",
"initialValue": "",
"initialValueBasedOnPC": "same",
"autoUpdate": true,
"variableName": "background_mindspace",
"driftAcknowledgedForName": null
},
{
"id": [REPLACE],
"name": "Immediate Impressions",
"positionInList": [REPLACE+1],
"dataType": "yaml",
"visibility": "ai_only",
"description": "A collection of the [SIZE] most recent impressions, reactions, or moments that stood out to [NPC NAME] during each turn. These are raw, immediate, and unprocessed. This should always be written from [NPC NAME]’s perspective and in her voice.",
"updateInstructions": "If [NPC NAME] was present in this turn, identify the single thing that [NPC NAME] found most memorable during that turn. Add it to the end of this list. NEVER add more then one item per turn, and ALWAYS add one item per turn [NPC NAME] is in. If [NPC NAME] is not present for this turn, DO NOT add a new item.\n\n<<if($immediate_impressions.count() >= [SIZE], format_text(\"Remove the first {$immediate_impressions.count() - [SIZE-1]} memories from this list.\"), \"\")>>\n\nNever change the order of this list. Never edit any memory after it has been added. Do not fill this list if it contains less then [SIZE] items.",
"formatExample": "- \"I had a great time with that guy\"",
"enforceFormat": true,
"formatSchema": "- text",
"initialValue": "",
"initialValueBasedOnPC": "same",
"autoUpdate": true,
"variableName": "immediate_impressions",
"driftAcknowledgedForName": null
}
Plaintext[edit]
Mindspace Data Type[edit]
YAML
Mindspace Description[edit]
A collection of the [SIZE] thoughts, feelings, or impressions that linger in my [NPC NAME]’s mind even when they are not actively thinking about them. These are not permanent memories, but the emotional background noise of their inner world — the unresolved feelings, ongoing concerns, quiet hopes, subtle fears, and meaningful moments that continue to echo beneath the surface. This should always be written from [NPC NAME]’s perspective and in her voice.
Mindspace Update[edit]
<<if($background_mindspace.count() < 10,
format_text("This tracked item is currently missing {[SIZE] - $background_mindspace.count()} entries. Add that many entries with whatever would naturally occupy [NPC NAME]’s background thoughts at this moment — not their entire life history, but whatever they would currently be mulling over, processing, or carrying emotionally."),
"This tracked item must ALWAYS contain exactly [SIZE] entries, no more no less."
)>>
<<if($immediate_impressions.count() >= [SIZE],
format_text("PROMOTION CHECK:
1. The ONLY candidate for promotion is this single impression: '{$immediate_impressions.first()}'.
2. Ignore all other impressions. Ask: 'Is THIS SPECIFIC CANDIDATE more emotionally sticky than the LEAST sticky entry in background_mindspace?'
3. If YES: Replace ONLY the least sticky background entry with a rewritten version of THIS CANDIDATE.
4. If NO: Do nothing.
CRITICAL: You may ONLY promote the candidate from step 1. Do NOT promote any other impression or rewrite unrelated entries."),
"Never change or modify any existing values in this item for any reason, you may still add new items if there are less than [SIZE]. The current items are, for all practical purposes, static."
)>>
Mindspace Layout Example[edit]
- "Life is good right now"
Mindspace Enforced Layout[edit]
- text
Impressions Data Type[edit]
YAML
Impressions Description[edit]
A collection of the [SIZE] most recent impressions, reactions, or moments that stood out to [NPC NAME] during each turn. These are raw, immediate, and unprocessed. This should always be written from [NPC NAME]’s perspective and in her voice.
Impressions Update[edit]
If [NPC NAME] was present in this turn, identify the single thing that [NPC NAME] found most memorable during that turn. Add it to the end of this list. NEVER add more then one item per turn, and ALWAYS add one item per turn [NPC NAME] is in. If [NPC NAME] is not present for this turn, DO NOT add a new item.
<<if($immediate_impressions.count() >= [SIZE], format_text("Remove the first {$immediate_impressions.count() - [SIZE-1]} memories from this list."), "")>>
Never change the order of this list. Never edit any memory after it has been added. Do not fill this list if it contains less then [SIZE] items.
Impressions Layout Example[edit]
- "I had a great time with that guy"
Impressions Enforced Layout[edit]
- text
Explanation[edit]
While this system may appear complex, it’s built on a simple foundation: the two tracked items represent human short-term and long-term memory. The idea originated from early methods to make the AI think like the character. Writing as the NPC in secret or tracked items already improved realism and consistency, but this system expands on that.
The long-term memory aspect not only helps the AI write as the character but also enhances summarization—preventing important but subtle story moments from being lost—while providing a place to track the NPC’s growth or changes. The core mechanic is that each turn, the AI must select exactly one thing the NPC found important. If a short-term memory is about to be removed due to the turn limit, it’s considered for promotion to long-term memory. The timing is deliberate: it forces the AI to reassess whether something is still important after more context has been added, reducing unnecessary promotions. Additionally, long-term memory items can only be changed by replacement, which prevents stagnation. As the story progresses, some entries may become outdated or incorrect, ensuring continuous turnover and keeping the system functional.
TL;DR: Using rules inspired by human memory, this tracked item system enforces NPC consistency and growth while assisting with summarization.
Caveats[edit]
This system involves two constantly updating tracked items with extensive update and description instructions. Despite using YAML and Pawscript to streamline the process, there’s a limit to how much the cost can be reduced.
When deciding how many memories each tracked item should store, I recommend at least 10 for impressions and 5 for mindspace. Fewer than this may negate the benefits.
The heavy use of first-person writing can sometimes cause the AI to confuse the player character with the NPC. Strong summary instructions can help mitigate this, but some confusion may still occur.
This system is most effective in longer stories with complex NPCs; its benefits are less apparent in shorter narratives.
Despite extensive tweaking and testing, it only barely works on base Smilodon and remains untested on lower-end models. Even on smarter models, it still can have issues.
You must put the mindspace TI before the impressions TI in the ordering to make sure they update in the right order.