Krishnendu Biswas

How my agents remember: five layers of memory I can read and edit

How 10 agents on one Azure server remember what matters across months of daily work. Small notes I can read and edit keep the active rules fresh, while full searchable history on my own server preserves every past conversation.

The Core Difference: Hosted AI memory lives on a provider's servers in its format; I can view or delete entries but not version, script or move the files. Mine is split into five layers I own. What must survive permanently sits in plain Markdown files with strict character caps, so I can open, audit and edit them in seconds with any text editor. Past conversation history is kept in a local SQLite database on my own server (LCM-X), allowing agents to search earlier turns without crowding their active working window.

Production platform
Hermes Agent on Azure VPS · SQLite WAL · Markdown
Design principle
Short curated files for permanent rules · Searchable database for history
Human oversight
Agent operations run with human approval gates for irreversible actions
5Memory layers
10Agents
2,200Char cap on notes
441,511Messages in history

Architecture: the five layer hierarchy

Each layer has one specific job, one retention rule, one size budget, and one gate on who may write to it. The model reads only L1 directly. Everything else arrives through the context assembler or a tool call.

Five layer memory hierarchy Three inbound sources, chat messages, kanban cards and scheduled jobs, reach a context assembler. The assembler reads five layers: L1 session window, L2 ephemeral scratch, L3 curated persistent memory capped at 2,200 and 2,600 characters, L4 the knowledge base of markdown files, and L5 the skills library. Overflow from L1 is compacted into lcm.db by LCM-X and recalled through search tools. Discord or Telegram Kanban card Scheduled job Context assembler loads L3 and L4 in full, L5 skills on demand, history through LCM-X L1 Session window and system prompt live context, never stored as is, cap: model window L2 Ephemeral scratch TMPDIR per agent, idle entries pruned after 24 hours L3 Curated persistent memory MEMORY.md and USER.md, cap 2,200 and 2,600 characters L4 Curated knowledge base markdown files, one topic each, read on demand L5 Procedural skills library SKILL.md with references and scripts, per agent overflow recall lcm.db SQLite WAL FTS5 LCM-X keeps every raw row Solid: session assembly. Dashed: LCM-X compaction and recall. Five layer memory hierarchy: inbound messages reach a context assembler that reads five layers, and LCM-X compacts session overflow into lcm.db Discord or Telegram Kanban card, scheduled job Context assembler L3 and L4 in full, L5 on demand L1 Session window live context, cap: model window L2 Ephemeral scratch pruned after 24 idle hours L3 Curated memory cap 2,200 and 2,600 characters L4 Knowledge base one topic per markdown file L5 Skills library SKILL.md, references, scripts L1 overflow lcm.db (LCM-X) SQLite WAL with FTS5 every raw row kept Dashed: compaction
Diagram A. Five layer memory architecture. Only L1 is read directly by the model. L3 and L4 load in full, L5 skills load on demand when a task matches, and older history is compacted into lcm.db for search and recall.

The problem: remembering everything makes agents worse

Agents that try to remember everything fail in two quiet ways. The context window fills with stale detail, so the model spends attention on noise. And a store full of unchecked fragments lets the model recall things that were never true. Neither failure raises a clean error.

Cutting old messages blindly is not the answer either: the agent forgets earlier decisions, and a retried task starts from zero. I wanted durable memory small enough to read and audit in full, every durable write reversible, and the bulk of history compressed for the model without ever being thrown away.

How the five layers work

Layer specifications

Memory layer specifications
Layer Scope Persistence Capacity cap Storage engine Tool access gate
L1 Session window and system prompt One session of one agent Not stored as is; overflow is compacted into lcm.db Model context window; compaction starts at 0.4 of the window Prompt assembled in memory No tool; always present
L2 Ephemeral scratch One task of one agent Entries idle for 24 hours are pruned Disk, pruned Filesystem; TMPDIR points to the agent's cache/scratch Shell and file tools; never read back unless promoted
L3 Curated persistent memory Every session of one agent Permanent; timestamped snapshot before each curator run 2,200 characters of notes, 2,600 of user profile (router agent; each agent sets its own) Markdown, written by temp file, fsync and rename memory tool only; refuses writes over the cap; queued writes reviewed by the curator
L4 Curated knowledge base Facts shared by all agents, loaded by domain Permanent, edited by hand One topic per file; 20 files Markdown in ~/.hermes/knowledge Read on demand; md_guard.py checks every edit and runs daily
L5 Procedural skills library Per agent Permanent, versioned 980 SKILL.md files, 249 distinct skills SKILL.md plus references and scripts Loaded when a task matches; configuration drift audited hourly by the infrastructure agent
LCM-X (beneath L1) Full history of one agent; each agent has its own lcm.db Permanent raw rows; removal only by an explicit owner purge Disk; 441,511 raw messages in the router agent's store SQLite in WAL mode with FTS5 indexes lcm_grep, lcm_recall, lcm_expand

Curation, human approval gates, and rollback

Writes to persistent memory (L3) never go straight to disk. They pass through a structured validation pipeline with snapshots, so any incorrect fact can be rolled back instantly.

  1. Pending queue: Proposed memory writes requiring review are saved as JSON records in ~/.hermes/pending/memory rather than applied directly.
  2. Pre-filter: A fast gate checks candidate writes to discard transient session noise before deeper evaluation.
  3. Review tier: Fable, the top reasoning tier, evaluates each candidate. Durable operational facts and user preferences are accepted. Routine task progress is logged only, since LCM-X already records full transcripts. Irreversible changes (such as deletions, security boundaries, or suppression lists) wait for my manual confirmation.
  4. Snapshot backup: Before modifying memory, the curator copies MEMORY.md and USER.md to timestamped files in memories/backups (3 snapshots on disk today).
  5. Atomic apply: Approved updates use atomic file operations (write to temporary file, fsync, and rename) behind file locks, ensuring no agent ever reads a partially written file. The memory tool strictly rejects updates exceeding the character limit until old entries are consolidated.
  6. Audit ledger and recovery: A local SQLite ledger tracks the source session and reasoning behind every remembered fact. If an incorrect claim is detected, restoring the previous snapshot takes one command. Explicit forgetting leaves a clean audit tombstone.

In addition, an automated hourly job backs up agent state and memory files, and a daily guard script checks that knowledge edits stay within their assigned topic files.

LCM-X: lossless context memory

LCM-X operates beneath L1. Every message sent or received is recorded once in lcm.db with an immutable store_id. When the active context window fills up, LCM-X compacts older messages into a depth 0 summary node that references the exact store_ids it summarizes. When multiple depth 0 nodes accumulate, they compact into a depth 1 node. This creates a clean directed acyclic graph where summaries always link back to the exact source messages, and compaction never deletes or alters raw history.

LCM-X lossless compaction and retrieval DAG Raw messages each keep an immutable store id. Depth 0 summary nodes each cover a range of raw messages by store id. A depth 1 node covers depth 0 nodes. A keyword search over FTS5 indexes of messages and summaries can land on any level, and lcm_expand returns the original messages behind any summary. Search: lcm_grep, lcm_recall FTS5 over messages and summaries ranked by match strength and recency lcm_expand returns the originals behind a summary by node_id or store_id Depth 1 summary covers nodes S1 and S2 S1 depth 0 summary covers store_id 101 to 104 S2 depth 0 summary covers store_id 105 to 108 id 101 id 102 id 103 id 104 id 105 id 106 id 107 id 108 compact compact Raw messages: written once, never rewritten by compaction lcm.db, journal_mode=WAL: 441,511 raw messages, 990 summary nodes, 0 dangling references LCM-X compaction: raw messages compact into depth 0 summaries, which compact into a depth 1 summary; search can land on any level and lcm_expand returns the originals Search: lcm_grep FTS5, messages and summaries lcm_expand summary back to originals Depth 1 summary covers S1 and S2 S1 depth 0 store_id 101 to 104 S2 depth 0 store_id 105 to 108 compact raw messages 101 to 104 raw messages 105 to 108 lcm.db, journal_mode=WAL 441,511 raw messages 990 summary nodes 0 dangling references
Diagram B. Compaction shortens what the model reads, not what the store keeps. Every summary node lists the ids it covers, so a search can land on any level and lcm_expand can return the original text. Bottom figures are probed from the router agent's lcm.db at build time.

Live telemetry and verification

Every figure on this page is either an architectural design constant or the output of a live, read-only system check probed on 1 Nov 2026.

Telemetry figures and how to reproduce them
Figure Value Reproduce with Source
Memory layers 5 Design, see the table above declared
Router agent notes 2,199 of 2,200 characters wc -m ~/.hermes/memories/MEMORY.md probed at build
Router agent user profile 2,571 of 2,600 characters wc -m ~/.hermes/memories/USER.md probed at build
Character caps 2,200 and 2,600 grep char_limit ~/.hermes/config.yaml probed at build
Memory snapshots on disk 3 ls ~/.hermes/memories/backups/MEMORY_*.md | wc -l probed at build
Knowledge files 20 ls ~/.hermes/knowledge/*.md | wc -l probed at build
Skill files 980 (249 distinct names) find ~/.hermes/skills ~/.hermes/profiles/*/skills -name SKILL.md | wc -l declared, verified 7 Oct 2026
Agents 10 router plus ls -d ~/.hermes/profiles/*/ probed at build
lcm.db journal mode wal sqlite3 lcm.db 'PRAGMA journal_mode;' probed at build
Raw messages 441,511 across 947 sessions SELECT COUNT(*) FROM messages; probed at build
Summary nodes 990 (922 at depth 0, 63 at depth 1, 5 at depth 2) SELECT depth, COUNT(*) FROM summary_nodes GROUP BY depth; probed at build
Dangling references 0 of 50,569 integrity query below probed at build
Host Azure VPS uname -r contains azure probed at build

Integrity check for the lossless DAG claim: every store_id and node_id referenced in a summary node must exist in the database. Result on 1 Nov 2026: 0 dangling references out of 50,569.

SELECT COUNT(*) FROM summary_nodes s, json_each(s.source_ids) j
WHERE (s.source_type = 'messages'
       AND NOT EXISTS (SELECT 1 FROM messages m WHERE m.store_id = j.value))
   OR (s.source_type <> 'messages'
       AND NOT EXISTS (SELECT 1 FROM summary_nodes n WHERE n.node_id = j.value));

Prototype vs production lineage

Prototyping history

From 11 Sep 2026 I ran a local prototype on my MacBook to test two ideas: local vector embeddings for semantic recall, and the Hindsight memory service for episodic, dated facts that agents could search by meaning. On 20 Sep 2026 the platform migrated to the Azure VPS. The Hindsight daemon moved with it and kept running on the server. On the night of 20 to 21 Sep, its nightly ingest job extracted 0 items because it called a CLI command that did not exist. The daemon was also a heavy always-on process; my own notes from that time record that external daemons using more than 1 GB of RAM caused gateway stalls. On 22 Sep 2026 I looked for alternatives, listed the practical disadvantages, and removed Hindsight. Production memory uses the five structured layers described above: built-in curated Markdown with hard character caps, a knowledge base of plain files, and LCM-X on SQLite for compressed history recall. No extra daemon, no RAM cost, and the memory files are readable and editable by hand.

Prototype compared with production
AttributeLaptop prototype (11 to 20 Sep 2026)Azure VPS production (Current)
Historical recallLocal vector embeddingsLCM-X on SQLite WAL with FTS5 keyword indexing
Persistent memoryHindsight APICurated Markdown files with strict character caps
Deployment statusRan on laptop, then on Azure VPS; removed around 22 Sep 2026Active production system

Key design decisions

Failure modes and automated recovery

Failure modes and recovery
Failure conditionDetection mechanismAutomated recovery path
Write exceeds L3 character capMemory tool enforces hard boundaryRejects write and prompts consolidation
Incorrect fact added to memoryCurator audit and ledger trackingRestore previous snapshot or mark with tombstone
Summary lacks needed detailSummary lists covered store_idslcm_expand retrieves full original turns
Dangling reference in summary DAGAutomated integrity build queryPage reports count; builds flag inconsistencies
Knowledge file driftDaily md_guard.py scanAlerts on misplaced content across topic files

Explore the platform

See how memory connects with the rest of the multi-agent operations platform

Stack

Hermes Agent · LCM-X · SQLite WAL · FTS5 · Python · Markdown