You wrote a system prompt that nails the voice on turn one — punchy, second-person, no marketing fluff. Three turns later the model is writing “In today’s fast-paced digital landscape” again. By turn ten it has invented bullet headers, added emoji, and shifted to corporate-blog formality. The prompt did not change. The model did not change. What drifted is the effective weight of your style instructions relative to everything else in the conversation.
Fastest fix: move the style block out of the chat message and into a place the model re-reads every turn (ChatGPT Custom Instructions or a Project, a Claude Project, or .cursor/rules/*.mdc), and rewrite each soft adjective as a countable rule the model can verify line by line. Style drift is almost always a hard-vs-soft constraint problem: you told the model what feeling you wanted, not what rules the output must satisfy.
This page walks through why drift happens and how to convert soft style descriptions into measurable constraints the model can self-check.
Common causes
1. Style described in adjectives, not rules
Adjectives like “punchy”, “professional”, “warm”, “concise” map to no specific behavior. The model resolves them to the training-distribution average of text matching that label — which is exactly the corporate-blog voice you do not want.
How to spot it: your style instruction has zero numbers, zero forbidden words, and zero structural rules. Pure adjectives.
2. Earlier style examples got pushed out of effective context
In a long thread, the model attends most strongly to recent tokens. Even when the full window is large (Claude Opus 4.7 and Sonnet 4.6 and Gemini 3.1 Pro are 1M tokens as of June 2026; ChatGPT Plus exposes roughly 320 pages in-app), the early instruction loses attention weight, not necessarily its place in the window. If your style anchor was paragraph 1 of turn 1 and you are now on turn 25, the model leans on its defaults again.
How to spot it: drift correlates with turn count. Turns 1-3 fine, turns 8+ wrong.
3. Soft cues compete with content cues
If your last message had a long technical paragraph and a one-line “keep it casual”, the model weights the technical paragraph as the dominant frame and the casual tag as a hint. Hints lose to volume.
How to spot it: drift shows up when the user message is content-heavy and the style instruction is a tail bullet.
4. Sampling variance, not drift at all
At a high temperature the same prompt produces different styles run to run by design. You are seeing variance, not drift, but the fix is the same: tighten constraints. (Note: on GPT-5.x reasoning models you cannot set temperature at all — see Step 5 — so variance there comes from reasoning, not from a temperature knob.)
How to spot it: re-running the same prompt fresh (no history) still gives different styles.
5. Model snapshot changed mid-thread
Some platforms silently rotate model versions. The voice you tuned for last week’s snapshot does not transfer. ChatGPT, for example, moved its default to GPT-5.5 around April 23, 2026; voices tuned on the previous default shifted overnight.
How to spot it: drift correlates with a date, not with turn count or prompt content.
Diagnosis: which bucket are you in
| Symptom pattern | Most likely cause | Go to |
|---|---|---|
| Bad from turn 1, every time | Adjectives, not rules (cause 1) | Step 1 + Step 2 |
| Fine early, wrong after ~8 turns | Anchor lost attention weight (cause 2) | Step 3 |
| Wrong only on content-heavy turns | Soft cue outweighed (cause 3) | Step 1 + Step 4 |
| Different every fresh run | Sampling / reasoning variance (cause 4) | Step 5 |
| Changed on a specific date | Model snapshot rotated (cause 5) | Step 5 |
Before you change anything
- Save one clean “good style” output and one “drifted” output so you can diff them.
- Note model name, the platform / API, and (if it applies) temperature or reasoning effort.
- Try the same prompt in a fresh conversation to separate drift from sampling variance.
- Check the platform’s changelog for model snapshot updates in the last week (OpenAI’s is at
platform.openai.com/docs/changelog). - Confirm the system prompt or project instructions actually loaded — some UIs silently truncate, and Custom Instructions fields cap around 1,500 characters each.
Information to collect
- The exact style instruction as written, including its position in the prompt.
- Turn number where drift first appeared.
- Model and snapshot, plus temperature/top-p (non-reasoning) or reasoning effort (GPT-5.x reasoning).
- A “good” output and a “bad” output side by side.
- Total token count of the conversation when drift began.
Shortest path to fix
Steps 1-3 fix most cases.
Step 1: Convert adjectives to measurable rules
Replace soft descriptors with countable constraints:
| Adjective | Measurable rule |
|---|---|
| ”Punchy" | "Max 15 words per sentence. No sentence may start with ‘In’." |
| "Professional" | "No exclamation marks. No second person. No contractions." |
| "Warm" | "Use ‘we’ at least once per paragraph. No bold headers." |
| "Concise" | "Total output under 120 words. No more than 4 sentences.” |
The model is much better at “this sentence is under 15 words: yes/no” than at “is this punchy: vibe”.
Step 2: Provide one explicit style anchor
Paste a 2-3 sentence example of the exact voice you want, labeled:
Voice example (write in exactly this voice):
"Pull the env var from the dashboard. Paste it into your .env.local.
Restart the dev server. If it still fails, you grabbed the wrong key."
One concrete anchor outperforms three pages of adjectives.
Step 3: Move the style block somewhere the model re-reads every turn
Re-pasting the style block at turns 6, 12, 18 works, but it is busywork. The durable fix is to put it where the platform feeds it back to the model on every turn:
- ChatGPT: Settings > Personalization > Custom Instructions (loads on every new chat, two fields ~1,500 characters each), or create a Project and put the rules in the project’s instructions so they apply to every chat in that workspace.
- Claude: create a Project and put the style rules in the project’s custom instructions (roughly 8,000 characters as of June 2026). For Claude Code, use
CLAUDE.md(or split files under.claude/rules/). - Cursor: put the rules in
.cursor/rules/*.mdc(the current recommended location; the old single.cursorrulesfile still works but is legacy). AnAGENTS.mdin the repo root is the cross-tool fallback read by Cursor, Codex, and others.
A persisted style block survives context-window pressure in a way that a one-time chat message cannot.
Step 4: Add a self-check footer
Append to every prompt:
After writing, verify your output against these rules:
- Rule 1: max 15 words per sentence
- Rule 2: no exclamation marks
- Rule 3: every sentence starts with a verb
If any fail, rewrite that sentence and check again.
The model is decent at self-audit when given a concrete checklist. Keep the rules binary (yes/no), not subjective.
Step 5: Pin the version and the right variance knob
Lock the snapshot, not just the friendly name. “Default” silently changes; a dated snapshot does not.
- Non-reasoning models (and Claude / Gemini chat models): pin model snapshot and temperature. If you found the good voice at temperature 0.3, lock 0.3. Use a model picker or the API rather than “default”.
- GPT-5.x reasoning models: as of June 2026 these reject
temperatureandtop_p— the API returns an “unsupported parameter” error. The knob isreasoning_effortinstead (GPT-5.5 levels:xhigh,high,medium,low, andnone). Style is steadiest at lower reasoning effort; if you specifically needtemperature, you must run withreasoning_effort: none. See the official Using GPT-5.5 guide.
So “pin temperature” is correct for non-reasoning runs but not something you can do on a GPT-5.x reasoning model — pin reasoning effort there instead.
Step 6: For format drift, enforce structure instead of describing it
If the drift is structural (bullets vs prose, JSON vs Markdown), do not describe the shape in prose and hope. Enforce it.
- In a chat UI: give an exact template and one filled example: “Return only this shape, nothing else:
{"summary": "...", "next_step": "..."}.” - On the API: use OpenAI Structured Outputs — set
response_formatto ajson_schemawithstrict: true, which constrains the model so it cannot return a shape that violates the schema. As of June 2026, OpenAI recommends not re-describing the schema in the prompt when you use this; the barejson_objectJSON Mode is now legacy and only guarantees valid JSON syntax, not your fields. See the Structured Outputs guide. Claude and Gemini have equivalent tool-/schema-constrained output modes.
Structure is enforceable in a way that vibe is not.
How to confirm the fix
- Run the same prompt three times in three fresh sessions; outputs should pass all your rules.
- Run a 10-turn session and check turn 1, turn 5, and turn 10 outputs against the rules — no degradation.
- Have someone unfamiliar with the project read the output and describe the voice; their description should match your intent.
- Diff the old “drifted” output against the new output and confirm the previously failed rules now pass.
If it still fails
- Reduce to a one-rule prompt — if even one rule fails to stick, the model or platform is the issue, not your prompt.
- Switch from chat UI to API; UIs inject their own system prompts that can override yours.
- Try a different model family — some follow constraints more reliably than others.
- If you need deep personal voice (your writing, not a generic style), give 5-shot examples of your real writing rather than rules.
FAQ
Why does temperature 0 not stop the drift? Because most style drift is not sampling variance — it is attention weight. At temperature 0 the model is deterministic for a fixed input, but as the conversation grows the input changes, so the output drifts anyway. Temperature only controls run-to-run randomness on an identical input. And on GPT-5.x reasoning models you cannot set temperature at all; lower the reasoning effort instead.
My ChatGPT Custom Instructions are set but the voice still drifts. Why? Two common reasons. First, each Custom Instructions field caps around 1,500 characters, so a long style block gets silently truncated — trim it to the binary rules that matter. Second, Custom Instructions apply to new chats, but a long existing thread can still overpower them; for important work create a Project and put the rules in the project instructions.
Is putting the JSON schema in the prompt enough?
In a chat UI it is the best you have, so give an exact template plus one filled example. On the API it is not the right tool — use Structured Outputs (response_format with a json_schema and strict: true), which actually constrains the output. Plain “respond in JSON” (JSON Mode) is legacy and only guarantees syntax, not your fields.
The voice changed and I did not touch anything. What happened? A model snapshot probably rotated under you. ChatGPT moved its default to GPT-5.5 around April 23, 2026. If drift tracks a date rather than turn count, pin a dated snapshot via the API or model picker and re-tune your style block on it.
Should I use rules or examples? Both, for different jobs. Rules are best for measurable constraints (length, forbidden words, structure). Examples are best for hard-to-name personal voice — 3-5 short samples of real writing beat any adjective. Keep examples short; long anchors get averaged toward the generic mean.
Prevention
- Maintain one short “style block” and put it in platform-level config (Custom Instructions / Project /
.cursor/rules), not in individual messages. - For repeated work, never rely on a single in-message style cue.
- Keep style anchors short: 2-3 sentences. Long anchors get averaged.
- Audit drift weekly: re-run a canonical prompt and diff against last week’s output, and check the platform changelog for snapshot changes.
- For brand voice, pin the snapshot plus the variance knob (temperature for non-reasoning, reasoning effort for GPT-5.x) plus the style block, and treat the whole thing as one config.
- When formatting matters, enforce structure (Structured Outputs / schema) instead of describing it in prose.