Production-grade personalized memory system for AI agents.
Give your AI agent persistent, searchable, evolving memory — powered by PostgreSQL + pgvector + bge-m3. Built as an OpenClaw plugin that fully replaces the built-in SQLite memory with a unified PostgreSQL backend.
User: "Do you remember what we discussed about the deployment last week?"
Agent: *searches 44 conversation memories + 118 workspace file chunks*
Agent: "Yes — last Tuesday you decided to move from Vercel to Cloudflare Workers..."
AI agents forget everything between sessions. Most "memory" solutions are either:
- Too simple — just append to a text file
- Too complex — require a PhD in vector databases
- Not unified — workspace files and conversation history live in separate silos
Agentic Memory solves this with a single PostgreSQL backend that handles:
| Layer | What It Stores | How It's Searched |
|---|---|---|
| Vector memories | Conversations, decisions, insights | pgvector HNSW (semantic) |
| Structured facts | User preferences, contacts, config | Key-value lookup |
| Workspace index | SKILL.md, AGENTS.md, docs/*.md | Hybrid vector + FTS |
| File attachments | Images, PDFs, documents | MinIO + metadata search |
| Access log | What was recalled and when | Time-decay scoring |
One memory_search call queries everything. No need to know which backend holds the answer.
┌─────────────────────────────────────────────────────────────┐
│ AI Agent (OpenClaw) │
│ │
│ memory_search tool exec curl tool │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌──────────────────┐ │
│ │ memory-api │────────▶│ Memory Service │ │
│ │ plugin │ curl │ FastAPI :18800 │ │
│ │ │ │ │ │
│ │ Registers: │ │ /search │ Hybrid │
│ │ • memory_ │ │ /store │ vector+FTS │
│ │ search │ │ /facts │ │
│ │ • memory_ │ │ /recall │ │
│ │ get │ │ /workspace/* │ │
│ │ • runtime │ │ /compact │ │
│ └─────────────┘ │ /extract │ │
│ │ /v1/embeddings │ │
│ └────────┬─────────┘ │
│ │ │
│ ┌──────────────┼──────────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │PostgreSQL│ │ Redis │ │ MinIO │ │
│ │+ pgvector│ │ cache │ │ files │ │
│ │+ HNSW │ │ dedup │ │ │ │
│ │+ tsvector│ │ │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ Embedding: BAAI/bge-m3 (1024-dim, local) │
└─────────────────────────────────────────────────────────────┘
docker compose up -dThis starts PostgreSQL (with pgvector), Redis, and MinIO.
cd memory-service
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtuvicorn main:app --host 127.0.0.1 --port 18800On first start, bge-m3 (568MB) downloads automatically. After that, startup takes ~3 seconds.
# Health check
curl http://localhost:18800/health
# → {"status":"ok","pg":true,"redis":true,"minio":true,"embedding_model_loaded":true}
# Store a memory
curl -X POST http://localhost:18800/store \
-H "Content-Type: application/json" \
-d '{"content": "User prefers dark mode and vim keybindings", "category": "preference"}'
# Search memories
curl -X POST http://localhost:18800/search \
-H "Content-Type: application/json" \
-d '{"query": "editor preferences", "max_results": 5}'# Copy plugin to OpenClaw plugins directory
cp -r openclaw-plugin/memory-api ~/.openclaw/plugins/
# Symlink OpenClaw SDK (required for plugin imports)
ln -s $(npm root -g)/openclaw ~/.openclaw/plugins/memory-api/node_modules/openclaw
# Copy example config
cp config/openclaw.json.example ~/.openclaw/openclaw.json
# Edit with your settings
# Restart gateway
openclaw gateway restart
# Verify
openclaw memory status --deepUser says something → Agent decides what's worth remembering
│
┌──────┴──────┐
│ Tier Check │
└──────┬──────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────┐ ┌──────────────┐
│ vector │ │ fact │ │ cache │
│ │ │ │ │ │
│ Decisions, │ │ Contacts,│ │ Conversation │
│ insights, │ │ prefs, │ │ highlights │
│ project ctx │ │ schedule │ │ (4hr TTL) │
│ │ │ │ │ │
│ → pgvector │ │ → facts │ │ → Redis │
│ HNSW index │ │ table │ │ │
└──────────────┘ └──────────┘ └──────────────┘
Vector memories get embedded with bge-m3 (1024-dim) and stored in PostgreSQL with an HNSW index for fast approximate nearest-neighbor search.
Facts are structured key-value pairs (e.g., domain=preference, key=timezone, value="America/Vancouver"). Exact lookup, no embedding needed.
Cache is for short-lived conversation context. Stored in Redis with a 4-hour TTL. Deduplication via content hash prevents storing the same thing twice within 5 minutes.
When memory_search("deployment strategy") is called:
- Embed the query with bge-m3 (query mode)
- Vector search — pgvector HNSW finds top-N semantically similar memories
- FTS search — PostgreSQL tsvector finds keyword matches
- Workspace search — same hybrid search across indexed workspace files
- Merge — Reciprocal Rank Fusion (RRF) combines results from all sources
- Time decay — older memories are scored lower (configurable half-life per category)
- Return — top results with path, line numbers, score, snippet
Memories fade over time using exponential decay:
score = raw_score × 2^(-age_days / half_life)
| Category | Half-life | Meaning |
|---|---|---|
| conversation_highlight | 14 days | Recent chat context fades fast |
| general | 30 days | General knowledge |
| project | 45 days | Project-specific context |
| insight | 60 days | Learned patterns |
| decision | 90 days | Important decisions persist |
| skill | 180 days | Skills/abilities last longest |
The memory service indexes your OpenClaw workspace markdown files into PostgreSQL:
~/.openclaw/workspace/
├── AGENTS.md ─┐
├── IDENTITY.md │ Scanned, chunked,
├── SOUL.md │ embedded with bge-m3,
├── memory/ │ stored in pgvector
│ ├── 2026-03-27.md │
│ └── 2026-03-28.md ─┘
└── skills/
└── web-search/
└── SKILL.md ─── Also indexed
Chunking strategy: Markdown files are split into chunks of ~512 tokens with 50-token overlap. Each chunk gets a bge-m3 embedding and is stored with its file path + line range for precise citation.
Incremental sync: Only changed files are re-indexed (hash comparison). Call POST /workspace/sync after editing workspace files, or POST /workspace/sync {"force": true} for a full re-index.
The auto-extract hook runs at session boundaries (new session, reset, pre-compaction) to automatically extract durable memories from the conversation:
Session about to compact
│
▼
auto-extract hook fires
│
├─ Reads last 30 messages from session transcript
├─ Calls POST /extract with {messages, auto_store: true}
│ │
│ ▼
│ Claude Haiku analyzes messages
│ Extracts: [
│ {tier: "fact", category: "preference", content: "User prefers tabs over spaces"},
│ {tier: "vector", category: "decision", content: "Decided to use PostgreSQL over MongoDB"},
│ ]
│ │
│ ▼
│ Each extracted memory is stored via /store or /facts
│
└─ Session compacts normally
When conversations get long, the /compact endpoint summarizes them:
curl -X POST http://localhost:18800/compact \
-H "Content-Type: application/json" \
-d '{"messages": [...], "force": true}'Returns a structured summary with sections:
- Context — what was being discussed
- Key Decisions — what was decided
- Action Items — what needs to be done
- Important Details — technical specifics
- Current State — where things stand
| Method | Path | Description |
|---|---|---|
| GET | /health |
Service health + component status |
| POST | /store |
Store a memory (auto-classifies tier) |
| POST | /search |
Semantic + FTS hybrid search |
| POST | /recall |
Context recall (returns relevant memories for a topic) |
| POST | /facts |
Store/query structured facts |
| GET | /facts |
List all active facts |
| POST | /compact |
Summarize conversation messages |
| POST | /extract |
Extract durable memories from messages |
| Method | Path | Description |
|---|---|---|
| POST | /workspace/sync |
Sync workspace files to pgvector |
| POST | /workspace/search |
Search indexed workspace chunks |
| GET | /workspace/status |
Indexing status |
| GET | /workspace/read |
Read a workspace file by path |
| Method | Path | Description |
|---|---|---|
| POST | /v1/embeddings |
bge-m3 embeddings in OpenAI format |
This endpoint allows any tool expecting OpenAI's embedding API to use local bge-m3 instead. No API key required.
The memory-api plugin registers a custom MemoryRuntime that fully replaces OpenClaw's built-in SQLite-based memory:
What it replaces:
- SQLite + sqlite-vec → PostgreSQL + pgvector (HNSW)
- SQLite FTS5 → PostgreSQL tsvector + GIN
- Local embedding cache → Redis
- File-based storage → MinIO
What it registers:
registerMemoryRuntime— takes overgetMemorySearchManagerregisterTool("memory_search")— hybrid search across all sourcesregisterTool("memory_get")— read specific workspace file linesregisterMemoryPromptSection— injects memory system instructions into agent promptregisterMemoryFlushPlan— pre-compaction memory persistence
Why execFile('curl') instead of fetch():
The plugin runs inside OpenClaw's gateway Node.js process, which may sandbox fetch() calls to localhost. By spawning curl as a child process (via child_process.execFile), we bypass this sandbox — the same way OpenClaw's exec tool successfully calls localhost APIs.
- PostgreSQL 15+ with pgvector extension
- Redis 7+
- MinIO (or any S3-compatible storage)
- Python 3.11+ with ~600MB disk for bge-m3 model
- Node.js 22+ (for OpenClaw plugin)
- ~2GB RAM for bge-m3 embedding model
All settings are in memory-service/config.py and can be overridden via environment variables:
export MEMORY_PG_HOST=db.example.com
export MEMORY_PG_PASSWORD=your-secure-password
export MEMORY_REDIS_URL=redis://redis:6379/0
export MEMORY_TIMEZONE=America/VancouverSee config/openclaw.json.example for OpenClaw integration config.
agentic-memory/
├── memory-service/ # FastAPI backend
│ ├── main.py # API endpoints
│ ├── config.py # Configuration (env vars)
│ ├── embeddings.py # bge-m3 embedding pipeline
│ ├── storage.py # PostgreSQL operations
│ ├── workspace.py # Workspace file indexing
│ ├── compact.py # Conversation compaction
│ ├── extract.py # Memory extraction from transcripts
│ ├── retrieval.py # Search & retrieval logic
│ ├── cache.py # Redis caching & dedup
│ ├── files.py # MinIO file operations
│ ├── models.py # Pydantic request/response models
│ ├── task_queue.py # Async task execution
│ └── requirements.txt
├── openclaw-plugin/
│ ├── memory-api/ # OpenClaw memory plugin
│ │ ├── index.js # Plugin entry (registerMemoryRuntime)
│ │ ├── package.json
│ │ └── openclaw.plugin.json
│ └── hooks/
│ └── auto-extract/ # Session memory extraction hook
│ ├── handler.js
│ └── HOOK.md
├── config/ # Example configurations
│ ├── openclaw.json.example
│ └── AGENTS.md.example
├── sql/
│ └── schema.sql # Full PostgreSQL schema
├── docker-compose.yml # Infrastructure (pg + redis + minio)
└── docs/
└── ARCHITECTURE.md # Detailed architecture documentation
If your agent lost exec, read, write, or edit tools after upgrading OpenClaw, see docs/UPGRADE-2026.3.28.md for the fix.
TL;DR: The tools.allow field in openclaw.json acts as a whitelist. You must include group:fs and group:runtime for file and exec tools to work. Also, do NOT use profile: "full" — use profile: "coding" instead.
- Docker → systemd migration: All 5 Docker containers (PostgreSQL, Redis, MinIO, Flask API, Discord bot) migrated to native systemd services
- PostgreSQL port: 5434 → 5432 (removed Docker port mapping overhead)
- Redis port: 6380 → 6379
- Released ~2GB of Docker image/volume storage
- Memory service now runs as native uvicorn process
- See docs/SYSTEMD-MIGRATION.md for the full migration guide
- Upgraded from 3.28 → 3.31 → 4.1
- Custom PostgreSQL task store patch (
task-store-pg.mjs) — replaces SQLite task registry - Fixed tool allowlist: added
group:fsandgroup:runtimetotools.allow - Fixed profile: use
"coding"not"full"(full = empty object in source) - Fix: Document OpenClaw 2026.3.28
tools.allowwhitelist issue that disables exec/read/write - Fix: Document
profile: "full"vsprofile: "coding"gotcha (full = empty allowlist) - Fix: Update
openclaw.json.examplewith correct tool group allowlist - Docs: Add
docs/UPGRADE-2026.3.28.md— complete upgrade guide with exec fix, session cache clearing, and SQLite → PostgreSQL migration notes - Fix: Use
process.env.HOMEinstead of hardcoded paths in plugin
- First open-source release: PostgreSQL + pgvector + bge-m3 memory system
- Features: vector search, structured facts, workspace indexing, auto-extraction, time decay
MIT
Built by NexAgent AI Solutions as the memory backbone for the OpenClaw AI agent platform.
Inspired by the memory architecture patterns in Claude Code.