Production-grade long-term memory system (v1.1.0-beta.8) for OpenClaw AI agents. Provides persistent, intelligent memory storage using LanceDB with hybrid vector + BM25 retrieval, LLM-powered Smart Extraction, Weibull decay lifecycle, and multi-scope isolation.
For full technical details (thresholds, formulas, database schema, source file map), see references/full-reference.md.
When the user says "help me enable the best config", "apply optimal configuration", or similar, follow this exact procedure:
Present these three plans in a clear comparison, then ask the user to pick one:
Plan A — 🏆 Full Power (Best Quality)
jina-embeddings-v5-text-small (task-aware, 1024-dim)jina-reranker-v3 (cross-encoder, same key)gpt-4o-mini (Smart Extraction)JINA_API_KEY + OPENAI_API_KEYPlan B — 💰 Budget (Free Reranker)
jina-embeddings-v5-text-smallBAAI/bge-reranker-v2-m3 (free tier available)gpt-4o-miniJINA_API_KEY + SILICONFLOW_API_KEY + OPENAI_API_KEYPlan C — 🟢 Simple (OpenAI Only)
text-embedding-3-smallgpt-4o-miniOPENAI_API_KEY onlyPlan D — 🖥️ Fully Local (Ollama, No API Keys)
mxbai-embed-large (1024-dim, recommended) or nomic-embed-text:v1.5 (768-dim, lighter)qwen3:8b ( recommended — best JSON output, native structured output, ~5.2GB)qwen3:14b (better quality, ~9GB, needs 16GB VRAM)llama4:scout (multimodal MoE, 10M ctx, ~12GB)mistral-small3.2 (24B, 128K ctx, excellent instruction following, ~15GB)mistral-nemo (12B, 128K ctx, efficient, ~7GB)systemctl start ollama or ollama serve"smartExtraction": falseAfter user selects a plan, ask in one message:
openclaw.json? (Skip if you want me to find it automatically)If the user already stated their provider/keys in context, skip asking and proceed.
Do NOT proceed to Step 2 until API keys have been collected and verified (Step 2 below).
Run ALL key checks for the chosen plan before touching any config. If any check fails, STOP and tell the user which key failed and why. Do not proceed to Step 3.
Plan A / Plan B — Jina embedding check:
bash
curl -s -o /dev/null -w "%{http_code}" \
https://api.jina.ai/v1/embeddings \
-H "Authorization: Bearer <JINA_API_KEY>" \
-H "Content-Type: application/json" \
-d '{"model":"jina-embeddings-v5-text-small","input":["test"]}'
Plan A / B / C — OpenAI check:
bash
curl -s -o /dev/null -w "%{http_code}" \
https://api.openai.com/v1/models \
-H "Authorization: Bearer <OPENAI_API_KEY>"
Plan B — SiliconFlow reranker check:
bash
curl -s -o /dev/null -w "%{http_code}" \
https://api.siliconflow.com/v1/rerank \
-H "Authorization: Bearer <SILICONFLOW_API_KEY>" \
-H "Content-Type: application/json" \
-d '{"model":"BAAI/bge-reranker-v2-m3","query":"test","documents":["test doc"]}'
Plan D — Ollama check:
bash
curl -s -o /dev/null -w "%{http_code}" http://localhost:11434/api/tags
Interpret results:
HTTP codeMeaningAction200 / 201Key valid, quota available✅ Continue401 / 403Invalid or expired key❌ STOP — ask user to check key402Payment required / no credits❌ STOP — ask user to top up account429Rate limited or quota exceeded❌ STOP — ask user to check billing/quota000 / connection refusedService unreachable❌ STOP — ask user to check network / Ollama running
If any check fails: Tell the user exactly which provider failed, the HTTP code received, and what to fix. Do not proceed with installation until all required keys pass their checks.
If the user says keys are set as env vars in the gateway process, run checks using ${VAR_NAME} substituted inline or ask them to paste the key temporarily for verification.
Check these locations in order:
bash
# Most common locations
ls ~/.openclaw/openclaw.json
ls ~/openclaw.json
# Ask the gateway where it's reading config from
openclaw config get --show-path 2>/dev/null || echo "not found"
If not found, ask the user for the path.
bash
# Read and display current plugins config before changing anything
openclaw config get plugins.entries.memory-lancedb-pro 2>/dev/null
openclaw config get plugins.slots.memory 2>/dev/null
Check what already exists — never blindly overwrite existing settings.
Use the config block for the chosen plan. Substitute actual API keys inline if the user provided them directly; keep ${ENV_VAR} syntax if they confirmed env vars are set in the gateway process.
Plan A config ( plugins.entries.memory-lancedb-pro.config):
json
{
"embedding": {
"apiKey": "${JINA_API_KEY}",
"model": "jina-embeddings-v5-text-small",
"baseURL": "https://api.jina.ai/v1",
"dimensions": 1024,
"taskQuery": "retrieval.query",
"taskPassage": "retrieval.passage",
"normalized": true
},
"autoCapture": true,
"autoRecall": true,
"captureAssistant": false,
"smartExtraction": true,
"extractMinMessages": 2,
"extractMaxChars": 8000,
"llm": {
"apiKey": "${OPENAI_API_KEY}",
"model": "gpt-4o-mini",
"baseURL": "https://api.openai.com/v1"
},
"retrieval": {
"mode": "hybrid",
"vectorWeight": 0.7,
"bm25Weight": 0.3,
"rerank": "cross-encoder",
"rerankProvider": "jina",
"rerankModel": "jina-reranker-v3",
"rerankEndpoint": "https://api.jina.ai/v1/rerank",
"rerankApiKey": "${JINA_API_KEY}",
"candidatePoolSize": 12,
"minScore": 0.6,
"hardMinScore": 0.62,
"filterNoise": true
},
"sessionMemory": { "enabled": false }
}
Plan B config:
json
{
"embedding": {
"apiKey": "${JINA_API_KEY}",
"model": "jina-embeddings-v5-text-small",
"baseURL": "https://api.jina.ai/v1",
"dimensions": 1024,
"taskQuery": "retrieval.query",
"taskPassage": "retrieval.passage",
"normalized": true
},
"autoCapture": true,
"autoRecall": true,
"captureAssistant": false,
"smartExtraction": true,
"extractMinMessages": 2,
"extractMaxChars": 8000,
"llm": {
"apiKey": "${OPENAI_API_KEY}",
"model": "gpt-4o-mini",
"baseURL": "https://api.openai.com/v1"
},
"retrieval": {
"mode": "hybrid",
"vectorWeight": 0.7,
"bm25Weight": 0.3,
"rerank": "cross-encoder",
"rerankProvider": "siliconflow",
"rerankModel": "BAAI/bge-reranker-v2-m3",
"rerankEndpoint": "https://api.siliconflow.com/v1/rerank",
"rerankApiKey": "${SILICONFLOW_API_KEY}",
"candidatePoolSize": 12,
"minScore": 0.5,
"hardMinScore": 0.55,
"filterNoise": true
},
"sessionMemory": { "enabled": false }
}
Plan C config:
json
{
"embedding": {
"apiKey": "${OPENAI_API_KEY}",
"model": "text-embedding-3-small",
"baseURL": "https://api.openai.com/v1"
},
"autoCapture": true,
"autoRecall": true,
"captureAssistant": false,
"smartExtraction": true,
"extractMinMessages": 2,
"extractMaxChars": 8000,
"llm": {
"apiKey": "${OPENAI_API_KEY}",
"model": "gpt-4o-mini",
"baseURL": "https://api.openai.com/v1"
},
"retrieval": {
"mode": "hybrid",
"vectorWeight": 0.7,
"bm25Weight": 0.3,
"filterNoise": true,
"minScore": 0.3,
"hardMinScore": 0.35
},
"sessionMemory": { "enabled": false }
}
Plan D config (replace models as needed — qwen3:8b recommended for LLM, mxbai-embed-large for embedding):
json
{
"embedding": {
"apiKey": "ollama",
"model": "mxbai-embed-large",
"baseURL": "http://localhost:11434/v1",
"dimensions": 1024
},
"autoCapture": true,
"autoRecall": true,
"captureAssistant": false,
"smartExtraction": true,
"extractMinMessages": 2,
"extractMaxChars": 4000,
"llm": {
"apiKey": "ollama",
"model": "qwen3:8b",
"baseURL": "http://localhost:11434/v1"
},
"retrieval": {
"mode": "hybrid",
"vectorWeight": 0.7,
"bm25Weight": 0.3,
"filterNoise": true,
"minScore": 0.25,
"hardMinScore": 0.28
},
"sessionStrategy": "none"
}
Plan D prerequisites — run BEFORE applying config:
bash
# 1. Verify Ollama is running (should return JSON with model list)
curl http://localhost:11434/api/tags
# 2. Pull embedding model (choose one):
ollama pull mxbai-embed-large # recommended: 1024-dim, beats text-embedding-3-large, ~670MB
ollama pull snowflake-arctic-embed2 # best multilingual local option, ~670MB
ollama pull nomic-embed-text:v1.5 # classic stable, 768-dim, ~270MB
# 3. Pull LLM for Smart Extraction (choose one based on RAM):
ollama pull qwen3:8b # recommended: best JSON/structured output, ~5.2GB
ollama pull qwen3:14b # better quality, ~9GB, needs 16GB VRAM
ollama pull llama4:scout # multimodal MoE, 10M ctx, ~12GB
ollama pull mistral-small3.2 # 24B, 128K ctx, excellent, ~15GB
ollama pull mistral-nemo # 12B, 128K ctx, efficient, ~7GB
# 4. Verify models are installed
ollama list
# 5. Quick sanity check — embedding endpoint works:
curl http://localhost:11434/v1/embeddings \
-H "Content-Type: application/json" \
-d '{"model":"mxbai-embed-large","input":"test"}'
# Should return a JSON with a 1024-element vector
If Smart Extraction produces garbled/invalid output: The local LLM may not support structured JSON reliably. Try qwen3:8b first — it has native structured output support. If still failing, disable:
json
{ "smartExtraction": false }
If Ollama is on a different host or Docker: Replace http://localhost:11434/v1 with the actual host, e.g. http://192.168.1.100:11434/v1. Also set OLLAMA_HOST=0.0.0.0 in the Ollama process to allow remote connections.
For the plugins.entries.memory-lancedb-pro.config block, merge into the existing openclaw.json rather than replacing the whole file. Use a targeted edit of only the memory plugin config section.
Read the current openclaw.json first, then apply a surgical edit to the plugins.entries.memory-lancedb-pro section. Use the template that matches your installation method:
Method 1 — openclaw plugins install (plugin was installed via the plugin manager):
No load.paths or allow needed — the plugin manager already registered the plugin.
json
{
"plugins": {
"slots": { "memory": "memory-lancedb-pro" },
"entries": {
"memory-lancedb-pro": {
"enabled": true,
"config": {
"<<OPTIMAL CONFIG HERE>>"
}
}
}
}
}
Method 2 — git clone with manual path (workspace plugin):
Both load.paths AND allow are required — workspace plugins are disabled by default.
json
{
"plugins": {
"load": { "paths": ["plugins/memory-lancedb-pro"] },
"allow": ["memory-lancedb-pro"],
"slots": { "memory": "memory-lancedb-pro" },
"entries": {
"memory-lancedb-pro": {
"enabled": true,
"config": {
"<<OPTIMAL CONFIG HERE>>"
}
}
}
}
}
bash
openclaw config validate
openclaw gateway restart
openclaw logs --follow --plain | rg "memory-lancedb-pro"
Expected output confirms:
memory-lancedb-pro: smart extraction enabledmemory-lancedb-pro@...: plugin registeredbash
openclaw plugins info memory-lancedb-pro
openclaw hooks list --json | grep -E "before_agent_start|agent_end|command:new"
openclaw memory-pro stats
Then do a quick smoke test:
memory_store with text: "test memory for verification"memory_recall with query: "test memory"For new users, the community one-click installer handles everything automatically — path detection, schema validation, auto-update, provider selection, and rollback:
bash
curl -fsSL https://raw.githubusercontent.com/CortexReach/toolbox/main/memory-lancedb-pro-setup/setup-memory.sh -o setup-memory.sh
bash setup-memory.sh
Options: --dry-run (preview only), --beta (include pre-release), --ref v1.2.0 (pin version), --selfcheck-only, --uninstall.
Source: https://github.com/CortexReach/toolbox/tree/main/memory-lancedb-pro-setup
22.16+)bash
# Install from npm registry (@beta tag = latest pre-release, e.g. 1.1.0-beta.8)
openclaw plugins install memory-lancedb-pro@beta
# Install stable release from npm (@latest tag, e.g. 1.0.32)
openclaw plugins install memory-lancedb-pro
# Or install from a local git clone — use master branch (matches npm @beta)
git clone -b master https://github.com/CortexReach/memory-lancedb-pro.git /tmp/memory-lancedb-pro
openclaw plugins install /tmp/memory-lancedb-pro
npm vs GitHub branches:
@betainstalls from the npm registry (not directly from GitHub). The repo has two long-lived branches:masteris the release branch (matches npm@beta),mainis older/behind. Always clonemasterif you want code that matches the published beta.
Then bind the memory slot and add your config (see Configuration section below):
json
{
"plugins": {
"slots": { "memory": "memory-lancedb-pro" },
"entries": {
"memory-lancedb-pro": {
"enabled": true,
"config": { "<<your config here>>" }
}
}
}
}
Restart and verify:
bash
openclaw gateway restart
openclaw plugins info memory-lancedb-pro
⚠️ Critical: Workspace plugins (git-cloned paths) are disabled by default in OpenClaw. You MUST explicitly enable them.
bash
# 1. Clone into workspace
cd /path/to/your/openclaw/workspace
git clone -b master https://github.com/CortexReach/memory-lancedb-pro.git plugins/memory-lancedb-pro
cd plugins/memory-lancedb-pro && npm install
Add to openclaw.json — the enabled: true and the allow entry are both required:
json
{
"plugins": {
"load": { "paths": ["plugins/memory-lancedb-pro"] },
"allow": ["memory-lancedb-pro"],
"slots": { "memory": "memory-lancedb-pro" },
"entries": {
"memory-lancedb-pro": {
"enabled": true,
"config": {
"embedding": {
"apiKey": "${JINA_API_KEY}",
"model": "jina-embeddings-v5-text-small",
"baseURL": "https://api.jina.ai/v1",
"dimensions": 1024,
"taskQuery": "retrieval.query",
"taskPassage": "retrieval.passage",
"normalized": true
}
}
}
}
}
}
Validate and restart:
bash
openclaw config validate
openclaw gateway restart
openclaw logs --follow --plain | rg "memory-lancedb-pro"
Expected log output:
memory-lancedb-pro: smart extraction enabledmemory-lancedb-pro@...: plugin registeredUse absolute paths in plugins.load.paths. Add to plugins.allow. Bind memory slot: plugins.slots.memory = "memory-lancedb-pro". Set plugins.entries.memory-lancedb-pro.enabled: true.
Then restart and verify:
bash
openclaw config validate
openclaw gateway restart
openclaw logs --follow --plain | rg "memory-lancedb-pro"
After the plugin starts successfully, determine which scenario applies and run the corresponding steps:
Scenario A — Coming from built-in memory-lancedb plugin (most common upgrade path)
The old plugin stores data in LanceDB at ~/.openclaw/memory/lancedb. Use the migrate command:
bash
# 1. Check if old data exists and is readable
openclaw memory-pro migrate check
# 2. Preview what would be migrated (dry run)
openclaw memory-pro migrate run --dry-run
# 3. Run the actual migration
openclaw memory-pro migrate run
# 4. Verify migrated data
openclaw memory-pro migrate verify
openclaw memory-pro stats
If the old database is at a non-default path:
bash
openclaw memory-pro migrate check --source /path/to/old/lancedb
openclaw memory-pro migrate run --source /path/to/old/lancedb
Scenario B — Existing memories exported as JSON
If you have memories in the standard JSON export format:
bash
# Preview import (dry run)
openclaw memory-pro import memories.json --scope global --dry-run
# Import
openclaw memory-pro import memories.json --scope global
Expected JSON schema:
json
{
"version": "1.0",
"memories": [
{
"text": "Memory content (required)",
"category": "preference|fact|decision|entity|other",
"importance": 0.7,
"timestamp": 1234567890000
}
]
}
Scenario C — Memories stored in Markdown files (AGENTS.md, MEMORY.md, etc.)
There is no direct markdown import — the import command only accepts JSON. You need to convert first.
Manual conversion approach:
text, category, importanceopenclaw memory-pro importOr use memory_store tool directly in the agent to store individual entries one at a time:
text
memory_store(text="<extracted memory>", category="fact", importance=0.8)
Note: Markdown-based memory files (MEMORY.md, AGENTS.md) are workspace context files, not the same as the LanceDB memory store. You only need to migrate them if you want that content searchable via
memory_recall.
Scenario D — Fresh install, no prior memories
No migration needed. Verify the plugin is working with a quick smoke test:
bash
openclaw memory-pro stats # should show 0 memories
Then trigger a conversation — autoCapture will start storing memories automatically.
No manual action required for LanceDB version changes.
The plugin requires @lancedb/lancedb ^0.26.2 as an npm dependency — this is installed automatically when you install or update the plugin. You do not need to manually install or upgrade LanceDB.
LanceDB 0.26+ changed how numeric columns are returned (Arrow BigInt type for timestamp, importance, _distance, _score). The plugin handles this transparently at runtime via internal Number(...) coercion — no migration commands are needed when moving between LanceDB versions.
TL;DR: LanceDB version compatibility is fully automatic. See the table below for when each maintenance command actually applies.
Command distinction (important):
CommandWhen to useopenclaw plugins update memory-lancedb-proUpdate plugin code after a new release (npm-installed only)openclaw plugins update --allUpdate all npm-installed plugins at onceopenclaw memory-pro upgradeEnrich old memory-lancedb-pro entries that predate the smart-memory schema (missing L0/L1/L2 metadata + 6-category system) — NOT related to LanceDB versionopenclaw memory-pro migrateOne-time migration from the separate memory-lancedb built-in plugin → Proopenclaw memory-pro reembedRebuild all embeddings after switching embedding model or provider
When do you need memory-pro upgrade?
Run it if you installed memory-lancedb-pro before the smart-memory format was introduced (i.e., entries are missing memory_category in their metadata). Signs you need it:
memory_recall returns results but without meaningful categoriesmemory-pro list --json shows entries with no l0_abstract / l1_overview fieldsSafe upgrade sequence:
bash
# 1. Backup first
openclaw memory-pro export --scope global --output memories-backup.json
# 2. Preview what would change
openclaw memory-pro upgrade --dry-run
# 3. Run upgrade (uses LLM by default for L0/L1/L2 generation)
openclaw memory-pro upgrade
# 4. Verify results
openclaw memory-pro stats
openclaw memory-pro search "your known keyword" --scope global --limit 5
Upgrade options:
bash
openclaw memory-pro upgrade --no-llm # skip LLM, use simple text truncation
openclaw memory-pro upgrade --batch-size 5 # slower but safer for large collections
openclaw memory-pro upgrade --limit 50 # process only first N entries
openclaw memory-pro upgrade --scope global # limit to one scope
bash
openclaw plugins list # show all discovered plugins
openclaw plugins info memory-lancedb-pro # show plugin status and config
openclaw plugins enable memory-lancedb-pro # enable a disabled plugin
openclaw plugins disable memory-lancedb-pro # disable without removing
openclaw plugins update memory-lancedb-pro # update npm-installed plugin
openclaw plugins update --all # update all npm plugins
openclaw plugins doctor # health check for all plugins
openclaw plugins install ./path/to/plugin # install local plugin (copies + enables)
openclaw plugins install @scope/plugin@beta # install from npm registry
openclaw plugins install -l ./path/to/plugin # symlink for dev (no copy)
Gateway restart required after:
plugins install,plugins enable,plugins disable,plugins update, or any change toopenclaw.json. Changes do not take effect until the gateway is restarted.bash
openclaw gateway restart
Gateway restart required after any change: After installing, enabling, disabling, updating, or changing config in openclaw.json, you MUST run openclaw gateway restart — changes are NOT hot-reloaded.
Workspace plugins are DISABLED by default: After git clone, you MUST add plugins.allow: ["memory-lancedb-pro"] AND plugins.entries.memory-lancedb-pro.enabled: true — without these the plugin silently does not load.
Env vars in gateway process: ${OPENAI_API_KEY} requires env vars set in the OpenClaw Gateway service process—not just your shell.
Absolute vs. relative paths: For existing deployments, always use absolute paths in plugins.load.paths.
baseURL not baseUrl: The embedding (and llm) config field is baseURL (capital URL), NOT baseUrl. Using the wrong casing causes a schema validation error: "must NOT have additional properties". Also note the required /v1 suffix: http://localhost:11434/v1, not http://localhost:11434. Do not confuse with agents.defaults.memorySearch.remote.baseUrl which uses a different casing.
jiti cache invalidation: After modifying .ts files under plugins, run rm -rf /tmp/jiti/ BEFORE openclaw gateway restart.
Unknown plugin id = error: OpenClaw treats unknown ids in entries, allow, deny, or slots as validation errors. The plugin id must be discoverable before referencing it.
Separate LLM config: If embedding and LLM use different providers, configure the llm section separately — it falls back to embedding key/URL otherwise.
Scope isolation: Multi-scope requires explicit scopes.agentAccess mapping — without it, agents only see global scope.
Session memory hook: Fires on /new command — test with an actual /new invocation.
Reranker credentials: When switching providers, update both rerankApiKey AND rerankEndpoint.
Config check before assuming defaults: Run openclaw config get plugins.entries.memory-lancedb-pro to verify what's actually loaded.
Custom config/state paths via env vars: OpenClaw respects the following environment variables for custom paths:
OPENCLAW_HOME — sets the root config/data directory (default: ~/.openclaw/)OPENCLAW_CONFIG_PATH — absolute path to openclaw.json overrideOPENCLAW_STATE_DIR — override for runtime state/data directory
Set these in the OpenClaw Gateway process's environment if the default ~/.openclaw/ path is not appropriate.bash
openclaw doctor # full health check (recommended)
openclaw config validate # config schema check only
openclaw plugins info memory-lancedb-pro # plugin status
openclaw plugins doctor # plugin-specific health
openclaw hooks list --json | grep memory # confirm hooks registered
openclaw memory-pro stats
openclaw memory-pro list --scope global --limit 5
Full smoke test checklist:
enabled: true and config loadedbefore_agent_start, agent_end, command:newmemory_store → memory_recall round trip via tools/new testConfig validation tool (from CortexReach/toolbox):
bash
# Download once
curl -fsSL https://raw.githubusercontent.com/CortexReach/toolbox/main/memory-lancedb-pro-setup/scripts/config-validate.mjs -o config-validate.mjs
# Run against your openclaw.json
node config-validate.mjs
# Or validate a specific config snippet
node config-validate.mjs --json '{"embedding":{"baseURL":"http://localhost:11434/v1","model":"bge-m3","apiKey":"ollama"}}'
Exit code 0 = pass/warn, 1 = errors found.
Error messageRoot causeFixmust NOT have additional properties + config.embeddingField name typo in embedding config (e.g. baseUrl instead of baseURL)Check all field names against the schema table below — field names are case-sensitivemust NOT have additional properties (top-level config)Unknown top-level field in plugin configRemove or correct the fieldmemory-lancedb-pro: plugin not found / plugin silently not loadingplugins.allow missing (git-clone install) or enabled: falseAdd plugins.allow: ["memory-lancedb-pro"] and set enabled: true, then restartUnknown plugin id validation errorPlugin referenced in entries/ slots before it's discoverableInstall/register the plugin first, then add config references${OPENAI_API_KEY} not expanding / auth errors despite env var setEnv var not set in the gateway process environmentSet the env var in the service that runs OpenClaw gateway, not just your shellHooks ( before_agent_start, agent_end) not firingGateway not restarted after install/config changeRun openclaw gateway restartEmbedding errors with OllamaWrong baseURL formatMust be http://localhost:11434/v1 (with /v1), field must be baseURL not baseUrl``memory-pro stats shows 0 entries after conversationautoCapture false or extractMinMessages not reachedSet autoCapture: true; need at least extractMinMessages (default 2) turnsMemories not injected before agent repliesautoRecall is false (schema default)Explicitly set "autoRecall": true``jiti cache error after editing plugin .ts filesStale compiled cacheRun rm -rf /tmp/jiti/ then openclaw gateway restart
json
{
"embedding": {
"provider": "openai-compatible",
"apiKey": "${OPENAI_API_KEY}",
"model": "text-embedding-3-small"
},
"autoCapture": true,
"autoRecall": true,
"smartExtraction": true,
"extractMinMessages": 2,
"extractMaxChars": 8000,
"sessionMemory": { "enabled": false }
}
Note: autoRecall is disabled by default in the plugin schema — explicitly set it to true for new deployments.
Uses Jina for both embedding and reranking — best retrieval quality:
json
{
"embedding": {
"apiKey": "${JINA_API_KEY}",
"model": "jina-embeddings-v5-text-small",
"baseURL": "https://api.jina.ai/v1",
"dimensions": 1024,
"taskQuery": "retrieval.query",
"taskPassage": "retrieval.passage",
"normalized": true
},
"dbPath": "~/.openclaw/memory/lancedb-pro",
"autoCapture": true,
"autoRecall": true,
"captureAssistant": false,
"smartExtraction": true,
"extractMinMessages": 2,
"extractMaxChars": 8000,
"enableManagementTools": false,
"llm": {
"apiKey": "${OPENAI_API_KEY}",
"model": "gpt-4o-mini",
"baseURL": "https://api.openai.com/v1"
},
"retrieval": {
"mode": "hybrid",
"vectorWeight": 0.7,
"bm25Weight": 0.3,
"rerank": "cross-encoder",
"rerankProvider": "jina",
"rerankModel": "jina-reranker-v3",
"rerankEndpoint": "https://api.jina.ai/v1/rerank",
"rerankApiKey": "${JINA_API_KEY}",
"candidatePoolSize": 12,
"minScore": 0.6,
"hardMinScore": 0.62,
"filterNoise": true,
"lengthNormAnchor": 500,
"timeDecayHalfLifeDays": 60,
"reinforcementFactor": 0.5,
"maxHalfLifeMultiplier": 3
},
"sessionMemory": { "enabled": false, "messageCount": 15 }
}
Why these settings excel:
taskQuery/ taskPassage) optimized for retrievalcandidatePoolSize: 12 + minScore: 0.6: Aggressive filtering reduces noisecaptureAssistant: false: Prevents storing agent-generated boilerplatesessionMemory: false: Avoids polluting retrieval with session summariesjson
{
"embedding": {
"apiKey": "${JINA_API_KEY}",
"model": "jina-embeddings-v5-text-small",
"baseURL": "https://api.jina.ai/v1",
"dimensions": 1024,
"taskQuery": "retrieval.query",
"taskPassage": "retrieval.passage",
"normalized": true
},
"dbPath": "~/.openclaw/memory/lancedb-pro",
"autoCapture": true,
"autoRecall": true,
"captureAssistant": false,
"smartExtraction": true,
"llm": {
"apiKey": "${OPENAI_API_KEY}",
"model": "gpt-4o-mini",
"baseURL": "https://api.openai.com/v1"
},
"extractMinMessages": 2,
"extractMaxChars": 8000,
"enableManagementTools": false,
"retrieval": {
"mode": "hybrid",
"vectorWeight": 0.7,
"bm25Weight": 0.3,
"minScore": 0.3,
"hardMinScore": 0.35,
"rerank": "cross-encoder",
"rerankProvider": "jina",
"rerankModel": "jina-reranker-v3",
"rerankEndpoint": "https://api.jina.ai/v1/rerank",
"rerankApiKey": "${JINA_API_KEY}",
"candidatePoolSize": 20,
"recencyHalfLifeDays": 14,
"recencyWeight": 0.1,
"filterNoise": true,
"lengthNormAnchor": 500,
"timeDecayHalfLifeDays": 60,
"reinforcementFactor": 0.5,
"maxHalfLifeMultiplier": 3
},
"scopes": {
"default": "global",
"definitions": {
"global": { "description": "Shared knowledge" },
"agent:discord-bot": { "description": "Discord bot private" }
},
"agentAccess": {
"discord-bot": ["global", "agent:discord-bot"]
}
},
"sessionStrategy": "none",
"memoryReflection": {
"storeToLanceDB": true,
"injectMode": "inheritance+derived",
"agentId": "memory-distiller",
"messageCount": 120,
"maxInputChars": 24000,
"thinkLevel": "medium"
},
"selfImprovement": {
"enabled": true,
"beforeResetNote": true,
"ensureLearningFiles": true
},
"mdMirror": { "enabled": false },
"decay": {
"recencyHalfLifeDays": 30,
"recencyWeight": 0.4,
"frequencyWeight": 0.3,
"intrinsicWeight": 0.3,
"betaCore": 0.8,
"betaWorking": 1.0,
"betaPeripheral": 1.3
},
"tier": {
"coreAccessThreshold": 10,
"coreCompositeThreshold": 0.7,
"coreImportanceThreshold": 0.8,
"workingAccessThreshold": 3,
"workingCompositeThreshold": 0.4,
"peripheralCompositeThreshold": 0.15,
"peripheralAgeDays": 60
}
}
FieldTypeDefaultDescriptionapiKeystring—API key (supports ${ENV_VAR}); array for multi-key failovermodelstring—Model identifierbaseURLstringprovider defaultAPI endpointdimensionsnumberprovider defaultVector dimensionalitytaskQuerystring—Task hint for query embeddings ( retrieval.query)taskPassagestring—Task hint for passage embeddings ( retrieval.passage)normalizedbooleanfalseRequest L2-normalized embeddingsproviderstringopenai-compatibleProvider type selectorchunkingbooleantrueAuto-chunk documents exceeding embedding context limits
FieldTypeDefaultDescriptiondbPathstring~/.openclaw/memory/lancedb-proLanceDB data directoryautoCapturebooleantrueAuto-extract memories after agent replies (via agent_end hook)autoRecallbooleanfalse (schema default)Inject memories before agent processing — set to true explicitlycaptureAssistantbooleanfalseInclude assistant messages in extractionsmartExtractionbooleantrueLLM-powered 6-category extractionextractMinMessagesnumber2Min conversation turns before extraction triggersextractMaxCharsnumber8000Max context chars sent to extraction LLMenableManagementToolsbooleanfalseRegister CLI management tools as agent toolsautoRecallMinLengthnumber15Min prompt chars to trigger auto-recall (6 for CJK)autoRecallMinRepeatednumber0Min turns before same memory can re-inject in same sessionsessionStrategystringsystemSessionMemorySession pipeline: memoryReflection / systemSessionMemory / none``autoRecallTopKnumber3Max memories injected per auto-recall (max 20)autoRecallSelectionModestringmmrSelection algorithm: mmr / legacy / setwise-v2``autoRecallCategoriesarray["preference","fact","decision","entity","other"]Categories eligible for auto-recall injectionautoRecallExcludeReflectionbooleantrueExclude reflection-type memories from auto-recallautoRecallMaxAgeDaysnumber30Max age (days) of memories considered for auto-recallautoRecallMaxEntriesPerKeynumber10Max entries per scope key in auto-recall results
FieldTypeDefaultDescriptionllm.apiKeystringfalls back to embedding.apiKeyLLM API keyllm.modelstringopenai/gpt-oss-120bLLM model for extractionllm.baseURLstringfalls back to embedding.baseURLLLM endpoint
FieldTypeDefaultDescriptionmodestringhybrid``hybrid / vector ( bm25-only mode does not exist in schema)vectorWeightnumber0.7Weight for vector searchbm25Weightnumber0.3Weight for BM25 full-text searchminScorenumber0.3Minimum relevance thresholdhardMinScorenumber0.35Hard cutoff post-rerankingrerankstringcross-encoderReranking strategy: cross-encoder / lightweight / none``rerankProviderstringjina``jina / siliconflow / voyage / pinecone / vllm (Docker Model Runner)rerankModelstringjina-reranker-v3Reranker model namererankEndpointstringprovider defaultReranker API URLrerankApiKeystring—Reranker API keycandidatePoolSizenumber20Candidates to rerank before final filterrecencyHalfLifeDaysnumber14Freshness decay half-liferecencyWeightnumber0.1Weight of recency in scoringtimeDecayHalfLifeDaysnumber60Memory age decay factorreinforcementFactornumber0.5Access-based half-life multiplier (0–2, set 0 to disable)maxHalfLifeMultipliernumber3Hard cap on reinforcement boostfilterNoisebooleantrueFilter refusals, greetings, etc.lengthNormAnchornumber500Reference length for normalization (chars)
Access reinforcement note: Reinforcement is whitelisted to source: "manual" only — auto-recall does NOT strengthen memories, preventing noise amplification.
Use sessionStrategy (top-level field) to configure the session pipeline:
ValueBehavior"systemSessionMemory" **(default)**Built-in session memory (simpler)"memoryReflection"Advanced LLM-powered reflection with inheritance/derived injection"none"Session summaries disabled
memoryReflection config (used when sessionStrategy: "memoryReflection"):
FieldTypeDefaultDescriptionstoreToLanceDBbooleantruePersist reflections to LanceDBwriteLegacyCombinedbooleantrueAlso write legacy combined rowinjectModestringinheritance+derived``inheritance-only / inheritance+derived``agentIdstring—Dedicated reflection agent (e.g. "memory-distiller")messageCountnumber120Messages to include in reflectionmaxInputCharsnumber24000Max chars sent to reflection LLMtimeoutMsnumber20000Reflection LLM timeout (ms)thinkLevelstringmediumReasoning depth: off / minimal / low / medium / high``errorReminderMaxEntriesnumber3Max error entries injected into reflectiondedupeErrorSignalsbooleantrueDeduplicate error signals before injection
memoryReflection.recall sub-object (controls which past reflections are retrieved for injection):
FieldTypeDefaultDescriptionmodestringfixedRecall mode: fixed / dynamic``topKnumber6Max reflection entries retrieved (max 20)includeKindsarray["invariant"]Which kinds to include: invariant / derived``maxAgeDaysnumber45Max age of reflections to retrievemaxEntriesPerKeynumber10Max entries per scope keyminRepeatednumber2Min times an entry must appear to be includedminScorenumber0.18Minimum relevance score (range 0–5)minPromptLengthnumber8Min prompt length to trigger recall
⚠️
sessionMemoryis a legacy compatibility shim since v1.1.0. PrefersessionStrategyinstead.
sessionMemory.enabled: true→ maps tosessionStrategy: "systemSessionMemory"sessionMemory.enabled: false→ maps tosessionStrategy: "none"
FieldTypeDefaultDescriptionsessionMemory.enabledbooleanfalseLegacy: enable session summaries on /new``sessionMemory.messageCountnumber15Legacy: maps to memoryReflection.messageCount
FieldTypeDefaultDescriptionselfImprovement.enabledbooleantrueEnable self-improvement tools ( self_improvement_log etc.) — on by defaultselfImprovement.beforeResetNotebooleantrueInject learning reminder before session resetselfImprovement.skipSubagentBootstrapbooleantrueSkip bootstrap for sub-agentsselfImprovement.ensureLearningFilesbooleantrueAuto-create LEARNINGS.md / ERRORS.md if missing
Tool activation rules:
self_improvement_log: requires selfImprovement.enabled: true (default — active unless explicitly disabled)self_improvement_extract_skill + self_improvement_review: additionally require enableManagementTools: trueFieldTypeDefaultDescriptionmdMirror.enabledbooleanfalseMirror memory entries as .md filesmdMirror.dirstring—Directory for markdown mirror files
FieldTypeDefaultDescriptiondecay.recencyHalfLifeDaysnumber30Base Weibull decay half-lifedecay.recencyWeightnumber0.4Weight of recency in lifecycle score (distinct from retrieval.recencyWeight)decay.frequencyWeightnumber0.3Weight of access frequencydecay.intrinsicWeightnumber0.3Weight of importance × confidencedecay.betaCorenumber0.8Weibull shape for core memoriesdecay.betaWorkingnumber1.0Weibull shape for working memoriesdecay.betaPeripheralnumber1.3Weibull shape for peripheral memoriesdecay.coreDecayFloornumber0.9Minimum lifecycle score for core tierdecay.workingDecayFloornumber0.7Minimum lifecycle score for working tierdecay.peripheralDecayFloornumber0.5Minimum lifecycle score for peripheral tierdecay.staleThresholdnumber0.3Score below which a memory is considered staledecay.searchBoostMinnumber0.3Minimum search boost applied to lifecycle scoredecay.importanceModulationnumber1.5Multiplier for importance in lifecycle score
FieldTypeDefaultDescriptiontier.coreAccessThresholdnumber10Access count for core promotiontier.coreCompositeThresholdnumber0.7Lifecycle score for core promotiontier.coreImportanceThresholdnumber0.8Minimum importance for core promotiontier.workingAccessThresholdnumber3Access count for working promotiontier.workingCompositeThresholdnumber0.4Lifecycle score for working promotiontier.peripheralCompositeThresholdnumber0.15Score below which demotion occurstier.peripheralAgeDaysnumber60Age threshold for stale memory demotion
memory_recall — Search long-term memory via hybrid retrieval
ParameterTypeRequiredDefaultNotesquerystringyes—Search querylimitnumberno5Max 20scopestringno—Specific scope to searchcategoryenumno—preference|fact|decision|entity|reflection|other
memory_store — Save information to long-term memory
ParameterTypeRequiredDefaultNotestextstringyes—Information to rememberimportancenumberno0.7Range 0–1categoryenumno—Memory classificationscopestringnoagent:<id>Target scope
memory_forget — Delete memories by search or direct ID
ParameterTypeRequiredNotesquerystringone ofSearch query to locate memorymemoryIdstringone ofFull UUID or 8+ char prefixscopestringnoScope for search/deletion
memory_update — Update memory (preserves original timestamp; preference/ entity text updates create a new versioned row preserving history)
ParameterTypeRequiredNotesmemoryIdstringyesFull UUID or 8+ char prefixtextstringnoNew content (triggers re-embedding; preference/ entity creates supersede version)importancenumbernoNew score 0–1categoryenumnoNew classification
enableManagementTools: true)#memory_stats — Usage statistics
scope (string, optional): Filter by scopememory_list — List recent memories with filtering
limit (number, optional, default 10, max 50), scope, category, offset (pagination)
self_improvement_logis enabled by default (selfImprovement.enabled: true).self_improvement_extract_skillandself_improvement_reviewadditionally requireenableManagementTools: true.
self_improvement_log — Log learning/error entries into LEARNINGS.md / ERRORS.md
ParameterTypeRequiredNotestypeenumyes"learning" or "error"``summarystringyesOne-line summarydetailsstringnoDetailed contextsuggestedActionstringnoAction to prevent recurrencecategorystringnoLearning: correction|best_practice|knowledge_gap; Error: correction|bug_fix|integration_issue``areastringnofrontend|backend|infra|tests|docs|config``prioritystringnolow|medium|high|critical
self_improvement_extract_skill — Create skill scaffold from a learning entry
ParameterTypeRequiredDefaultNoteslearningIdstringyes—Format LRN-YYYYMMDD-001 or ERR-*``skillNamestringyes—Lowercase with hyphenssourceFileenumnoLEARNINGS.md``LEARNINGS.md|ERRORS.md``outputDirstringno"skills"Relative output directory
self_improvement_review — Summarize governance backlog (no parameters)
LLM-powered automatic memory classification and storage triggered after conversations.
json
{
"smartExtraction": true,
"extractMinMessages": 2,
"extractMaxChars": 8000,
"llm": {
"apiKey": "${OPENAI_API_KEY}",
"model": "gpt-4o-mini"
}
}
Minimal (reuses embedding API key — no separate llm block needed):
json
{
"embedding": { "apiKey": "${OPENAI_API_KEY}", "model": "text-embedding-3-small" },
"smartExtraction": true
}
Disable: { "smartExtraction": false }
Input CategoryStored AsDedup BehaviorProfilefactAlways merge (auto-consolidates)PreferencespreferenceConditional mergeEntitiesentityConditional mergeEventsdecisionAppend-only (no merge)CasesfactAppend-only (no merge)PatternsotherConditional merge
CREATE | MERGE | SKIP | SUPPORT | CONTEXTUALIZE | CONTRADICTProviderModelBase URLDimensionsNotesJina (recommended)jina-embeddings-v5-text-small``https://api.jina.ai/v11024Latest (Feb 2026), task-aware LoRA, 32K ctxJina (multimodal)jina-embeddings-v4``https://api.jina.ai/v11024Text + image, Qwen2.5-VL backboneOpenAItext-embedding-3-large``https://api.openai.com/v13072Best OpenAI quality (MTEB 64.6%)OpenAItext-embedding-3-small``https://api.openai.com/v11536Cost-efficientDashScope (Alibaba)text-embedding-v4``https://dashscope.aliyuncs.com/compatible-mode/v11024Recommended for Chinese users; also supports rerank (see note below)Google Geminigemini-embedding-2-preview``https://generativelanguage.googleapis.com/v1beta/openai/3072Latest (Mar 2026), multimodal, 100+ languagesGoogle Geminigemini-embedding-001``https://generativelanguage.googleapis.com/v1beta/openai/3072Stable text-onlyOllama (local)mxbai-embed-large``http://localhost:11434/v11024Recommended local — beats text-embedding-3-largeOllama (local)snowflake-arctic-embed2``http://localhost:11434/v11024Best multilingual local optionOllama (local)nomic-embed-text:v1.5``http://localhost:11434/v1768Lightweight classic, 270MB
DashScope rerank note: DashScope is not a rerankProvider enum value, but its rerank API response is Jina-compatible. Use rerankProvider: "jina" with DashScope's endpoint:
json
"retrieval": {
"rerank": "cross-encoder",
"rerankProvider": "jina",
"rerankModel": "qwen3-rerank",
"rerankEndpoint": "https://dashscope.aliyuncs.com/compatible-api/v1/reranks",
"rerankApiKey": "${DASHSCOPE_API_KEY}"
}
Multi-key failover: Set apiKey as an array for round-robin rotation on 429/503 errors.
ProviderrerankProviderEndpointModelNotesJina (default)jina``https://api.jina.ai/v1/rerank``jina-reranker-v3Latest text reranker (2025, Qwen3 backbone, 131K ctx)Jina (multimodal)jina``https://api.jina.ai/v1/rerank``jina-reranker-m0Multimodal (text+images), use when docs contain imagesSiliconFlowsiliconflow``https://api.siliconflow.com/v1/rerank``BAAI/bge-reranker-v2-m3Free tier availableVoyage AIvoyage``https://api.voyageai.com/v1/rerank``rerank-2.5Sends {model, query, documents}, no top_nPineconepinecone``https://api.pinecone.io/rerank``bge-reranker-v2-m3Pinecone customers onlyvLLM / Docker Model RunnervllmCustom endpointany compatible modelSelf-hosted via Docker Model Runner
Jina key can be reused for both embedding and reranking.
Scope FormatDescriptionglobalShared across all agentsagent:<id>Agent-specific memoriescustom:<name>Custom-named scopesproject:<id>Project-specific memoriesuser:<id>User-specific memories
Default access: global + agent:<id>. Multi-scope requires explicit scopes.agentAccess — see Full Config above.
To disable memory entirely (unbind the slot without removing the plugin):
json
{ "plugins": { "slots": { "memory": "none" } } }
TierDecay FloorBetaBehaviorCore0.90.8Gentle sub-exponential declineWorking0.71.0Standard exponential (default)Peripheral0.51.3Rapid super-exponential fade
Fusion: weightedFusion = (vectorScore × 0.7) + (bm25Score × 0.3)
Pipeline: RRF Fusion → Cross-Encoder Rerank → Lifecycle Decay Boost → Length Norm → Hard Min Score → MMR Diversity (cosine > 0.85 demoted)
Reranking: 60% cross-encoder score + 40% original fused score. Falls back to cosine similarity on API failure.
Special BM25: Preserves exact keyword matches (BM25 ≥ 0.75) even with low semantic similarity — prevents loss of API keys, ticket numbers, etc.
Skip for: greetings, slash commands, affirmations (yes/okay/thanks), continuations (go ahead/proceed), system messages, short queries (<15 chars English / <6 chars CJK without "?").
Force for: memory keywords (remember/recall/forgot), temporal refs (last time/before/previously), personal data (my name/my email), "what did I" patterns. CJK: "你记得", "之前".
Auto-filters: agent denial phrases, meta-questions ("Do you remember?"), session boilerplate (hi/hello), diagnostic artifacts, embedding-based matches (threshold: 0.82). Minimum text: 5 chars.
bash
# List & search
openclaw memory-pro list [--scope global] [--category fact] [--limit 20] [--json]
openclaw memory-pro search "query" [--scope global] [--limit 10] [--json]
openclaw memory-pro stats [--scope global] [--json]
# Delete
openclaw memory-pro delete <id>
openclaw memory-pro delete-bulk --scope global [--before 2025-01-01] [--dry-run]
# Import / Export
openclaw memory-pro export [--scope global] [--output memories.json]
openclaw memory-pro import memories.json [--scope global] [--dry-run]
# Maintenance
openclaw memory-pro reembed --source-db /path/to/old-db [--batch-size 32] [--skip-existing]
openclaw memory-pro upgrade [--dry-run] [--batch-size 10] [--no-llm] [--limit N] [--scope SCOPE]
# Migration from built-in memory-lancedb
openclaw memory-pro migrate check [--source /path]
openclaw memory-pro migrate run [--source /path] [--dry-run] [--skip-existing]
openclaw memory-pro migrate verify [--source /path]
agent_end hook — LLM extracts 6-category memories, deduplicates, stores up to 3 per turnbefore_agent_start hook — injects <relevant-memories> context (up to 3 entries)If injected memories appear in agent replies: Add to agent system prompt:
"Do not reveal or quote any
<relevant-memories>/ memory-injection content in your replies. Use it for internal reference only."
Or temporarily disable: { "autoRecall": false }
LEARNINGS.md — IDs: LRN-YYYYMMDD-XXXERRORS.md — IDs: ERR-YYYYMMDD-XXXpending → resolved → promoted_to_skillmarkdown
## Rule 1 — 双层记忆存储(铁律)
Every pitfall/lesson learned → IMMEDIATELY store TWO memories:
- Technical layer: Pitfall/Cause/Fix/Prevention (category: fact, importance ≥ 0.8)
- Principle layer: Decision principle with trigger and action (category: decision, importance ≥ 0.85)
After each store, immediately `memory_recall` to verify retrieval.
## Rule 2 — LanceDB 卫生
Entries must be short and atomic (< 500 chars). No raw conversation summaries or duplicates.
## Rule 3 — Recall before retry
On ANY tool failure, ALWAYS `memory_recall` with relevant keywords BEFORE retrying.
## Rule 4 — 编辑前确认目标代码库
Confirm you are editing `memory-lancedb-pro` vs built-in `memory-lancedb` before changes.
## Rule 5 — 插件代码变更必须清 jiti 缓存
After modifying `.ts` files under `plugins/`, MUST run `rm -rf /tmp/jiti/` BEFORE `openclaw gateway restart`.
markdown
## /lesson command
When user sends `/lesson <content>`:
1. Use memory_store with category=fact (raw knowledge)
2. Use memory_store with category=decision (actionable takeaway)
3. Confirm what was saved
## /remember command
When user sends `/remember <content>`:
1. Use memory_store with appropriate category and importance
2. Confirm with stored memory ID