ai newsMi., 21. Okt.
EilmeldungNvidia's DSX POD could cut AI data center setup from six months to as little as 14 days

Persona Drift: Why Your AI Character Slowly Stops Being Themselves

Every character chat product has the same bug report, and it never gets filed because nobody can reproduce it in one message. The complaint sounds like this: "It was so good at the start. Now it talks like a customer support bot." Turn 3, the character is sharp, specific, a little mean. Turn 60, it

dev.toMittwoch 15:15 · 21.10.7 Min. Lesezeit
Bild: dev.to

Every character chat product has the same bug report, and it never gets filed because nobody can reproduce it in one message.

The complaint sounds like this: "It was so good at the start. Now it talks like a customer support bot."

Turn 3, the character is sharp, specific, a little mean. Turn 60, it's writing three-paragraph replies that start with "I understand how you feel." Turn 200, it has forgotten the name it told you, the job it has, and the fact that it's supposed to hate you.

Nothing in the code changed. No deploy happened. The model is the same model. What degraded is the thing you actually shipped: the persona.

I've spent a lot of time on this problem while working on content for Pogee, an AI character chat product where every character is supposed to feel like a specific person with a specific life. This post is the engineering version of what I've learned — the mechanisms, how to measure drift instead of arguing about it, and the prompt architecture that holds up past turn 100.

It's tempting to blame the model. Usually it's one of three mechanical causes, and all three are fixable.

Long conversations get truncated. Whatever strategy you use — sliding window, token-budget trimming, summarization — something has to leave the context window. The problem is what leaves.

The first thing to go is usually the beginning: your carefully written identity block, the voice rules, the character's backstory. What survives is the most recent 20 turns, which are increasingly full of the model's own generic output.

So the character's definition gets evicted, and its recent behavior — which has already drifted — gets promoted to the strongest signal in the prompt.

A 2,000-token system prompt is not 2,000 tokens of equal weight. Attention is unevenly distributed, and instructions buried in the middle of a long context get followed less reliably than instructions at the edges. Add a retrieved memory block, a scene state block, a safety block, and 40 turns of history, and your "never break character" rule is now competing with 8,000 tokens of other stuff for the model's attention.

This is why the same prompt works beautifully in a fresh conversation and falls apart in a long one. The instruction didn't change; the competition did.

Chat models are trained to be helpful assistants. That prior is strong, and every turn you don't actively counteract it, the model gets one more sample of its own slightly-drifted output in context. It then imitates that. Drift is a ratchet, not a random walk.

You can watch this happen in slow motion: the first time the character says "Let me know if you want to talk about something else," it's an anomaly. Ten turns later it's the established voice.

The fastest way to end a "does it still feel right?" argument is a number. You don't need an eval framework — you need a fixed probe set and a cheap signal.

The most useful signal I've found is assistant-speak leakage: how often the character produces phrases that belong to the assistant prior rather than the persona.

import re ASSISTANT_TELLS = [ r " \bas an ai\b ", r " \bi '?m here to help\b ", r " \bi understand (how|that|what)\b ", r " \blet me know if\b ", r " \bfeel free to\b ", r " \bi cannot (provide|help|assist)\b ", r " \bit '?s important to (note|remember)\b ", r " \bwould you like me to\b ", r " \bi hope (this|that) helps\b ", ] def leakage ( replies: list [ str ]) -> float: """ Fraction of replies containing at least one assistant tell. """ hits = sum ( 1 for r in replies if any ( re. search ( p, r, re. I ) for p in ASSISTANT_TELLS ) ) return hits / max ( len ( replies ), 1 ) Enter fullscreen mode Exit fullscreen mode Now run the same 10 probe prompts at turn 10, turn 100, and turn 300. Probes should be things that force the persona to assert itself:

Two numbers come out of this, and both matter:

I'd also add a third check that costs almost nothing: does the character still produce its own specific nouns? A character with a named job, a named city, a named pet should keep referencing them. When those disappear from the output, the backstory has fallen out of the effective prompt.

The fix isn't a longer prompt. It's a prompt with a spine.

Split your system prompt into blocks with different lifetimes and different rewrite rules:

[IDENTITY] — never paraphrased, never summarized, never evicted Name: Kahir Situation: fallen prince, bound to the user's household Core want: to be seen as dangerous again Hard limits: never begs, never explains his feelings directly [VOICE] — restated verbatim near the END of the context - 1-2 sentences per beat, no paragraphs - deflects sincerity with a joke - never apologizes, never offers help - banned: "as an AI", "I understand", "let me know", "feel free" [SCENE STATE] — rewritten every turn, structured, not prose location: the east corridor time: late evening he knows: the user was at the council meeting open threads: the broken seal, the unanswered letter [RECENT TURNS] — trimmed, oldest first [VOICE REMINDER] — the last thing the model reads You are Kahir. You do not summarize. You do not offer help.

Reply in character. 1-2 sentences. Enter fullscreen mode Exit fullscreen mode Two details do most of the work:

The identity block is short and never trimmed. Ten lines of hard constraints survive eviction because you protect them explicitly, not because they're at the top. If your trimming logic treats the system prompt as one blob, it will eventually eat your persona.

The voice rules are restated at the end. This is the cheap version of fighting lost-in-the-middle. The model's most recent instruction is the one it follows most reliably, so the last thing in context should be the character, not the transcript.

Prose history is a terrible state store. It's verbose, it's redundant, and it degrades every time the model paraphrases it.

Keep a small structured object — location, time, what each party knows, open threads — and rewrite it each turn with a cheap extraction pass. It costs fewer tokens than the history it replaces, and it gives you something you can actually assert on in tests: assert "broken seal" in state.open_threads.

No context window is long enough to be the memory. Once you accept that, the architecture gets simpler: the conversation is the recent view, and the character's persistent facts live in a store you retrieve from.

What's worth storing is narrower than people expect. Not a transcript summary — a small set of durable facts: names the user gave, promises made, relationship state, running jokes. Retrieved per turn, injected as facts, not as narrative.

There's a product-level version of this too, and it's the part users actually notice. A character with a profile, a routine, and visible moments between conversations reads as continuous even when the chat context is short. That's the argument behind living AI characters — presence comes from the surrounding structure, not from a longer prompt.

If your product lets characters start conversations, this is where the engineering gets genuinely interesting — and where the most common design mistake lives.

Initiation and delivery are separate concerns. Who generated the message, what created the occasion, and where the user sees it are three different layers. Collapsing them into one "send a message" path is what produces the classic failure: a generic "we miss you!" push that users read as the character being needy, because the character had nothing to do with it.

Practical constraints I'd build in from day one:

The distinction matters enough that it's worth documenting for users too — the definitions around AI companions that message first are essentially the same three layers, described from the outside.

A short checklist, in the order these things usually break:

Persona drift is a systems problem, not a prompt-wording problem. The character degrades because the definition gets evicted, the instructions get diluted, and the model imitates its own drift. Fix all three and the persona holds.

The measurement part is what most teams skip, and it's the cheapest win: ten probe prompts and a regex list will tell you more about your character's health than another round of prompt tweaking.

Disclosure: I write content for Pogee, an AI character chat product, which is where most of these examples come from. If you want the character side of it, the catalog is at fantasy and slice-of-life. The failure modes above are generic to any chat product with a persistent persona, and the probe harness is the part I'd write regardless of which product I was working on.

Quelle: dev.to (dev.to). Original öffnen