Persistent semantic memory graph service for multi-agent AI systems. Drop-in memory layer for the Hermes/Kavi Claw agent framework.
Every time you call an LLM, it forgets everything. RAG helps with retrieval, but vector stores aren't real memory — they're sophisticated search indexes.
Real memory has structure:
| Property | Vector Store | MNEMOS |
|---|---|---|
| Relationships | ❌ Flat documents | ✅ Typed graph edges |
| Temporal decay | ❌ Static embeddings | ✅ Ebbinghaus forgetting curve |
| Contradiction handling | ❌ Silently conflicts | ✅ LLM micro-agent adjudication |
| Memory types | ❌ One homogeneous index | ✅ Episodic / Semantic / Procedural |
| Associative retrieval | ❌ Top-K cosine | ✅ Vector → graph traversal hybrid |
| Multi-agent isolation | ❌ Shared namespace | ✅ Per-agent scoped profiles |
MNEMOS gives your agents a working memory that actually behaves like one: it forgets low-salience facts, strengthens frequently accessed paths, detects contradictions, and organizes knowledge into typed memory structures.
┌─────────────────────────────────────────────────────────────────┐
│ HERMES AGENT FRAMEWORK │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Skill A │ │ Skill B │ │ Orchestrator │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
│ │ HermesHook │ │ │
└──────────┼─────────────────┼──────────────────────┼─────────────┘
│ │ AgentTrace │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ MNEMOS FastAPI Service │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────────────┐ │
│ │ /ingest │ │ /query │ │ /agent/{id}/memory │ │
│ └──────┬──────┘ └──────┬──────┘ └──────────────────────┘ │
│ │ │ │
│ ┌──────▼──────┐ ┌──────▼───────────────────────────────┐ │
│ │ FactExtract │ │ QueryPlanner │ │
│ │ (LLM call) │ │ embed → Qdrant → Neo4j traversal │ │
│ └──────┬──────┘ └──────────────────────────────────────┘ │
│ │ │
│ ┌──────▼──────────────────────────────────┐ │
│ │ ContradictionResolver │ │
│ │ candidate pruning → LLM adjudication │ │
│ └──────┬──────────────────────────────────┘ │
│ │ │
└─────────┼────────────────────────────────────────────────────── ┘
│
┌────▼────────────────────────────────────────────────┐
│ Storage Layer │
│ │
│ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ Neo4j 5.x │ │ Qdrant 1.9 │ │
│ │ Memory Graph │ │ Vector Embeddings │ │
│ │ │ │ │ │
│ │ (Episodic)──────│ │ 384-dim sentence │ │
│ │ (Semantic) │ │ transformer vecs │ │
│ │ (Procedural) │ │ │ │
│ └──────────────────┘ └──────────────────────┘ │
│ │
│ ┌──────────────────┐ │
│ │ Redis │ │
│ │ Agent sessions │ │
│ │ Query cache │ │
│ └──────────────────┘ │
└──────────────────────────────────────────────────────┘
│
┌────▼────────────────────────────────────────────────┐
│ APScheduler (Decay Engine) │
│ │
│ Every 6h: R(t) = e^(-t/S) × salience_weight │
│ Resurrect edges when reinforced above threshold │
└──────────────────────────────────────────────────────┘
MNEMOS models three distinct memory types, mirroring cognitive science:
"What happened" — Concrete events, agent execution traces, timestamped interactions.
(AgentRun:Episodic {
content: "User asked about AWS costs, tool_call=calculator, result=$1,240/mo",
salience: 0.82,
agent_id: "kavi-orchestrator",
created_at: "2026-01-15T14:23:00Z"
})
"What is true" — Extracted facts, entity relationships, world knowledge.
(Entity:Semantic {
content: "AWS EC2 t3.medium costs $0.0416/hour in us-east-1",
salience: 0.95,
confidence: 0.88
})
-[:RELATES_TO {weight: 0.9}]->
(Entity:Semantic { content: "AWS pricing" })
"How to do it" — Successful skill execution patterns, tool usage strategies.
(Procedure:Procedural {
content: "To calculate AWS costs: use calculator tool with hourly_rate × 730",
salience: 0.75,
success_count: 14
})
- Docker & Docker Compose
- Python 3.11+
- OpenAI or DeepSeek API key
git clone https://github.com/onurkavi/mnemos.git
cd mnemos
cp .env.example .env
# Edit .env with your API keysdocker-compose up -dThis starts Neo4j (bolt://localhost:7687), Qdrant (http://localhost:6333), Redis (localhost:6379), and the MNEMOS API (http://localhost:8000).
pip install -r requirements.txt
python scripts/seed_demo_data.pypython scripts/demo.pypip install mnemos-sdk # or: pip install -e .from mnemos.sdk.client import MnemosClient
from mnemos.models import AgentTrace
client = MnemosClient(base_url="http://localhost:8000", agent_id="my-agent")
# Ingest an agent execution trace
trace = AgentTrace(
agent_id="my-agent",
session_id="sess_abc123",
inputs={"user_message": "What's the capital of France?"},
tool_calls=[
{"tool": "web_search", "args": {"query": "capital of France"}, "result": "Paris"}
],
outputs={"response": "The capital of France is Paris."},
errors=[],
duration_ms=1240
)
await client.ingest_trace(trace)
# Query memory with natural language
context = await client.query(
"What do we know about France?",
top_k=5,
include_graph_hops=2
)
print(context.memories)
# [MemoryNode(content="The capital of France is Paris", salience=0.91, ...)]
# Get a specific entity
entity = await client.get_entity("entity_paris_001")
print(entity.relations) # Graph neighborsasync with MnemosClient(base_url="http://localhost:8000", agent_id="my-agent") as client:
result = await client.query("AWS cost estimates we've calculated")
for memory in result.memories:
print(f"[{memory.type}] {memory.content} (salience={memory.salience:.2f})")Drop the HermesHook into any Hermes skill for zero-config memory ingestion:
from mnemos.sdk.hermes_hook import HermesHook
# Wrap a skill execution
hook = HermesHook(
mnemos_url="http://localhost:8000",
agent_id="kavi-orchestrator"
)
@hook.trace_skill("web_search")
async def web_search_skill(query: str) -> dict:
# Your existing skill logic
result = await do_web_search(query)
return {"result": result}
# MNEMOS automatically captures inputs, outputs, timing, errors
# and ingests structured memory after each skill callfrom mnemos.sdk.hermes_hook import HermesHook
from mnemos.sdk.client import MnemosClient
class KaviOrchestrator:
def __init__(self):
self.memory = MnemosClient(
base_url=os.getenv("MNEMOS_URL"),
agent_id="kavi-orchestrator"
)
self.hook = HermesHook(client=self.memory)
async def run(self, user_message: str) -> str:
# Retrieve relevant memory before planning
context = await self.memory.query(user_message, top_k=10)
# Inject memory context into system prompt
memory_context = context.format_for_prompt()
plan = await self.planner.plan(user_message, context=memory_context)
# Execute skills with auto-tracing
async with self.hook.session(session_id=plan.session_id):
result = await plan.execute()
return result| Method | Path | Description |
|---|---|---|
POST |
/ingest |
Ingest an AgentTrace |
POST |
/query |
Natural language memory query |
GET |
/entity/{id} |
Get entity + graph neighbors |
POST |
/agent/{agent_id}/memory |
Get agent's full memory profile |
DELETE |
/memory/{id} |
Delete a memory node |
GET |
/health |
Service health check |
GET |
/docs |
OpenAPI docs (Swagger UI) |
MNEMOS implements the Ebbinghaus forgetting curve:
R(t) = e^(-t / S)
Where R is retention (0–1), t is time elapsed since last access, and S is the stability factor (scales with salience and reinforcement count).
- Edges with
weight < decay_threshold(default0.05) are markedarchived - When a memory is accessed again, it gets resurrected with a new stability factor
- APScheduler runs the decay sweep every 6 hours
When new facts conflict with existing memory:
- Candidate pruning — embedding similarity search finds potentially conflicting nodes
- LLM adjudication — a micro-agent (DeepSeek/GPT-4o) receives both facts + provenance
- Graph update — winning fact gets
confidence += 0.1, loser getssuperseded_byedge
# Automatic — triggered on every /ingest call
# Manual resolution:
await client.resolve_contradictions(entity_id="entity_aws_price_001")All settings via environment variables (see .env.example):
| Variable | Default | Description |
|---|---|---|
NEO4J_URI |
bolt://localhost:7687 |
Neo4j connection |
NEO4J_USER |
neo4j |
Neo4j username |
NEO4J_PASSWORD |
— | Neo4j password |
QDRANT_HOST |
localhost |
Qdrant host |
QDRANT_PORT |
6333 |
Qdrant port |
OPENAI_API_KEY |
— | OpenAI key (or use DeepSeek) |
DEEPSEEK_API_KEY |
— | DeepSeek key |
LLM_PROVIDER |
openai |
openai or deepseek |
DECAY_INTERVAL_HOURS |
6 |
Decay sweep interval |
DECAY_THRESHOLD |
0.05 |
Archive threshold |
EMBEDDING_MODEL |
all-MiniLM-L6-v2 |
Sentence transformer model |
REDIS_URL |
redis://localhost:6379 |
Redis connection |
mnemos/
├── mnemos/
│ ├── __init__.py
│ ├── config.py # Pydantic Settings
│ ├── models.py # Core Pydantic models
│ ├── graph/
│ │ ├── neo4j_client.py # Async Neo4j driver wrapper
│ │ └── schema.py # Node labels, relationship types
│ ├── memory/
│ │ ├── ingestion.py # AgentTrace → graph pipeline
│ │ ├── fact_extractor.py # LLM structured output extraction
│ │ ├── retrieval.py # QueryPlanner: vector + graph hybrid
│ │ ├── decay.py # Ebbinghaus decay engine
│ │ └── contradiction.py # ContradictionResolver
│ ├── api/
│ │ ├── routes.py # FastAPI route handlers
│ │ └── middleware.py # Agent identity scoping
│ └── sdk/
│ ├── client.py # MnemosClient Python SDK
│ └── hermes_hook.py # Hermes skill auto-tracing hook
├── scripts/
│ ├── demo.py # Interactive demo
│ └── seed_demo_data.py # Sample data population
├── tests/
│ ├── test_fact_extractor.py
│ ├── test_decay.py
│ └── test_retrieval.py
├── docker-compose.yml
├── requirements.txt
└── .env.example
# Install dev dependencies
pip install -r requirements.txt
# Run tests
pytest tests/ -v
# Run with hot reload
uvicorn mnemos.api.routes:app --reload --port 8000
# Check Neo4j schema
python -c "from mnemos.graph.neo4j_client import Neo4jClient; import asyncio; asyncio.run(Neo4jClient().init_schema())"MIT License — Copyright (c) 2026 Onur Kavi
MNEMOS — from the Greek μνήμη (mneme), meaning memory. In Greek mythology, Mnemosyne was the goddess of memory and mother of the nine Muses.