Skip to content

The Inversion of AI Tooling: From Access to Architecture

15 min read · AI Agents ·Memory Granularity ·Toolchain Design ·Governance

Share

Six months ago, I wrote about the Hong Kong AI setup, phone numbers, DNS routing, payment workarounds. Bottleneck was geography. Could you even reach tools?

That problem is solved now. A $5 VPS and Shadowsocks made access cheap. Tools are everywhere. But solving access didn’t simplify things. It surfaced next problem. When you can reach every model and run every agent, hard question stops being “can I connect?” and starts being “what should connect to what, and who decides?” One bottleneck disappeared and dozen coordination problems took its place.

This is inversion. Failure modes moved inside environment. Access is solved; orchestration and trust are hard part. As I argued in Agents Need Architecture, Not Prompts, real failures don’t come from what you write in prompt. They come from what your architecture permits. Stack I’m about to describe is what grew when I took that claim seriously and applied it to my own environment.

Inversion was gradual. Each new capability removed external constraint and revealed internal one. Payment workarounds unlocked model access. Model access revealed that one agent couldn’t hold multiple contexts. Splitting agents revealed that one memory system couldn’t serve three kinds of recall. Splitting memory revealed that toolchain itself needed taxonomy. Each solution created next problem, and stack grew in response.

Four bottlenecks drove this growth, in sequence: context collision, when one system prompt tried to hold everything and domains bled into each other. Memory fragmentation, when single embedding store couldn’t answer three different kinds of question. Tool sprawl, when growing list of runtimes, models, and subscriptions became category error waiting to happen. Trust ambiguity, when local and hosted components sat side by side with no explicit boundary.

What follows is stack that emerged. It looks complex on paper, three agent profiles, three memory systems, model router, orchestrator, an ADE, and toolchain spanning runtimes, models, and subscriptions. None of it is there because I was collecting. Every layer exists because simpler alternative was tried first and broke.

One Agent, Three Profiles

The breaking point came when a single system prompt tried to hold everything — ops instructions, writing style guides, social media personas, nutrition tracking logic, all pulling against each other.

Two contamination patterns made the single-agent approach untenable. First, social context leaked into private ops decisions. Conversational tone from a previous interaction would bleed into infrastructure commands. The agent wasn’t crashing. It was producing output that looked right and was slightly wrong — the hardest failure to catch, because nothing alerts you to it. You find it later, when a decision surfaces that was softened by the wrong context.

Second, nutrition’s dedicated logic collided with governance rules. Calorie tracking sat next to deployment policies in the same prompt, and neither domain got full attention. The system prompt became a negotiation between competing concerns, and the agent was losing that negotiation in ways that only showed up after the fact.

context collision

Single System Prompt
ops + writing + social + nutrition

Contamination:
social leaks into ops
nutrition collides with governance

Split into three profiles

Norty
private, full access
governance / coding / ops / infra
OmniRoute → GLM 5.2

Nora
public-facing
community, friend circles
SearXNG + Firecrawl

Bento
single-purpose
nutrition tracking
Telegram + SQLite

The fix was structural: separate agents for separate concerns. A system prompt is not a sufficient boundary. Sharing one prompt across domains makes the agent’s context porous — domains bleed into each other regardless of how carefully you write the instructions. A prompt can say “don’t mix these contexts.” A structural split makes mixing them impossible.

So I split one agent into three profiles, each a configuration of Hermes, the open-source agent framework by Nous Research that serves as the stack’s orchestrator. Not separate programs — distinct personalities with distinct tool access, running on the same runtime.

Norty is the private, full-access profile. Governance, coding, ops, infrastructure. OmniRoute routing with GLM 5.2 as the default model, served via Ollama Cloud Pro. Norty has keys to everything because Norty’s scope is everything I build and operate. No social context. No nutrition logic. Just work.

Nora is the public-facing profile: community, friend circles, limited tools. SearXNG for search, Firecrawl for web scraping. Nora is not Norty with fewer permissions — she’s an independent persona with a distinct role. The tool restriction is the entire point. Nora doesn’t need infrastructure access, and giving it to her would be a scope breach waiting to happen.

Bento is single-purpose: nutrition tracking. Telegram interface, SQLite database. No model routing complexity. No governance rules. No social media. Bento knows about calories and macros and nothing else. When Bento makes a decision, there is zero chance it was influenced by ops context or a writing style guide — those contexts don’t exist in Bento’s world. Bento can’t leak what it doesn’t contain.

Each profile gets a distinct toolset. The tools a profile can access define what it can do, and there is no separate permission system layered on top. The tool list is the boundary. This is where the stack first hits two structural walls: authority — who can do what — and scope — keeping contexts from contaminating each other. The profile split exists because one agent trying to be everywhere ended up being reliable nowhere.

Memory at Three Granularities

Splitting profiles solved context contamination, but surfaced next bottleneck: memory.

Single memory system can’t serve three different kinds of recall. Flat embedding store built for conversational similarity will return most semantically similar exchange when asked “why was that architecture decision made three months ago?”, which might be discussion about completely different project that happened to use similar words. Structure of codebase and timeline of decisions are invisible to it. Failure isn’t that it returns nothing. It returns something plausible and wrong.

Agent needs three granularities of recall: conversational continuity across sessions, structural understanding of codebase, and temporal reasoning about past decisions. These are different problems. One memory system can’t cover all three. So stack has three, each scoped to different recall.

Honcho handles cross-session conversation memory. Self-hosted via Docker, peer-based, with dialectic reasoning, it answers “what did we talk about last week?” It integrates directly with Hermes, giving each profile continuity across sessions without leaking context between them. Conversation memory can’t tell you which module function lives in, since it was never meant to.

Graphify handles code structure indexing. It builds graph-based knowledge representation of codebase, showing which functions call which, which modules depend on which, and where boundaries are. It replaced now-deprecated Codebase-Memory MCP. When coding agent needs to understand architecture before making change, Graphify provides that map. But code indexing can’t reconstruct last week’s reasoning; it knows structure, not history.

Graphiti handles temporal knowledge. Built by Zep, it’s bi-temporal knowledge graph backed by Neo4j that tracks both valid time (when something was true) and system time (when we learned about it). It addresses gap other two can’t touch: state of knowledge at point in time, including why decision was made, in context of what was known at that moment. But temporal knowledge graph can’t hold current conversation context. It’s built for reasoning across time, not continuity within session.

These aren’t three competing solutions to same problem. They’re three systems for three different granularities of recall, analogous to short-term, long-term, and procedural memory in one brain. Each exists because single memory system, asked to cover all three, returned answers that were plausible and wrong.

The Toolchain Is a Topology, Not a List

Once profiles were split and memory was specialized, the next bottleneck surfaced: tool sprawl. The list of components had grown, including runtimes, models, subscriptions, a router, an orchestrator, an ADE, and listing them without their relationships was producing category confusion. A model isn’t a runtime. A subscription isn’t a router. An orchestrator isn’t any of these. The question stopped being “what tools are in the stack?” and became “what connects to what, and what doesn’t?”

Here is every agent-layer component, grouped by category and distinct job.

ComponentCategoryJob
Claude Code CLIRuntime/CLIPrimary development, deep reasoning, multi-file changes
Kimi Code CLIRuntime/CLISecondary, quick patches, external reviews
OpenCode CLIRuntime/CLIProvider-agnostic open-source coding agent (MIT, ~165K GitHub stars)
Codex CLIRuntime/CLIVerification and code review only, audit path
GLM 5.2ModelNorty’s default model, served via Ollama Cloud Pro
Kimi K3ModelZero-shot coding model, accessed via Kimi Code CLI on Allegro plan
MiniMax M3ModelMinor helper, 1M-token context, via direct API and OpenCode Go
OpenCode GoSubscription$10/mo, 14 models, ZDR agreements with all providers
Ollama Cloud ProSubscriptionHosted Ollama tier serving GLM 5.2 and open-weight models
Claude Max 20xSubscription$200/mo, unlocks Claude Code
Kimi AllegroSubscription$99/mo, unlocks Kimi Code
OmniRouteModel RouterSelf-hosted at localhost:20128, routes by task and cost
HermesOrchestratorManages profiles, memory, cron, multi-platform gateway
OrcaADEParallel agents in isolated git worktrees, BYO agent CLI
CortexEditorial pipelineSecond independent OmniRoute consumer, distinct from Hermes

The categories prevent category errors. OpenCode CLI is a free, MIT-licensed runtime. OpenCode Go is a paid subscription, same brand, different category. OmniRoute is a router, not a runtime. Orca is an ADE, not an IDE. A model is not a runtime; a subscription is not a model. Flattening these into one list loses the information that makes the topology legible.

But categories are only interesting because of the edges between them.

routes via

routes via

to

spawns

spawns

spawns

billed directly

billed directly

billed directly

MCP inside

standalone CLI

GitHub app

Norty

Hermes

Nora

Bento

OmniRoute

Cortex

GLM 5.2, DeepSeek V4, Kimi K3, ...

Orca ADE

Claude Code
primary

Kimi Code
secondary

OpenCode CLI

Anthropic

Kimi Allegro

Various providers

Codex
review-only

Direct review

PR trigger

The three profiles each run as configurations of Hermes. When a profile needs a model, Hermes calls OmniRoute, which picks provider and model by task and cost. Cortex, the editorial and radar pipeline powering this site’s content workflow, is a second, independent OmniRoute consumer. It does not go through Hermes. Hermes and Cortex are siblings, not a chain.

Orca spawns coding CLIs as parallel agents in isolated git worktrees. Claude Code is primary: deep work, multi-file projects. Kimi Code is secondary: quick patches. OpenCode CLI is the provider-agnostic fallback. Each coding CLI calls its own provider directly: Claude Code → Anthropic (Claude Max 20x), Kimi Code → Kimi Allegro. These do not route through OmniRoute. They are vendor products on their own subscriptions.

Codex is review-only, with three surfaces: Codex MCP inside Claude Code for in-session review, a standalone CLI for direct review, and a GitHub app triggered by PR events. That third surface is worth noticing, invoked by a repository event, not by any component in the stack. A reviewer that no component invokes, wired to a repository event rather than a call graph.

The coding hierarchy: Claude (primary) > Kimi (secondary) > Codex (minor, review-only) > MiniMax M3 (minor, broad-context helper, not a daily driver).

And then the non-edges, as load-bearing as the edges, because they are the mistakes the topology is designed to prevent.

OmniRoute does not route the coding CLIs. They are vendor products on their own subscriptions. OmniRoute serves what I built, the profiles via Hermes, and Cortex, not what I buy.

Nothing calls Orca. It is an environment operated by hand, not a service in an automated chain. Orca calls out, spawning CLIs; nothing calls in.

There is no OmniRoute → Orca → Codex pipeline. Those three are on separate planes: a router, a hand-driven environment, and a reviewer. They don’t chain.

The topology isn’t there because I was collecting components. It’s there because tool sprawl forced a structural answer. When you have runtimes, models, subscriptions, a router, an orchestrator, and an ADE all in play, the only way to keep them straight is to know what each one is, what it connects to, and what it doesn’t. IDE is for you. ADE is for you and your agents. That’s the Orca distinction. An IDE hosts your session. An ADE hosts multiple agent sessions in parallel, each in its own isolated worktree, each with its own runtime and model assignment.

Trust Is a Boundary, Not a Feature

Fourth bottleneck is trust ambiguity. When local and hosted components sit side by side with no explicit boundary, default assumption becomes “it’s all private” or “it’s all exposed”, and both are wrong.

Stack spans both worlds. Some components run locally on a GMKtec NucBox M7 Ultra, an AMD Ryzen 7 PRO 6850U mini PC with 64GB of DDR5. Others are hosted services with their own policies. Boundary is not a feature you toggle. It’s a structural property of where each component lives and what it can reach.

ComponentWhere it runsTrust model
Ollama BGE-M3LocalFully private, embeddings only, no internet route
OmniRouteLocal (router)Bridges both worlds, routes to local or hosted depending on task
OpenCode GoHosted (third-party)Service-dependent, ZDR agreements signed with all providers
Claude CodeHosted (Anthropic)Service-dependent, governed by Anthropic’s data policy
Kimi CodeHosted (Moonshot)Service-dependent, governed by Kimi’s data policy

Ollama BGE-M3 runs truly locally, handling embeddings for semantic search and code indexing with no internet route. That is private. OpenCode Go is a hosted service. Model inference goes through their API, and while they have signed zero data retention agreements with all providers, trust model is different from a local process. OmniRoute bridges both worlds: it can route to local Ollama for embeddings or to hosted backends for inference, depending on task. There is no blanket “zero data retention” claim across the stack. Each service has its own policy. The important thing is knowing which is which.

Trust boundary is also enforced at profile level. Each profile’s toolset defines what it can reach, and that toolset is its trust perimeter. Norty has full access, keys to the kingdom, appropriate to its scope. Nora has SearXNG and Firecrawl only, public-facing, no infrastructure access. Bento has Telegram and SQLite only, smallest trust perimeter, unable to reach models or infrastructure.

Outward-facing access-control layer is a single integrated piece: Cloudflare Tunnel plus Google SSO, gated by Google Workspace as identity provider. cloudflared makes an outbound connection to Cloudflare’s edge, no inbound ports, nothing listening on a public interface. Cloudflare Access sits in front, and only identities authenticated through Google Workspace get through. Free tier, up to 50 users.

Trust isn’t a setting you configure once. It’s a boundary drawn at every layer: where a component runs, what a profile can access, and what the network allows in. The stack doesn’t make blanket privacy claims. It makes explicit boundaries, and each boundary has a reason.

The Walls That Shaped It

Stack looks complex. Three profiles, three memory systems, topology of runtimes and routers and subscriptions, trust boundaries drawn at multiple layers. Natural question is: why this much complexity?

Answer isn’t personal preference. It’s three structural walls that every multi-agent setup eventually hits.

Authority is first wall. Who can do what? When one agent has access to everything, question is easy. Answer is “everything.” But moment you split into profiles, authority becomes design problem. Norty can deploy infrastructure. Nora can’t. Bento can’t even reach model. Tool list is authority boundary, and it has to be enforced structurally, not through prompt that says “don’t use these tools,” but through configuration that makes using them impossible.

Scope is second wall. What context does each agent hold? Single-prompt approach failed because scope was porous. Social context leaked into ops, nutrition collided with governance. Splitting into profiles solved scope at agent level, but surfaced scope problems at memory level: one memory system can’t serve three granularities of recall. Scope isn’t just about keeping agents apart. It’s about making each component hold exactly context it needs and nothing else.

Verification is third wall. How do you know output is correct? When one agent produces everything, verification is ad hoc. You read output and decide if it looks right. When multiple agents produce outputs that feed into each other, verification needs structure. Codex exists as review-only path, separate from primary coding CLIs. Non-edges in topology, where OmniRoute doesn’t route coding CLIs, nothing calls Orca, there is no OmniRoute → Orca → Codex pipeline, are verification boundaries. They ensure component that writes code is not component that reviews it.

These three walls map directly onto four bottlenecks. Context collision is scope problem, with domains bleeding into each other because boundary was prompt, not structure. Memory fragmentation is scope problem at different granularity, where one store is asked to serve three kinds of recall. Tool sprawl is authority problem. When components aren’t categorized and their edges aren’t explicit, you don’t know who can reach what. Trust ambiguity is authority and verification problem. Without explicit boundaries between local and hosted, and without profile-scoped tool access, you’re guessing at what’s private.

Walls aren’t bugs. They’re constraints that exist whether you acknowledge them or not. Stack is what happens when you stop working around them and start building with them.

This article described stack. Next one will describe what happens when you hit each wall head-on: failure modes, design decisions, and patterns that emerge when authority, scope, and verification become first-class concerns rather than afterthoughts.

Share