From 3216769225e0c57370957b6a76bbddc5d0c88488 Mon Sep 17 00:00:00 2001 From: Alvis Date: Sun, 5 Jul 2026 06:10:13 +0000 Subject: [PATCH] docs: add Adolf solution architecture Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2 --- docs/ARCHITECTURE.md | 139 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 docs/ARCHITECTURE.md diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 00000000..28264212 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,139 @@ +# 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.