Pacific Slate: a personal AI system

Pacific Slate is a personal AI system that works over my own knowledge and the sources I follow, and does useful work in the background instead of only answering when asked.

It runs on a private remote server, remembers context across sessions, and is built from specialized agents I route work to. This page is a plain overview: what it is, how it’s put together, and how it handles privacy. Technical details are at the end if interested.

Up front: I’m an operator and systems architect, not a career software engineer. I designed the architecture, made the model-routing, reliability, and cost decisions, and built it AI-natively, in partnership with a range of AI platforms, agents, and coding tools.

What it does for me, day to day. It keeps itself current on my world. Whatever’s wired in electronically (mail, calendar, messages, the feeds and accounts I follow) flows in on its own, so it already knows the state of play when I ask. It puts current events to work on ordinary tasks: a heads-up before a meeting, a standing watch on something I care about, the daily admin that usually eats an afternoon. And I reach all of it from a normal Claude client over a standard MCP connection — the same assistant I already use, now backed by my own memory, data, and tools.


▶ Live demo

pacslate.com/demo — the real UI, running on fabricated sample data with no private backend.

The personal layer is sample data and there’s no private backend; the production system is private by design.

The canvas (sample data) — the agent’s answer streams in as movable result cards, each tagged with the model that served it and the call’s cost. The pointer is a one-time guided preview: it drags a card and re-themes the workspace, then hands control back the moment you move the mouse or type.

The Pacific Slate situational-awareness monitor: a dark world map with live USGS seismic markers and sample aircraft positions, a right rail of live seismic events, sample markets, and a public news feed, and a left rail showing a clearly-labeled sample personal layer.

The monitor — USGS seismic and a public headline feed run live (client-side); markets, aircraft, and the personal layer are sample data.


Why I built it

The frontier chat tools I was using didn’t hold context across sessions the way I wanted, filtered at the output layer, and kept my data on their side. I wanted something that was mine end to end: runs continuously, remembers what matters, reasons over my own knowledge, and pushes useful things to me instead of waiting to be asked.

The harder part: take a lot of noisy, scattered input (my own notes and documents, the feeds and sources I care about) and turn it into something verified and actionable instead of just another summary.

One design choice shaped the rest: the model is the part I don’t own, so I built the system so it’s the part I can swap. The durable pieces — my data, the memory, the tools, the routing and orchestration logic — are mine and stay put; the model is rented, reached over standard interfaces, so moving to a better one (or another provider) is mostly a config change rather than a rebuild. Day to day I drive it through Claude Code, but the design doesn’t depend on that.

What it’s made of

A plain-language tour of the pieces:

Privacy, stated plainly

This isn’t a classified system and doesn’t pretend to be one. The realistic problem with everyday AI tools isn’t that packets cross the internet — it’s that the vendor owns your history, trains on it, and you can’t get it back. Pacific Slate inverts that: you own the system of record, and inference is a commodity you rent on your own terms.

It’s a pragmatic posture on purpose: serious about control, still using frontier models. The claim isn’t “nothing ever leaves.” It’s that you own the system of record, you control where things go, and your data isn’t being fed into someone else’s product.

As shown in the OpenRouter account settings (screenshot, 2026-06-29): the train-on-data toggles (paid and free) are off and prompt-publishing is off — so providers don’t train on or publish your inputs. Zero-data-retention is left off globally so the full model roster stays available — enforcing ZDR-only drops every non-ZDR endpoint, some of which host the open-weight models the system runs on — and it can be set per request when a call needs it.

What it runs on

This is a recipe you adapt, not a product you install. The point isn’t my exact build. You point your own coding agent at this architecture and have it rebuild the parts you want, with whatever you already have: your models, your tools, your box. Go as deep or as shallow as you need.

A workable floor:

So on the order of $50–80/mo for a sovereign, always-on, multi-agent assistant, and you scale up only where you want depth — a bigger box to self-host models, premium models for the reasoning seats, a video tool for video. The structure doesn’t change. The cost discipline that makes that floor real — complexity-scored routing, cheap-model defaults, a hard budget ceiling, context caching — is the same machinery described in §4. (My own build runs heavier, because I develop it daily and run background automations; the architecture is what keeps it cheap when you don’t.)

How it fits together

The short version of a request’s life:

A request comes in → the operator decides which specialist should own it → that agent pulls what it needs from memory and the knowledge base and calls any tools it needs over MCP → the model router picks the right model (and fails over if one is down) → the answer is synthesized, returned to the canvas, and anything worth keeping is written back to memory. Cost is metered on every call; the whole path is traced.

Pacific Slate architecture

Click the diagram to enlarge.

Diagram source (Mermaid)
flowchart TD
    U[User · Next.js canvas UI] -->|AG-UI over SSE| API[Cognitive API · Starlette]
    API --> ORCH[Operator · routes the request]
    ORCH <-->|Redis event stream · awareness mesh| SPEC[Specialist agents<br/>coder · researcher · analyst · reviewer · …]
    SPEC --> ROUTER[Resilient model router<br/>role → model + fallback chain]
    ROUTER --> LLMs[(Multiple model providers<br/>role-priced · ends on a self-hosted model)]
    SPEC -->|MCP| GW[MCP gateway]
    GW --> TOOLS[~two dozen MCP tool-servers<br/>memory · knowledge · exec · git · infra · feeds · …]
    SPEC --> RAG[(Private knowledge corpus<br/>~27k docs / 294k chunks)]
    SPEC --> GRAPH[(Continuity graph)]
    API --> COST[Cost tracker · per-request budget guardrail]
    API --> OBS[Observability · request + model-swap tracing]
    subgraph Infra[Private server · Docker · Cloudflare Tunnel]
        API
        ORCH
        SPEC
        ROUTER
        GW
        TOOLS
        RAG
        GRAPH
    end

For the technically curious

The sections below go down to the design decisions and the failure modes. Specific model and component counts are current as of this writing; the architecture is model-agnostic by design, so individual models change without the structure changing.

Model-agnostic by design

The model is the one rented part, so the system treats it as swappable, behind two standard interfaces:

To be clear: Claude is the engine I use day to day, and the one wired end to end right now. But MCP and the OpenAI-compatible chat format are both widely-supported, non-proprietary interfaces — not one vendor’s lock-in — so “swap the engine, keep the substrate” is a property of the design, not just an aspiration: the work that’s mine (memory, data, tools, routing, verification) doesn’t move when the model does.

1. Request lifecycle

One request, end to end:

  1. The canvas (a Next.js single-page app) opens a streaming connection and posts the prompt. Streaming uses the AG-UI protocol over Server-Sent Events, proxied to the backend so tokens, tool calls, and model-resolution events all arrive on one stream.
  2. The backend is a Starlette service exposing two surfaces: the AG-UI SSE endpoint for the canvas, and an OpenAI-compatible endpoint so other clients can talk to the same pipeline.
  3. The operator agent classifies the request — direct answer, or hand off to a specialist — and routes accordingly.
  4. The chosen specialist pulls context (memory, knowledge base) and calls whatever tools it needs over MCP.
  5. The resilient model layer resolves the actual model for that call (with cost-aware downgrades and health-aware fallback — see §4), streams the completion, and meters the cost.
  6. The answer streams back to the canvas as a card, tagged with the model that served it and the cost; anything durable is written back to memory.

Two latency tiers fall out of this: the operator answers simple things inline (one model call), and routes anything that needs a specialist (two model calls). The point of the streaming design is that the user sees tokens, tool invocations, and which model is running as they happen, not after a black-box pause.

2. Agent orchestration

The core is a multi-agent tree built on Google’s Agent Development Kit (ADK) — eight agents in all: one operator (the root) plus seven specialists — coder, researcher, analyst, productivity, reviewer, evaluator, and a coder-scoped research sub-agent that ADK counts as its own instance. Each runs as its own agent instance with its own instruction, its own tool allow-list, and its own model.

Why specialize instead of one big model for everything:

ADK enforces a single-parent constraint on sub-agents, which is why the coder’s research arm is a separate instance from the standalone researcher even though they share a model and tools. Specialists also publish discoveries to a Redis event stream that peers read as ambient context — a lightweight awareness mesh — and longer async jobs hand off to a separate background orchestrator, so not everything funnels through one synchronous call path.

3. The MCP integration layer

Tools are not hard-wired into agents. They’re exposed as Model Context Protocol servers behind a single gateway (FastMCP over streamable HTTP, token-authenticated). Roughly two dozen tool-servers sit behind it — memory, knowledge retrieval, code execution, git, browser/screenshot, infrastructure, news and web feeds, PDF, SQLite, and more — each its own process.

Two decisions worth calling out:

4. Model routing and resilience

This is the part that took the most iteration: the first LLM call is easy; keeping calls reliable and cheap under real conditions is where the bugs lived.

Routing. Role-to-model assignment is declarative config, not hardcoded — each role names a primary model and an ordered fallback chain, plus vendor-recommended generation params that are applied after the router resolves the final model. Swapping a model is a config edit, not a code change.

Cost-aware selection, decided before the call. A deterministic scorer rates each prompt’s complexity 1–5 — word count, keyword classes, code markers, the agent’s role weight, conversation depth — with no model in the loop. That score, combined with budget state, picks the model tier. Under ~80% of the monthly budget, everyone runs on their assigned model; 80–95% downgrades only trivial tasks; 95–100% downgrades everything except essential high-complexity work; at 100% it fails closed on non-essential calls. Every interactive agent is flagged essential and never hard-blocked — only the background quality-evaluator is — so user-facing work falls through to progressively cheaper models instead of failing outright. Spend is tracked per request against a hard monthly ceiling, with crash-safe atomic writes so the ledger survives a restart.

Health-aware fallback. In the ADK version I built on, the model wrapper called the provider directly and didn’t consult the underlying library’s fallback config — so that global fallback setting did nothing. (The gateway does support server-side fallback across hosted models; this wrapper exists for what that can’t do — falling through to the self-hosted local model, and applying the health/cooldown logic below.) I found that gap and wrapped the model layer with one that actually implements it:

One limit: today every vendor in those chains is reached through a single router, so the router itself is the one shared dependency — which is exactly why any workload can be pinned to a model running on the box, as the escape hatch.

The failure that taught me the most was silent. Agents would intermittently get worse — more hedging, the occasional refusal — with nothing in the error logs. It wasn’t the prompts. Under budget pressure the cost router was quietly swapping an agent’s model for a cheaper one that returned 200 OK while behaving differently. Silent degradation is worse than a hard failure, because you debug the wrong layer for an hour first. The fix was observability on the swap itself: every model substitution now emits a trace event (to the live canvas badge and to tracing), so the first question on any behavioral regression is “which model actually served this call?” before anyone touches the prompt.

5. Memory architecture

The goal is persistent memory — relevant on recall and honest about staleness — not a context window that resets every chat. It’s layered:

Two principles run through all of it:

6. The proactive layer (and signal vs. noise)

A lot of the value is that it runs when I’m not looking at it. A scheduled routine pulls from many sources, synthesizes, and delivers a short brief — but the hard part isn’t pulling, it’s filtering.

Every candidate item runs through an explicit relevance filter before it’s allowed into a brief: is it actually relevant to what I’m working on, is it material, is it genuinely new — or is it just noise, which is the default it has to beat. Crucially, what gets dropped is logged, not silently discarded — there’s an audit trail of “considered and rejected,” so the filter is inspectable instead of a black box. A separate consolidation stage de-duplicates, normalizes, and runs a credential/PII filter before anything is persisted — which is also part of the privacy story: sensitive strings are filtered at ingestion rather than after they’re in the store.

7. Infrastructure and operations

It’s a running system, so it’s built to be operated, not just demoed:

8. Engineering principles I kept coming back to

Known limits

No system is free of trade-offs; the real ones here:

Stack at a glance

(Current as of writing; architecture is model-agnostic — the roles and structure are the durable part.)

Layer Choice
Agent framework Google ADK (multi-agent tree: operator + specialists)
Backend Starlette — AG-UI over SSE + OpenAI-compatible endpoint
Interop MCP gateway + OpenAI-compatible endpoint — engine-agnostic by design
Frontend Next.js single-page canvas (streaming result cards)
Inter-agent bus Redis event streams
Tooling ~two dozen MCP servers behind one gateway; BM25 tool search
Operator / routing a long-context, native-tool-calling model
Coder a purpose-built code model (large context)
Researcher a fast long-context model, single-pass (1 model call + a few tool calls)
Analyst a large mixture-of-experts reasoning model
Reviewer / evaluator independent of the code-writers; evaluator scores on a different model family; local-first, paid fallback
Memory full-text knowledge corpus + continuity graph + semantic memory + tiered context
Infra Docker, four core segmented tiers, Cloudflare Tunnel, sandboxed exec
Reliability per-role fallback chains, health tracker w/ escalating cooldown, TTFT watchdog, graceful degrade
Cost / observability per-request metering to a monthly ceiling, full call + model-swap tracing

Selected artifacts


The goal: take agentic AI from an architecture to a system that actually runs every day, under real cost, reliability, and privacy constraints, and keep it honest.

The live system is private by design; this page describes its architecture and decisions, not the running deployment.