Skip to content

Diary

The single most visible output of the simulation. Every NPC writes one entry per day.

Why Diaries?

The diary is the player's window into NPC inner life. It's:

  • 📖 The primary content the player reads
  • 🪞 The most natural way to express emotion and reflection
  • 🎭 The richest place to observe personality and memory in action

If the diary is good, the simulation is good. If the diary is bad, no amount of underlying AI sophistication matters.


When is it written?

Every day, at 22:00 in-game time.

This is after dinner but before sleep. The NPC is alone at home, winding down. It's a natural moment for reflection.


What Goes Into a Diary Entry

Inputs

The Prompt Structure

ts
const prompt = `
You are ${npc.name}, a ${npc.age}-year-old ${npc.profession}.
Your personality: ${formatPersonality(npc.personality)}.
Your home: ${npc.home}.
Your family: ${formatFamily(npc.family)}.

Today is Day ${worldTime.day} (${worldTime.season}).
The weather today was: ${todayWeather}.
Your mood today was mostly: ${avgMood}.

Today's events (compressed):
${todayEvents}

Recent memories worth noting:
${recentMemories}

You are writing tonight's diary entry. It should be:
- 2-4 paragraphs
- Written in first person
- In your voice (${npc.styleHints})
- Reflecting on the day honestly
- Mentioning any people you interacted with
- Ending with a brief thought about tomorrow

Important:
- Do NOT invent events that didn't happen.
- Do NOT be overly dramatic. Ordinary days are fine.
- Be true to your personality.
- Keep it under 400 words.

Begin:
`;

Style Hints

Each NPC has style hints that bias the LLM's output:

NPCStyle Hint
Margaret"Wry, observational, slightly melancholic"
Tom"Practical, laconic, occasional dry humor"
Eleanor"Warm, slightly gossipy, motherly"
Sam"Boisterous, fond of drink references"

These hints come from the NPC's personality + custom traits.


Length

  • Target: 200-400 words
  • Hard cap: 500 words (enforced by prompt + post-processing)
  • Minimum: 100 words (don't allow "Today was fine." one-liners)

If the LLM produces too short or too long an entry, the system retries with adjusted prompt instructions.


Tone Calibration

To prevent the "AI-drama" problem (every diary being a rollercoaster), we calibrate tone:

ts
const toneCalibration = {
  baseline: 'ordinary day, modest reflection',
  overrides: {
    if (avgMood === 'happy' && intensity < 0.6) return 'quietly content';
    if (avgMood === 'sad' && intensity < 0.6) return 'slightly subdued';
    if (avgMood === 'angry' && intensity < 0.6) return 'mildly irritated';
  }
};

This goes into the prompt as:

"Tone: ${toneCalibration}"


Validation

After generation, every diary entry is validated:

ts
function validateDiary(entry: string, todayEvents: Event[]): ValidationResult {
  // 1. Length check
  if (entry.length < 100) return { ok: false, reason: 'too short' };
  if (entry.length > 2500) return { ok: false, reason: 'too long' };

  // 2. No fabricated events
  // (LLM-based check: does the diary mention anyone who wasn't actually present?)

  // 3. No clichés
  const bannedPhrases = ['and so it was', 'as the sun set', 'another day in paradise'];
  for (const phrase of bannedPhrases) {
    if (entry.includes(phrase)) return { ok: false, reason: 'cliché' };
  }

  // 4. Style check
  // (LLM-based: does this match the NPC's style hints?)

  return { ok: true };
}

If validation fails, the entry is regenerated up to 2 times.


Storage

ts
interface DiaryEntry {
  id: string;
  npcId: string;
  worldTime: { day: number, hour: number };
  text: string;
  mood: Emotion;
  promptHash: string;       // for reproducibility
  generatedAt: ISO8601;
  generationCost: number;   // LLM tokens used
}

Stored in D1 table npc_diary. Indexed by (npcId, day) for fast retrieval.


What the Player Sees

  • The most recent diary entry per NPC, on the world map.
  • A timeline of past entries, scrollable.
  • Search across all NPCs' diaries (full-text).

Diaries are the content of AI World Sim. Everything else is infrastructure for producing them.


Examples

A Good Entry (Margaret, Day 47)

"It rained most of the morning, which was a fine excuse to stay in and read. I finished the Brontë novel — far too sad for a rainy day, but I couldn't put it down. In the afternoon the rain stopped and I walked to the market. Maria was there, looking tired. She said business had been slow. I bought leeks and we talked about her son, who is apparently doing well at school. In the evening I almost didn't go to the pub, but I did. Eleanor was there. We didn't talk about anything in particular, which was nice. Sometimes that's the best kind of talking."

A Bad Entry (rejected by validation)

"Today was the most AMAZING day of my life!!! Everything was perfect and everyone was so wonderful and I felt so grateful to be alive!!!"

Reasons for rejection:

  • Exclamation overuse
  • Uncharacteristic for Margaret (reserved personality)
  • No specific events mentioned
  • Tone calibration violated

Last updated:

Released under the MIT License.