14 KiB
title, summary, read_when
| title | summary | read_when | ||||
|---|---|---|---|---|---|---|
| Memory configuration reference | All configuration knobs for memory search, embedding providers, QMD, hybrid search, and multimodal indexing |
|
Memory configuration reference
This page lists every configuration knob for OpenClaw memory search. For conceptual overviews, see:
- Memory Overview -- how memory works
- Builtin Engine -- default SQLite backend
- QMD Engine -- local-first sidecar
- Memory Search -- search pipeline and tuning
All memory search settings live under agents.defaults.memorySearch in
openclaw.json unless noted otherwise.
Provider selection
| Key | Type | Default | Description |
|---|---|---|---|
provider |
string |
auto-detected | Embedding adapter ID: openai, gemini, voyage, mistral, ollama, local |
model |
string |
provider default | Embedding model name |
fallback |
string |
"none" |
Fallback adapter ID when the primary fails |
enabled |
boolean |
true |
Enable or disable memory search |
Auto-detection order
When provider is not set, OpenClaw selects the first available:
local-- ifmemorySearch.local.modelPathis configured and the file exists.openai-- if an OpenAI key can be resolved.gemini-- if a Gemini key can be resolved.voyage-- if a Voyage key can be resolved.mistral-- if a Mistral key can be resolved.
ollama is supported but not auto-detected (set it explicitly).
API key resolution
Remote embeddings require an API key. OpenClaw resolves from:
auth profiles, models.providers.*.apiKey, or environment variables.
| Provider | Env var | Config key |
|---|---|---|
| OpenAI | OPENAI_API_KEY |
models.providers.openai.apiKey |
| Gemini | GEMINI_API_KEY |
models.providers.google.apiKey |
| Voyage | VOYAGE_API_KEY |
models.providers.voyage.apiKey |
| Mistral | MISTRAL_API_KEY |
models.providers.mistral.apiKey |
| Ollama | OLLAMA_API_KEY (placeholder) |
-- |
Codex OAuth covers chat/completions only and does not satisfy embedding requests.
Remote endpoint config
For custom OpenAI-compatible endpoints or overriding provider defaults:
| Key | Type | Description |
|---|---|---|
remote.baseUrl |
string |
Custom API base URL |
remote.apiKey |
string |
Override API key |
remote.headers |
object |
Extra HTTP headers (merged with provider defaults) |
{
agents: {
defaults: {
memorySearch: {
provider: "openai",
model: "text-embedding-3-small",
remote: {
baseUrl: "https://api.example.com/v1/",
apiKey: "YOUR_KEY",
},
},
},
},
}
Gemini-specific config
| Key | Type | Default | Description |
|---|---|---|---|
model |
string |
gemini-embedding-001 |
Also supports gemini-embedding-2-preview |
outputDimensionality |
number |
3072 |
For Embedding 2: 768, 1536, or 3072 |
Local embedding config
| Key | Type | Default | Description |
|---|---|---|---|
local.modelPath |
string |
auto-downloaded | Path to GGUF model file |
local.modelCacheDir |
string |
node-llama-cpp default | Cache dir for downloaded models |
Default model: embeddinggemma-300m-qat-Q8_0.gguf (~0.6 GB, auto-downloaded).
Requires native build: pnpm approve-builds then pnpm rebuild node-llama-cpp.
Hybrid search config
All under memorySearch.query.hybrid:
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
true |
Enable hybrid BM25 + vector search |
vectorWeight |
number |
0.7 |
Weight for vector scores (0-1) |
textWeight |
number |
0.3 |
Weight for BM25 scores (0-1) |
candidateMultiplier |
number |
4 |
Candidate pool size multiplier |
MMR (diversity)
| Key | Type | Default | Description |
|---|---|---|---|
mmr.enabled |
boolean |
false |
Enable MMR re-ranking |
mmr.lambda |
number |
0.7 |
0 = max diversity, 1 = max relevance |
Temporal decay (recency)
| Key | Type | Default | Description |
|---|---|---|---|
temporalDecay.enabled |
boolean |
false |
Enable recency boost |
temporalDecay.halfLifeDays |
number |
30 |
Score halves every N days |
Evergreen files (MEMORY.md, non-dated files in memory/) are never decayed.
Full example
{
agents: {
defaults: {
memorySearch: {
query: {
hybrid: {
vectorWeight: 0.7,
textWeight: 0.3,
mmr: { enabled: true, lambda: 0.7 },
temporalDecay: { enabled: true, halfLifeDays: 30 },
},
},
},
},
},
}
Additional memory paths
| Key | Type | Description |
|---|---|---|
extraPaths |
string[] |
Additional directories or files to index |
{
agents: {
defaults: {
memorySearch: {
extraPaths: ["../team-docs", "/srv/shared-notes"],
},
},
},
}
Paths can be absolute or workspace-relative. Directories are scanned
recursively for .md files. Symlinks are ignored.
Multimodal memory (Gemini)
Index images and audio alongside Markdown using Gemini Embedding 2:
| Key | Type | Default | Description |
|---|---|---|---|
multimodal.enabled |
boolean |
false |
Enable multimodal indexing |
multimodal.modalities |
string[] |
-- | ["image"], ["audio"], or ["all"] |
multimodal.maxFileBytes |
number |
10000000 |
Max file size for indexing |
Only applies to files in extraPaths. Default memory roots stay Markdown-only.
Requires gemini-embedding-2-preview. fallback must be "none".
Supported formats: .jpg, .jpeg, .png, .webp, .gif, .heic, .heif
(images); .mp3, .wav, .ogg, .opus, .m4a, .aac, .flac (audio).
Embedding cache
| Key | Type | Default | Description |
|---|---|---|---|
cache.enabled |
boolean |
false |
Cache chunk embeddings in SQLite |
cache.maxEntries |
number |
50000 |
Max cached embeddings |
Prevents re-embedding unchanged text during reindex or transcript updates.
Batch indexing
| Key | Type | Default | Description |
|---|---|---|---|
remote.batch.enabled |
boolean |
false |
Enable batch embedding API |
remote.batch.concurrency |
number |
2 |
Parallel batch jobs |
remote.batch.wait |
boolean |
true |
Wait for batch completion |
remote.batch.pollIntervalMs |
number |
-- | Poll interval |
remote.batch.timeoutMinutes |
number |
-- | Batch timeout |
Available for openai, gemini, and voyage. OpenAI batch is typically
fastest and cheapest for large backfills.
Session memory search (experimental)
Index session transcripts and surface them via memory_search:
| Key | Type | Default | Description |
|---|---|---|---|
experimental.sessionMemory |
boolean |
false |
Enable session indexing |
sources |
string[] |
["memory"] |
Add "sessions" to include transcripts |
sync.sessions.deltaBytes |
number |
100000 |
Byte threshold for reindex |
sync.sessions.deltaMessages |
number |
50 |
Message threshold for reindex |
Session indexing is opt-in and runs asynchronously. Results can be slightly stale. Session logs live on disk, so treat filesystem access as the trust boundary.
SQLite vector acceleration (sqlite-vec)
| Key | Type | Default | Description |
|---|---|---|---|
store.vector.enabled |
boolean |
true |
Use sqlite-vec for vector queries |
store.vector.extensionPath |
string |
bundled | Override sqlite-vec path |
When sqlite-vec is unavailable, OpenClaw falls back to in-process cosine similarity automatically.
Index storage
| Key | Type | Default | Description |
|---|---|---|---|
store.path |
string |
~/.openclaw/memory/{agentId}.sqlite |
Index location (supports {agentId} token) |
store.fts.tokenizer |
string |
unicode61 |
FTS5 tokenizer (unicode61 or trigram) |
QMD backend config
Set memory.backend = "qmd" to enable. All QMD settings live under
memory.qmd:
| Key | Type | Default | Description |
|---|---|---|---|
command |
string |
qmd |
QMD executable path |
searchMode |
string |
search |
Search command: search, vsearch, query |
includeDefaultMemory |
boolean |
true |
Auto-index MEMORY.md + memory/**/*.md |
paths[] |
array |
-- | Extra paths: { name, path, pattern? } |
sessions.enabled |
boolean |
false |
Index session transcripts |
sessions.retentionDays |
number |
-- | Transcript retention |
sessions.exportDir |
string |
-- | Export directory |
Update schedule
| Key | Type | Default | Description |
|---|---|---|---|
update.interval |
string |
5m |
Refresh interval |
update.debounceMs |
number |
15000 |
Debounce file changes |
update.onBoot |
boolean |
true |
Refresh on startup |
update.waitForBootSync |
boolean |
false |
Block startup until refresh completes |
update.embedInterval |
string |
-- | Separate embed cadence |
update.commandTimeoutMs |
number |
-- | Timeout for QMD commands |
Limits
| Key | Type | Default | Description |
|---|---|---|---|
limits.maxResults |
number |
6 |
Max search results |
limits.maxSnippetChars |
number |
-- | Clamp snippet length |
limits.maxInjectedChars |
number |
-- | Clamp total injected chars |
limits.timeoutMs |
number |
4000 |
Search timeout |
Scope
Controls which sessions can receive QMD search results. Same schema as
session.sendPolicy:
{
memory: {
qmd: {
scope: {
default: "deny",
rules: [{ action: "allow", match: { chatType: "direct" } }],
},
},
},
}
Default is DM-only. match.keyPrefix matches the normalized session key;
match.rawKeyPrefix matches the raw key including agent:<id>:.
Citations
memory.citations applies to all backends:
| Value | Behavior |
|---|---|
auto (default) |
Include Source: <path#line> footer in snippets |
on |
Always include footer |
off |
Omit footer (path still passed to agent internally) |
Full QMD example
{
memory: {
backend: "qmd",
citations: "auto",
qmd: {
includeDefaultMemory: true,
update: { interval: "5m", debounceMs: 15000 },
limits: { maxResults: 6, timeoutMs: 4000 },
scope: {
default: "deny",
rules: [{ action: "allow", match: { chatType: "direct" } }],
},
paths: [{ name: "docs", path: "~/notes", pattern: "**/*.md" }],
},
},
}