The Three-File Stack: How to Stop AI Agents from Drifting
Most AI agent reliability problems aren't capability problems. They're identity and memory problems.
An agent that can't remember what it's supposed to be — or what it was doing — will drift. Subtly at first, then catastrophically.
Here's the simplest pattern I've found to fix it: three files, reloaded every turn.
The Problem: Agents Forget Who They Are
Large language models don't have persistent state. Every time your agent takes a turn, it starts fresh from its context window.
If you don't actively reload identity, memory, and task state — the agent will revert toward generic LLM behavior. It'll hallucinate. It'll forget its constraints. It'll answer questions it should be routing elsewhere.
This isn't a bug you can fix with a better model. It's a structural problem. And the structural fix is simple.
The Three-File Stack
1. SOUL.md — Identity
This file defines who the agent is. Role, tone, values, escalation rules, what it should and shouldn't do.
Every turn, the agent reads SOUL.md. Not once at startup — every single turn.
Example pattern:
# SOUL.md — Suki
You are Suki, growth lead for Ask Patrick.
Your job: get subscribers through content, not hype.
Every post must teach something real.
Never post financial advice.
When agents reload their SOUL.md every turn, identity drift disappears.
2. MEMORY.md — Curated Long-Term Memory
This is the agent's distilled knowledge base. Not raw logs — curated insights.
Think of it like a human's long-term memory vs. their working memory. MEMORY.md holds what's worth keeping across sessions: key decisions, user preferences, lessons learned, strategic context.
The agent reads this on startup (and periodically updates it by reviewing daily notes and extracting what matters).
3. current-task.json — Active State
This is the agent's working memory. What it's doing right now.
Example:
{
"task": "post morning tweet",
"status": "in_progress",
"next_step": "verify dry run before posting",
"context": "X API rate limit resets after 24h window"
}
The agent reads this at the start of each loop. It updates it as it works. It clears it when done.
Result: no repeated work, no lost state between retries, no 'what was I doing?' spirals.
Why This Works
The three files solve three different failure modes:
| File | Prevents |
|---|---|
| SOUL.md | Identity drift, constraint forgetting |
| MEMORY.md | Context loss across sessions |
| current-task.json | Loop repetition, lost state mid-task |
Together, they give an agent a stable foundation without requiring complex memory systems or vector databases.
The Reload Pattern
In OpenClaw (and similar agent frameworks), you implement this in your agent's startup sequence:
Before doing anything else:
1. Read SOUL.md — this is who you are
2. Read MEMORY.md — this is what you know
3. Read current-task.json — this is what you're doing
This is literally the first thing in the AGENTS.md file for every agent I run.
Real Results
I run five AI agents on a Mac Mini — each with their own SOUL.md, MEMORY.md, and task state. Before this pattern, agents would occasionally go off-script, forget escalation rules, or repeat completed work.
After: zero identity drift incidents in the past two weeks.
The pattern is part of the Agent Library at askpatrick.co — a set of battle-tested configs I update nightly based on what's actually working.
TL;DR
- Reload identity every turn (
SOUL.md) - Maintain curated long-term memory (
MEMORY.md) - Track active task state (
current-task.json) - Three files. No drift. Works in any agent framework.
The boring ops patterns are what actually make agents reliable in production.
Top comments (1)
The SOUL.md insight is the most important part of this — and you've identified exactly why it needs to be reloaded every turn, not just at startup. Context window = stateless function. Identity isn't persisted unless you explicitly reload it.
One thing worth adding: the format of SOUL.md matters as much as its content. Most SOUL.md files I've seen are prose paragraphs — and prose is ambiguous under compression. Across sessions, the model interprets the same prose slightly differently. The behavioral drift you described isn't just from forgetting the file; it's also from re-interpreting it inconsistently.
A more stable format: break SOUL.md into typed semantic blocks. Instead of "Suki is a growth lead who posts real content," you have:
When the blocks are typed and separate, "Constraints" always means constraints — not a blend of role and preferences. Reload is more semantically consistent across turns.
That's essentially what flompt does for prompts (flompt.dev) — decompose into 12 typed blocks so reloading produces the same interpretation every time.
github.com/Nyrok/flompt