Skip to content

feat(memory/qmd): support QMD daemon mode instead of spawning per-query processes #14482

Description

@smysle

Problem

When memory.backend = "qmd" is configured, OpenClaw spawns a new qmd process for every operation (qmd query, qmd update, qmd embed) via child_process.spawn() in QmdMemoryManager.runQmd().

QMD runs on Bun + node-llama-cpp and loads multiple GGUF models (embedding ~300M, query-expansion ~1.7B, reranker ~600M). Each spawn triggers a full model reload cycle:

  • Memory spike: peak ~6.6 GB per process
  • Bun v1.3.9 + node-llama-cpp native modules = segfault under concurrent spawns (multi-threaded model loading crashes)
  • Latency: cold-start model loading adds seconds to every search

This makes memory.backend = "qmd" essentially unusable on Linux with the current spawn-per-query architecture.

Solution: QMD Daemon Mode

QMD already supports a daemon/server mode (MCP over HTTP on a configurable port). When running as a daemon:

  • Models are loaded once and stay resident in memory
  • Queries complete in ~1.8s (vs timeout/crash with spawn)
  • Hybrid search (BM25 + semantic + reranker) works perfectly
  • No repeated Bun startup overhead

Proposed config surface

memory: {
  backend: "qmd",
  qmd: {
    // New: connect to a running QMD daemon instead of spawning
    daemon: {
      enabled: true,
      url: "http://localhost:8181",  // or unix socket
      // Optional: auto-start daemon if not running
      autoStart: true
    },
    // Existing spawn config still works as fallback
    command: "qmd",
  }
}

Implementation options

  1. HTTP client in runQmd(): When daemon.enabled, replace spawn() with HTTP requests to the daemon endpoint
  2. Auto-lifecycle: Optionally start/stop the daemon with the gateway (like how qmd update/qmd embed already run on boot)
  3. Fallback: If daemon is unreachable, fall back to spawn mode (or builtin SQLite)

Environment

  • OpenClaw: 2026.2.9
  • OS: Ubuntu Linux 6.17.0-14-generic (x64)
  • QMD: installed via bun install -g
  • Bun: v1.3.9
  • No GPU (CPU-only inference)

Workaround

Currently using a wrapper script that proxies qmd query calls to the daemon via HTTP, configured as memory.qmd.command.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions