# Adolf — Solution Architecture **Status:** Proposed · **Date:** 2026-07-05 · **Owner:** alvis Adolf is a self-hosted personal AI assistant on Agap, reachable first over Matrix (`@bot:mtx.alogins.net`). It is built on **OpenClaw** (the multi-channel assistant shell), driven by the **Kimi Code CLI** on the existing flat `kimi login` subscription (no per-token cost), with **Cognee** as long-term memory. --- ## 1. Goals & decisions - **Base = OpenClaw** (`github.com/openclaw/openclaw`, Node.js). Provides the multi-channel inbox, session/agent routing, SOUL.md persona, tool/MCP registry, and later WebChat + voice. - **Brain = Kimi Code CLI subscription, hybrid.** The CLI owns the agent loop, but OpenClaw's tools/MCP are **not bypassed** — they are exposed to the CLI as a **shared MCP layer**. - **Memory = Cognee, "auto + tool".** Auto-ingest every exchange and inject retrieved memory each turn, *and* expose explicit `memory_search`/`memory_add` MCP tools. - **Cognee's backend LLM also runs on the Kimi subscription — via its own separate wrapper** (`cognee-llm`), because its usage (stateless, structured, batch, no media/streaming) is unlike OpenClaw's. Embeddings stay on LiteLLM (`embedder` = nomic-embed). - **The existing `kimi-agent` service is left untouched** (still used for OpenWebUI experiments); we build **new, separate wrappers**. - **First surface = Matrix chat** only (voice/pipecat, WebChat, OpenWebUI deferred). ### Why the hybrid works (de-risked) OpenClaw runs its own agent loop and expects a *raw LLM* backend (provider `api: openai-completions` or `anthropic-messages`, configured with `baseUrl`/`apiKey`). The Kimi CLI is *itself* an agent, so naively wrapping it would bypass OpenClaw's tools. Confirmed escape hatch: **Kimi Code CLI supports MCP** (`~/.kimi/mcp.json`, headless `--mcp-config-file `, `kimi mcp add/list/remove/auth`) plus **image/video input**, session **resume** (`-r`), headless `-p`, and `--output-format stream-json`. We point the CLI at the same MCP servers OpenClaw uses → the CLI can call OpenClaw/cognee tools while OpenClaw stays the channel/session/persona/memory layer. --- ## 2. Topology & data flow ``` Matrix ⇄ OpenClaw (Gateway: inbox, session routing, SOUL.md "Adolf") │ OpenAI /v1/chat/completions (streaming, images) model = "adolf" ▼ adolf-llm (NEW wrapper container, :8010) • maps OpenClaw session → Kimi session (1:1 resume) • cognee.search() → inject memories into prompt (auto-retrieve) • spawn: kimi -r -p --output-format stream-json --mcp-config-file • stream stream-json → SSE deltas (real streaming) • persist inbound images to session dir, reference by path (media) • after turn: cognee.add(user+assistant) (auto-ingest) │ ▲ MCP ▼ │ kimi CLI (agent) ── MCP ──► shared MCP servers: • cognee-mcp (memory_search / add / cognify) • openclaw-tools bridge (OpenClaw integrations) cognee ──► cognee-llm (SEPARATE wrapper, :8011) • stateless one-shot: kimi -p (fresh disposable session per call) • NO resume, NO media, NO MCP, NON-streaming, structured/JSON out • high concurrency for batch cognify ──► embeddings: LiteLLM `embedder` (nomic-embed), NOT the CLI ``` Reused infra (all in `agap_git/openai/docker-compose.yml`): **Qdrant** `:6333` (cognee vectors), **LiteLLM** `:4000` (embeddings + optional observability + fallback LLM), **Langfuse**, Postgres. --- ## 3. Components ### 3.1 OpenClaw ("Adolf") — the shell Node 24 (≥22.19). Cloned into this repo. Configuration: - **Model provider** (custom): `{ baseUrl: "http://adolf-llm:8010/v1", apiKey: "${ADOLF_KEY}", api: "openai-completions", models: [{ id: "adolf", input: ["text","image"] }] }`; default agent model `adolf`. - **SOUL.md** persona "Adolf" (Russian-friendly, concise). - **Matrix channel** with bot creds from Vaultwarden (`MATRIX_ADOLF_PASSWORD` / `MATRIX_ADOLF_TOKEN`); route room/peer → the Adolf agent. - **MCP registry** = cognee-mcp + the openclaw-tools bridge; the same server list feeds `shared-mcp.json` (single source of truth). ### 3.2 `adolf-llm` — conversational wrapper (:8010) New `agap_git/openai/adolf-llm/` (`Dockerfile` + `server.js`), own CLI-home volume + conversations workspace. OpenAI-compatible `GET /v1/models` + `POST /v1/chat/completions` (model `adolf`). Reuses proven bits from `agap_git/openai/kimi-agent/server.js` (session map w/ prune, `convTurns`/ `textOf`, `runKimi` spawn + stream-json parse, resume). Adds: - **Real streaming** — parse stream-json assistant deltas → OpenAI `chat.completion.chunk` SSE. - **Media** — persist `image_url`/base64 parts to the session dir, reference file paths in the prompt. - **Session correspondence** — key the map on OpenClaw's session id (not a history hash) → 1:1 `kimi -r` resume → Moonshot context-cache hits → token savings. Fallback: improved history-hash. - **Shared MCP** — `--mcp-config-file shared-mcp.json` so the CLI can call OpenClaw/cognee tools. - **Cognee hooks** — pre-turn `cognee.search()` inject; post-turn async `cognee.add()`. ### 3.3 `cognee-llm` — stateless wrapper (:8011) New `agap_git/openai/cognee-llm/`, own disposable workspace + CLI-home volume (same subscription). Shares the `runKimi`/stream-json core but with the **opposite policy**: stateless one-shot (`kimi -p`, fresh temp dir per call, **no resume**), **non-streaming**, **no media, no MCP**, structured/JSON-oriented, low temperature, bounded parallelism for batch cognify. Cognee's `LLM_API_BASE` → `http://cognee-llm:8011/v1`. > ⚠️ Risk: cognify issues many structured calls; the agentic CLI adds per-call latency and may not > reliably emit clean JSON. Fallback: route cognee's LLM to a LiteLLM model (`judge`/local qwen) > while keeping Kimi for the assistant. Decide after a batch cognify test. ### 3.4 Cognee memory service `cognee` + `cognee-mcp` containers. Backends: **Qdrant** (vectors), **graph store** = Kuzu (embedded) or Neo4j (decision), **LLM** via `cognee-llm`, **embeddings** via LiteLLM `embedder`. Persist under `/mnt/ssd/dbs/cognee/`. `cognee-mcp` exposes `memory_search`/`memory_add`/`cognify` into `shared-mcp.json`. Memories scoped per OpenClaw user/session. ### 3.5 Shared MCP layer ("smart tool integration") `shared-mcp.json` is the contract both consumers (OpenClaw + Kimi CLI) use. Cognee-mcp is off-the-shelf. The **openclaw-tools bridge** exposes the OpenClaw integrations Adolf needs (send-to-channel, cron/reminders, browser/nodes) as MCP tools. Start minimal, grow. --- ## 4. Open gates to verify early 1. **Kimi CLI home/MCP path & package** — current container uses `@moonshot-ai/kimi-code` + `/root/.kimi-code`; upstream is now `MoonshotAI/kimi-cli` + `~/.kimi/mcp.json`. Confirm package, home dir, `--mcp-config-file`, and that `kimi login` subscription auth persists in the new containers' CLI-home volumes. 2. **OpenClaw → provider session identity** — does OpenClaw forward a stable session id to a custom provider (header / `user` / `metadata`)? Decides robust mapping vs. hash fallback. 3. **Headless image input** — confirm the CLI accepts images in `-p` runs via file-path references. 4. **Graph store** — Kuzu embedded vs Neo4j service for cognee. 5. **Cognee-llm suitability** — batch cognify latency/JSON reliability via the CLI vs LiteLLM fallback. --- ## 5. End-to-end verification - Matrix message → OpenClaw → `adolf-llm` → `kimi` → **streamed** reply token-by-token in Matrix. - Send an **image** on Matrix → CLI reads it (describe-the-image test). - Follow-up → same **Kimi session resumed** (`sessions.json` maps to one session; reduced prompt tokens vs. cold turn). - **Memory** — a stated fact is recalled in a later session via `memory_search`; cognee graph/Qdrant populated (auto-ingest); cognify's LLM served by `cognee-llm` (or LiteLLM fallback). - **MCP tool** — a tool call works mid-turn (e.g. set a reminder via the bridge). - The **old `kimi-agent`** service is unchanged and still serves OpenWebUI.