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
memorytool:add/replace/remove— there is no read action (the agent sees memory in-prompt).replace/removematch by a unique-substringold_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.replaceis 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 viahermes 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 → thememorytool is dropped from the schema entirely (never leaves a tool advertised that can't work). An external provider viamemory.provider(Honcho, Mem0, Hindsight, OpenViking, Holographic, RetainDB, ByteRover, Supermemory — 8 plugins) keeps its own tools and runs alongside, never replacing. write_approval: truegates 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:
SubdirectoryHintTrackerwatches 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_charsor 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_callplugin 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_manageis the agent's procedural memory;/learnauthors 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_hintsconfig key appends/replaces per-platform text (e.g. per-Telegramappend/replacerules) — 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
AIAgentfork 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 viadisplay.memory_notifications: on|off|verbose(gateway only). - Frequency/permission controls:
memory.nudge_interval,skills.creation_nudge_interval,auxiliary.background_review.enabled: false(manual/refinestill works), curatedextra_toolswhitelist for the fork. Fork usage logged insession_model_usagewithtask='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_approvalstage 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: 168since 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: truemay archive unused bundled skills; hub skills are always exempt. Phase 2 LLM consolidation (umbrella-building, merging) is opt-in (curator.consolidate: trueor--consolidate) — 50–100 aux calls per sweep. First-run defers one full interval so you can pin/opt-out first;--dry-runpreviews. - Safety rails: tar snapshot before every pass (keep 5) +
hermes curator rollback [--id]+ append-only JSONL ledger with per-mutation before/after sha256 blobs andcurator rollback <entry-id>for single-mutation undo; ledger is telemetry, never a gate.hermes curator status/ledger/pin/adopt/restore/list-archived/pauseetc. Reports in~/.hermes/logs/curator/<ts>/REPORT.md. - Context compression (the loop's runtime substrate): pluggable
ContextEngineABC, default lossyContextCompressor,context.enginepicks 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 pendingon 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):
- https://hermes-agent.nousresearch.com/docs/user-guide/features/memory
- https://hermes-agent.nousresearch.com/docs/user-guide/features/context-files
- https://hermes-agent.nousresearch.com/docs/developer-guide/prompt-assembly
- https://hermes-agent.nousresearch.com/docs/user-guide/features/skills (full page; skill_manage / write_approval / self-improvement sections)
- https://hermes-agent.nousresearch.com/docs/user-guide/features/curator
- https://hermes-agent.nousresearch.com/docs/developer-guide/context-compression-and-caching
- https://hermes-agent.nousresearch.com/docs/developer-guide/micro-compaction
- 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.

