Progettazione di sistemi multi-modello: quando un singolo modello non è sufficiente

Scegli il pattern più semplice che funziona.

Indice

I sistemi a modello singolo sono semplici. I sistemi multi-modello sono potenti. La sfida non è scegliere i modelli: è progettare l’architettura che li orchestra.

Un sistema multi-modello non si tratta di avere più modelli. Si tratta di avere il modello giusto per il compito giusto al momento giusto.

Modelli di progettazione dei sistemi LLM multi-modello

Modelli di architettura

Cinque modelli coprono la maggior parte dei casi d’uso:

Modello Complessità Quando usarlo Compromesso
Modello Singolo Minima Prototipazione, compiti semplici Capacità limitata
Sequenziale Bassa Flussi di lavoro multistep Latenza più elevata
Parallelo Media Compiti indipendenti Costo più elevato
Gerarchico Alta Ragionamento complesso Orchestrazione complessa
Ensemble Massima Decisioni critiche Costo massimo

Scegli il più semplice che funziona. La complessità è reale e si accumula.

Architettura sequenziale

Elabora i compiti attraverso una catena di modelli, ciascuno specializzato in un passaggio.

Modello 1: Pipeline

Modello Pipeline — l’output di ogni modello alimenta il successivo:

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 current

La latenza si somma. Tre modelli in sequenza significano tre volte la latenza. Usa questo modello solo se ogni passaggio richiede effettivamente un modello diverso.

Modello 2: Router

Modello Router — classifica il compito e instrada verso lo specialista:

class ModelRouter:
    def __init__(self):
        self.classifier = "qwen3-1.7b"
        self.specialists = {
            "code": "qwen2.5-coder-7b",
            "math": "qwen3-32b",
            "creative": "claude-sonnet-4",
            "general": "qwen3-8b",
        }

    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)

Il classificatore è il punto debole. Se classifica male, instradi verso il modello sbagliato e perdi in qualità. Usa un classificatore abbastanza buono: anche uno piccolo funziona se le categorie sono chiare.

Architettura parallela

Elabora compiti indipendenti simultaneamente.

Modello 1: Fan-Out

Fan-out — esegui lo stesso prompt attraverso più modelli:

import asyncio

class ModelFanOut:
    def __init__(self):
        self.models = [
            "qwen3-8b",
            "qwen3-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 per confronti, test A/B o quando si vuole scegliere l’output migliore. Costoso, ma il guadagno in qualità vale la pena per decisioni critiche.

Modello 2: Voting

Voting — combina gli output tramite consenso:

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]

Il voto a maggioranza funziona per la classificazione. Per i compiti di generazione, è più difficile: serve somiglianza semantica, non corrispondenze esatte.

Architettura gerarchica

Usa modelli a diversi livelli di astrazione.

Modello 1: Planner-Executor

Planner-Executor — un modello forte pianifica, modelli più piccoli eseguono:

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"], "qwen3-8b")
            result = self.call_model(executor, step["prompt"])
            results.append(result)
        return self.call_model(self.planner, f"Synthesize: {results}")

Il planner fa il lavoro pesante. Gli executor gestiscono compiti specifici. Questo modello funziona bene quando il passaggio di pianificazione è costoso ma i passaggi di esecuzione sono economici.

Modello 2: Supervisor-Worker

Supervisor-Worker — un supervisore delega e revisiona:

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}")

Il supervisore è il collo di bottiglia. Pianifica, delega e revisiona. Assicurati che sia abbastanza veloce, altrimenti l’intero sistema rallenta.

Architettura Ensemble

Combina più modelli per decisioni critiche.

Modello 1: Weighted Ensemble

Weighted Ensemble — punteggi l’output di ogni modello e scegli il più alto:

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)

I pesi riflettono la tua fiducia in ogni modello. Regolisti in base alle prestazioni reali, non ai benchmark.

Modello 2: Consensus Ensemble

Consensus Ensemble — richiedi l’accordo ed escalala se non c’è:

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(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("qwen3-32b", prompt)

La soglia controlla quanto è rigoroso il consenso. 0.7 significa due terzi di accordo. Abbassala per decisioni più rapide, alzasla per maggiore sicurezza.

Quando i sistemi multi-modello hanno senso

I sistemi multi-modello hanno senso quando hai carichi di lavoro misti, hai bisogno di alta qualità per decisioni critiche o stai ottimizzando per costo o latenza.

Non hanno senso quando tutti i compiti hanno complessità simile, stai facendo prototipazione o la semplicità conta più dell’ottimizzazione.

La regola pratica: parti con un modello. Aggiungine altri solo quando raggiungi un vincolo reale — costo, latenza o qualità. Non progettare complessità prima di averne bisogno.

Compromessi

Modello Costo Latenza Qualità Complessità
Modello Singolo Minimo Minima Variabile Minima
Sequenziale Medio Alta Alta Media
Parallelo Alto Bassa Alta Media
Gerarchico Alto Alta Massima Alta
Ensemble Massimo Media Massima Massima

Ogni modello comporta un compromesso. Scegli quello che corrisponde ai tuoi vincoli.

Correlati

Iscriviti

Ricevi nuovi articoli su sistemi, infrastruttura e ingegneria AI.