Autonomous memory for AI agents. Bi-temporal graph + episodic log + procedural skills. MCP-native.
  • Go 99.7%
  • Makefile 0.2%
Find a file
airaneel f66e345aa3
All checks were successful
build-image / build (push) Successful in 19s
Подведомственность — не «состав», и ходить по ней можно только вниз
`includes` тащил три разных отношения разом: орган и учреждённый им
институт, вещь и её части, группу и её членов. Маршруту нужно первое и
противопоказаны второе с третьим, а одним ключом их не различить —
поэтому вчера `includes` сняли с обхода целиком, и вместе с шумом ушёл
реальный заход: ФГБУ «ВНИИИМТ» стал недостижим из любой точки графа,
хотя двухзвенный путь к нему лежал в графе прозой.

Отсюда `subordinate_to`: учреждение → орган, который его учредил и ведёт.
Подчинённый субъектом, потому что обратное направление — та мысль,
которую пишут про ЧЕЛОВЕКА, руководящего управлением, и свёрнутая она
посадила бы этого человека на место министерства. Единственный инверс —
`oversees`; `supervises` и `manages` не взяты по той же причине, по
которой когда-то не взяли `runs`.

РАЗЛИЧЕНИЕ НЕ В ПОЗИЦИИ, А В НАПРАВЛЕНИИ. Вниз — свободно на любой
глубине: собственный институт ведомства, а за ним человек внутри
института, это один ход продолженный. Вверх — только последним звеном,
потому что поднявшись в родителя можно спуститься в любого другого его
подведа, и это ровно форма «Андрей → СГМУ → Минздрав России → Дайхес»:
шаг вверх, за ним шаг вниз.

Первый черновик разрешал подчинённость только терминальным звеном. Он
удерживал форму с сиблингами и заодно выбрасывал случай, который нужен
чаще, — дойти до ЧЕЛОВЕКА внутри подведомственного учреждения, где
подчинённость стоит в середине цепочки.

Заодно правило про описания сущностей вынесено в первую строку параметра
и переписано как запрет. Оно лежало в четвёртом абзаце — и не сработало:
за один проход в описания девяти сущностей уехали директора, доли,
выручка, лицензии, телефоны и должности с датами. Правило, до которого
надо дочитать до четвёртого абзаца, тратится после того, как кончилось
внимание.

Guarded by TestSubordinationIsWalkedDownward (маршрут к начальнику
института — та проверка, что решает; маршрут к самому институту проходит
при любом из двух правил) и TestASharedParentIsNotARoute на отказ.
Проверено мутациями трижды, третья — выброшенный черновик.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 02:24:04 +03:00
.forgejo/workflows ci: correct the workflow header about registry auth 2026-07-28 13:17:57 +03:00
cmd JSON-поверхность на huma: доменные типы публикуются, DTO не заводятся 2026-08-17 19:08:14 +03:00
configs skills: raise the floor to 0.60, where no wrong recipe comes back alone 2026-07-30 14:56:54 +03:00
internal Подведомственность — не «состав», и ходить по ней можно только вниз 2026-08-21 02:24:04 +03:00
pkg/types Начало, которого никто не заявлял, — это не сегодняшняя дата 2026-08-21 02:23:06 +03:00
.gitignore Sprint 0: bootstrap mnemos memory server 2026-04-27 15:08:17 +03:00
.golangci.yml Sprint 0: bootstrap mnemos memory server 2026-04-27 15:08:17 +03:00
.mcp.json gopls подключён как MCP-сервер проекта 2026-08-16 12:16:49 +03:00
CLAUDE.md Подведомственность — не «состав», и ходить по ней можно только вниз 2026-08-21 02:24:04 +03:00
complexity_test.go Функция ограничена цикломатической сложностью 10, а не строками 2026-08-16 12:18:10 +03:00
docker-compose.yml Sprint 0: bootstrap mnemos memory server 2026-04-27 15:08:17 +03:00
Dockerfile build: cross-compile natively instead of emulating the target arch 2026-07-14 11:36:35 +03:00
go.mod JSON-поверхность на huma: доменные типы публикуются, DTO не заводятся 2026-08-17 19:08:14 +03:00
go.sum JSON-поверхность на huma: доменные типы публикуются, DTO не заводятся 2026-08-17 19:08:14 +03:00
LICENSE Sprint 0: bootstrap mnemos memory server 2026-04-27 15:08:17 +03:00
main.go Sprint 0: bootstrap mnemos memory server 2026-04-27 15:08:17 +03:00
Makefile JSON-поверхность на huma: доменные типы публикуются, DTO не заводятся 2026-08-17 19:08:14 +03:00
PLAN.md scope: three recall modes + mnemos_scopes discovery 2026-07-12 14:04:22 +03:00
README.md extractor: the graph has one producer, so cut the other 2026-07-30 10:30:36 +03:00
rules_test.go Три списка, которые вели руками, теперь сверяются с тем, что описывают 2026-08-16 12:17:32 +03:00

mnemos

Autonomous memory for AI agents. Bi-temporal graph + episodic log + procedural skills. One Go binary, one Postgres. MCP-native. MIT.

mnemos is a memory server that any MCP-compatible client (Claude Desktop, Claude Code, Cursor, Windsurf, custom agents) can connect to. It stops agents from forgetting between sessions and gives them a real bi-temporal knowledge graph instead of a flat vector store.

Why another memory system

mnemos Memory MCP MemPalace Mem0 Graphiti
Bi-temporal graph ⚠️ flat
Procedural skills
Multi-agent scoping ⚠️ ⚠️
Sleep consolidation
Single binary
Any LLM (OAI-compat) n/a

How it works

Writes are fast and dumb; understanding happens later — like sleep.

  1. observe appends an event to the episodic log (embed + insert, no LLM).
  2. The consolidate worker ticks in the background: extracts subject predicateobject facts from unconsolidated events via any OpenAI-compatible LLM, and writes them into the graph bi-temporally — a repeated fact reinforces the existing edge instead of duplicating it, a contradicting fact supersedes the old one (which stays queryable as history), an out-of-order historical fact backfills a closed interval. The reflect step folds message → tool_call* chains into procedural skills with a running success rate (a correction event inside a chain counts as failure).
  3. recall is hybrid: vector search over facts and episodes, plus 1-hop graph expansion from query-matched entities, plus pins, plus skills — filtered by scope and the bi-temporal pivot (at).
  4. Decay: importance of untouched memories halves every decay_half_life; anything recall returns gets its clock reset. Old noise fades, things you keep coming back to stay strong.

Scopes are slash-separated namespaces (andy/raidflow) — materialized paths, so both ancestor and descendant lookups are cheap. Recall has three modes:

  • empty scope → whole tenant. No scope given means "search everything I have" — the sensible default for an agent that hasn't mapped its memory.
  • explicit scope → walk-up. andy/raidflow sees itself + ancestors up to global, but not siblings or children. This is the isolation guarantee: project A's facts never leak into project B.
  • descendants: true → subtree. Widens an explicit scope to also include everything nested under it.

Call mnemos_scopes to discover which namespaces exist before targeting one. Facts, events, skills and pins are scoped; entities (nodes) are global. Two scopes may hold contradicting facts.

Quick start

docker compose up -d postgres ollama       # pgvector + local LLM
docker compose exec ollama ollama pull nomic-embed-text
docker compose exec ollama ollama pull llama3.1:8b-instruct

go build -o mnemos .
./mnemos migrate up
./mnemos serve                             # StreamableHTTP on :8090

MCP endpoint: http://localhost:8090/mcp. Also served on the same port: /healthz, /readyz, /metrics (Prometheus). For local stdio clients: server.transport: stdio.

MCP tools

Tool Purpose Key args
mnemos_observe record an event into the episodic log content*, kind (message/tool_call/observation/decision/correction/reflection), scope, parent_id
mnemos_recall hybrid retrieval: vector + 1-hop graph + temporal query*, scope (empty = whole tenant), descendants, at (RFC3339), since/until, limit
mnemos_facts bi-temporal facts about an entity ("what was true at T") node_id*, at
mnemos_skill procedural patterns: "how did I do this before" task*, scope, descendants, limit
mnemos_search_nodes find entities by name (trigram) or meaning (vector) query*, kind, limit
mnemos_scopes list namespaces that hold memory (counts, last activity)
mnemos_pin core memory, always returned by recall text*, scope, ttl

There is deliberately no forget/update/delete — contradictions are handled by bi-temporal supersession in the consolidate worker.

Configuration

Defaults < YAML file < MNEMOS_* env vars. Full annotated example: configs/mnemos.example.yaml.

./mnemos --config /etc/mnemos.yaml serve
# or pure env:
MNEMOS_DATABASE_DSN="postgres://mnemos:pw@host:5432/mnemos?sslmode=disable" \
MNEMOS_EMBEDDINGS_BASE_URL="http://ollama:11434/v1" ./mnemos serve

Embeddings take any OpenAI-compatible endpoint (Ollama, OpenAI, DeepSeek, vLLM, proxies). Embedding dimension is fixed at 768 by the schema — for OpenAI text-embedding-3-* set dimensions: 768 and request_dimensions: true. The embedder is the only model mnemos calls: fact extraction was removed once it had been switched off for good, and the agent writing through MCP is the sole producer of the graph.

Postgres needs the vector, pg_trgm and btree_gist extensions. vector is not trusted, so on a shared instance pre-create it as superuser — migrations use IF NOT EXISTS.

Importing existing memory

./mnemos import claude ~/Downloads/claude-export/   # Claude.ai export.zip contents
./mnemos import markdown ~/notes --scope andy/notes # one event per .md file

Imports write plain events; the consolidate worker turns them into graph facts on its own schedule (backfilled timestamps land as historical facts).

Development

make test        # unit tests; integration tests skip without a DB
make lint vet

Integration tests run against any pgvector Postgres:

docker run -d --rm -e POSTGRES_USER=mnemos -e POSTGRES_PASSWORD=mnemos \
  -e POSTGRES_DB=mnemos_test -p 54329:5432 pgvector/pgvector:pg16
MNEMOS_TEST_DATABASE_URL="postgres://mnemos:mnemos@localhost:54329/mnemos_test?sslmode=disable" \
  go test ./... -count=1

Architecture, schema and design decisions: PLAN.md. Conventions and invariants for contributors (and agents): CLAUDE.md.

Status

Sprints 07 complete: storage, memory service, MCP server, bi-temporal write path (supersession, dedup/reinforce), decay, importers, observability (Prometheus + OTel). Running against Postgres 16/17 + pgvector 0.8. Not yet: multi-tenant auth, web UI, replication (see PLAN.md §8).

License

MIT