Skip to content

Hermes: Mnemosyne long-term memory

An agent that forgets everything between two conversations is just a chatbot with tools. What distinguishes Hermes from a one-off LLM call is that it accumulates: what is said on Monday is still available on Thursday.

Mnemosyne is the external memory provider that delivers this persistence — a local SQLite database with hybrid vector + FTS5 search and background capture on every turn.

Hermes’ native memory is a MEMORY.md file re-injected into the system prompt. Simple and readable, but it plateaus quickly.

Native MEMORY.mdMnemosyne
StorageA Markdown fileSQLite database
SearchNone — everything is re-injectedHybrid: vector + FTS5
BudgetCharacter cap in the promptSelective recall driven by the query
CaptureExplicit writeBackground task on every turn
StructureFree textEpisodes, temporal triples, graph

The native file is not removed for all that: it stays in place and becomes the active memory again if the external provider is disabled.

memory:
memory_enabled: true
user_profile_enabled: true
memory_char_limit: 2200
user_char_limit: 1375
provider: mnemosyne
nudge_interval: 10
flush_min_turns: 6

Two distinct budgets: memory_char_limit for what the agent has retained, user_char_limit for the user profile it builds up across exchanges.


Why wrapper mode rather than a custom image?

Section titled “Why wrapper mode rather than a custom image?”

This is the structuring decision of the installation. Three routes were possible:

RouteCostProblem
Custom imageA Dockerfile extending the upstream imageBreaks the DIUN flow: no more automatic detection of upstream updates, manual rebuild on every version
Patching /opt/hermesModifying the application tree inside the containerOverwritten on every image update
Wrapper modeSide venv + plugin, entirely under /opt/dataSurvives recreates and image updates

Wrapper mode wins because /opt/data is a bind mount: everything living there is outside the image lifecycle. The container stays strictly the upstream image, DIUN keeps detecting new versions, and there is nothing to rebuild.

Capture runs on every turn, over everything passing through the conversation: meeting notes, technical decisions, information about contacts. Sending that stream to an external vectorisation service would amount to continuously exporting the content of the exchanges.

Mnemosyne computes its embeddings locally and stores everything in a SQLite file on the data volume. No conversation data leaves the VPS to be indexed.


ItemPath
Side venv (mnemosyne-hermesmnemosyne-memory[embeddings])/opt/data/.mnemosyne/venv
Plugin shim + manifest (discovery by Hermes)/opt/data/plugins/mnemosyne/
Bundled skill/opt/data/skills/memory/mnemosyne-memory-override/
SQLite database/opt/data/mnemosyne/data/mnemosyne.db

Activation is a single line: memory.provider: mnemosyne in the config file.

The plugin declares 20 tools and 3 hooks (pre_llm_call, on_session_start, post_tool_call):

FamilyTools
Memorymnemosyne_remember, mnemosyne_recall, mnemosyne_update, mnemosyne_forget, mnemosyne_invalidate
Graph and triplesmnemosyne_triple_add, mnemosyne_triple_query, mnemosyne_graph_query, mnemosyne_graph_link
Scratchpadmnemosyne_scratchpad_write, mnemosyne_scratchpad_read, mnemosyne_scratchpad_clear
Consolidationmnemosyne_sleep
Transfermnemosyne_export, mnemosyne_import, mnemosyne_sync_push, mnemosyne_sync_pull, mnemosyne_sync_status
Diagnosticsmnemosyne_stats, mnemosyne_diagnose

The hooks do the invisible work: on_session_start loads the relevant context, pre_llm_call injects the recall into the prompt, post_tool_call captures what deserves keeping.

Fenêtre de terminal
# Is the provider actually active?
docker exec hermes hermes memory status # → Provider: mnemosyne, available
# Database statistics
docker exec --user hermes -e HOME=/opt/data -e HERMES_HOME=/opt/data hermes \
/opt/data/.mnemosyne/venv/bin/mnemosyne stats
# Update
docker exec --user hermes -e HOME=/opt/data hermes \
/opt/data/.mnemosyne/venv/bin/pip install --no-cache-dir -U mnemosyne-hermes
docker compose -f ai-stack/docker-compose.yaml restart hermes

The database goes into the daily backup. The venv and the fastembed model cache are excluded: they are heavy and reinstall in one command.

A consequence worth knowing for a restore: the database comes back as-is, but the venv has to be recreated before the provider becomes available again.


LimitImpactMitigation
Memory tied to the containerA second user would share the same memory and the same profileOne container per user rather than a widened allowlist
Consolidation without an LLMEpisode grouping stays mechanicalMNEMOSYNE_HOST_LLM_ENABLED=true, at the cost of quota
Opaque contentThe SQLite database is not readable in ObsidianExport as markdown notes, planned
Wrapper fragile to a Python bumpSilent failure after an image updateCheck hermes memory status after every update
No memory shared with the workstationWhat Hermes knows, the local assistant does notBidirectional sync, planned

If memory has to become readable:

  • Export memories as markdown notes into vps-vault, with provenance frontmatter.
  • The point goes beyond reading: memory versioned in Git becomes auditable and correctable by hand.
  • The mnemosyne-operations skill already covers the governance of that export.

If memory has to be shared between the agent and the workstation:

  • The mnemosyne_sync_push / mnemosyne_sync_pull tools already exist on the plugin side.
  • What remains is choosing the source of truth on conflict, and deciding whether client-side encryption is needed.

If volume becomes a problem:

  • mnemosyne_invalidate and mnemosyne_forget already allow targeted pruning.
  • A regular consolidation pass compresses better than brutal deletion.

  • Hermes Agent — The deployment and its security model
  • Plugins — Mnemosyne is one of the four active plugins
  • Skillsmnemosyne-operations, the memory governance procedure
  • Database backup — The database is backed up, the venv is not
  • AI Stack — Qdrant, the infrastructure’s other vector database
  • Glossary — Long-term memory, Embeddings, Vector Database