Adolf memory: migrate Cognee -> Hindsight + Kimi quota tooling
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
This commit is contained in:
219
adolf/HINDSIGHT-MIGRATION.md
Normal file
219
adolf/HINDSIGHT-MIGRATION.md
Normal file
@@ -0,0 +1,219 @@
|
||||
# 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), and `cognee-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=False` all
|
||||
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 `/mcp` on the same port — ~30 tools
|
||||
including `retain` / `recall` / `reflect`. This **removes the need for a
|
||||
separate `cognee-mcp` proxy container entirely**.
|
||||
- **Retain learns on its own.** `retain` runs Hindsight's extraction/reflection
|
||||
pipeline internally (optionally `async`), 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 `:4000` and/or
|
||||
Ollama), so we can **delete `cognee-llm`** rather 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-memory` in `openclaw.json` with the
|
||||
same hook grants Cognee needed: `hooks.allowConversationAccess: true` and
|
||||
`allowPromptInjection: true` (external plugins must opt in).
|
||||
- **Recall stays off the hot LLM path.** Hindsight `recall` is retrieval
|
||||
(semantic + BM25 + graph + temporal) with evidence scoring — no generative
|
||||
synthesis — so it's the direct analogue of Cognee's LLM-free `onlyContext`
|
||||
recall. The LLM-backed synthesis path is `reflect`, exposed as a deliberate
|
||||
tool, not run per-turn.
|
||||
- **No freshness dial / sweep.** Retain with `async:true` returns 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:
|
||||
|
||||
```jsonc
|
||||
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)
|
||||
|
||||
1. **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 why `cognee-llm` is
|
||||
deleted, not ported. Confirm Hindsight's provider env-var names on the image.
|
||||
2. **Embeddings.** Prefer the local **Ollama** embedder already available
|
||||
(`nomic-embed` / `bge-m3` at `host.docker.internal:11436`) or Hindsight's
|
||||
built-in, to keep embeddings off any paid path.
|
||||
3. **Storage path.** Persist `pg0` under `/mnt/ssd/dbs/hindsight/` to match the
|
||||
Agap storage layout (replaces `/mnt/ssd/dbs/cognee/`).
|
||||
4. **UI exposure.** Whether to reverse-proxy the `:9999` UI (Caddy) or keep it
|
||||
internal-only.
|
||||
5. **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 `hindsight` container to
|
||||
`openai/docker-compose.yml` (image, ports 8888/9999, `pg0` volume, LLM +
|
||||
embedding provider env → LiteLLM/Ollama), bring it up, confirm `/docs` + a
|
||||
round-trip `retain`→`recall`. Resolves §6 decisions.
|
||||
- **H2 · Wire built-in MCP as an Adolf tool** — swap `mcp.servers.cognee` →
|
||||
`mcp.servers.hindsight` (`/mcp/{bank}/`) in `openclaw.json`; verify Adolf can
|
||||
call `retain`/`recall`/`reflect` as tools.
|
||||
- **H3 · `hindsight-memory` OpenClaw plugin (forced hooks)** — build the plugin
|
||||
replacing `cognee-openclaw-plugin`: `before_prompt_build`→recall inject,
|
||||
`agent_end`→`retain(async)`, `hindsight_recall`/`hindsight_reflect` tools, bank
|
||||
scoping; activate in `openclaw.json`. Delete the cognify-sweep machinery.
|
||||
- **H4 · Decommission Cognee** — remove `cognee`, `cognee-mcp`, `cognee-llm`
|
||||
services + volumes, the `cognee-openclaw-plugin`, and all `openclaw.json`/
|
||||
`shared-mcp.json` cognee 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.
|
||||
@@ -43,8 +43,12 @@
|
||||
// Margin above the server.js SSE heartbeat cadence (empty-content
|
||||
// keepalive delta every ~25s once idle) so the idle watchdog never
|
||||
// fires on long thinking/tool/MCP phases even if a heartbeat tick
|
||||
// is delayed (kb #71).
|
||||
timeoutSeconds: 300,
|
||||
// is delayed (kb #71). Raised to 10min: long agentic turns (Cognee
|
||||
// tool-loops / recalls up to 150s each) were producing no *content*
|
||||
// progress for >300s, tripping "no response from model" and surfacing
|
||||
// an error before the agent finished. The wrapper now also kills the
|
||||
// kimi child on disconnect, so an over-timeout turn no longer orphans.
|
||||
timeoutSeconds: 600,
|
||||
models: [
|
||||
{ id: "adolf", name: "Adolf", input: ["text", "image"] },
|
||||
],
|
||||
@@ -93,9 +97,9 @@
|
||||
// OpenClaw's documented CLI-native alias for transport: "streamable-http".
|
||||
mcp: {
|
||||
servers: {
|
||||
cognee: {
|
||||
hindsight: {
|
||||
type: "http",
|
||||
url: "http://cognee-mcp:8000/mcp",
|
||||
url: "http://hindsight:8888/mcp/adolf/",
|
||||
},
|
||||
"openclaw-tools": {
|
||||
type: "http",
|
||||
@@ -130,18 +134,56 @@
|
||||
},
|
||||
},
|
||||
|
||||
// Cognee memory plugin (P8) — installed external plugin under
|
||||
// .openclaw/extensions/cognee-memory. Activation entry is required for the
|
||||
// gateway to load it at startup (discovery alone is not enough).
|
||||
// Memory plugins (P8). Activation entry is required for the gateway to
|
||||
// load a plugin at startup (discovery alone is not enough).
|
||||
plugins: {
|
||||
entries: {
|
||||
// Cognee memory plugin — DISABLED as of kb #75 (H3): superseded by
|
||||
// hindsight-memory below. Left `enabled: false` rather than removed —
|
||||
// full teardown (plugin dir, cognee/cognee-mcp/cognee-llm containers)
|
||||
// is kb #75's H4, a separate step so the Hindsight path can be proven
|
||||
// out first. Kept disabled (not both active) to avoid double
|
||||
// prependContext injection and double persisted turns while both
|
||||
// backends exist side by side.
|
||||
"cognee-memory": {
|
||||
enabled: true,
|
||||
enabled: false,
|
||||
// External (non-bundled) plugins must opt in to conversation + prompt-injection
|
||||
// hook access explicitly. before_prompt_build => allowPromptInjection;
|
||||
// agent_end => allowConversationAccess.
|
||||
hooks: { allowConversationAccess: true, allowPromptInjection: true },
|
||||
config: {},
|
||||
// Throttle background cognify hard to stop it draining the Kimi quota:
|
||||
// one cognify per dataset per hour (was 5 min default), and a longer
|
||||
// persist timeout so /add doesn't fail-and-retry when cognee is busy.
|
||||
config: {
|
||||
sweepIntervalMs: 3600000, // 1h (default 300000 = 5min)
|
||||
persistTimeoutMs: 20000, // 20s (default 8000)
|
||||
},
|
||||
},
|
||||
// Hindsight memory plugin (kb #75, H3) — installed external plugin
|
||||
// under .openclaw/extensions/hindsight-memory, bind-mounted read-only
|
||||
// from openai/hindsight-openclaw-plugin (see that project's
|
||||
// docker-compose.yml adolf.volumes). Structural successor to
|
||||
// cognee-memory above: LLM-free recall inject (before_prompt_build) +
|
||||
// async retain (agent_end) against the hindsight service, bank
|
||||
// "adolf" (same bank the mcp.servers.hindsight tool surface above
|
||||
// uses, so hook-based and tool-based memory stay one consistent
|
||||
// store). No cognify/sweep config here — Hindsight's retain does
|
||||
// extraction/consolidation server-side, so that whole class of
|
||||
// config (sweepIntervalMs etc. above) doesn't apply.
|
||||
"hindsight-memory": {
|
||||
enabled: true,
|
||||
// Same opt-in requirement as cognee-memory above: before_prompt_build
|
||||
// => allowPromptInjection; agent_end => allowConversationAccess.
|
||||
hooks: { allowConversationAccess: true, allowPromptInjection: true },
|
||||
},
|
||||
// Kimi quota readout (kb #62) — installed external plugin, bind-mounted
|
||||
// read-only from openai/quota-command-openclaw-plugin (see that
|
||||
// project's docker-compose.yml adolf.volumes) onto
|
||||
// .openclaw/extensions/quota-command. Registers a `/quota` native
|
||||
// command; no hooks, so no allowConversationAccess/allowPromptInjection
|
||||
// opt-in needed.
|
||||
"quota-command": {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user