Young's Mind
Architecture

Hermes internals — memory, context, prompt assembly, self-evaluation and improvement

Hermes internals — memory, context, prompt assembly, self-evaluation and improvement

Summary

  • Memory is a fixed, frozen budget; context is everything else. Two profile-scoped files (MEMORY.md 2,200 chars, USER.md 1,375 chars) are injected as a frozen system-prompt snapshot, always in context, agent-managed by the memory tool; session recall of everything else is session_search over SQLite FTS5, not memory pressure.1
  • The system prompt is a 3-tier cached object — stable (identity/SOUL + tool guidance), context (project files, git snapshot), volatile (skills index, memory snapshots, runtime facts last) — deliberately separated from per-call additions (ephemeral prompts, prefill, gateway overlays) so provider prompt caches survive whole sessions.4
  • Hermes self-evaluates and improves through two independent loops: a per-turn judge (goal_judge) that drives goal-loop continuation/parking, and a background self-improvement review (~every 10 turns) that persists lessons as agent-created skills for the Curator to lifecycle-manage — plus curation-side linters that flag, never silently rewrite, skills that drift into log-shaped prose.[9]8
  • This note is doc-verified, not a live-trace. Where the docs are silent (memory-file on-disk format, FTS internals, consolidation prompt internals, turn-counter logic), the gaps are listed rather than guessed; unchanged claims were cross-checked against hermes-agent.nousresearch.com/docs/llms.txt and only retained when a cited doc page carried them.[1][9]12

Memory — the frozen-budget model

  • Two files live in ~/.hermes/memories/ per profile: MEMORY.md (2,200 chars) and USER.md (1,375 chars), and are reshaped ONLY by the memory tool's add/replace/remove actions — there is no read action, because the whole snapshot already sits in the system prompt header with usage percentages and §-delimited entries.1
  • The agent writes here proactively (post-turn background review included); writes carry a write_approval staging gate, duplicate-content rejection, and a blocked pattern list (injection/exfiltration/invisible Unicode). Over-limit writes fail with current usage, forcing an in-tool consolidation before retry. No auto-compaction of memory exists — a full memory must be consolidated by the agent, not pruned by the runtime.1
  • External memory providers (8 plugins: Honcho, OpenViking, Mem0, Hindsight, Holographic, RetainDB, ByteRover, Supermemory) run alongside the built-in files, not instead of them.1
  • session_search is deliberately not memory: it stores and queries real session data from ~/.hermes/state.db (SQLite FTS5, sub-second, no LLM in the loop) so recall is on-demand rather than in-context; this split (fixed budget vs. instant recall) is the core memory design in Hermes.2

Context — what fills the window, and what never costs

  • Context files use a first-match-wins priority chain (​.hermes.md → AGENTS.md → CLAUDE.md → ​.cursorrules, one only), and are loaded per project from the current working directory rather than a central store; SOUL.md is independent and always resolved from $HERMES_HOME/SOUL.md only, never from a project directory.[3]6
  • In git repos, AGENTS.md files from the git root down to the CWD merge into one block (specific-wins, provenance headers, dedupe); in non-git directories only the CWD file is loaded. Progressive discovery via SubdirectoryHintTracker injects subdirectory AGENTS.md blocks into later tool results — at most 5 ancestors, 8 files, 8,000 chars, once per directory per session.3
  • ~/.hermes/state.db is the canonical session store (WAL, schema v23): sessions, messages, messages_fts (FTS5 + trigram/cjk tables), compression locks, async delegations; api_content is a byte-fidelity sidecar used for prompt-cache-stable replay after compression splits, with parent_session_id preserving lineage.2
  • Write contention is handled with a 1-second SQLite busy timeout, 15 jittered retries, BEGIN IMMEDIATE and a PASSIVE checkpoint every 50 writes — the design accommodates multiplexed gateway processes sharing the same DB.2
  • @ context references are a CLI-side expansion (@file:, @folder:, @diff, @git:, @url:) that injects fetched material into the next user turn, bounded by a soft 25% warning and a hard 50% refusal. Sensitive paths (.ssh/, .aws/, $HERMES_HOME/.env, service configs) are always blocked, traversal outside the workspace root is rejected, and binaries are rejected by MIME/null-byte check. On gateways/Telegram the equivalent is agent tools, not @ syntax.11

Prompt assembly — three tiers, one cache

  • The three tiers are ordered stable → context → volatile so that shared blocks match at the front and volatile blocks (memory snapshot, skills index, timestamp/session/model line, runtime env hints) sit at the very end. This is what preserves longest-prefix prompt caching across sessions, worktrees and models of the same project.4
  • Everything that varies per turn lives on the "API-call-time-only" path — ephemeral_system_prompt, prefill, gateway overlays, pre_llm_call plugin blocks — and is appended to that turn's user message rather than mutating the cached prompt. Mid-session changes like skill-index diffs or platform switches ride a one-shot note on the same per-turn channel, then converge into the cache at the next rebuild.4
  • The project context files are ordered so the shared part (project files) precedes the worktree-specific part (git snapshot), which is what lets the whole context-tier prefix be reused across sibling worktrees of one repo rather than invalidated per check-out.4
  • load_soul_md() reads and sanitizes SOUL.md, then its content replaces the built-in default identity block; build_context_files_prompt(skip_soul=True) exists to prevent a double load, so SOUL lives in slot #1 of the prompt and personality overlays come later as temporary session overrides rather than a separate channel.[4]6
  • Skills use progressive disclosure: the index (~3K tokens) sits in the system prompt, skills_list() is the on-demand level 0 view, and skill_view(name[, file]) pulls the body or a reference only when the task needs it — the docs' "skill is the better home for procedures" is the cache-stable trade-off against memory's fixed char budget.8

Context compression, caching, and cost of the loop

  • Hermes has two compressors, by design: a gateway-level hygiene net at 85% of context (fires only when history exceeds 4 messages, so the agent compressor missed it) and an in-loop ContextCompressor at 50% (default compression.threshold, floored to 0.75 for windows under 512K). The 85% exists specifically because a gateway firing at 50% compressed before every turn — the threshold drift is a lesson, not a typo.5
  • Trigger decisions lean on provider evidence, not estimates: the usage anchor combines real reported tokens with an estimate of appends since, and only unanchored over-threshold runs wait one request for a reported token count; provider-proven overflow compresses immediately. Pluggable context.engine swaps the whole internals, and plugin engines never auto-activate.[5]12
  • Prompt-cache invalidation is documented exactly: any mid-sequence insert/remove of messages after a hit prefix, and any post-compression region, invalidate that segment's cache; the system-prompt cache survives compression, and rolling system_and_3 re-establishes prompts within 1–2 turns. Model/credential identity is part of the cache key — a /model switch or key-pool rotation legitimately produces zero cache hits until the new identity establishes a prefix, which is the documented cost warning rather than a bug.5
  • Micro-compaction (opt-in) folds one assistant exchange per turn into a running summary, keeping user messages always verbatim; the price is a synchronous prompt-cache prefix break every turn, so it is a cadence dial — micro_compact_every_n_turns — rather than a free win. Failure ladders (cooldowns 60s→300s→900s) and provider-proven overflow bypasses are documented fail-safe paths.12

Delegation — prompt extension by context, not by prompt

  • A delegated child sees only the text the parent passed: delegate_task(tasks=[{goal, context}]) spawns an isolated conversation whose ONLY prompt extension is the context field, so paths, project shape and constraints must ride inside it rather than being inherited implicitly.7
  • Children inherit the parent's toolsets minus the blocked ones, cannot re-delegate by default (orchestrators with delegation.max_spawn_depth > 1 can), and only the final summary returns — intermediate tool calls never enter the parent's context, which is why summaries must be verified before being trusted as fact.7
  • Delegation durability is bounded by the parent process: background children are process-local, so cross-restart work needs cron or detached terminal processes instead of a stranded background child.

Skills pipeline — how a lesson becomes a future-session capability

  • A skill is a directory with a required SKILL.md plus optional references/ and scripts/, carrying structured frontmatter (name, description with trigger-first wording, version, author, license, platforms and Hermes metadata tags); this is the agentskills.io-compatible shape and it is what makes a lesson loadable rather than a prose memory dump.10
  • The chain back into future sessions: the system prompt carries the index, skills_list() is the level-0 view (~3K tokens), skill_view(name[, path]) loads the body or a reference file on demand, and mid-conversation changes ride a one-shot note on the user-message channel so the cached index converges at the next compaction rather than breaking cache mid-session.[10]8

Self-evaluation and improvement — three independent mechanisms

  • In-loop judge (goal_judge): every turn's last output is judged done | continue | blocked against the standing goal. This is what drives both chat-session /goals and Kanban --goal cards; fail-open on judge errors, so a broken judge cannot wedge the loop, and the turn budget is the backstop.4
  • Background self-improvement review fork: runs ~ every 10 agent turns in its own prompt cache (so it does not cost a paid call in the live conversation) and, via skill_manage with created_by: background_review, persists a lesson as an agent-created skill. This is the mechanism that gives Hermes "self-improvement" without human triage of every change.8
  • Curator: a two-phase skill lifecycle manager. Phase 1 is deterministic inactivity sweeps (active → stale @14 days → archived @30 days) into ~/.hermes/skills/.archive/, never auto-delete; phase 2 (opt-in via curator.consolidate) is an LLM pass that can merge, umbrella, or archive agent-created skills — the ~50–100 API calls are a deliberate opt-in cost, not silent background work. Telemetry lives in .usage.json; hand-written/external skills stay unmanaged unless explicitly adopted. Vault-style fused-rule: curator never deletes; the worst it does is archive.9
  • The three mechanisms never cross each other: the judge does not save skills, the self-improvement fork does not edit config, and curation's consent gate keeps hand-written skills out of the LLM's reach. This separation is the design that makes the loop simple to reason about.[9]8

What is deliberately NOT documented (do not treat as verified)

  • The exact on-disk format of MEMORY.md/USER.md entry boundaries (beyond the § delimiter convention visible in the prompt header).
  • How automatically-generated memory entries are captured (the hook, the interval, or the prompt that drives auto-capture).
  • The FTS5 index internals and how it is kept in sync with messages under high write contention.
  • Exact programmatic trigger of parent_session_id splits during compression, beyond the doc's own stated behavior.
  • The consolidated curator LLM prompt internals and exact turn-counter for the ~every-10-turns fork.
  • kanban_worker and spawned-Hermes-process prompt extensions are not in this note — they live in the Kanban worker-lane docs and are out of scope here.

Relation

2026-10-11-hermes-kanban-multi-agent-collab

Sources

1 https://hermes-agent.nousresearch.com/docs/user-guide/features/memory2 https://hermes-agent.nousresearch.com/docs/developer-guide/session-storage3 https://hermes-agent.nousresearch.com/docs/user-guide/features/context-files4 https://hermes-agent.nousresearch.com/docs/developer-guide/prompt-assembly5 https://hermes-agent.nousresearch.com/docs/developer-guide/context-compression-and-caching6 https://hermes-agent.nousresearch.com/docs/guides/use-soul-with-hermes7 https://hermes-agent.nousresearch.com/docs/guides/delegation-patterns8 https://hermes-agent.nousresearch.com/docs/user-guide/features/skills9 https://hermes-agent.nousresearch.com/docs/user-guide/features/curator10 https://hermes-agent.nousresearch.com/docs/developer-guide/creating-skills11 https://hermes-agent.nousresearch.com/docs/user-guide/features/context-references12 https://hermes-agent.nousresearch.com/docs/developer-guide/agent-loop

Copyright © 2026