Memory migration (H1-H5, kb#73-77,84): - hindsight service in openai/docker-compose.yml: LLM via Kimi (cognee-llm wrapper), multilingual GPU embeddings (bge-m3 via ollama), jina multilingual reranker; pg0 + model cache persisted - openclaw.json/shared-mcp.json: mcp.servers cognee -> hindsight (bank "adolf") - hindsight-openclaw-plugin: forced-hook memory (before_prompt_build recall + agent_end retain), replacing cognee's hook layer; cognify-sweep dropped - verified live: Russian retain->recall, cross-session recall, bank isolation Kimi quota (kb#62): - adolf-llm/server.js: LLM-free GET /usage route (Kimi managed-usage API) - quota-command-openclaw-plugin: /quota readout command Cognee stack left running (decommission is H4/kb#76). Kimi-quota-footer auto-append abandoned (streamed Matrix replies bypass outbound hooks). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014t8Qg9gi7H7HtT8MncoXAB
13 KiB
Adolf memory migration — Cognee → Hindsight
Status: Planned · Date: 2026-07-13 · Owner: alvis
This is the authoritative design + plan for replacing Adolf's long-term memory
subsystem (Cognee) with Hindsight (Vectorize, MIT, self-hosted). It
supersedes the Cognee-specific parts of docs/ARCHITECTURE.md and gates 4–5 of
docs/SPIKE-FINDINGS.md in the OpenClaw fork (/home/alvis/adolf).
Memory stays integrated into Adolf exactly the two ways Cognee was — as a tool (MCP) and as forced hooks (an OpenClaw memory plugin) — so no behaviour the user sees is lost; only the backend changes.
Scope note: this document + the kanboard Ready tasks (H1–H5) are the migration. No live service, compose file,
openclaw.json, or plugin code has been changed yet — those edits are the H-tasks.
1. Why migrate
Cognee works, but its Agap deployment carries three structural costs, all documented in the (now-retired) kanboard cognee tasks:
- Three bespoke services to keep the memory stack alive:
cognee(FastAPI + embedded Kuzu graph + Qdrant vectors),cognee-mcp(a patched MCP→HTTP proxy, local build overlay from kb#70), andcognee-llm(a stateless Kimi-CLI wrapper that exists only to give Cognee an LLM on the flat subscription). - The cognify pipeline is fragile and expensive. "Cognify" (turning raw turns into a graph) is an LLM step. On the Kimi CLI it runs ~5–24 s per call and drains the single-seat subscription quota (SPIKE gate 5 recommended LiteLLM instead). The plugin's async "cognify sweep" also silently stalled twice (kb#69) because of OpenClaw plugin-lifecycle edge cases, so freshly told facts weren't retrievable cross-session until the sweep was re-armed.
- Scoping is best-effort. Under
ENABLE_BACKEND_ACCESS_CONTROL=Falseall datasets share one graph/vector backend, so per-chat isolation leaks (kb#59).
2. What Hindsight gives us
- One container.
ghcr.io/vectorize-io/hindsight:latest— REST API on :8888, web UI on :9999, built-in PostgreSQL (pg0, persisted under/home/hindsight/.pg0). It owns its own vector + graph + temporal representation internally (the "four-network" model), so no external Qdrant or Kuzu is needed for memory. - Built-in MCP server mounted at
/mcpon the same port — ~30 tools includingretain/recall/reflect. This removes the need for a separatecognee-mcpproxy container entirely. - Retain learns on its own.
retainruns Hindsight's extraction/reflection pipeline internally (optionallyasync), so there is no separate "cognify sweep" to arm, throttle, or watch — the whole class of kb#69 bugs disappears. - Memory banks are first-class isolation units, scoped by URL path
(
/v1/{tenant}/banks/{bank_id}/…), so per-chat / per-user scoping is real, not best-effort. - Bring-your-own LLM/embeddings — OpenAI-compatible, Anthropic, Ollama,
LMStudio, etc. We point it at the infra we already run (LiteLLM
:4000and/or Ollama), so we can deletecognee-llmrather than port it.
Net: 3 services → 1, plus we drop the cognify-sweep machinery.
3. Target architecture
Matrix ⇄ OpenClaw ("Adolf" gateway)
│ provider adolf-llm (Kimi wrapper, :8010) — unchanged
▼
adolf-llm ── kimi CLI (agent)
│
┌─────────┴───────────────── memory is a boundary concern ─────────────┐
│ │
│ (a) FORCED HOOKS — hindsight-memory OpenClaw plugin │
│ before_prompt_build → recall → inject as prependContext │
│ agent_end → retain (async:true) the turn │
│ │
│ (b) TOOL — Hindsight built-in MCP in openclaw.json mcp.servers │
│ http://hindsight:8888/mcp/adolf/ → retain/recall/reflect/… │
└───────────────────────────────┬───────────────────────────────────────┘
▼
hindsight (ONE container)
:8888 REST + /mcp · :9999 UI
built-in Postgres (pg0)
LLM → LiteLLM :4000 (or Ollama) [decision, §6]
embeddings → Ollama / built-in [decision, §6]
Reused infra: LiteLLM :4000 and/or Ollama for Hindsight's model calls.
Retired: cognee, cognee-mcp, cognee-llm, Qdrant-for-cognee, Kuzu, the
cognee-openclaw-plugin, and all openclaw.json cognee references.
3.1 Surface (a) — forced hooks (the hindsight-memory plugin)
A new OpenClaw memory plugin replacing cognee-openclaw-plugin, modelled on the
same Honcho touchpoints Cognee used, so the plugin shape is familiar:
Cognee plugin (cognee-memory) |
Hindsight plugin (hindsight-memory) |
|---|---|
before_prompt_build → LLM-free graph recall inject |
before_prompt_build → recall → inject prependContext |
agent_end → raw /add (no inline cognify) |
agent_end → retain (async:true) the user+assistant turn |
| throttled cognify sweep (dirty-tracker, timers) | removed — retain does extraction/learning internally |
cognee_recall registered tool |
hindsight_recall (+ optional hindsight_reflect) tool |
- Activated via
plugins.entries.hindsight-memoryinopenclaw.jsonwith the same hook grants Cognee needed:hooks.allowConversationAccess: trueandallowPromptInjection: true(external plugins must opt in). - Recall stays off the hot LLM path. Hindsight
recallis retrieval (semantic + BM25 + graph + temporal) with evidence scoring — no generative synthesis — so it's the direct analogue of Cognee's LLM-freeonlyContextrecall. The LLM-backed synthesis path isreflect, exposed as a deliberate tool, not run per-turn. - No freshness dial / sweep. Retain with
async:truereturns fast and lets Hindsight extract/consolidate in the background; there is no plugin-owned timer to stall. - Same "untrusted metadata" framing on the injected block; same cleaning of
OpenClaw's
Conversation info (untrusted metadata):and the memory block out of stored/queried text.
3.2 Surface (b) — tool (built-in MCP)
Replace the cognee entry in openclaw.json mcp.servers with:
mcp: {
servers: {
hindsight: {
type: "http",
url: "http://hindsight:8888/mcp/adolf/", // bank-scoped by URL path
},
// openclaw-tools, kanboard, marketplace — unchanged
},
}
The single bank in the path (adolf, or a per-chat bank) selects isolation; the
built-in MCP then exposes retain, recall, reflect, plus mental-model /
directive / memory-browse tools. This replaces cognee-mcp's remember / recall
/ forget — and gives deliberate delete via the memory-management tools instead
of the hand-patched forget(data_id) from kb#70.
4. REST / MCP API mapping
Base path: http://hindsight:8888/v1/default (tenant default). Bank id is a
path parameter.
| Operation | Cognee (old) | Hindsight (new) |
|---|---|---|
| store a turn | POST /api/v1/add (+ later /cognify) |
POST /banks/{bank}/memories body {items:[{content,context,tags,timestamp}], async:true} |
| recall (no LLM) | POST /api/v1/search GRAPH_COMPLETION onlyContext:true |
POST /banks/{bank}/memories/recall body {query, budget, max_tokens, tags} |
| deep answer (LLM) | cognee-mcp recall (GRAPH_COMPLETION) |
POST /banks/{bank}/reflect body {query, budget, max_tokens, response_schema?} |
| delete an entry | patched forget(dataset, data_id) (kb#70) |
memory-management endpoints / MCP (delete, clear_memories) |
| list / inspect | dataset status polling | GET /banks/{bank}/memories/list, GET /banks |
| create bank | dataset created implicitly on add | PUT /banks/{bank} |
Built-in MCP tools live at http://hindsight:8888/mcp/{bank}/ (HTTP transport;
bank via URL path, X-Bank-Id header, or HINDSIGHT_MCP_BANK_ID default).
Exact request-body field names and any auth headers must be confirmed against the running instance's OpenAPI (
http://localhost:8888/docs) and the Hindsight configuration docs during H1 — treat the bodies above as the shape, not gospel.
5. Bank scoping
Mirror Cognee's per-conversation chat_<chatId> dataset with a per-conversation
bank: derive bank_id from OpenClaw's chat_id (the
Conversation info (untrusted metadata): block; see SPIKE gate 2), sanitized to
chat_<slug>. A single shared adolf bank is the simpler alternative if
cross-chat recall is actually wanted — decide in H3. Banks are hard isolation in
Hindsight, so per-chat is now safe (unlike Cognee's leaky datasets).
6. Open decisions (resolve in H1)
- LLM backend for retain/reflect. SPIKE gate 5 already concluded the
extraction workload should not sit on the Kimi CLI (latency + single-seat
quota). Recommendation: point Hindsight's LLM at LiteLLM
:4000(or a local Ollama model for zero marginal cost). This is whycognee-llmis deleted, not ported. Confirm Hindsight's provider env-var names on the image. - Embeddings. Prefer the local Ollama embedder already available
(
nomic-embed/bge-m3athost.docker.internal:11436) or Hindsight's built-in, to keep embeddings off any paid path. - Storage path. Persist
pg0under/mnt/ssd/dbs/hindsight/to match the Agap storage layout (replaces/mnt/ssd/dbs/cognee/). - UI exposure. Whether to reverse-proxy the
:9999UI (Caddy) or keep it internal-only. - Auth. Open by default; enable the tenant API-key extension
(
HINDSIGHT_API_TENANT_API_KEY,Authorization: Bearer) if the service is reachable beyond the compose network.
7. Data migration
Cognee's Kuzu graph is not portable into Hindsight's store. The memory corpus
is low-value conversational history, so start Hindsight empty rather than
building an exporter. Optionally replay a handful of durable facts by calling
retain once at cutover. The two throwaway datasets left in Cognee
(chat_verify, chat_webchat) are discarded with the stack.
8. Migration phases (kanboard Ready, project Adolf)
- H1 · Deploy Hindsight service — add the
hindsightcontainer toopenai/docker-compose.yml(image, ports 8888/9999,pg0volume, LLM + embedding provider env → LiteLLM/Ollama), bring it up, confirm/docs+ a round-tripretain→recall. Resolves §6 decisions. - H2 · Wire built-in MCP as an Adolf tool — swap
mcp.servers.cognee→mcp.servers.hindsight(/mcp/{bank}/) inopenclaw.json; verify Adolf can callretain/recall/reflectas tools. - H3 ·
hindsight-memoryOpenClaw plugin (forced hooks) — build the plugin replacingcognee-openclaw-plugin:before_prompt_build→recall inject,agent_end→retain(async),hindsight_recall/hindsight_reflecttools, bank scoping; activate inopenclaw.json. Delete the cognify-sweep machinery. - H4 · Decommission Cognee — remove
cognee,cognee-mcp,cognee-llmservices + volumes, thecognee-openclaw-plugin, and allopenclaw.json/shared-mcp.jsoncognee references. Free/mnt/ssd/dbs/cognee. - H5 · End-to-end verification — state a fact → fresh session → recalled via injected memory (no LLM on the recall path); measure recall latency; confirm per-bank isolation; confirm no sweep/timer exists to stall.
9. What is unchanged
The Kimi/OpenClaw/Matrix substrate is untouched: adolf gateway, adolf-llm
(:8010) provider, the SSE-heartbeat/idle-watchdog fix (kb#71), the
openclaw-tools bridge, kanboard/marketplace MCP servers, Matrix allow-list
and E2EE. Only the memory backend and its two integration surfaces change.