From 0ac035748679c9ae748dbd2c85f5c736380b37a8 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 27 Apr 2026 14:43:45 +0100 Subject: [PATCH] docs(memory): explain qmd collection compatibility --- docs/concepts/memory-qmd.md | 54 +++++++++++++++++++++++++++++++++ docs/reference/memory-config.md | 20 ++++++------ 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/docs/concepts/memory-qmd.md b/docs/concepts/memory-qmd.md index 1d69fe96078..daa425c92d4 100644 --- a/docs/concepts/memory-qmd.md +++ b/docs/concepts/memory-qmd.md @@ -70,6 +70,37 @@ The first search may be slow -- QMD auto-downloads GGUF models (~2 GB) for reranking and query expansion on the first `qmd query` run. +## Search performance and compatibility + +OpenClaw keeps the QMD search path compatible with both current and older QMD +installs. + +On startup, OpenClaw checks the installed QMD help text once per manager. If the +binary advertises support for multiple collection filters, OpenClaw searches all +same-source collections with one command: + +```bash +qmd search "router notes" --json -n 10 -c memory-root-main -c memory-dir-main +``` + +This avoids starting one QMD subprocess for every durable-memory collection. +Session transcript collections stay in their own source group, so mixed +`memory` + `sessions` searches still give the result diversifier input from both +sources. + +Older QMD builds only accept one collection filter. When OpenClaw detects one +of those builds, it keeps the compatibility path and searches each collection +separately before merging and deduplicating results. + +To inspect the installed contract manually, run: + +```bash +qmd --help | grep -i collection +``` + +Current QMD help says collection filters can target one or more collections. +Older help usually describes a single collection. + ## Model overrides QMD model environment variables pass through unchanged from the gateway @@ -166,9 +197,32 @@ with no extra dependencies. runs as a service, create a symlink: `sudo ln -s ~/.bun/bin/qmd /usr/local/bin/qmd`. +If `qmd --version` works in your shell but OpenClaw still reports +`spawn qmd ENOENT`, the gateway process likely has a different `PATH` than your +interactive shell. Pin the binary explicitly: + +```json5 +{ + memory: { + backend: "qmd", + qmd: { + command: "/absolute/path/to/qmd", + }, + }, +} +``` + +Use `command -v qmd` in the environment where QMD is installed, then recheck +with `openclaw memory status --deep`. + **First search very slow?** QMD downloads GGUF models on first use. Pre-warm with `qmd query "test"` using the same XDG dirs OpenClaw uses. +**Many QMD subprocesses during search?** Update QMD if possible. OpenClaw uses +one process for same-source multi-collection searches only when the installed +QMD advertises support for multiple `-c` filters; otherwise it keeps the older +per-collection fallback for correctness. + **BM25-only QMD still trying to build llama.cpp?** Set `memory.qmd.searchMode = "search"`. OpenClaw treats that mode as lexical-only, does not run QMD vector status probes or embedding maintenance, and leaves diff --git a/docs/reference/memory-config.md b/docs/reference/memory-config.md index a14bd4d70d0..fc07ef55f2f 100644 --- a/docs/reference/memory-config.md +++ b/docs/reference/memory-config.md @@ -439,19 +439,19 @@ When sqlite-vec is unavailable, OpenClaw falls back to in-process cosine similar 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 | +| Key | Type | Default | Description | +| ------------------------ | --------- | -------- | ------------------------------------------------------------------------------------- | +| `command` | `string` | `qmd` | QMD executable path; set an absolute path when service `PATH` differs from your shell | +| `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 | `searchMode: "search"` is lexical/BM25-only. OpenClaw does not run semantic vector readiness probes or QMD embedding maintenance for that mode, including during `memory status --deep`; `vsearch` and `query` continue to require QMD vector readiness and embeddings. -OpenClaw prefers current QMD collection and MCP query shapes, but keeps older QMD releases working by trying compatible collection pattern flags and older MCP tool names when needed. When QMD advertises support for multiple collection filters, same-source collections are searched with one QMD process; older QMD builds keep the per-collection compatibility path. +OpenClaw prefers current QMD collection and MCP query shapes, but keeps older QMD releases working by trying compatible collection pattern flags and older MCP tool names when needed. When QMD advertises support for multiple collection filters, same-source collections are searched with one QMD process; older QMD builds keep the per-collection compatibility path. Same-source means durable memory collections are grouped together, while session transcript collections remain a separate group so source diversification still has both inputs. QMD model overrides stay on the QMD side, not OpenClaw config. If you need to override QMD's models globally, set environment variables such as `QMD_EMBED_MODEL`, `QMD_RERANK_MODEL`, and `QMD_GENERATE_MODEL` in the gateway runtime environment.