Memory & Context management

Overview

eVi has two complementary mechanisms for remembering things across time:

Memory is for facts that should outlive a conversation. Context management is about fitting the current conversation into a finite window.

Both are local-first and private: nothing leaves your machine except the LLM calls you already make to your configured backend.

How it works

Memory

Memory is intentionally simple — markdown on disk, one file per topic, keyed by a safe filename with no extension (source: evi/memory.py).

The model interacts with memory through five tools (source: evi/tools/memory.py), all in the memory permission category:

ToolWhat it does
remember(name, content, tags="")Save/overwrite a memory. Empty tags keeps any existing tags.
recall(name)Return the full body of a memory.
forget(name)Soft-delete a memory (moves it to .attic/).
list_memories()JSON list of {name, summary, tags}.
recall_by_tag(tag)JSON list of memories carrying a tag.

Memory is only attached to the agent when the memory tool toggle is on (it is on by default). It is wired into both the CLI REPL and the Web UI; in the multi-user web mode each user gets their own memory directory under ~/.evi/users/<name>/memory/.

Context management (compaction)

Token counts are estimated, not tokenized: eVi uses the ~4-chars-per-token rule of thumb (source: evi/llm/agent.py _approx_tokens, and evi/context_report.py). This is good enough for "how full is my context" reporting, not a substitute for a real tokenizer.

Two things drive automatic compaction (_maybe_autocompact), checked after each turn:

  1. Message count — if the in-memory history exceeds llm.compact_after_messages messages.
  2. Capacity — if estimated tokens reach llm.compact_when_pct percent of llm.context_size.

When either fires, compact_history() runs:

The context report (evi/context_report.py) categorises history into four buckets — system, user (you), assistant, and tools (tool calls + tool results) — and reports tokens per bucket, total used, the ceiling, and percent full.

Setup

Config file

Settings live in ~/.evi/config.toml (%USERPROFILE%\.evi\config.toml). First run writes defaults. You can also point eVi elsewhere with the EVI_HOME environment variable, which relocates the whole ~/.evi/ tree (memory included).

Memory tool toggle — section [tools]:

[tools]
memory = true   # default: on — attaches the memory store + tools to the agent

If you want memory writes/reads/deletes to happen without a permission prompt, the memory category is already in the default auto-approve list — section [auto]:

[auto]
auto_approve = ["fs", "code", "memory", "skills", "image"]

Compaction + context window — section [llm]. Defaults shown:

[llm]
context_size          = 32768   # approx token ceiling for your model (0 = unknown)
compact_after_messages = 40     # compact once history exceeds this many messages (0 = disabled)
compact_keep_recent    = 10     # most-recent messages left un-summarised
compact_when_pct       = 85     # compact when usage reaches this % of context_size

Notes on these keys:

Optional: pip extras

Core memory and compaction need no extra dependencies — it's plain Markdown files and a call to your existing LLM backend. Optional integrations:

Usage

REPL slash commands (CLI and Web both)

The assistant uses the remember / recall / forget / list_memories / recall_by_tag tools on its own during conversation — just talk to it ("remember that I prefer tabs over spaces", "what do you have stored about project-x?").

Web UI

CLI commands

Managing files by hand

Because memory is just Markdown, you can edit, add, or remove files directly in ~/.evi/memory/. After editing config or memory while a session is open, /reload re-reads config.toml and refreshes the memory/skill index without restarting.

Examples

Example 1 — tune compaction for a small-context model

You're running a 7B model with an 8K window and want eVi to compact earlier and keep fewer recent turns. Edit ~/.evi/config.toml:

[llm]
model            = "qwen2.5-7b-instruct"
context_size     = 8192
compact_when_pct = 70    # start compacting at 70% full instead of 85%
compact_keep_recent = 6  # keep the last 6 messages verbatim

Then in a REPL session, check where the window is going and force a compaction if needed:

> /ctx
Context — 52 messages, ~5,910 tokens of 8,192 (72%)
  system prompt  ########----------------  1,420 (24%)
  you            #####-------------------   980 (16%)
  assistant      ########----------------  1,510 (25%)
  tools          ########----------------  2,000 (33%)

> /compact
compacted 38 messages into a summary

Example 2 — seed a memory by hand and let the model use it

Create a memory file directly (the first non-empty line becomes the index summary; the trailing comment carries tags):

# ~/.evi/memory/preferences.md
# My working preferences

- Editor: Neovim, tabs not spaces
- Always run tests before committing
- Prefer concise answers

<!-- tags: workflow, personal -->

On the next turn eVi rebuilds INDEX.md and exposes this in its system prompt, so the model can fetch it on demand. The equivalent flow driven entirely by the assistant's tools looks like:

remember(name="preferences",
         content="# My working preferences\n\n- Editor: Neovim, tabs not spaces\n...",
         tags="workflow, personal")
  -> saved memory 'preferences' to ~/.evi/memory/preferences.md [tags: workflow, personal]

recall_by_tag(tag="workflow")
  -> [{"name": "preferences", "summary": "My working preferences", "tags": ["workflow", "personal"]}]

recall(name="preferences")
  -> # My working preferences ...

Notes / limits

Reference

Edit on GitHub ↗