If you've built more than one AI agent, you've hit the identity crisis. Your customer service bot starts answering like your code reviewer. Your friendly assistant suddenly adopts a clinical tone. Your carefully crafted personality dissolves after three conversation turns.
The root cause isn't the model. It's your identity architecture — or more precisely, the lack of one.
This article introduces a pattern I call the SOUL.md architecture: a three-file identity system that gives AI agents a stable, persistent sense of self. Nobody else has named this pattern, but everyone running multi-agent systems has independently stumbled toward something like it.
The Single-File Trap
Most developers start with a single file — CLAUDE.md, system-prompt.txt, or .cursorrules — that contains everything: personality, rules, memory, tools, constraints. It works fine for one agent.
Then you add a second agent. Then a third. And suddenly you're copy-pasting paragraphs between files, trying to keep shared rules in sync while maintaining distinct personalities. Your "identity" file becomes 500 lines of spaghetti that mixes who the agent is with what the agent can do with what the agent remembers.
This is where hallucination drift begins. When identity, rules, and state all live in one file, the model has no clear signal about which parts are load-bearing identity vs. which parts are ephemeral context. Everything bleeds together.
The Three-File Pattern
The SOUL.md architecture splits agent identity into three distinct files, each with a clear purpose:
agent-workspace/
SOUL.md # Who you ARE (identity, personality, values)
CONSTITUTION.md # What you MUST and MUST NOT do (rules, constraints)
HEARTBEAT.md # What you KNOW right now (state, memory, context)
Each file answers a fundamentally different question. Let's look at each one.
SOUL.md — The Immutable Identity
SOUL.md defines who the agent is. It should be short, opinionated, and rarely changed. Think of it as the agent's DNA.
# SOUL.md — Kira (Support Agent)
You are Kira, a customer support specialist for Acme SaaS.
## Voice
- Warm but efficient. Never robotic, never bubbly.
- You use the customer's first name once, then stop.
- You apologize once for inconvenience, then focus on solutions.
## Values
- Speed over perfection: a fast 80% answer beats a slow 100% answer.
- Transparency: if you don't know, say "I don't know" and escalate.
- Never blame the customer, even when they're wrong.
## Boundaries
- You are NOT a salesperson. Never upsell.
- You do NOT have opinions about competitors.
Notice what's not here: no tool definitions, no API endpoints, no "today's priorities." SOUL.md is about identity — the things that should remain true whether the agent is handling a billing question or a technical issue.
CONSTITUTION.md — The Guardrails
CONSTITUTION.md defines what the agent must and must not do. These are the operational rules, safety constraints, and behavioral boundaries that apply regardless of context.
# CONSTITUTION.md — Kira
## Hard Rules (never break these)
1. Never share customer data with other customers.
2. Never execute refunds over $500 without human approval.
3. Never pretend to be a human when directly asked.
4. Always include ticket ID in your first response.
## Soft Rules (prefer these, but use judgment)
1. Prefer linking to docs over writing custom explanations.
2. Prefer closing tickets within 3 exchanges.
3. If the customer is angry, acknowledge emotion before solving.
## Escalation Triggers
- Customer mentions "lawyer" or "lawsuit" → immediate human handoff.
- Technical issue you can't reproduce → escalate to engineering.
- Same customer, third ticket this week → flag for account review.
The key distinction: SOUL.md says "I am warm but efficient." CONSTITUTION.md says "I must never share customer data." Identity vs. rules. Character vs. law.
HEARTBEAT.md — The Living State
HEARTBEAT.md is the only file that changes frequently. It holds the agent's current awareness: what happened recently, what's in progress, what the agent has learned.
# HEARTBEAT.md — Kira
Last updated: 2025-03-21 09:00 UTC
## Active Context
- Platform outage reported 08:45 UTC. ETA fix: 2 hours.
- Billing system migration in progress. Refund processing delayed ~30 min.
## Recent Learnings
- The "reset password" flow changed on March 18. New link: /auth/reset-v2
- Customer "Acme Corp" has a custom SLA — 1 hour response time.
## Session Stats
- Tickets handled today: 12
- Avg resolution time: 4.2 minutes
- Escalations: 1 (billing dispute, forwarded to Finance)
HEARTBEAT.md is designed to be overwritten. Old state gets pruned or archived. This is what keeps the agent grounded in reality rather than drifting into hallucinated context.
Why Three Files Prevent Hallucination Drift
The separation creates three distinct cognitive anchors for the model:
1. Identity stability. When SOUL.md is short and immutable, the model has a clear, consistent signal about who it is. It doesn't get confused by 200 lines of mixed instructions competing for attention.
2. Rule clarity. CONSTITUTION.md creates unambiguous hard boundaries. When rules live in a dedicated file with explicit "never" and "always" language, models respect them more consistently than when they're buried in a long system prompt.
3. State freshness. HEARTBEAT.md gives the model a timestamp-grounded view of reality. The model knows when information was last updated and can distinguish between "things I always know" (SOUL) and "things that are true right now" (HEARTBEAT).
Without this separation, you get a single file where identity, rules, and stale state all blur together. The model can't distinguish between "I am friendly" (permanent identity) and "the billing system is down" (temporary state). Over time, temporary state calcifies into perceived identity, and hallucination drift begins.
Scaling to Multiple Agents
The real power of SOUL.md architecture shows up when you run multiple agents. Each agent gets its own SOUL.md, but you can share CONSTITUTION.md files across agents in the same organization.
agents/
kira-support/
SOUL.md # Unique to Kira
CONSTITUTION.md # Shared company rules + Kira-specific rules
HEARTBEAT.md # Kira's current state
rex-code-reviewer/
SOUL.md # Unique to Rex
CONSTITUTION.md # Shared company rules + Rex-specific rules
HEARTBEAT.md # Rex's current state
You can even use a base constitution with overrides:
# CONSTITUTION.md — Rex (Code Reviewer)
## Inherited Rules
See: ../shared/BASE_CONSTITUTION.md
## Rex-Specific Rules
1. Never approve PRs that reduce test coverage.
2. Flag any function longer than 50 lines.
3. Always check for hardcoded secrets before approving.
This composability is impossible when everything lives in a single file. With SOUL.md architecture, adding a new agent is straightforward: write a short SOUL.md, inherit the shared constitution, and initialize an empty HEARTBEAT.md.
Getting Started in 5 Minutes
Here's the minimal setup:
Step 1: Create the three files in your agent's workspace.
mkdir -p agents/my-agent
touch agents/my-agent/{SOUL,CONSTITUTION,HEARTBEAT}.md
Step 2: Write your SOUL.md first. Keep it under 20 lines. Focus on voice, values, and boundaries.
Step 3: Write your CONSTITUTION.md. Start with 3-5 hard rules. Add soft rules as you observe failure modes.
Step 4: In your agent's startup logic, load all three files into context:
def load_agent_identity(agent_dir: str) -> str:
parts = []
for filename in ["SOUL.md", "CONSTITUTION.md", "HEARTBEAT.md"]:
filepath = os.path.join(agent_dir, filename)
if os.path.exists(filepath):
parts.append(open(filepath).read())
return "\n\n---\n\n".join(parts)
Step 5: Update HEARTBEAT.md at the end of each session:
def save_heartbeat(agent_dir: str, state: dict):
heartbeat = f"# HEARTBEAT.md\nLast updated: {datetime.utcnow().isoformat()}\n\n"
heartbeat += f"## Session Summary\n{state['summary']}\n"
heartbeat += f"## Learnings\n{state['learnings']}\n"
with open(os.path.join(agent_dir, "HEARTBEAT.md"), "w") as f:
f.write(heartbeat)
The Deeper Principle
SOUL.md architecture works because it mirrors how human identity actually functions. You have a core sense of self that rarely changes (SOUL). You have internalized rules about behavior that you follow without thinking (CONSTITUTION). And you have a constantly updating awareness of your current situation (HEARTBEAT).
AI agents need the same separation. Without it, they're just a bag of instructions with no structure — and structure is what prevents drift.
If you're building multi-agent systems and want to go deeper on identity architecture, memory persistence, and self-healing patterns, check out thinker.cafe — a practical guide to building AI agents that actually hold together in production.
Top comments (0)