Most production agent apps should not bet everything on a single model. The useful multi-model architecture examples share a theme: match model capability and cost to the specific subtask, and isolate failures so one provider outage doesn’t take down the product. Below are five patterns we’ve shipped or seen survive real traffic, with concrete tradeoffs and minimal code.
1. Intent-Routed Specialist Pool
The simplest way to cut cost and latency is to never send a trivial request to a frontier model. A lightweight classifier—often a small instruction-tuned model or even a heuristic—maps the incoming query to an intent, then dispatches to a specialist model fine-tuned or prompted for that domain. In a support agent, “refund status” goes to a small model with strict JSON output; “debug my Kubernetes crash” goes to a larger coder model.
This pattern keeps the expensive model cold for most traffic. The router itself must be fast and cheap; if you spend 200ms and $0.01 on routing, you’ve eaten the savings.
INTENT_MODEL_MAP = {
"refund": "mistral-7b-instruct",
"tech_support": "gpt-4o",
"small_talk": "mistral-7b-instruct",
}
def route(messages):
intent = classify_intent(messages) # cheap local call
model = INTENT_MODEL_MAP.get(intent, "gpt-4o-mini")
return client.chat.completions.create(model=model, messages=messages)
The failure mode is misrouting. Log intent confidence and shadow-route a sample to the large model to measure divergence. If the small model hallucinates a refund policy, you want to know before the customer does.
2. Cascaded Draft and Critic
A cascaded architecture runs a small model first to produce a draft, then a larger model critiques or rewrites only when needed. This is speculative execution for language: most simple answers are accepted as-is, and the heavy model acts as a guardrail rather than a primary generator.
Implement it as two calls with a conditional. The critic prompt should be strict: “Accept if correct and safe, otherwise rewrite.” You pay the small-model token cost on every call, and the large-model cost only on rejects.
draft = client.chat.completions.create(
model="llama-3.1-8b-instruct",
messages=messages
).choices[0].message.content
critic = client.chat.completions.create(
model="gpt-4o",
messages=messages + [{"role": "assistant", "content": draft},
{"role": "user", "content": "Accept or rewrite:"]}
)
In our measurements on internal triage bots, this cut frontier-model token volume by ~70% with a 2% accuracy regression on edge cases. The regression is acceptable when the critic is given a clear rejection path to a human queue.
3. Parallel Ensemble with Majority Aggregation
When a single wrong answer is expensive—fraud classification, medical triage-adjacent, contract clause extraction—run the same prompt across three or more diverse models and aggregate deterministically. Diversity matters more than count: pick models from different families (e.g., Claude, Llama, Mistral) so they don’t share training biases.
Aggregation should be code, not another LLM call. For extractive tasks, take the most common parsed entity. For generation, use a scoring model or a fixed rubric.
const models = ["gpt-4o", "claude-3-5-sonnet", "llama-3.1-70b"];
const responses = await Promise.all(
models.map(m => openai.chat.completions.create({ model: m, messages }))
);
const votes = responses.map(r => parseLabel(r.choices[0].message.content));
const result = majorityVote(votes); // deterministic
Latency is the max of the slowest call, not the sum, if fanned out properly. Cost is multiplicative, so reserve this for high-value decisions. Never ensemble a 70B and a 7B expecting magic; the weak model just adds noise.
4. Tool-Scoped Model Assignment
Agents with tool access often pretend one model does everything. In reality, generating a SQL query, describing an image, and writing a polite email are different skills. Assign a model per tool boundary: the orchestrator is a mid-size model that decides which tool to call, but the tool handler invokes a specialized model internally.
This keeps the orchestrator context clean and lets you swap the vision model without touching agent logic.
{
"tool": "image_describe",
"model_hint": "vision-llama-11b",
"input": "s3://bucket/diagram.png"
}
The agent loop stays on gpt-4o-mini; the image tool calls its own endpoint. You get isolation: a vision provider outage degrades one capability, not the whole agent. Pass provider cache-control hints where supported so repeated tool inputs hit cache.
5. Resilient Fallback Topology
Providers throttle, regions fail, and deprecations happen at 2am. A resilient topology defines a primary and one or more secondary models with automatic failover on 429/5xx or degraded quality. You can build this yourself with retry logic, but an OpenAI-compatible gateway that already does it saves a lot of boilerplate.
n4n.ai exposes one endpoint across 240+ models with automatic fallback when a provider is rate-limited or degraded, and per-token usage metering so you can attribute cost per fallback. That turns a multi-page retry handler into a single client config.
# single endpoint, fallback handled upstream
client = OpenAI(base_url="https://api.n4n.ai/v1", api_key=KEY)
resp = client.chat.completions.create(
model="gpt-4o",
messages=messages,
extra_headers={"x-fallback-allow": "claude-3-5-sonnet,llama-3.1-70b"}
)
The caveat: fallbacks change output distribution. Log which model actually served the request (usage metadata) and alert if fallback rate exceeds 5%. Otherwise you’ll discover the “frontier” agent has been running on a 7B for a week.
Synthesis
| Pattern | Primary Benefit | Cost Multiplier | Complexity | Best For |
|---|---|---|---|---|
| Intent-Routed Pool | Cheap triage | 0.3–0.6x | Low | High-volume support |
| Cascaded Draft/Critic | Guardrail w/ savings | 0.4–1.2x | Medium | Safe content gen |
| Parallel Ensemble | Max accuracy | 3–5x | Medium | High-stakes classify |
| Tool-Scoped Model | Capability isolation | 1–2x | Medium | Multimodal agents |
| Resilient Fallback | Uptime | 1–1.5x | Low–Med | Production SLA |
Pick based on where your risk is: cost, accuracy, or availability. Most mature systems combine at least two—routing at the edge, fallback at the core.