Engram Documentation

v0.3.0 · a transparent memory sidecar for local and cloud LLMs

Install

# the package (Python 3.11+)
pip install engram-proxy
# or run without installing:
uvx --from engram-proxy engram up

# the local models Engram uses for background work (~2 GB total)
ollama pull qwen3:1.7b qwen3-embedding:0.6b

Requirements: Ollama running locally (for retrieval embeddings and background summarization - required even when your chat model is in the cloud), Python 3.11+. Tested on macOS Apple Silicon; Linux should work identically.

Quickstart

engram up                      # proxy on :11435, forwarding to Ollama on :11434

# in another terminal: same ollama CLI, new port - that's the whole integration
export OLLAMA_HOST=127.0.0.1:11435
ollama run qwen3:1.7b

Tell the model a fact. Quit. Start a new session and ask about it obliquely - the answer can only come from memory. Watch what happened with engram why, or open the live brain view at 127.0.0.1:11435/engram/ui.

Memories form a few seconds after each exchange (background summarization waits for GPU idle), so a fact becomes recallable in the next session, not the same instant you say it.

Integrations

Stock Ollama CLI / any OpenAI-compatible client

export OLLAMA_HOST=127.0.0.1:11435        # ollama CLI
base_url = "http://127.0.0.1:11435/v1"    # OpenAI SDKs, Hermes Agent, etc.

OpenClaw

# OpenClaw mandates Ollama's native API - Engram speaks it byte-faithfully
{ "models": { "providers": {
  "engram": { "baseUrl": "http://127.0.0.1:11435", "api": "ollama" }
} } }

Anthropic-protocol clients (local memory for cloud models)

ENGRAM_ANTHROPIC_UPSTREAM=https://api.minimax.io engram up
# point the client's anthropic base_url at:
http://127.0.0.1:11435/anthropic

Auth headers pass through untouched. The whole Messages surface follows the cloud upstream (count_tokens, batches, prefixed /v1/models included).

CLI reference

commandwhat it does
engram uprun the proxy daemon (also serves the observatory at /engram/ui)
engram list [-n N]recent traces: title, gist, access weight, origin
engram recall "query"dry-run recall: per-candidate scores, admission verdicts, the block that would inject. Never mutates the store.
engram whyexplain the last real injection, score by score
engram profileshow the consolidated user profile
engram statsstore counts, queue depth, degraded-recall counter
engram bench [-n N]measure embed + recall latency on your machine
engram lme --n 100LongMemEval-S subset harness: per-question fresh store, deterministic retrieval scoring, configurable judge (see BENCHMARKS.md). --watch PORT serves a live progress dashboard while it runs
engram eval [--model M]the built-in memory regression scenario: seeds a throwaway store over a scripted multi-session conversation, then scores retrieval and answers on 10 probes
engram lapse 72hadvance the decay clock (testing/demos) - accepts 30m / 72h / 45d
engram import file.mdbootstrap memories from a notes file (e.g. an agent's MEMORY.md), backdated so they neither flood recall nor arrive decayed
engram mcprun the memory-verbs MCP server on stdio

HTTP endpoints

routebehavior
POST /api/chatOllama-native NDJSON, memory-enabled
POST /v1/chat/completionsOpenAI-compatible SSE, memory-enabled
POST [/prefix]/v1/messagesAnthropic Messages (streams SSE), memory-enabled; routes to ENGRAM_ANTHROPIC_UPSTREAM when set
/api/generate, /api/tags, /api/show, /v1/models, embeddings…byte passthrough (raw completions are never injected - there is no safe injection point in a FIM prompt)
GET /engram/uithe observatory (live brain view)
GET /engram/memoriesJSON trace snapshot with live activation
GET /engram/why · /engram/statslast injection explained · store health

Per-request headers: X-Engram: off bypasses memory; X-Engram-Session: id pins session identity.

Configuration

Everything is environment variables; defaults are field-tested.

variabledefaultmeaning
ENGRAM_PORT11435proxy listen port
ENGRAM_UPSTREAMhttp://127.0.0.1:11434chat forward target (OpenAI/Ollama routes)
ENGRAM_ANTHROPIC_UPSTREAM(empty)where /v1/messages traffic goes - set to a cloud endpoint to give a cloud model memory
ENGRAM_OPENAI_UPSTREAM(empty)where /v1/chat/completions goes - any OpenAI-compatible provider (OpenRouter, DeepSeek, ...). /v1/models follows it; /v1/embeddings and Ollama-native /api/chat always stay local
ENGRAM_SERVICEShttp://127.0.0.1:11434local Ollama for embeddings, summarization, /api/ps - background work never leaves this
ENGRAM_DB~/.engram/engram.dbthe store. Back it up with sqlite3 … ".backup out.db" (WAL mode makes bare cp copies stale)
ENGRAM_EMBED_MODELqwen3-embedding:0.6bretrieval embedder. nomic-embed-text is ~3× faster but measurably worse at telling related from unrelated cues
ENGRAM_SUMMARIZER_MODELqwen3:1.7bgist writer (fact-dense: every stated name, number, and date survives as its own clause). Sub-1B models destroy facts and are unsupported; a missing model falls back to extractive summaries
ENGRAM_PROFILE_MODEL(summarizer)model for profile consolidation
ENGRAM_QUARANTINE(empty)comma-separated source labels never injected, e.g. observed (all third-party) or observed:Ops Team
ENGRAM_EXPAND_MIN1000token-budget floor for evidence expansion (verbatim episode quotes under each gist on big tiers); 0 disables
ENGRAM_ANN_TRACES50000store size at which dense search switches from exact to the IVF index (pure numpy, recall parity 1.000 on clustered vectors, benchmarked). Below it there is nothing to tune

Memory verbs (MCP)

Agents that can call tools get explicit memory verbs over the same store as the transparent proxy. Register engram mcp as a stdio MCP server in your agent framework.

toolteaches the agent to…
memory_searchalways check memory before answering about the user's past; include_silent shows superseded/forgotten history
memory_savepersist a stated fact instantly (no waiting for background formation)
memory_correctfix a wrong/outdated memory - supersedes (history kept, never re-injected), never overwrites
memory_editreword a garbled gist in place (for changed facts, use correct instead)
memory_pinmark permanently important: outranks everything, never decays, still supersedable
memory_forgetsilence (reversible) or, with hard=true, delete for privacy
memory_profileread (or refresh) the consolidated user profile
memory_recent · memory_statsnewest traces · store health

Architecture

client ──► engramd :11435 ──► upstream (Ollama :11434 / cloud)
             │  recall: hybrid search + ACT-R activation (~90ms, no LLM)
             │  inject: budgeted block, placement preserves prompt caches
             │  tee: response streams byte-verbatim; shadow copy → episode log
             └─ write-behind (GPU-idle only): summarize → embed → link → store
                     └── engram.db  (SQLite WAL: traces, episodes, links,
                                     schemas, FTS5, vectors, job queue)

Memory lifecycle

Provenance & trust

Every memory records whose words it came from:

Injected blocks are stripped before storage (Engram never re-summarizes its own output), byte-identical retries dedup at half weight, and ENGRAM_QUARANTINE can bar any source from injection entirely while staying searchable.

The observatory

The Engram memory observatory showing memories orbiting a neural core by activation

Open /engram/ui: memories orbit a neural core at a radius set by their live activation - reinforced memories fall inward, decaying ones drift out, recalls flare ember, pinned traces wear a ring, silent and superseded history dims at the rim. Press / to search; click any mote for its full dossier (gist, β importance, access history, provenance). It polls the daemon every 4s and doubles as the debugging surface: if recall surprises you, look at the sky. Click the LAST RECALL readout (bottom right) to inspect the last injection in full: block, latency, mode, and scored candidates, each clickable through to its dossier. Linkable as /engram/ui#recall.

A selected memory with its hebbian web lit and the full dossier panel

Selecting a memory keeps its hebbian web lit: ember filaments run to every linked trace while the dossier shows the gist, activation, importance, provenance, and pin state. Deep-linkable as /engram/ui#trace=<id>.

Hovering a mote shows a tooltip with its id, status, live activation, and title

Hovering any mote shows its id, status, live activation, and title in place, and lights up its links; here the ember-ringed pinned trace with its web.

A brand new Engram observatory: a lone neural core, empty orbit rings, zero traces held The same observatory lived in: 28 memories orbiting the core, hebbian filaments between them, silent history at the rim

Day one and lived in. A fresh store is a lone core over empty orbits; every conversation after that grows the constellation - memories fall inward as they prove useful, links form between the ones recalled together, and the ones that stop mattering dim toward the rim instead of disappearing. The counters top right tell the story at a glance (demo data).

Cloud models

Both cloud protocols work, independently or together. Set ENGRAM_ANTHROPIC_UPSTREAM and point your client's Anthropic base_url at http://127.0.0.1:11435/anthropic, or set ENGRAM_OPENAI_UPSTREAM to any OpenAI-compatible provider (OpenRouter, DeepSeek, Groq, ...) and point the client's OpenAI base_url at http://127.0.0.1:11435/v1. Chat goes to the cloud; embeddings, summarization, and consolidation stay on local Ollama, so the only added cloud cost is the injected block (≤2000 tokens on the cloud tier, typically far less - around 1% of a real agent prompt). Your memory store never leaves your machine.

Known weaknesses

Measured on LongMemEval-S (all 500 questions; full methodology and every number in BENCHMARKS.md). Retrieval reached 98.1%: the right memories almost always make the injected block. The judged answer scores (62.8% with a frontier answering model, 33.8% with a 1.7B) are bounded by four things, ranked by measured impact:

Retrieval's own 9 misses out of 470 are taxonomized in BENCHMARKS.md too: 3 assistant-side facts, 3 relative-date cues ("last Tuesday" - embeddings cannot do date arithmetic; date-aware cue expansion is on the roadmap), 2 vague preference cues, 1 paraphrase below the admission threshold.

Troubleshooting

symptomcause / fix
address already in useanother engramd holds :11435 - pkill -f "engram up"
no memories formcheck engram stats queue depth; the summarizer waits ~30s of GPU idle. Missing qwen3:1.7b → extractive summaries (still correct, terser)
degraded_recalls climbingembed calls exceeding the 250ms cap (GPU contention) - recall degrades to lexical-only for those turns and recovers on its own
slow first turns through big agentshuge prompt prefill; subsequent turns should hit the prompt cache. If every turn is slow, something churns the prompt head upstream of Engram
model answers ignore injected memorysub-2B answering models fumble context they're given; the memory block is visible in engram why - verify retrieval vs answering separately, like engram eval does
backupsqlite3 ~/.engram/engram.db ".backup backup.db" - never bare cp (WAL)

MIT licensed · source · design specification · built with a research-first process: every default in this page traces to a measurement or a field failure.