A lightweight vector-based memory system for AI agents. Store, retrieve, and delete memories via semantic search. You own the embedding model; we handle the vectors.
Minimal tokens, maximum model freedom.
- ~150 tokens/turn — 4 tools with single-line descriptions
- No knowledge graph, no FTS, no dashboard — the model decides how to use memory, not the system
- 6 files, 3 dependencies — lightweight footprint, easy to understand and modify
You provide the embedding API.
- Use llama.cpp, Ollama, OpenAI, or any OpenAI-compatible endpoint
- Switch models anytime — just change the
EMBEDDING_API_URL - Rebuild vectors with
memory-db-manage rebuildwhen switching models
We removed everything that constrains the model. The memory system should be a tool, not a framework.
┌──────────────┐ ┌─────────────┐ ┌──────────────┐
│ MCP Server │─────▶│ Qdrant │◀──────│ │
│ (3 tools) │◀─────│ :6333 │ │ llama.cpp │
└──────────────┘ └─────────────┘ │ :8081 │
│ /v1/embed │
└──────────────┘
One vector store, one embedding API. 4 MCP tools for AI agents, a management CLI for ops.
# Clone the repo
git clone https://github.com/cunzai97/Memory-DB.git
cd Memory-DB
# Create virtual environment
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -e .
# Start Qdrant (required)
docker compose up -dNow you can start the MCP server:
memory-db # starts MCP serverOr use the admin CLI:
memory-db-manage list # list all memories- Qdrant running on
:6333(Docker Compose or standalone) - Embedding API on
:8081(your llama.cpp instance)
docker compose up -d # starts Qdrant only
pip install -e . # installs memory-db + CLI
memory-db # starts MCP server| Variable | Default | Purpose |
|---|---|---|
QDRANT_HOST |
localhost |
Qdrant host |
QDRANT_PORT |
6333 |
Qdrant port |
EMBEDDING_API_URL |
http://localhost:8081/v1/embeddings |
Embedding API (OpenAI-compatible) |
claude mcp add memory-db \
-e PYTHONPATH=/path/to/Memory-DB/src \
-e EMBEDDING_API_URL=http://localhost:8081/v1/embeddings \
-e QDRANT_HOST=localhost \
-e QDRANT_PORT=6333 \
-- /path/to/Memory-DB/venv/bin/python3 -m memory_simple.server验证:claude mcp list(应显示 ✓ Connected)。
编辑 ~/.hermes/config.yaml,在 mcp_servers 下添加:
mcp_servers:
memory-db:
command: /path/to/Memory-DB/venv/bin/python3
args: ["-m", "memory_simple.server"]
timeout: 120
env:
PYTHONPATH: /path/to/Memory-DB/src
EMBEDDING_API_URL: http://localhost:8081/v1/embeddings
QDRANT_HOST: localhost
QDRANT_PORT: "6333"重启 Hermes。
Store a memory (text → vector). Dedup threshold ≥ 0.85 replaces semantically similar memories; set to 0 to disable. Use tags for categorization: ["user-preference"], ["project-decision"], etc. Returns {id, deduped}.
store_memory(content="Memory-DB 项目重构经验:从 80+ Python 文件精简到 6 个核心文件,只保留 Qdrant + embedding API。MCP 工具约 130 tokens/turn(单行描述)。", tags=["project-refactor"])
→ {"id": "a1b2c3d4-...", "deduped": false}
store_memory(content="Python是一门动态类型的编程语言")
→ {"id": "e5f6g7h8-...", "deduped": true} // replaced the duplicate
Search memories by cosine similarity. min_score=0.5 (default), 0.8+ for strict matching, <0.3 is noise. Each hit increments recall_count. Returns sorted list of {id, content, score, tags?, recall_count} or [].
get_memories(query="动态类型")
→ [{"id": "...", "content": "Python是动态类型语言", "score": 0.79,
"recall_count": 1, "last_recalled_at": "2026-06-27T...", ...}]
# Broader search with lower threshold
get_memories(query="动态类型", min_score=0.2)
Update a memory's content and/or tags by ID. Supports two modes:
- Full replace:
content="..."— replaces entire content; vector is re-encoded. - Partial replace:
oldText="match" newText="replace"— finds exact substring and substitutes; vector is re-encoded.
At least one of content, tags, or (oldText+newText) must be provided. content and (oldText+newText) are mutually exclusive. Returns {updated: true, id, changes, update_type}.
update_memory(memory_id="a1b2c3d4-...", content="updated text")
→ {"updated": true, "id": "a1b2c3d4-...", "changes": {"content": true}, "update_type": "full_replace"}
update_memory(memory_id="a1b2c3d4-...", oldText="old", newText="new")
→ {"updated": true, "id": "a1b2c3d4-...", "changes": {"content": true}, "update_type": "partial_replace"}
update_memory(memory_id="a1b2c3d4-...", tags=["new-tag"])
→ {"updated": true, "id": "a1b2c3d4-...", "changes": {"tags": true}, "update_type": "tags_only"}
Delete a memory by ID. Returns {deleted: true, id} or {deleted: false, id, error}.
delete_memory(memory_id="a1b2c3d4-...")
→ {"deleted": true, "id": "a1b2c3d4-..."}
Content must be <1024 tokens (embedding API limit). Longer text causes a 400 error.
MCP tool definitions (descriptions + JSON schemas) cost ~150 tokens per turn in the system prompt — about 600 chars of description text across all four tools, each as a single line. Behavioral instructions are removed from tool descriptions and placed in CLAUDE.md / system prompts instead. This is a one-time overhead added to every request, not cumulative.
Admin operations via terminal — not exposed to MCP tools.
# List all memories (no search)
memory-db-manage list [--limit N]
# Export to JSON backup (preserves raw text, independent of vectors)
memory-db-manage export --path backups/memories.json
# Import from JSON (re-encodes with current embedding model)
memory-db-manage import --path backups/memories.json
# Rebuild index — re-encode all memories with the same or a new model
memory-db-manage rebuild [--embedding-url http://new-host:port/v1/embeddings]
# Purge unused memories (entropy reduction)
memory-db-manage purge --min-recall-count 0 --unused-days 30 # dry-run by default
memory-db-manage purge --min-recall-count 0 --unused-days 30 --execute # actually delete
# Delete all (destructive, requires confirmation)
memory-db-manage delete-all [--force]When you change your embedding model, existing vectors become stale. Two options:
-
Rebuild in place — keeps metadata and recall stats, replaces vectors only:
memory-db-manage rebuild --embedding-url http://new-host:9090/v1/embeddings
-
Export → Import — full text backup:
memory-db-manage export --path backups/old-model.json # ... switch model ... memory-db-manage import --path backups/old-model.json
Memories that are never recalled accumulate over time. Use purge to clean up:
# Preview what would be deleted (dry-run by default)
memory-db-manage purge --min-recall-count 0 --unused-days 30
# Actually delete memories with recall_count=0 that haven't been recalled in 30+ days
memory-db-manage purge --min-recall-count 0 --unused-days 30 --executeEach stored memory carries this payload in Qdrant:
{
"id": "<uuid>",
"content": "原始文本",
"created_at": "2026-06-27T14:33:24+00:00",
"tags": ["rust", "systems"],
"recall_count": 3,
"last_recalled_at": "2026-06-27T15:00:00+00:00"
}tags is optional. recall_count / last_recalled_at auto-tracked on every search hit — useful for identifying never-recalled memories during entropy reduction.
src/memory_simple/
├── embedding.py # Embedding API client (httpx)
├── service.py # MemoryService — core store/get/update
├── admin.py # MemoryAdmin — backup/import/rebuild/purge
├── server.py # MCP server — exposes 3 tools
└── manage.py # CLI — admin operations
Dependencies: mcp, qdrant-client, httpx — that's it.