Conception de systèmes multi-modèles : quand un seul modèle ne suffit plus
Choisissez le modèle le plus simple qui fonctionne.
Les systèmes à modèle unique sont simples. Les systèmes à multi-modèles sont puissants. Le défi ne réside pas dans le choix des modèles, mais dans la conception de l’architecture qui les orchestre.
Un système à multi-modèles ne consiste pas à accumuler des modèles. Il s’agit d’utiliser le bon modèle pour la bonne tâche, au bon moment.

Patterns d’architecture
Cinq patterns couvrent la plupart des cas d’utilisation :
| Pattern | Complexité | Quand l’utiliser | Compromis |
|---|---|---|---|
| Modèle Unique | La plus faible | Prototypage, tâches simples | Capacités limitées |
| Séquentiel | Faible | Flux de travail en plusieurs étapes | Latence accrue |
| Parallèle | Moyenne | Tâches indépendantes | Coût accru |
| Hiérarchique | Élevée | Raisonnement complexe | Orchestration complexe |
| Ensemble | La plus élevée | Décisions critiques | Coût le plus élevé |
Choisissez le plus simple qui fonctionne. La complexité est réelle et elle s’accumule.
Architecture séquentielle
Traitez les tâches à travers une chaîne de modèles, chacun spécialisé dans une étape.
Pattern 1 : Pipeline
Pattern Pipeline — la sortie de chaque modèle alimente le suivant :
class ModelPipeline:
def __init__(self):
self.models = [
{"model": "qwen2.5-1.5b", "task": "classify"},
{"model": "qwen2.5-7b", "task": "extract"},
{"model": "qwen2.5-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 current
La latence s’additionne. Trois modèles en séquence signifient trois fois la latence. N’utilisez ceci que si chaque étape nécessite effectivement un modèle différent.
Pattern 2 : Routeur
Pattern Routeur — classifiez la tâche, routez vers le spécialiste :
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)
Le classificateur est le maillon faible. S’il mal classifie, vous routez vers le mauvais modèle et perdez en qualité. Utilisez un classificateur suffisamment bon — même un petit fonctionne si les catégories sont claires.
Architecture parallèle
Traitez des tâches indépendantes simultanément.
Pattern 1 : Fan-Out (Éventail)
Fan-out — lancez le même prompt à travers plusieurs modèles :
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)
Utile pour la comparaison, les tests A/B, ou lorsque vous souhaitez choisir la meilleure sortie. C’est coûteux, mais le gain de qualité en vaut la peine pour les décisions critiques.
Pattern 2 : Vote
Vote — combinez les sorties par consensus :
class ModelVoting:
def __init__(self):
self.models = [
"qwen2.5-7b",
"qwen2.5-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]
Le vote majoritaire fonctionne pour la classification. Pour les tâches de génération, c’est plus difficile — vous avez besoin de similarité sémantique, pas de correspondances exactes.
Architecture hiérarchique
Utilisez des modèles à différents niveaux d’abstraction.
Pattern 1 : Planificateur-Exécuteur
Planificateur-exécuteur — un modèle puissant planifie, des modèles plus petits exécutent :
class PlannerExecutor:
def __init__(self):
self.planner = "qwen2.5-32b"
self.executors = {
"code": "qwen2.5-coder-7b",
"search": "qwen2.5-7b",
"math": "qwen2.5-7b",
}
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}")
Le planificateur fait le gros du travail. Les exécuteurs gèrent les tâches spécifiques. Ce pattern fonctionne bien lorsque l’étape de planification est coûteuse mais que les étapes d’exécution sont bon marché.
Pattern 2 : Superviseur-Travailleur
Superviseur-travailleur — un superviseur délègue et passe en revue :
class SupervisorWorker:
def __init__(self):
self.supervisor = "qwen2.5-32b"
self.workers = ["qwen2.5-7b", "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}")
Le superviseur est le goulot d’étranglement. Il planifie, délègue et passe en revue. Assurez-vous qu’il est suffisamment rapide, sinon tout le système ralentit.
Architecture d’ensemble (Ensemble)
Combinez plusieurs modèles pour les décisions critiques.
Pattern 1 : Ensemble Pondéré
Ensemble pondéré — notez la sortie de chaque modèle, choisissez la plus élevée :
class WeightedEnsemble:
def __init__(self):
self.models = {
"qwen2.5-32b": 0.5,
"claude-sonnet-4": 0.3,
"qwen2.5-7b": 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)
Les poids reflètent votre confiance en chaque modèle. Ajustez-les en fonction des performances réelles, pas des benchmarks.
Pattern 2 : Ensemble par Consensus
Ensemble par consensus — exigez un accord, escaladez s’il n’y en a pas :
class ConsensusEnsemble:
def __init__(self, threshold: float = 0.7):
self.threshold = threshold
self.models = [
"qwen2.5-32b",
"claude-sonnet-4",
"qwen2.5-7b",
]
def decide(self, prompt: str) -> str:
responses = [
self.call_model(model, prompt)
for model 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)
Le seuil contrôle la rigueur du consensus. 0,7 signifie un accord des deux tiers. Baissez-le pour des décisions plus rapides, augmentez-le pour une plus grande confiance.
Quand les systèmes à multi-modèles ont du sens
Les systèmes à multi-modèles ont du sens lorsque vous avez des charges de travail mixtes, que vous avez besoin d’une haute qualité pour les décisions critiques, ou que vous optimisez pour le coût ou la latence.
Ils n’ont pas de sens lorsque toutes les tâches ont une complexité similaire, que vous êtes en phase de prototypage, ou que la simplicité prime sur l’optimisation.
La règle empirique : commencez avec un modèle. Ajoutez-en d’autres lorsque vous heurtez une contrainte réelle — coût, latence ou qualité. Ne concevez pas de complexité avant d’en avoir besoin.
Compromis (Tradeoffs)
| Pattern | Coût | Latence | Qualité | Complexité |
|---|---|---|---|---|
| Modèle Unique | Le plus faible | La plus faible | Variable | La plus faible |
| Séquentiel | Moyen | Élevée | Élevée | Moyenne |
| Parallèle | Élevé | Faible | Élevée | Moyenne |
| Hiérarchique | Élevé | Élevée | La plus élevée | Élevée |
| Ensemble | Le plus élevé | Moyenne | La plus élevée | La plus élevée |
Chaque pattern implique un compromis. Choisissez celui qui correspond à vos contraintes.
Liés
- Stratégies de Routage de Modèles — routage basé sur les capacités, conscient du coût et de la latence
- Optimisation des Coûts pour les Systèmes LLM — budget de tokens, modèles de repli, mise en cache
- Garde-fous LLM en Pratique — validation des entrées, filtrage des sorties, sécurité
- Architecture LLM — pilier de conception système : routage, coût, garde-fous et orchestration