Designing Multi‑Model Systems: 4 Common Architecture Patterns and When to Use Them
The article examines multi‑model system design, presenting four core architecture patterns—sequential (pipeline, router), parallel (fan‑out, voting), hierarchical (planner‑executor, supervisor‑worker), and ensemble (weighted, consensus)—and discusses their trade‑offs, implementation details, and scenarios where multi‑model approaches add real value.
Architecture Patterns
Most scenarios can be grouped into five architecture patterns. Simpler solutions that satisfy requirements should be chosen first, because added complexity inevitably raises maintenance costs.
Sequential Architecture
Sequential architectures split a task into multiple stages, each handled by a model specialized for that stage.
Pattern 1: Pipeline
In a pipeline, the output of one model becomes the input of the next.
class ModelPipeline:
def __init__(self):
self.models = [
{"model": "qwen3-1.7b", "task": "classify"},
{"model": "qwen3-8b", "task": "extract"},
{"model": "qwen3-32b", "task": "reason"}
]
def process(self, input: str) -> str:
current = input
for model_config in self.models:
current = self.call_model(
model_config["model"],
self.create_prompt(model_config["task"], current)
)
return currentSerial execution accumulates latency; the overall delay is roughly three times a single call, so this pattern is justified only when each step truly requires a distinct capability.
Pattern 2: Router
The router first determines the task type and then forwards the request to the appropriate specialist model.
class ModelRouter:
def __init__(self):
self.classifier = "qwen2.5-1.5b"
self.specialists = {
"code": "qwen2.5-coder-7b",
"math": "qwen2.5-32b",
"creative": "claude-sonnet-4",
"general": "qwen2.5-7b"
}
def route(self, prompt: str) -> str:
task_type = self.classify(prompt)
model = self.specialists.get(task_type, self.specialists["general"])
return self.call_model(model, prompt)The quality of the whole flow depends on the classifier; when classification criteria are clear, a small classifier is usually sufficient.
Parallel Architecture
Parallel architectures are suitable for independent tasks that can be processed simultaneously by multiple models.
Pattern 1: Fan‑Out
Fan‑Out sends the same prompt concurrently to several models.
import asyncio
class ModelFanOut:
def __init__(self):
self.models = ["qwen2.5-7b", "qwen2.5-32b", "claude-sonnet-4"]
async def process(self, prompt: str) -> list[str]:
tasks = [self.call_model(model, prompt) for model in self.models]
return await asyncio.gather(*tasks)This pattern is useful for A/B testing or selecting the best answer from multiple outputs; the extra cost is acceptable for critical decisions.
Pattern 2: Voting
Voting lets several models make a joint decision, using the majority vote as the final output.
class ModelVoting:
def __init__(self):
self.models = ["qwen3-8b", "qwen3-32b", "claude-sonnet-4"]
def vote(self, prompt: str) -> str:
responses = [self.call_model(model, prompt) for model in self.models]
from collections import Counter
votes = Counter(responses)
return votes.most_common(1)[0][0]Voting works well for classification tasks; for generation tasks, semantic similarity rather than exact string match must be considered.
Hierarchical Architecture
Hierarchical designs assign different responsibilities to different layers of models.
Pattern 1: Planner‑Executor
A stronger model (planner) creates a plan, and smaller executor models carry out the individual steps.
class PlannerExecutor:
def __init__(self):
self.planner = "qwen3-32b"
self.executors = {
"code": "qwen2.5-coder-7b",
"search": "qwen3-8b",
"math": "qwen3-8b"
}
def process(self, task: str) -> str:
plan = self.call_model(self.planner, f"Plan: {task}")
results = []
for step in self.parse_plan(plan):
executor = self.executors.get(step["type"], "qwen2.5-7b")
result = self.call_model(executor, step["prompt"])
results.append(result)
return self.call_model(self.planner, f"Synthesize: {results}")This architecture is suitable when planning is costly but execution is lightweight.
Pattern 2: Supervisor‑Worker
The supervisor assigns tasks and reviews the results returned by workers.
class SupervisorWorker:
def __init__(self):
self.supervisor = "qwen3-32b"
self.workers = ["qwen3-8b", "qwen2.5-coder-7b"]
def process(self, task: str) -> str:
assignments = self.call_model(self.supervisor, f"Assign: {task}")
results = []
for assignment in self.parse_assignments(assignments):
result = self.call_model(assignment["worker"], assignment["task"])
results.append(result)
return self.call_model(self.supervisor, f"Review: {results}")The supervisor can become a bottleneck; if its response lags, the whole system suffers.
Ensemble Architecture
Ensemble architectures combine multiple models to improve reliability for critical decisions.
Pattern 1: Weighted Ensemble
Each model’s output receives a weight reflecting trust; the final answer is the highest‑scoring one.
class WeightedEnsemble:
def __init__(self):
self.models = {"qwen3-32b": 0.5, "claude-sonnet-4": 0.3, "qwen3-8b": 0.2}
def decide(self, prompt: str) -> str:
responses = {model: self.call_model(model, prompt) for model in self.models}
scores = {}
for model, response in responses.items():
score = self.evaluate(response) * self.models[model]
scores[response] = scores.get(response, 0) + score
return max(scores, key=scores.get)Weights should be continuously adjusted based on real‑world performance rather than static benchmark results.
Pattern 2: Consensus Ensemble
Consensus Ensemble requires a majority agreement; if the agreement threshold is not met, a stronger fallback model handles the request.
class ConsensusEnsemble:
def __init__(self, threshold: float = 0.7):
self.threshold = threshold
self.models = ["qwen3-32b", "claude-sonnet-4", "qwen3-8b"]
def decide(self, prompt: str) -> str:
responses = [self.call_model(m, prompt) for m in self.models]
from collections import Counter
votes = Counter(responses)
max_votes = max(votes.values())
if max_votes / len(self.models) >= self.threshold:
return votes.most_common(1)[0][0]
return self.call_model("qwen2.5-32b", prompt)The threshold (e.g., 0.7) controls how strict the agreement requirement is; lowering it yields faster conclusions, raising it increases confidence.
When to Use Multi‑Model Systems
If a workload contains heterogeneous tasks, critical decisions demand higher output quality, or you need to balance cost against latency, multi‑model systems provide tangible benefits. Conversely, when tasks have similar complexity, the system is still in prototyping, or simplicity outweighs optimization, a single‑model solution is preferable. The recommended practice is to start with one model and introduce additional models only when genuine cost, latency, or quality bottlenecks appear.
Conclusion
Every architecture involves trade‑offs; no single pattern fits all scenarios. The key is to identify the current system constraints and select the architecture that best aligns with those constraints.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
DeepHub IMBA
A must‑follow public account sharing practical AI insights. Follow now. internet + machine learning + big data + architecture = IMBA
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
