AI Architecture¶
How AI work flows through the cluster — from the surface that takes the request to the model that answers it, with every routing, approval, and escalation hop named.
This chapter is architectural. Operational procedures
(how to trigger a job, where to look when a workflow stalls) live in
the vault runbook at ~/vaults/claude/runbooks/home-ops/workflow_automation.md
per HOMELAB-SPEC Layer 2 #5.
Big picture¶
2026-07-06: the
langgraph-agentsfleet — the FastAPI multi-agent runtime this diagram used to show as the hub between every surface, Windmill bridge, and inference backend — was removed entirely, along withsync-receiver, thepostgres-langgraph-checkpointsCNPG cluster, thelanggraph-vaultPVCs, and 16 Windmill workflows. The diagram below reflects what's actually running today; see Agent fleet — status today for the decommission detail.
flowchart TB
subgraph Surfaces[Surfaces — how work enters]
OWUI[Open WebUI<br/>chat]
Khoj[Khoj UI<br/>personal AI]
AM[AlertManager<br/>firing alert]
end
subgraph Bridges[Bridges — Windmill TS workflows]
WPaperless[paperless-rag-ingest.ts<br/>paperless-rag-tombstone.ts]
WLightrag[lightrag-rag-ingest.ts<br/>lightrag-rag-tombstone.ts]
WFail[windmill-failure-watcher.ts]
WWork[workaround-watcher.ts]
end
subgraph Inference[Inference]
OllamaP40[(ollama / P40<br/>qwen2.5:7b · gte-small)]
VllmDriver[(vllm-driver-spark / GB10<br/>Qwen3.6-35B-A3B-FP8 · OpenAI /v1)]
end
subgraph Tools[Tool surfaces]
Gw[MCP Gateway<br/>16 MCP servers behind Istio]
Q[(Qdrant<br/>vector DB)]
PG[(Postgres CNPG<br/>memory)]
end
subgraph Outputs[Outputs]
Push[Pushover<br/>direct page]
end
OWUI -->|chat| OllamaSpark
OWUI -->|tool calls| Gw
OWUI --> Q
Khoj --> OllamaP40
AM --> Push
WPaperless --> Q
WLightrag --> Q
There are no dashed (cold-path) edges left in this diagram — the two things that used to be dashed, langgraph's gated Claude API escalation and its OTLP export to Langfuse, were both removed with the fleet. Langfuse itself — the trace sink, with its bundled ClickHouse/Valkey/MinIO and its dedicated CNPG Postgres cluster — was removed 2026-07-06. The open keep-dormant-vs-remove question noted in earlier passes of this chapter is now resolved: removed.
Also gone: claude-runner, not by this decommission but by a chain
leading through it. claude-runner (a CronJob-based Claude Code CLI
runner for Renovate PR triage + cost commentary) was retired
2026-05-23 — its function was absorbed into langgraph-agents at the
time. Now that langgraph-agents is also gone, that function is gone
twice over: kubernetes/apps/automation/claude-runner/ does not exist
on disk today. The rest of this chapter still describes claude-runner
in several places below as if it were live CronJob-based infrastructure
— that content predates this pass and was not otherwise rewritten here
(out of scope for the langgraph-agents/HolmesGPT decommission this
edit covers), but it should be read as historical, not current state.
There is no automated Claude Code or Claude API pipeline in the
cluster today; Claude Code use is interactive-only.
Known gap, not yet fixed: HA voice ("inbox …") and the Zulip
Triager DM bot both used to be surfaces feeding this diagram — both
POSTed toward the now-deleted /inbox endpoint. The HA voice
rest_command (in the separate home-assistant-config repo) still
exists and will silently fail; the Zulip Triager webhook
(zulip-triager-webhook.ts) was deleted outright. Neither surface is
shown above because neither currently does anything.
Ingress surfaces — what enters the cluster as work¶
| Surface | Transport | Lands at |
|---|---|---|
| HA voice ("inbox …") | Whisper STT → ollama_voice conversation → HA rest_command → Authelia-JWT POST |
Broken, not yet fixed. The rest_command (in the separate home-assistant-config repo) still POSTs toward the Windmill langgraph-inbox.ts webhook, which was deleted 2026-07-06 along with the fleet it forwarded to. |
| Open WebUI chat | Browser → Authelia OIDC → Open WebUI backend | Routes to vllm-driver-spark (default, OpenAI /v1) as of 2026-07-24. The langgraph-agent-as-model registration was removed 2026-07-06 — Open WebUI's only remaining tool surface is the MCP gateway. |
| Khoj UI | Browser → gateway extAuth (Authelia) → khoj | Khoj's own embedding pipeline; chat via ollama P40 |
AlertManager firing alert (severity=critical) |
Webhook receiver | Pushover directly — no Windmill hop, no AI investigation step (the windmill-investigate route/receiver and alertmanager-holmesgpt-notify.ts were removed 2026-07-06) |
| Cron — RAG ingest/tombstone, self-watch | Windmill scheduled trigger | The 7 surviving .ts workflows under kubernetes/apps/home/windmill/workflows/ (paperless→Qdrant+LightRAG unified in paperless-rag-fanout since 2026-07-20) — see Workflow Automation |
| Cron — Renovate PR triage / cost commentary | Kubernetes CronJob | claude-runner (kubernetes/apps/automation/claude-runner/app/cronjob-*.yaml) |
Removed 2026-07-06, no longer ingress surfaces: Zulip DM to the
Triager bot (zulip-triager-webhook.ts deleted) and operator taps on
ntfy for approval actions (langgraph-agents /approval endpoint
deleted). Neither has a replacement today.
There are 8 Windmill TypeScript workflows in the repo today (down
from 23 — 16 scripts were deleted 2026-07-06: 14 langgraph-*.ts
fleet scripts plus smoke-approval-flow.ts and
zulip-triager-webhook.ts; then paperless-rag-fanout.ts was added
2026-07-20, unifying the two split RAG ingests, which remain on disk
with schedules paused for rollback); they're all under
kubernetes/apps/home/windmill/workflows/.
Inference backends¶
| Backend | Hardware | Service URL | Models | Notes |
|---|---|---|---|---|
ollama |
P40 (Pascal, 24 GB) on worker8 | http://ollama.ai.svc.cluster.local:11434 |
qwen2.5:7b, qwen3:8b (voice), bge-m3 (memory rebuild), gte-small/nomic-embed-text (khoj) | The pre-Spark generation. ≤8b chat, embeddings, voice STT/TTS pipeline support. |
vllm-driver-spark |
GB10 (Grace-Blackwell, 128 GB unified) | http://vllm-driver-spark.ai.svc.cluster.local:8000/v1 |
Qwen3.6-35B-A3B-FP8 (chat default) | Replaced ollama-spark, which was decommissioned 2026-07-26. Runs single-tenant — a co-resident coder did not fit (see vllm_spark_migration_plan.md). |
tei-embed-spark |
GB10 | http://tei-embed-spark.ai.svc.cluster.local:3000 |
bge-m3 (1024-dim embeds) | Took over all embeddings from ollama-spark 2026-07-25. Open WebUI RAG, LightRAG, memory-mcp, paperless fan-out. |
| Claude Code | Anthropic-hosted, CLI | claude-runner only |
per-task | claude CLI baked into ghcr.io/rwlove/claude-runner:0.1.1; called from CronJobs with --max-turns 20. |
Removed 2026-07-06: the langgraph-gated Claude API escalation lane
(ENABLE_CLAUDE_API, $5/task/$10/agent/day/$30/global/day cost
caps enforced in langgraph-agents code) — deleted along with the
fleet. There is no automated in-cluster path to the Claude API today.
Routing decisions belong in .agents/instructions/gpu-routing.md in
this repo. It used to defer to a "canonical" doc in the
langgraph-agents source repo, but that doc described langgraph's own
now-decommissioned per-agent routing factory — nothing was actually
cluster-wide there worth deferring to, so gpu-routing.md is now
self-sufficient.
RAG paths¶
Three distinct retrieval pipelines exist today. They share the Spark embedder (bge-m3) but otherwise don't overlap.
Open WebUI RAG¶
User-facing chat with retrieval over Open WebUI's own collections, plus web search.
- Embedder: bge-m3 via tei-embed-spark (
kubernetes/apps/collab/open-webui/app/helmrelease.yaml,RAG_EMBEDDING_ENGINE: openai). - Reranker: BGE reranker-v2-m3 in-process, sentence-transformers on CPU (line 66). Adds ~2.5 GiB to the pod's resident set.
- Vector DB: Qdrant at
http://qdrant.databases.svc.cluster.local:6333(line 69). - Web search: SearXNG (
collab.svc.cluster.local:8080) viaRAG_WEB_SEARCH_ENGINE=searxng(line 68). - Tool server also wired in: MCP gateway (
mcp-system.svc.cluster.local:8080/mcp) — visible to chat as callable tools (lines 88-112). HolmesGPT's tool-server registration was removed 2026-07-06 along with the rest of the deployment.
Phase A bge-m3 cutover (2026-05-20, PR #11792) showed bge-m3 (1024-dim) beat nomic-embed-text (768-dim) by +23 MRR@10 pts on a 50-doc Paperless eval. The cluster moved to bge-m3 for new embedding work.
Khoj — personal AI assistant¶
A parallel RAG surface aimed at notes + the operator's documents, not the agent fleet.
- Embedder: configured post-bootstrap in
/server/admin→SearchModelConfig. Default isthenlper/gte-small(~130 MB) pulled from HuggingFace into thekhoj-modelsPVC. Can be flipped to ollama nomic-embed-text via the admin UI by settingapi_type=OPENAI. - Chat:
qwen2.5:7bon P40 ollama (kubernetes/apps/ai/khoj/app/helmrelease.yaml:71-72). - Web search: SearXNG (
kubernetes/apps/ai/khoj/app/helmrelease.yaml:64). - Storage: two RWO PVCs —
khoj-config(config + Django state) andkhoj-models(HF embedding model cache).
Khoj does not consume the MCP gateway. It is a self-contained personal-AI app, and always was — it never consumed langgraph-agents either, before that fleet was removed 2026-07-06.
Paperless RAG ingest¶
Document-store-to-vector-store pipeline run by Windmill, not by any agent.
- Source: paperless-ngx via API token (
PAPERLESS_TOKENwhitelisted for Windmill workers atkubernetes/apps/home/windmill/app/helmrelease.yaml:77). - Ingest:
paperless-rag-ingest.tspulls new/changed docs, embeds via tei-embed-spark bge-m3, writes to Qdrant. - Tombstone:
paperless-rag-tombstone.tsremoves vectors for deleted docs. - Vector DB: Qdrant — same instance Open WebUI uses, with separate collections.
There is currently a known gap: Open WebUI's Knowledge UI manages its
own collection namespacing and does not directly read the
Windmill-ingested paperless collection. Operator-side access is via
paperless-mcp (the MCP server), not Open WebUI's KB UI.
memory-mcp knowledge graph¶
Cross-agent shared memory, not user-facing. Unaffected by the 2026-07-06 langgraph-agents decommission — this is memory-mcp's own backend, not langgraph's.
- Backend: CNPG cluster
postgres-langgraph-memorywith pgvector (1024-dim column). The name predates the decommission; it's memory-mcp's database today, confirmed viaDATABASE_URL, CNP egress, and the schema-init Job all referencing it independently of the now-deleted langgraph-agents. - Embedder: bge-m3 via tei-embed-spark
(
kubernetes/apps/mcp-system/memory-mcp/app/helmrelease.yaml:39-41). - Surface:
memory-mcpMCP server (kubernetes/apps/mcp-system/memory-mcp/), exposed through the gateway. - Writers: Claude Code and Open WebUI both write via the MCP gateway's memory-mcp tools (create-entity, add-observation, graph-walk, etc.). langgraph-agents used to write via direct SQL before it was removed 2026-07-06 — it's no longer a consumer or writer of any kind.
Agent fleet — status today¶
The entire langgraph-agents fleet below was removed 2026-07-06 —
the FastAPI runtime, its Postgres checkpoints, its vault PVCs, its
public routes, all of it. The table is kept as a historical record of
what the fleet's internal agent graph looked like; every row past
HolmesGPT describes a deleted thing. memory-mcp (separate app,
still live) and postgres-langgraph-memory (separate database, still
live) are unaffected — see memory-mcp knowledge graph
above.
| Agent | Surface | Status | Notes |
|---|---|---|---|
| HolmesGPT | — | ❌ removed 2026-07-06 | Deployment, RBAC, CNP, SecurityPolicy, and Open WebUI tool-server registration all deleted. No value delivered — see kubernetes/apps/observability/holmesgpt/ in git history for the last-live manifests. |
| triager | langgraph-agents fleet | ❌ removed 2026-07-06 | Was the default route for every untargeted /inbox. Voice ("inbox …") + Zulip-DM ingress. qwen2.5:7b on P40. |
| supervisor | langgraph-agents fleet | ❌ removed 2026-07-06 | Was the in-graph fallback router when a specialist rejected work. |
| reporter | langgraph-agents fleet | ❌ removed 2026-07-06 | Was the universal in-graph terminus — every chain ended here, rendering raw state into user-facing markdown. |
| historian | langgraph-agents fleet | ❌ removed 2026-07-06 | Was a daily 22:00 ET activity-log digest → Zulip #digests, pinned via target_agent in the now-deleted langgraph-daily-digest.ts. |
| reviewer | langgraph-agents fleet | ❌ removed 2026-07-06 | Was a weekly Sat 06:00 ET vault hygiene sweep (aging TODOs, drift findings, dead [[wiki-links]]). |
| storage-operator | langgraph-agents fleet | ❌ removed 2026-07-06 | Was Alertmanager rook-ceph + databases namespaces + weekly Sun 07:00 ET drift sweep. |
| network-operator | langgraph-agents fleet | ❌ removed 2026-07-06 | Was Alertmanager network namespace + weekly Sat 04:00 ET Lovenet drift sweep. |
| observability-operator | langgraph-agents fleet | ❌ removed 2026-07-06 | Was Alertmanager observability namespace + weekly Sat 03:00 ET PrometheusRule/silence/flap drift. |
| ml-operator | langgraph-agents fleet | ❌ removed 2026-07-06 | Was Alertmanager ai + mcp-system namespaces + weekly Sat 02:00 ET GPU/Ollama/Frigate drift. |
| smart-home-operator | langgraph-agents fleet | ❌ removed 2026-07-06 | Was Alertmanager home + collab namespaces + intent-drift cron. |
| homelab-engineer | langgraph-agents fleet | ❌ removed 2026-07-06 | Was the Alertmanager default route for any unmapped namespace. |
| researcher | langgraph-agents fleet | ❌ removed 2026-07-06 | Was an hourly renovate-triage cron (drafted a Zulip card per open Renovate PR). |
| errand-runner | langgraph-agents fleet | ❌ removed 2026-07-06 | Was the only agent that called MCP write (HA, paperless, etc.), gated on signed approval token. |
| note-maker | langgraph-agents fleet | ❌ removed 2026-07-06 | Was reachable via /inbox (HA voice "inbox …"); no recurring trigger. |
| coder | langgraph-agents fleet | ❌ removed 2026-07-06 | Was reachable via /inbox; no recurring trigger. |
| security | langgraph-agents fleet | ❌ removed 2026-07-06 | Was cold — needed Frigate HTTP client wiring that was never built. |
| auditor | langgraph-agents fleet | ❌ removed 2026-07-06 | Was cold — needed OSV.dev / GHSA HTTP client wiring that was never built. |
| artist | langgraph-agents fleet | ❌ removed 2026-07-06 | Was cold — ComfyUI MCP allowlist was never populated. |
| property-coordinator | langgraph-agents fleet | ❌ removed 2026-07-06 | Was ad-hoc /inbox only; no recurring trigger. |
| health-tracker | langgraph-agents fleet | ❌ removed 2026-07-06 | Was cold, local-only; manual /inbox from Obsidian; data class restricted it to local only. |
| doc-writer (Scribner) | langgraph-agents (planned) | 🟥 never built | Was aspirational even before the decommission. Goal was: drafts README + docs/ patches as diffs when commits land. Still not built, and now has no fleet to build it in. |
Tool-binding gap (historical, load-bearing while the fleet was live). Every agent above except
errand-runnerusedwith_structured_output()against the prompt content it received — it reasoned over text but did NOT dynamically query its MCP allowlist. Operator weekly drift crons produced LLM reasoning over the prompt, not data-grounded analysis. Seereference_agent_fleet_tool_binding_gapin memory for the full writeup; moot now that the fleet is gone, kept for anyone evaluating a future replacement.
Approval and escalation flow (historical — removed 2026-07-06)¶
The entire approval loop below — the langgraph pause/resume state
machine, the two Windmill bridges, the Zulip #approvals stream, and
the ntfy tap-to-approve buttons — was removed with the fleet
2026-07-06. There is no replacement approval mechanism today because
there is no automated task pipeline producing anything to approve. The
original approval-loop design (state machine, Windmill bridges, Zulip
stream, ntfy tap-to-approve buttons) lives in git history if it is ever
needed.
Cost caps used to fire before Claude API egress, polling
/admin/costs/today on langgraph-agents — moot now; there is no
in-cluster Claude API lane to cap.
Claude API vs Claude Code — separate escalation lanes¶
The Claude API (via langgraph) column below was removed 2026-07-06
along with the rest of the fleet — ENABLE_CLAUDE_API, the
per-task/agent/day cost caps, and postgres-langgraph-checkpoints are
all gone. There is no automated in-cluster path to the Claude API
today. The table is kept as a historical record of how the two lanes
compared when both existed; the Claude Code / claude-runner column
was not otherwise re-verified in this pass.
| Claude API (via langgraph) — ❌ removed 2026-07-06 | Claude Code (via claude-runner) | |
|---|---|---|
| Trigger | An agent step escalates because the local model failed, hit an uncertainty marker, or is tagged requires_cloud |
Kubernetes CronJob fires at the scheduled hour |
| Caller | langgraph-agents agent step |
claude CLI in ghcr.io/rwlove/claude-runner |
| Tool surface | MCP gateway via the agent's tool list | claude-runner image's baked-in MCP allowlist (gh + the gateway via cluster network) |
| Cost control | In-cluster cost-cap watchers ($5/task, $10/agent/day, $30/global/day) |
Daily cost-cap-commentary CronJob projects monthly spend and surfaces an upgrade signal if trending past $30/mo |
| Activation gate | ENABLE_CLAUDE_API env flag |
ks.yaml suspend gate + presence of anthropic_api_key in 1Password |
| State | Postgres-checkpointed in postgres-langgraph-checkpoints |
Stateless per-run; workspace is emptyDir tmpfs |
| Output | Vault file + Zulip thread + Langfuse trace | One Zulip card per PR (pr-triage) or one summary card (cost-commentary) |
The two lanes never consumed each other. claude-runner did not call
langgraph-agents; it was a parallel reasoning surface that read the
cluster directly via gh MCP and used langgraph's
/admin/costs/today endpoint only as a data source — that data source
is gone along with everything else in the removed column above.
Kill criteria for any claude-runner workflow (per
kubernetes/apps/automation/claude-runner/README.md:40-47):
- useful-card rate < 30% after 2 weeks
- zero acted-upon cards in 14 days
-
5 unintended noise reactions in any 7-day window
Document the kill in the plan's changelog and remove the CronJob.
Observability of the AI fleet¶
| Subject | Sink | Wired by |
|---|---|---|
| Langfuse traces | Langfuse (OTLP) | ❌ Removed 2026-07-06. langgraph-agents was Langfuse's only trace source; with the producer already gone, Langfuse itself (app + bundled ClickHouse/Valkey/MinIO + its CNPG Postgres cluster) was removed too — the keep-dormant-vs-remove question is resolved as remove. |
| Critical AlertManager alerts | Pushover | Direct pushover receiver — no AI investigation step (HolmesGPT + alertmanager-holmesgpt-notify.ts removed 2026-07-06) |
| Ollama (both) | Prometheus | scraped via standard ollama exporter Service in the ai namespace |
| GPU utilization | Prometheus via DCGM | GB10's DCGM counters are mostly broken — use POWER_USAGE as the proxy (see .agents/instructions/gpu-routing.md) |
| Windmill workflows | Windmill's own UI + Loki | Workflow logs ship via Vector → Loki under the windmill namespace |
| claude-runner | Zulip stream ops/pr-triage, ops/cost-cap-commentary + CronJob events |
No persistent state; useful-card rate is operator-observed |
Removed 2026-07-06: langgraph-agents traces/metrics rows (the
ServiceMonitor + PrometheusRule under
kubernetes/apps/ai/langgraph-agents/ are gone along with the app).
The claude-cost-rules PrometheusRule (tracking
langgraph_cost_usd_total / langgraph_calls_total — langgraph's own
Anthropic spend instrumentation) was deleted too, along with the
AlertManager claude-cost-warn/claude-cost-hard routes. This is
unrelated to the separate, still-pending Claude Code CLI cost-governor
pipeline (a local script + systemd exporter outside this repo) — don't
conflate the two. Grafana dashboards langgraph-agents.json,
aihomeops-state.json, and task-queue.json were deleted;
claude-code.json (Claude Code CLI cost tracking, unrelated) is kept.
Langfuse storage substrate — removed 2026-07-06. Langfuse's
bundled ClickHouse, Valkey, and MinIO subcharts, plus its dedicated
CNPG Postgres cluster (postgres-langfuse), were deleted along with
the app itself. Nothing else in the cluster depended on any of it.
File reference (quick index)¶
- Khoj —
kubernetes/apps/ai/khoj/app/helmrelease.yaml - khoj extAuth —
SecurityPolicyinkubernetes/apps/ai/khoj/(oauth2-proxy retired 2026-07-01, #12767) - memory-mcp —
kubernetes/apps/mcp-system/memory-mcp/app/helmrelease.yaml(backed by CNPGpostgres-langgraph-memory, still live) - ollama (P40) —
kubernetes/apps/ai/ollama/app/ - vllm-driver-spark (GB10) —
kubernetes/apps/ai/vllm-driver-spark/app/ - tei-embed-spark (GB10) —
kubernetes/apps/ai/tei-embed-spark/app/ - tei-spark —
kubernetes/apps/ai/tei-spark/(unsuspended 2026-05-21, PR #11893; PrometheusRule added in PR #11906) - open-webui —
kubernetes/apps/collab/open-webui/app/helmrelease.yaml - windmill —
kubernetes/apps/home/windmill/app/helmrelease.yaml - windmill workflows —
kubernetes/apps/home/windmill/workflows/*.ts(8 today, down from 23) - claude-runner —
kubernetes/apps/automation/claude-runner/ - MCP gateway —
kubernetes/apps/mcp-system/mcp-gateway/ - MCP servers — sibling directories under
kubernetes/apps/mcp-system/
Removed 2026-07-06 (no longer exist, no path to reference):
kubernetes/apps/ai/langgraph-agents/, kubernetes/apps/ai/sync-receiver/,
kubernetes/apps/ai/langfuse/, and
kubernetes/apps/databases/cloudnative-pg/config/langfuse/.
See also¶
- MCP Fleet Observability — gateway internals, per-server health
- Memory MCP — Cross-Agent Knowledge Graph — KG schema and ingest path
- Workflow Automation (Windmill) — current state of the Windmill workflows after the approval loop was removed
- TEI on Spark — reranker for RAG — text-embedding- inference deployment (vault-canonical runbook)