|
| 1 | +--- |
| 2 | +title: "Hermes Agent" |
| 3 | +sidebarTitle: "Hermes Agent" |
| 4 | +description: "Use LanceDB as a persistent, semantic memory backend for Hermes Agent — durable recall across sessions with vector and hybrid search." |
| 5 | +--- |
| 6 | + |
| 7 | +[Hermes Agent](https://github.com/NousResearch/hermes-agent) is a self-hosted, open-source |
| 8 | +personal agent from [Nous Research](https://nousresearch.com). You can talk to it from a |
| 9 | +terminal UI or reach the same agent from Telegram, Discord, and Slack, and it exposes a |
| 10 | +dedicated slot for external *memory providers* that run alongside its built-in notes. |
| 11 | + |
| 12 | +The [LanceDB memory plugin](https://github.com/lancedb/hermes-agent-memory) fills that slot. |
| 13 | +It gives Hermes durable, semantic recall across sessions: state a preference or a project |
| 14 | +convention once, and the agent can retrieve it weeks later in a brand-new session — even when |
| 15 | +you ask for it in completely different words. Everything runs inside Hermes' own Python |
| 16 | +process, storing a single LanceDB table on local disk. There's no memory server to operate. |
| 17 | + |
| 18 | +The mental model is clean: **Hermes owns the agent loop; LanceDB manages the durable |
| 19 | +long-term memory and offers semantic recall.** |
| 20 | + |
| 21 | +## Why LanceDB fits agent memory |
| 22 | + |
| 23 | +Out of the box, Hermes remembers with a small curated notes file frozen into the system |
| 24 | +prompt, plus lexical (keyword) search over past sessions. Both are useful, but keyword search |
| 25 | +misses paraphrases of what you originally typed — the exact thing you need when recalling a |
| 26 | +fact you phrased differently months ago. |
| 27 | + |
| 28 | +LanceDB is an embedded retrieval library, which makes it a natural fit here: |
| 29 | + |
| 30 | +- **No server to stand up** — it reads and writes a table on local disk, so the plugin ships |
| 31 | + as a dependency rather than a service to operate. |
| 32 | +- **One table holds everything** — content, metadata, and embeddings live together. A memory |
| 33 | + becomes a structured row with a category, tags, timestamps, and provenance, not just a text |
| 34 | + blob. |
| 35 | +- **Query it any way you need** — vector similarity for meaning, BM25 full-text for exact |
| 36 | + names and jargon, a hybrid of the two, or plain metadata filters to keep recall scoped to |
| 37 | + the right workspace. |
| 38 | +- **It scales up** — the same table abstraction carries over to larger LanceDB deployments |
| 39 | + later, so the local setup is never a dead end. |
| 40 | + |
| 41 | +## Install and activate |
| 42 | + |
| 43 | +<Tip> |
| 44 | +Want to try this without touching your existing Hermes setup? Run everything in an isolated |
| 45 | +profile: `hermes profile create demo`, then add `-p demo` to the commands below. When you're |
| 46 | +done, `rm -rf ~/.hermes/profiles/demo` removes all trace. |
| 47 | +</Tip> |
| 48 | + |
| 49 | +<Steps> |
| 50 | +<Step title="Install Hermes Agent"> |
| 51 | +Skip this if you already have Hermes installed. |
| 52 | + |
| 53 | +```bash |
| 54 | +curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash |
| 55 | +``` |
| 56 | +</Step> |
| 57 | + |
| 58 | +<Step title="Install the plugin"> |
| 59 | +This shallow-clones the plugin into `~/.hermes/plugins/lancedb/`. |
| 60 | + |
| 61 | +```bash |
| 62 | +hermes plugins install lancedb/hermes-agent-memory |
| 63 | +``` |
| 64 | +</Step> |
| 65 | + |
| 66 | +<Step title="Install runtime dependencies into Hermes' environment"> |
| 67 | +Hermes loads plugins inside its own Python interpreter, so the dependencies go *there* — not |
| 68 | +into a separate virtualenv. (This interpreter is shared across profiles, so you only install |
| 69 | +once.) |
| 70 | + |
| 71 | +```bash |
| 72 | +uv pip install --python ~/.hermes/hermes-agent/venv/bin/python3 lancedb openai pyyaml |
| 73 | +``` |
| 74 | +</Step> |
| 75 | + |
| 76 | +<Step title="Set your embeddings API key"> |
| 77 | +The plugin turns conversations into embeddings, so it needs an embeddings key. By default that |
| 78 | +is OpenAI, so set `OPENAI_API_KEY` in your environment or in `~/.hermes/.env`. |
| 79 | + |
| 80 | +<Info> |
| 81 | +Prefer a local or non-OpenAI model? The plugin uses an OpenAI-compatible client, so you can |
| 82 | +point it at any compatible endpoint (OpenRouter, Ollama, vLLM, …) in your config — no code |
| 83 | +change needed. See [Configuration](#configuration) below. |
| 84 | +</Info> |
| 85 | +</Step> |
| 86 | + |
| 87 | +<Step title="Activate and verify"> |
| 88 | +Switch memory on and pick this plugin: |
| 89 | + |
| 90 | +```bash |
| 91 | +hermes memory setup # choose "lancedb" |
| 92 | +``` |
| 93 | + |
| 94 | +Then confirm it's actually active before you start chatting — this is the one step worth not |
| 95 | +skipping, because Hermes quietly falls back to its built-in notes if the provider isn't set: |
| 96 | + |
| 97 | +```bash |
| 98 | +hermes memory status |
| 99 | +``` |
| 100 | + |
| 101 | +```text |
| 102 | +Memory status |
| 103 | +──────────────────────────────────────── |
| 104 | + Built-in: always active |
| 105 | + Provider: lancedb |
| 106 | +
|
| 107 | + Plugin: installed ✓ |
| 108 | + Status: available ✓ |
| 109 | +``` |
| 110 | + |
| 111 | +You want to see `Provider: lancedb` with both `installed ✓` and `available ✓`. |
| 112 | +</Step> |
| 113 | +</Steps> |
| 114 | + |
| 115 | +## The memory tools |
| 116 | + |
| 117 | +Once activated, the agent has four tools for working with long-term memory: |
| 118 | + |
| 119 | +| Tool | What it does | |
| 120 | +|:--|:--| |
| 121 | +| `lancedb_recall` | Semantic (vector, the default) or hybrid search over your workspace memory. Returns matching facts with scores and provenance. | |
| 122 | +| `lancedb_remember` | Stores a durable fact when you explicitly ask. Deduplicated by content hash, so remembering the same thing twice doesn't pile up rows. | |
| 123 | +| `lancedb_read` | Fetches a single memory by ID, optionally with the original conversation messages it was distilled from. | |
| 124 | +| `lancedb_forget` | Deletes safely: previews candidates first, then deletes by exact ID, so nothing disappears by accident. | |
| 125 | + |
| 126 | +Beyond these tools, the plugin also captures durable facts from your conversations |
| 127 | +automatically — an auxiliary model distills them before context is compressed and again when a |
| 128 | +session ends, so insights survive even when the raw messages are summarized away. |
| 129 | + |
| 130 | +## Walkthrough: teach it a project convention |
| 131 | + |
| 132 | +Let's make this concrete with the pain we opened on: re-explaining your setup every session. |
| 133 | +We'll save a convention once, then prove a brand-new session can recall it — touching all four |
| 134 | +tools along the way. |
| 135 | + |
| 136 | +### Remember |
| 137 | + |
| 138 | +Ask Hermes to commit a convention to long-term memory. Saying "remember in long-term memory" |
| 139 | +makes sure it lands in the LanceDB store, which shows up as the `⚡ lancedb_r` (`lancedb_remember`) |
| 140 | +line below: |
| 141 | + |
| 142 | +```text |
| 143 | +● Remember in long-term memory: for this project I only use uv, never pip, and I always add type hints to Python functions. |
| 144 | +
|
| 145 | + ┊ 🧠 memory +memory: "For this project, the user only uses uv for Python package management, never pip, and always adds type hints to Python functions." |
| 146 | + ┊ ⚡ lancedb_r 0.0s |
| 147 | + ─ ⚕ Hermes ──────────────────────────────────────────────────────────────── |
| 148 | +
|
| 149 | + Remembered. I've stored that project convention: use uv only, never pip, and always add type hints to Python functions. |
| 150 | +``` |
| 151 | + |
| 152 | +### Recall |
| 153 | + |
| 154 | +First, take Hermes' built-in notes out of the picture so recall can *only* come from LanceDB — |
| 155 | +the two layers run side by side otherwise, and either could answer: |
| 156 | + |
| 157 | +```bash |
| 158 | +hermes config set memory.memory_enabled false |
| 159 | +``` |
| 160 | + |
| 161 | +Now start a fresh session (`/new`) and ask for the convention back, worded completely |
| 162 | +differently from how you saved it: |
| 163 | + |
| 164 | +```text |
| 165 | +● /new |
| 166 | +● Look through your long-term memory for my Python project preferences. |
| 167 | +
|
| 168 | + ─ ⚕ Hermes ──────────────────────────────────────────────────────────────── |
| 169 | +
|
| 170 | + I found one durable memory about Python project preferences: |
| 171 | +
|
| 172 | + - Project Python tooling preference is uv; pip is not used; type hints are required. |
| 173 | +``` |
| 174 | + |
| 175 | +This is the whole point in one exchange. You asked about your *preferences* using none of the |
| 176 | +words you stored the fact with — no *uv*, no *pip*, no *type hints* — yet a fresh session, with |
| 177 | +none of the earlier conversation in context, pulls it straight back. Semantic search matches on |
| 178 | +meaning rather than exact wording, which is precisely where keyword search falls short. |
| 179 | + |
| 180 | +<Tip> |
| 181 | +Turn the built-in layer back on for everyday use with `hermes config set memory.memory_enabled true` |
| 182 | +— the two memory layers happily coexist. We only disabled it here to prove the recall came from |
| 183 | +LanceDB. |
| 184 | +</Tip> |
| 185 | + |
| 186 | +### Read |
| 187 | + |
| 188 | +You can also ask where a fact came from. Hermes attributes the answer to its stored memory |
| 189 | +rather than guessing from a file in the repo (under the hood, `lancedb_read` can also return |
| 190 | +the exact source messages a fact was distilled from): |
| 191 | + |
| 192 | +```text |
| 193 | +● Where did that uv preference come from? |
| 194 | +
|
| 195 | + ─ ⚕ Hermes ──────────────────────────────────────────────────────────────── |
| 196 | +
|
| 197 | + It came from the project's persistent memory, not from a codebase file I inspected. |
| 198 | +
|
| 199 | + Specifically, the recalled memory says: |
| 200 | + - "For this project, the user only uses uv for Python package management, never pip, and always adds type hints to Python functions." |
| 201 | +``` |
| 202 | + |
| 203 | +### Forget |
| 204 | + |
| 205 | +When a preference changes, ask Hermes to drop the old fact. The tool calls tell the whole |
| 206 | +story: the two `⚡ lancedb_f` (`lancedb_forget`) lines are it previewing matches and then |
| 207 | +deleting, and the trailing `⚡ lancedb_r` is it saving the replacement in the same breath: |
| 208 | + |
| 209 | +```text |
| 210 | +● I've switched from uv to pixi. Forget the uv preference. |
| 211 | +
|
| 212 | + ┊ ⚡ lancedb_f uv preference python package management never pip type hints 0.3s |
| 213 | + ┊ ⚡ lancedb_f 0.0s |
| 214 | + ┊ ⚡ lancedb_r 0.2s |
| 215 | + ─ ⚕ Hermes ──────────────────────────────────────────────────────────────── |
| 216 | +
|
| 217 | + Done — I removed the uv preference from memory and replaced it with: |
| 218 | +
|
| 219 | + - This project uses pixi for Python package management |
| 220 | + - Type hints on Python functions are still preferred |
| 221 | +``` |
| 222 | + |
| 223 | +Remember, recall, read, forget: four small operations that between them cover the entire |
| 224 | +lifecycle of a durable memory. |
| 225 | + |
| 226 | +## Retrieval modes |
| 227 | + |
| 228 | +Recall ships in `vector` mode by default — pure semantic search, which is what survives the |
| 229 | +paraphrasing you saw above. If you also need exact name or jargon matching, switch to `hybrid` |
| 230 | +(vector + BM25) and choose how the two legs are fused: RRF, a vector-biased linear blend, or a |
| 231 | +cross-encoder reranker. Mode is set per call; fusion is a config setting. |
| 232 | + |
| 233 | +```yaml |
| 234 | +# ~/.hermes/config.yaml |
| 235 | +plugins: |
| 236 | + lancedb: |
| 237 | + retrieval: |
| 238 | + mode: hybrid # vector (default) | hybrid |
| 239 | + reranker: |
| 240 | + type: rrf # how the vector + BM25 legs are fused |
| 241 | + # Swap RRF for a reranking pass (pulls in sentence-transformers + torch): |
| 242 | + # type: cross-encoder |
| 243 | + # model: cross-encoder/ettin-reranker-17m-v1 |
| 244 | + # rerank_top_n: 50 |
| 245 | +``` |
| 246 | + |
| 247 | +The cross-encoder is the one path that pulls in a local ML stack, so it stays opt-in. It |
| 248 | +defaults to the compact 17M-parameter [ettin reranker](https://huggingface.co/cross-encoder/ettin-reranker-17m-v1). |
| 249 | + |
| 250 | +## Inspect the store |
| 251 | + |
| 252 | +Everything lives in one table named `memories` at `~/.hermes/lancedb/memories.lance`. Because |
| 253 | +it's a plain LanceDB table, you can open it directly and see exactly what the agent has stored |
| 254 | +— a `kind` column separates extracted `fact` rows from the raw `turn` rows they were drawn |
| 255 | +from: |
| 256 | + |
| 257 | +```python |
| 258 | +import lancedb |
| 259 | + |
| 260 | +db = lancedb.connect("~/.hermes/lancedb") |
| 261 | +tbl = db.open_table("memories") |
| 262 | +print(tbl.to_pandas()[["kind", "category", "content"]].head()) |
| 263 | +``` |
| 264 | + |
| 265 | +## Configuration |
| 266 | + |
| 267 | +The plugin runs on sensible defaults once activated — you don't have to configure anything. |
| 268 | +`~/.hermes/config.yaml` is purely for overrides. Two common ones: |
| 269 | + |
| 270 | +Use a cheaper model for the auxiliary fact-extraction calls: |
| 271 | + |
| 272 | +```yaml |
| 273 | +# ~/.hermes/config.yaml |
| 274 | +auxiliary: |
| 275 | + lancedb_extraction: |
| 276 | + provider: openrouter |
| 277 | + model: google/gemini-3-flash |
| 278 | +``` |
| 279 | +
|
| 280 | +Point embeddings at a fully local endpoint (for example, Ollama) so nothing leaves your |
| 281 | +machine: |
| 282 | +
|
| 283 | +```yaml |
| 284 | +# ~/.hermes/config.yaml |
| 285 | +plugins: |
| 286 | + lancedb: |
| 287 | + embedding: |
| 288 | + model: nomic-embed-text |
| 289 | + base_url: http://localhost:11434/v1 |
| 290 | + api_key_env: OLLAMA_API_KEY # any value works for local Ollama |
| 291 | +``` |
| 292 | +
|
| 293 | +<Info> |
| 294 | +Changing the embedding model (or its dimension) against an existing store requires recreating |
| 295 | +the table — the plugin fails loudly on a dimension mismatch rather than silently returning |
| 296 | +nothing. Every option is documented in the plugin's [`default_config.yaml`](https://github.com/lancedb/hermes-agent-memory/blob/main/src/default_config.yaml). |
| 297 | +</Info> |
| 298 | + |
| 299 | +## Benchmark |
| 300 | + |
| 301 | +On [LongMemEval-S](https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned), a |
| 302 | +long-conversation QA benchmark, LanceDB's semantic recall clearly beat Hermes' built-in lexical |
| 303 | +search (0.66 vs. 0.53 answer accuracy) by finding the right messages even when the question was |
| 304 | +worded differently from the original conversation. For the full methodology, the |
| 305 | +per-question-type breakdown, and a reproducible harness, see the |
| 306 | +[blog post](https://www.lancedb.com/blog/semantic-memory-for-hermes-agent-with-lancedb) and the |
| 307 | +[benchmark harness](https://github.com/lancedb/hermes-agent-memory/tree/main/benchmarks). |
| 308 | + |
| 309 | +## Why this works well |
| 310 | + |
| 311 | +- **It's local-first and embedded.** The memory table lives on your disk with no server to run; |
| 312 | + the plugin installs as a dependency of Hermes' own environment. |
| 313 | +- **Recall survives paraphrasing.** Semantic search matches meaning, not spelling, which is the |
| 314 | + failure mode that sinks keyword-only session search. |
| 315 | +- **Memories are structured and traceable.** Each fact is a row with metadata and a link back |
| 316 | + to the messages it came from, and `forget` always previews before it deletes. |
| 317 | +- **Nothing about it is a dead end.** As your needs grow, the same table abstraction carries |
| 318 | + over to LanceDB [Enterprise](/enterprise) for automatic compaction, reindexing, and scale. |
| 319 | + |
| 320 | +To try it, install the plugin, enable it with `hermes memory setup`, and run the kind of |
| 321 | +workflow we walked through above. |
0 commit comments