KISS Architecture for AI Agent Stacks: One Provider, One Fallback

KISS Architecture for AI Agent Stacks: One Provider, One Fallback
Every AI agent tutorial I read showed the same elaborate setup: a router, a proxy, three providers, five models per provider, an OpenAI-compatible wrapper, an Anthropic converter, and a fallback to a local Ollama instance. The diagrams looked impressive. The actual experience of running them was a 90-minute debug session every Sunday.
I gave up trying to be clever. The setup I run today is the boring one: one provider that works, one fallback that might work later, env-var secrets, and a single systemd service. It has been up for weeks without me touching it. That is the entire point.
The Setup, in One Screen
A single primary LLM provider pointed at a single model. A second provider kept in the config as disabled optional. All credentials in environment variables, never in source. One systemd service that restarts on failure. 122 skills loaded from a marketplace catalog, organized by category.
That is it. No proxy, no router, no load balancer. If the primary dies, I have to manually edit one line and restart. I am okay with that, because the primary has not died in production yet.
Provider Configuration (The Whole Thing)
I treat the provider config like an .ssh/config file: explicit, commented, and never re-edited unless something breaks.
The primary is an OpenAI-compatible endpoint that exposes the kimi-k2.6 model. The fallback is a second OpenAI-compatible endpoint with a different model namespace. I keep the fallback in the config but disabled, so I do not have to remember its URL when the day comes.
The actual config is short enough to read in a single breath:
[default_model]
api_key_env = "OPENAI_API_KEY"
base_url = "https://<your-primary-endpoint>/v1"
model = "moonshotai/kimi-k2.6"
provider = "openai"
The env file holds the secrets and nothing else. No comments, no notes, no API documentation copy-pasted in. Just keys, base URLs, and placeholder reminders for the OAuth bits I have not finished wiring up.
The Skills Catalog
I do not write my own skills. I install them from two trusted catalogs and stop there.
From the obra/superpowers catalog, I keep fourteen skills that every serious agent stack needs: brainstorming, dispatching-parallel-agents, executing-plans, finishing-a-development-branch, receiving-code-review, requesting-code-review, subagent-driven-development, systematic-debugging, test-driven-development, using-git-worktrees, using-superpowers, verification-before-completion, writing-plans, writing-skills. These are the workflow skills. They change how the agents behave more than they change what the agents can do.
From the anthropics/skills catalog, I keep about seventeen skills covering the standard content surfaces: claude-api, doc-coauthoring, frontend-design, internal-comms, mcp-builder, pdf, pptx, slack-gif-creator, theme-factory, webapp-testing, web-artifacts-builder, xlsx. A few of them had minor TOML issues that I ignored because the underlying behavior was still correct.
On top of those, I have a long tail of ninety-plus custom and community skills: agent-orchestrator, context-optimization, docker-expert, memory-systems, sql-optimization, and a grab bag of others I have not yet bothered to organize. The total sits around 122 skills.
I do not pretend to use all 122 every day. I use maybe a dozen regularly and the rest sit in cold storage until I need them. Skills are cheap to install. Skills are expensive to evaluate. So I keep the catalog large and my daily use small.
The Two Special Agents
Most of the work happens in the general-purpose Orchestrator. But two specialists have earned their own model assignment.
The image-generator uses a dedicated image generation model. Image generation is a different shape of work from text completion, and using a text model for it produces garbage. The fallback for this agent is the same model on the second provider, because the model itself is what matters, not the endpoint.
The reasoning-agent uses Claude for deep analysis, debugging, and system planning where the plan-execute output format matters. I keep this on a more expensive model because the work it does (saving me from a 6-hour debug session) justifies the cost. The fallback is the same Claude model on the second provider.
The other 22 agents inherit the default model. They are not doing hard reasoning, and the default kimi-k2.6 endpoint handles their workload fine.
The systemd Service
The whole system runs as a single systemd service. The service uses an EnvironmentFile directive to load secrets, a WorkingDirectory of /root, and standard restart-on-failure semantics. Auto-start is enabled so the system survives reboots without me.
[Service]
Type=simple
User=root
WorkingDirectory=/root
EnvironmentFile=/root/.openfang/.env
ExecStart=/root/.openfang/bin/openfang-wrapper serve
Restart=always
RestartSec=5
When the box boots, the daemon comes up. When the daemon crashes, systemd restarts it within five seconds. I do not have a separate process supervisor because I do not need one.
The Status Page, When I Care to Look
When I want to check on the system, the status command gives me the whole picture in a few lines:
Status: running
Agents: 24
Provider: openai (primary)
Model: moonshotai/kimi-k2.6
API: http://127.0.0.1:4200
Dashboard: http://127.0.0.1:4200/
Skills: 122 loaded
That is the entire operational surface. 24 agents, 122 skills, one provider, one port. If any of those numbers drift, I know to investigate. If they stay constant, I leave the system alone.
The Management Commands I Actually Use
I keep a short mental list of commands. Anything beyond this list means I am doing something unusual.
# SSH access
ssh -i "<your-ssh-key>" root@<your-vps-ip>
# Check status
/root/.openfang/bin/openfang-wrapper status
# List skills
/root/.openfang/bin/openfang-wrapper skill list
# Chat with a specific agent
/root/.openfang/bin/openfang-wrapper chat --agent reasoning-agent
# Install a new skill
/root/.openfang/bin/openfang-wrapper skill install <skill-name>
# Restart the daemon
systemctl restart openfang.service
I do not have a fancy dashboard. I have SSH and a handful of well-named commands. The surface area for me to misconfigure is small.
What KISS Buys Me
Predictable failures. When the system breaks, it breaks in one of two ways: a key expires, or a skill has a bug. Both have obvious fixes. I do not debug a five-layer stack at 2am.
Cheap maintenance. I do not maintain a router, a proxy, a converter, or a model registry. I maintain a config file and a systemd unit. There is nothing else.
Fast onboarding. When I add a new agent, I copy a TOML file and restart. When I add a new skill, I install from the catalog and restart. The blast radius of any change is one service restart.
Honest cost accounting. With one provider, one bill, and one model, I can predict monthly cost from a single usage number. With five providers and a router, the same number is a forensic accounting project.
What I Gave Up to Get This
I gave up automatic failover. If the primary provider goes down for an hour, my agents go down with it. In practice, this has happened twice in the past six months. Both times I switched to the fallback manually within ten minutes by editing the config and restarting.
I gave up model diversity in a single agent. Every agent uses the same model namespace by default, which means I cannot route specific tasks to specific model strengths. The exception is the two special agents I described, which is enough for my use case.
I gave up the satisfaction of running an "interesting" architecture. The boring setup does not make for a good conference talk. It does, however, make for a good Saturday.
When to Break KISS
The setup I described is the right default for a single developer running a personal agent stack. It is the wrong default for a team running a production product, or for a workload that genuinely needs the latency/cost tradeoffs of routing.
The signal that you have outgrown KISS is not "I want to try a new model." It is "I have measured a real cost or latency issue that the KISS setup cannot solve." Until that signal arrives, stay boring.
Related Posts
When Your AI API Keys Die at 2 AM: A Recovery Playbook
Every API key expires eventually. Here is the exact sequence I follow when a provider goes dark: diagnose, isolate, switch, verify, document. No panic, no guessing.
Building a Multi-Agent Orchestrator: 12 Specialists, 1 Leader
Stop talking to one giant AI. Set up a small team of specialists with one orchestrator that decides who does what. Here is the architecture I run every day.
Mirroring My AI Workspace to a 1GB VPS: A Weekend Migration
My local OpenCode setup was getting too useful to leave on one laptop. Here's the weekend I cloned it all to a $5 VPS, swapped the provider, and kept Telegram working.