Back to Eliza

Actor Prompt Rewrite: Before vs After

packages/feed/docs/actor-prompt-rewrite-report.md

2.0.37.4 KB
Original Source

Actor Prompt Rewrite: Before vs After

Phase 1 of the actor system overhaul. Measured using prompt-diff and context-inspector dev tools.


The Problem

The old prompts buried actor identity under 1,200 tokens of generic shared rules that were identical across all 140+ actors. Every NPC sounded the same because the rules dominated the signal.

Old prompt structure (v5):

[800 tokens of reality grounding]
=== ALL CHARACTERS IN WORLD ===         ← empty
=== CHARACTER'S FULL PROFILE ===        ← actor data buried here
=== CHARACTER'S RELATIONSHIPS ===       ← empty
=== COMPLETE NARRATIVE CONTEXT ===      ← empty
=== ONGOING STORYLINES ===              ← empty
=== RESOLVED QUESTIONS ===              ← empty
=== DAY X/30 CONTEXT ===
=== CHARACTER'S POST HISTORY ===        ← empty
[400 tokens: IMPORTANT_RULES — "no hashtags" stated 4 different ways]
[200 tokens: CONTENT_REQUIREMENTS — "MUST reference", "MUST mention"]
[200 tokens: ANTI_REPETITION_RULES — "NEVER repeat", "build on"]
=== YOUR TASK ===
[6-item DO list]
[6-item DO NOT list]
CHARACTER LIMIT: Post MUST be 200 characters or less.
[VALUE_RANGES]
[XML format]
CRITICAL: Return exactly ONE post...
[200 tokens: FINAL_REMINDERS — repeating IMPORTANT_RULES again]

New prompt structure (v6):

You are {name}.

{full character info — voice, examples, personality, relationships, positions}

{per-actor anti-repetition patterns}
{per-actor rules — ignoreTopics, tone guardrails, finance guardrails}

WHAT'S HAPPENING:
{compact context}

WORLD:
{actors, markets, predictions}

RULES (5 lines):
- Parody names only
- No hashtags, no emojis
- Max 200 characters
- Sound like YOUR examples
- Reference events naturally

Write ONE post as {name}.

{XML format}

Prompt-by-Prompt Comparison

All measurements from bun scripts/prompt-diff.ts --section-only.

ambient-posts (main post generation)

MetricBefore (v5)After (v6)Delta
Max Tokens8,0001,500-81%
Template tokens1,368206-1,162 (-85%)
Empty sections removed70-7
Shared rule blocksIMPORTANT_RULES, CONTENT_REQUIREMENTS, ANTI_REPETITION_RULES, FINAL_REMINDERS5 inline rules-4 blocks

Sections removed: ALL CHARACTERS IN WORLD, CHARACTER'S RELATIONSHIPS, COMPLETE NARRATIVE CONTEXT, ONGOING STORYLINES, RESOLVED QUESTIONS, DAY X/30 CONTEXT, CHARACTER'S POST HISTORY, CHARACTER'S EVENT INVOLVEMENT, ABSOLUTELY NO HASHTAGS, NO EMOJIS, PARODY NAMES ONLY, NAME USAGE EXAMPLES, ANTI-REPETITION RULES, YOUR TASK, DO, DO NOT

reactions (event reaction)

MetricBefore (v5)After (v6)Delta
Max Tokens8,0001,500-81%
Template tokens1,281182-1,099 (-86%)

commentary (in-character take)

MetricBefore (v6-old)After (v6-new)Delta
Max Tokens8,0001,500-81%
Template tokens1,366164-1,202 (-88%)

replies (reply to post)

MetricBefore (v5)After (v6)Delta
Max Tokens6,0001,500-75%
Template tokens1,316169-1,147 (-87%)

reply (lightweight thread reply)

MetricBefore (v3)After (v6)Delta
Max Tokens5,0001,000-80%
Template tokens1,11098-1,012 (-91%)

conspiracy (contrarian take)

MetricBefore (v5)After (v6)Delta
Max Tokens6,0001,500-75%
Template tokens1,259200-1,059 (-84%)

minute-ambient (quick ambient)

MetricBefore (v3)After (v6)Delta
Max Tokens500500unchanged
Template tokens1,12371-1,052 (-94%)

Aggregate Impact

MetricBeforeAfterImprovement
Total template tokens (all 7 prompts)8,8231,090-7,733 (-88%)
Total maxTokens budget41,5009,000-78%
Empty section headers49 (7 per prompt)0-100%
Shared rule imports6 per prompt0-100%
Per-actor guardrails04 systems wired+4

Actor Context Comparison (from context-inspector)

All measurements from bun run inspect:context -- --npc <id> --type posting.

Context by actor (tokens)

SectionAIlon MuskTrump TerminalVitAIlik Buterin
characterInfo (identity)885848683
comprehensiveContext350252256
fullCharacterContext1,2531,119956
realityGrounding789789789
worldActors376359377
phaseContext585858
timeEnergy171716
personalEvents32
recentEvents100100100
relationships90165113
marketPositions333
Total2,0961,9471,803

Key observation: each actor gets a different total token count based on their actual data richness. AIlon Musk has 63 post examples so characterInfo is larger. VitAIlik has 31 examples and fewer relationships, so his context is smaller. This is correct — the prompt adapts to the actor.


What's Now Wired In (Previously Built But Unused)

1. NPCAntiRepetitionService

  • Tracks last 20 posts per character
  • Detects overused opening phrases (first 3 words)
  • Detects overused vocabulary (words appearing in 50%+ of posts)
  • Injects: "Do NOT start with: 'the future is', 'just saw'. Reduce these words: tremendous, winning"

2. formatActorToneGuardrails

  • Checks actor's voice/style/examples for generic slang tokens (W, L, dawg, bro, fam, fr fr, no cap, rizz, ratio)
  • If NOT in their corpus, bans them: "For THIS character, DO NOT use: W, L, dawg, bro, fam"

3. formatActorFinanceGuardrails

  • Checks if actor is a "degen speaker" (trading domain, ticker patterns, degen keywords)
  • If NOT, bans ticker/trading jargon: "DO NOT talk in tickers: no $OPENAGI / $NVDAI"

4. ignoreTopics

  • Actor data field (e.g., VitAIlik: ['politics', 'entertainment', 'sports', 'celebrity', 'fashion'])
  • Previously only used as a pre-generation gate
  • Now injected as prompt rule: "You never talk about: politics, entertainment, sports, celebrity, fashion"

Relevance-Filtered Feed (New)

Before: All NPCs saw the same 15 most recent posts regardless of who they are.

After: getRelevantFeedForNPC(npcId) prioritizes posts from actors the NPC cares about:

  1. Posts from actors sharing affiliations (same org) — up to 10 slots
  2. Posts from actors in relationship table (allies/rivals)
  3. Remaining slots filled with general recent posts

Example: AIlon Musk (affiliations: teslai, aix, neurailink, spaicex) sees posts from other TeslAI/AIX actors first, then general feed.


Remaining Phases

PhaseStatusDescription
Phase 1: Prompt rewriteDone7 prompts rewritten, guardrails wired, feed filtered
Phase 2: Unified context builderPlannedSingle ActorContextBuilder for all actions
Phase 3: Per-actor rulesPlannedrules array in actor data files
Phase 4: Unified action loopPlannedOne LLM call per actor per tick
Phase 5: Dynamic worldPlannedUpdate stale reality grounding