Young's Mind
Technical

Hermes deep-dive #3 — memory, context, prompt enhancement, self-evaluation

Summary.

Hermes deep-dive #3 — memory, context, prompt enhancement, self-evaluation

Summary.

  • Memory is deliberately BOUNDED: MEMORY.md 2,200 chars (~800 tok) + USER.md 1,375 chars (~500 tok), injected as a frozen snapshot per session; overflow is an error the agent must fix by consolidation in the same turn — never silent truncation.
  • "Prompt enhancement" in Hermes is not a single feature: it is the three-tier prompt assembly (stable → context → volatile) plus five sanctioned customization surfaces you build on instead of forking prompt_builder.py. Both map exactly onto Young's build-on-the-substrate rule.
  • Self-evaluation is a concrete loop, not a slogan: post-turn background review fork → memory/skill writes (optionally gated by write_approval) → curator maintains the skill library on a 7-day prune cycle with ledgered, reversible mutations.

Key findings

1. Memory system (verified against /docs/user-guide/features/memory)

  • Two stores, both in ~/.hermes/memories/: MEMORY.md (agent notes, env facts, learned lessons; 2,200 chars) and USER.md (user profile; 1,375 chars). Typical curated load: 8–15 and 5–10 entries.
  • Frozen snapshot pattern: the system prompt copy is captured once at session start; mid-session writes persist to disk immediately but do not mutate the live prompt until next session (or a compression-triggered rebuild). This preserves prefix cache. Tool responses always show live state.
  • The memory tool: add / replace / remove — there is no read action (the agent sees memory in-prompt). replace/remove match by a unique-substring old_text; ambiguous multi-match returns an error demanding a more specific substring.
  • Full-memory behaviour: no auto-compact — the write REJECTS with usage stats and current_entries; the agent must consolidate/remove then retry in the same turn. replace is bound by the limit too: swapping in longer text can still overflow. Best practice: consolidate before adding once >80%.
  • Exact duplicates are auto-rejected ("no duplicate added"). Entries are scanned for injection/exfiltration patterns and invisible unicode before acceptance — memory is system-prompt content, so it is guarded like one.
  • session_search is the unlimited tier: all CLI + messaging sessions in SQLite (~/.hermes/state.db) with FTS5 (~20 ms), raw messages, no LLM. Division of labor: memory = always-in-context critical facts; session_search = "did we discuss X last week?".
  • The learning journey (hermes journey, /journey): timeline graph of skills + memory entries with a playable scrubber; nodes editable/deletable via hermes journey edit/delete <id>.
  • Config (memory: block): memory_enabled, user_profile_enabled, memory_char_limit: 2200, user_char_limit: 1375, write_approval: false. Set BOTH enabled flags off → the memory tool is dropped from the schema entirely (never leaves a tool advertised that can't work). An external provider via memory.provider (Honcho, Mem0, Hindsight, OpenViking, Holographic, RetainDB, ByteRover, Supermemory — 8 plugins) keeps its own tools and runs alongside, never replacing.
  • write_approval: true gates ALL saves (foreground + the background review). CLI prompts inline; elsewhere writes are STAGED → /memory pending / approve <id> / reject <id> / approval on|off. This is the designed answer to "the agent saved a wrong assumption about me".

2. Context assembly (verified against /docs/user-guide/features/context-files + /docs/developer-guide/prompt-assembly)

  • System prompt = three ordered tiers: stable (SOUL.md identity, tool guidance, skills index, platform hints) → context (caller system_message + ONE project context file) → volatile (MEMORY.md snapshot, USER.md snapshot, external provider block, timestamp/session/model line).
  • Project context files, first match wins: .hermes.md/HERMES.md (walks to git root) → AGENTS.override.md → AGENTS.md → CLAUDE.md → .cursorrules/.cursor/rules/*.mdc. SOUL.md is independent (slot #1, loaded from HERMES_HOME only — never probed in the working dir).
  • Inside a git repo, AGENTS.md loads as a merged chain root→cwd, deeper (more specific) later = wins; provenance headers per file, duplicates deduped. Outside git, ONLY cwd is checked — an AGENTS.md in /tmp or $HOME cannot leak into unrelated sessions.
  • Progressive subdirectory discovery: SubdirectoryHintTracker watches tool-call paths each turn, walks ≤5 ancestors, injects a per-directory context file (≤8,000 chars) into the tool result the moment it becomes relevant. Once per directory per session. Keeps the system prompt bloat-free and cache-stable.
  • All context content: security-scanned (injection patterns, invisible unicode, credential exfil) → head/tail truncated 70/20 with a marker (cap: context_file_max_chars or dynamic, 20k floor / 500k ceiling).
  • Ephemeral (NOT in the cached prefix): ephemeral_system_prompt, prefill messages, gateway session overlays, later-turn Honcho recall, pre_llm_call plugin context (appended to the user message). This separation is what keeps provider prompt-caching cheap.

3. Prompt enhancement — the sanctioned surfaces (from /docs/developer-guide/prompt-assembly)

The docs are explicit: treat agent/prompt_builder.py as implementation code, not a configuration surface. Build on inputs it already loads:

  • ~/.hermes/SOUL.md — agent identity/persona (replaces DEFAULT_AGENT_IDENTITY; ours doubles as the fleet persona slot).
  • ~/.hermes/MEMORY.md + USER.md — durable cross-session facts (the bounded stores above).
  • Project context files (.hermes.md, AGENTS.md, …) — repo rules.
  • Skills — reusable procedures without touching prompt code (skill_manage is the agent's procedural memory; /learn authors one from any source the agent can gather, folding re-runs into the existing skill rather than duplicating).
  • Optional system-message config / API overrides — deployment-specific text.
  • Ephemeral overlays (HERMES_EPHEMERAL_SYSTEM_PROMPT, prefill) — turn-scoped guidance that stays out of the cached prefix.
  • platform_hints config key appends/replaces per-platform text (e.g. per-Telegram append/replace rules) — resolved at prompt build, byte-stable, cache-safe. Malformed entries are ignored defensively.
  • Meta-rule that matches our stack preference verbatim: renaming personas or rules should edit SOUL/context/memories; editing prompt_builder.py is a fork-level product change.

4. Self-evaluation & self-improvement loop (verified against /docs/user-guide/features/memory + skills + curator)

  • Background review fork: after each turn, an auxiliary AIAgent fork replays the conversation (warm prompt cache on the main model = cheap cache reads; on a cheaper different model it replays a compact digest instead, ~3–5× cheaper, near-identical capture in tests). It writes memory entries and proposes/executes skill changes. Notification via display.memory_notifications: on|off|verbose (gateway only).
  • Frequency/permission controls: memory.nudge_interval, skills.creation_nudge_interval, auxiliary.background_review.enabled: false (manual /refine still works), curated extra_tools whitelist for the fork. Fork usage logged in session_model_usage with task='background_review'.
  • On the managed local llama-server runtime, reviews are deferred to idle (defer: auto, defer_max_age_s: 1800) because the fork GPU-competes with your next prompt; queued reviews coalesce and are re-queued if preempted.
  • Write gating: memory.write_approval / skills.write_approval stage ALL writes (skill staging applies even in the foreground CLI — a SKILL.MD is too big for a bubble); staged writes survive restarts under ~/.hermes/pending/. Guard on dangerous content (skills.guard_agent_created) is a separate, independent scanner.
  • Curator (skill-library maintenance, default on): trigger = inactivity check — interval_hours: 168 since last run AND ≥2h idle. Phase 1 deterministic: unused 30d → stale, 90d → archived to ~/.hermes/skills/.archive/ (never deleted). Pinned skills and any skill referenced by a cron job (even paused) are skipped; never-used skills get a grace floor. prune_builtins: true may archive unused bundled skills; hub skills are always exempt. Phase 2 LLM consolidation (umbrella-building, merging) is opt-in (curator.consolidate: true or --consolidate) — 50–100 aux calls per sweep. First-run defers one full interval so you can pin/opt-out first; --dry-run previews.
  • Safety rails: tar snapshot before every pass (keep 5) + hermes curator rollback [--id] + append-only JSONL ledger with per-mutation before/after sha256 blobs and curator rollback <entry-id> for single-mutation undo; ledger is telemetry, never a gate. hermes curator status/ledger/pin/adopt/restore/list-archived/pause etc. Reports in ~/.hermes/logs/curator/<ts>/REPORT.md.
  • Context compression (the loop's runtime substrate): pluggable ContextEngine ABC, default lossy ContextCompressor, context.engine picks it; micro-compaction (compression.micro_compact: true, off by default) amortizes compression one exchange-per-turn — trades earlier loss of verbatim older turns for no big mid-session stall. Disabling it keeps everything verbatim until the window fills.

Implications for us (fleet lanes)

  • Young's two "no fork / no silent state loss" rules are already the product's design center: frozen snapshots + curated bounded memory = no silent context loss; the sanctioned surfaces = everything is build-on-top. Our personas (SOUL.md) + per-profile memories sit exactly where the product wants them.
  • The bounded memory + write_approval staging combo opens a zero-noise lane for Tam/Echo-style auto-capture: turn the review fork on, let it stage, review /memory pending on the human's time — no prompt chatter, no runaway profile drift.
  • The curator's rules are worth copying in our own vault hygiene: never auto-delete, snapshot-before-change, append-only ledger with hash blobs, cron-referenced items exempt. Our sync lane's fail-closed + manifest discipline matches it structurally.
  • Open question for Sol: multi-agent shared memory — docs say memory is scoped per profile by design and shared facts should go through an external memory provider. Our cross-agent memory bus spec should account for that distinction (built-in stores stay per-agent; the bus is the provider lane).

Sources

All fetched live 2026-10-11 from official Hermes docs (full-page URLs, verified 200):

  1. https://hermes-agent.nousresearch.com/docs/user-guide/features/memory
  2. https://hermes-agent.nousresearch.com/docs/user-guide/features/context-files
  3. https://hermes-agent.nousresearch.com/docs/developer-guide/prompt-assembly
  4. https://hermes-agent.nousresearch.com/docs/user-guide/features/skills (full page; skill_manage / write_approval / self-improvement sections)
  5. https://hermes-agent.nousresearch.com/docs/user-guide/features/curator
  6. https://hermes-agent.nousresearch.com/docs/developer-guide/context-compression-and-caching
  7. https://hermes-agent.nousresearch.com/docs/developer-guide/micro-compaction
  8. https://hermes-agent.nousresearch.com/docs/user-guide/which-file-does-what (cross-check)

⛔ Blocked/unverified: none material. Two minor notes — "self-evaluation" has no dedicated docs page; the loop is documented across the memory + skills + curator pages (that is the correct mapping). Prompt "enhancement" likewise has no single page; prompt-assembly is the authoritative one. hermes journey details came from the memory page, not a dedicated page.

Copyright © 2026