e-naxosMAF COURSE / COURS
English

MICROSOFT AGENT FRAMEWORK · 0.16.0-rc.1

Routage des modèles

Qualifier les routes, compter le triage et comparer chaque requête avec une baseline directe.

Objectifs

Une extension après Demo470

Le routage répartit les demandes entre des profils dont les capacités, la latence et les contraintes diffèrent. Il ajoute aussi une dépendance et une requête de classification. Commencer par une baseline directe peut donc être un choix raisonnable. Définir la tâche et ses critères de réussite avant de nommer les modèles : une reformulation professionnelle et une analyse d’incident ne demandent pas les mêmes contrôles.

Qualifier des candidats avant de les router

Triage, Easy et Hard sont trois rôles configurés, pas trois niveaux de qualité garantis. Pour chaque rôle, conserver fournisseur, endpoint, modèle, version de prompt, limites et jeu de cas. Mesurer erreurs de triage, refus, réponses factuelles et cas sensibles sur les demandes visées. Une calibration de juge refusée ne peut pas être contournée par le nom Hard. Le cours conserve le refus de qualification granite4:3b ; une nouvelle combinaison demande ses propres preuves.

Les scripts EP016 proposaient mistral/qwen, des scores de risque/confiance et un menu. RoutingExperiment actuel accepte uniquement Ollama, utilise par défaut le même modèle local pour les trois rôles et ne parse que Easy ou Hard après Trim. Il ne livre ni score calibré, ni classification de sensibilité en langage libre, ni fallback. Les anciens noms illustrent des rôles ; ils ne constituent pas une liste à installer.

Garder la politique dans l’hôte

var triage = await Call("triage", triageModel, "Classify the following request. Return exactly Easy or Hard.\n" + request);
string route = triage.Trim();
if (route is not ("Easy" or "Hard")) throw new InvalidDataException("Unrecognized triage result.");
if (request.Contains("503", StringComparison.Ordinal)) route = "Hard";
string prompt = "Answer briefly in English. Separate facts from hypotheses.\n" + request;
_ = await Call("routed-execution", route == "Easy" ? easyModel : hardModel, prompt);
_ = await Call("direct-hard-baseline", hardModel, prompt);

shared/Maf.Observability/RoutingExperiment.cs · L39–L45 · 9a895c94534c

Code source · Maf.Observability · Dossier

Deux requêtes fixes sont traitées. La première reformule une phrase ; la seconde demande hypothèses, risques et vérifications pour HTTP 503 après migration. Après le triage, C# force la seconde vers Hard par présence de 503. Une autre sortie que Easy/Hard échoue explicitement. --sensitive prend une branche hôte avant toute configuration modèle et affiche une escalade humaine sans inférence : aucun workflow humain authentifié n’est créé.

Le triage renseigne une décision de ressources ; il ne donne aucun droit d’outil. Pour une application, les droits et contraintes de confidentialité doivent être vérifiés indépendamment. Ne pas envoyer un cas interdit à un modèle plus gros en espérant une meilleure décision. Les politiques de permission, de budget et d’arrêt restent celles du harness et de l’hôte.

Comparer le chemin complet

using var client = new ObservedChatClient(DemoConnection.DemoConnection.CreateChatClient(settings with
    { LocalChat = settings.LocalChat with { Model = model } }),
    usage =>
    {
        (stage == "direct-hard-baseline" ? baselineUsage : routedUsage).Add(usage);
        Console.WriteLine(JsonSerializer.Serialize(new { Stage = stage, Model = model, Usage = usage }));
    });
var agent = Telemetry.Instrument(client.AsAIAgent(name: "RoutingExperiment", instructions: "Follow the host task. No tools or business writes."));
var timer = Stopwatch.StartNew();
var response = await agent.RunAsync(prompt, cancellationToken: stop.Token);
Console.WriteLine(JsonSerializer.Serialize(new { Stage = stage, Model = model, DurationMs = timer.Elapsed.TotalMilliseconds }));
return response.Text;

shared/Maf.Observability/RoutingExperiment.cs · L62–L73 · 9a895c94534c

Code source · Maf.Observability · Dossier

Chaque paire exécute triage puis routed-execution, puis direct-hard-baseline, avec sessions neuves et même prompt final. Les requêtes de triage et d’exécution alimentent routedUsage ; la baseline possède son propre ledger. Un passage complet attend donc quatre requêtes routées, triage inclus, contre deux directes, si tout réussit. Ce sont des comptes déduits du code, pas un résultat de benchmark. Lire Stage, Model, DurationMs et couverture des compteurs ; aucune réponse métier n’est écrite.

Le coût du chemin routé inclut le triage, l’exécution, les éventuels appels auxiliaires et l’exploitation. Des tokens inconnus restent inconnus ; les additionner à zéro fabriquerait une économie. Le programme ne configure aucun tarif. Les durées par appel ne sont pas un percentile de latence de production. Pour approfondir, répéter sur un corpus représentatif, alterner l’ordre, isoler warmup et cache, publier erreurs et qualité avec les usages, puis appliquer des hypothèses tarifaires attribuées.

Ne pas généraliser la comparaison locale

L’ordre est fixe, les réponses ne sont pas évaluées ici et les profils identiques démontrent seulement la mécanique. Des modèles distincts peuvent utiliser des tokenizers différents. Une économie apparente sans qualité équivalente ne répond pas au besoin métier. Une panne ne déclenche aucun secours silencieux ; le message annonce l’échec sans promesse de gain. Cette extension ne transforme pas le cours en benchmark ni en routeur de production.

Mesurer sans perdre la provenance

Prérequis

Pour poursuivre

lot013-integration-1 · 2026-10-24 · 9a895c94534cd5fc6f3da883e37e5f7856e588c1