X·Trillion

Research  ·  Agent architecture

Any model, one interface

When a new model ships, most teams stop and re-integrate. The right architecture treats the model as a swappable detail — so your agents can use it the day it arrives.

Any model, one interface — play video
Watch on the X·Trillion Research channel.

The frontier moves every few weeks. A new model lands, it is better or cheaper at something, and a familiar scramble begins: find the integration, swap the SDK, re-test the prompts, redeploy. By the time the work is done the next model is out. Teams that wire each model in by hand are permanently one release behind.

There is a better posture. Treat the model as the least permanent thing in the system — a detail chosen by configuration, behind a single interface — and adopting a new one stops being a project. It becomes a row in a table. This is the architecture our agents already run on, and it is why, when a model such as Fable returns, they can reach for it the same day.

The outside model is a callee, not the driver

The first idea is a separation of roles. One agent orchestrates — it holds the task, decides what happens next, and owns the result. When it needs a capability another model is better or cheaper at, it does not hand over control. It makes a call, the way it would call any other tool, and waits for an answer.

So the orchestrator might be Claude in your subscription, or a cheaper model running the high-volume loop. The model it calls out to — DeepSeek, a GPT model, Gemini, Fable — is a specialist consulted for one step and then set down again. It never takes the wheel.

Orchestrating agent Claude · DeepSeek call one interface result DeepSeek GPT Gemini Fable
The orchestrator keeps the wheel. Any provider answers through one interface — and a new one is added without touching the agent.

One client, chosen by config

Behind that single interface is one client. It does not know, or care, which provider it is talking to until it reads the configuration. A model is described by a small record — an identifier, a provider, the name of the key it needs, and a priority — and the client dispatches on the provider:

# a model is just a config row
ModelConfig(name, model_id, provider, api_key_name, priority)

client.chat(messages)
#  → dispatches to: anthropic | openai | google | xai

Each provider has one small adapter behind the dispatch — the shape of its request, the shape of its reply — and nothing above the client needs to know which one ran. DeepSeek is a particularly clean case: it exposes an Anthropic-compatible endpoint, so it rides the same code path as Claude with nothing changed but the base URL. The cross-provider work is mostly already done by the providers themselves.

Adding a model is adding a row

Once the model is a record, adopting a new one is editing a list. The day Fable returns, the entire change is a single entry — an identifier, the provider it speaks, the key to use:

# the day Fable ships:
ModelConfig("CLAUDE_FABLE", "claude-fable-5",
            "anthropic", "ANTHROPIC_API_KEY", priority=1)

Fable is a Claude model, so it reuses the existing Anthropic adapter — there is no new code to write at all. And because the roster of models is fetched from our key service at call time rather than baked into each agent, the change is frequently not even a deployment. Update the list in one place; every agent picks up the new model on its next call.

Send little, not everything

The outside model is never handed the whole conversation. It is given scoped, read-only tools — read a file, search with a pattern, list a directory, confined to the task — and it pulls only the few things the step requires. That is the lesson from Why splitting the work saves money, now applied across providers: a specialist fed a tight, relevant context is cheaper and sharper than one buried in everything that came before.

Priority, and a soft landing when a provider fails

Because a model is a record with a priority, the list is also a fallback order. The client tries each model in turn and moves to the next if one is unavailable, rate-limited, or declines the request:

for model in roster_by_priority:
    try:    return call(model, messages)
    except: continue   # try the next provider

No single provider is a point of failure, and no outage requires a code change to route around — the next model in the list simply answers instead. The same mechanism that makes adoption cheap makes the system resilient.

Keys and model ids in one place

None of this works if every agent hard-codes a key or a model string. They do not. Every provider key and every model identifier resolves from a single service at the moment of the call. Rotate a key, retire a model, or change which model a task prefers, and the change lands once and propagates everywhere — there is no sweep across a dozen repositories, and no agent quietly running last quarter's model.

The model is a detail. The architecture — one interface, chosen by config, fed a disciplined context — is the part worth owning.

Before you route anything off-site

One caution, the same one that gates the rest of this series. Sending source code or client data to a third-party model — DeepSeek, a GPT model, any external provider — is a data-governance decision, not a cost decision. Check your retention, regional-processing and contractual obligations first; a cheaper or more capable endpoint is not automatically an acceptable one. The architecture here makes any model reachable in one line. Which models are permitted for which data is a separate, deliberate choice — and the config-driven roster is exactly where that policy is best enforced.

Why this matters more than any single model

It is tempting to chase each new release. The more durable advantage is to stop having to. When the model is a swappable record behind one interface, the question "should we adopt the new model?" shrinks from a re-integration project to an afternoon's evaluation and a one-line change. You get to be early by default — and to drop a model just as cheaply if it disappoints.

That is the whole point of the cheaper-agents work brought together: pick a cheaper model where you can, cut the context you resend, split the providers — and put it all behind an interface that does not care which model it is talking to. Do that, and the frontier moving is no longer a fire drill. It is just a better row in the table.


Related — the cheaper-agents series: Part 1 — Cheaper models inside Claude Code  ·  Part 2 — Why splitting the work saves money  ·  Part 3 — Roll your own, across providers

First published on x-trillion.com. A version also appears on Medium.