e-naxosMAF COURSE / COURS
English

MICROSOFT AGENT FRAMEWORK · 0.16.0-rc.1

Dégradation et annulation

Prévoir un résultat dégradé explicite sans absorber l’annulation ni inventer une confiance mesurée.

Objectifs

La dépendance peut échouer

Le triage doit rester compréhensible si le modèle est indisponible, lent ou renvoie un contrat inutilisable. EP011 proposait une façade résiliente : le métier appelle un service, et ce service gère l’appel modèle. Le programme actuel garde cette séparation, remplace le menu de tournage par des arguments et annonce explicitement Origine, CauseBascule et Confiance. Le mode par défaut est une heuristique locale volontaire, sans tentative modèle.

Une tentative bornée, puis une issue définie

ResilientAgentTriageService utilise Polly pour borner une tentative modèle à 60 secondes ; --timeout attend volontairement puis expire après deux secondes. --unavailable lève une panne transport simulée, --invalid une sortie invalide simulée et --live appelle le modèle configuré. Aucun retry ni changement automatique de fournisseur. Le résultat modèle doit avoir une catégorie Access, Billing, Incident ou General et une décision non blanche.

public async Task<TriageResult> ClasserAsync(string request, ScenarioDemo scenario, CancellationToken cancellationToken)
{
    cancellationToken.ThrowIfCancellationRequested();
    if (scenario == ScenarioDemo.FallbackDirect)
        return fallback.Classer(request, "Explicit heuristic mode.");
    var pipeline = new ResiliencePipelineBuilder<TriageResult>().AddTimeout(
        scenario == ScenarioDemo.TimeoutSimule ? TimeSpan.FromSeconds(2) : timeout).Build();
    try
    {
        var result = await pipeline.ExecuteAsync(async token =>
        {
            if (scenario == ScenarioDemo.TimeoutSimule) await Task.Delay(Timeout.InfiniteTimeSpan, token);
            if (scenario == ScenarioDemo.OllamaIndisponibleSimule) throw new HttpRequestException("Simulated provider outage.");
            if (scenario == ScenarioDemo.SortieInvalideSimulee) throw new InvalidOperationException("Simulated invalid output.");
            var value = await model(request, token);
            if (value.Categorie is not ("Access" or "Billing" or "Incident" or "General")
                || string.IsNullOrWhiteSpace(value.Decision))
                throw new InvalidOperationException("Invalid triage output.");
            return value with { Origine = "Model", CauseBascule = null, Confiance = null,
                Explication = "Model classification; no calibrated confidence is available." };
        }, cancellationToken);
        cancellationToken.ThrowIfCancellationRequested();
        return result;
    }
    catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) { throw; }
    catch (Exception ex) when (ex is TimeoutRejectedException or OperationCanceledException
        or HttpRequestException or InvalidOperationException or System.Text.Json.JsonException)
    {
        cancellationToken.ThrowIfCancellationRequested();
        return fallback.Classer(request, ex.GetType().Name);

demos/Demo350_GracefulDegradation/ResilientAgentTriageService.cs · L25–L54 · 9a895c94534c

Code source · Demo350 · Dossier

Le filtre intercepte TimeoutRejectedException, OperationCanceledException, HttpRequestException, InvalidOperationException et JsonException. Il recontrôle d’abord le token de l’appelant. Les autres catégories d’exception se propagent. Cette liste est la politique actuelle ; elle ne distingue pas toute erreur de programmation pouvant apparaître sous InvalidOperationException. Aucun circuit breaker de pannes répétées n’est configuré, malgré sa présence dans l’ancienne liste de possibilités.

Faire moins et montrer la provenance

Chemins du service de triage
  1. Entrée + token → mode heuristique explicite ou tentative modèle
  2. Résultat modèle valide → Origine=Model, Confiance=null
  3. Échec pris en charge → mots-clés → Origine=Heuristic, cause visible, Confiance=null
  4. Annulation appelant → exception propagée, aucun résultat heuristique de remplacement

Le fallback cherche invoice/payment avant password/login/account, puis urgent/production ; sinon General. Une demande contenant « production » et « login » devient donc Access par ordre des règles. La décision demande une revue humaine, sans créer effectivement une demande dans un système externe. C’est un tri lexical limité. Les anciennes formulations de score plafonné sont corrigées : Confiance vaut null, même si le modèle prétend 0,99. Ni une heuristique déterministe ni un nombre produit par le modèle ne constitue une probabilité calibrée.

public TriageResult Classer(string request, string reason)
{
    var text = request.ToLowerInvariant();
    var category = text.Contains("invoice") || text.Contains("payment") ? "Billing"
        : text.Contains("password") || text.Contains("login") || text.Contains("account") ? "Access"
        : text.Contains("urgent") || text.Contains("production") ? "Incident" : "General";
    return new(category, "Create a support request for human review.", null,
        "Degraded mode: keyword heuristic only; confidence has not been measured.", "Heuristic", reason);

demos/Demo350_GracefulDegradation/DeterministicFallbackClassifier.cs · L7–L14 · 9a895c94534c

Code source · Demo350 · Dossier

Annuler sans basculer

Le service vérifie l’annulation avant le fallback explicite, après un résultat modèle et dans le filtre de panne. Si une panne réseau et l’annulation surviennent ensemble, l’annulation doit gagner. La console retourne alors 130 et annonce qu’aucun résultat heuristique n’a été produit. Les tests injectent un modèle qui annule puis échoue, ainsi qu’un appel déjà annulé. Ce sont des appels réels au service avec un transport contrôlé ; ils ne démontrent pas que tous les fournisseurs arrêtent immédiatement leurs calculs.

Rendre la panne observable

dotnet run --project demos/Demo350_GracefulDegradation/Demo350.App.csproj -c Release --no-build
dotnet run --project demos/Demo350_GracefulDegradation/Demo350.App.csproj -c Release --no-build --unavailable
dotnet run --project demos/Demo350_GracefulDegradation/Demo350.App.csproj -c Release --no-build --invalid
dotnet run --project demos/Demo350_GracefulDegradation/Demo350.App.csproj -c Release --no-build --timeout

Comparer les quatre JSON. Tous doivent indiquer Heuristic et null pour Confiance, mais leurs causes diffèrent : mode explicite, HttpRequestException, InvalidOperationException, TimeoutRejectedException. Le code de sortie 0 signifie ici qu’un résultat de triage dégradé a été retourné ; il n’a pas le même sens métier que Completed dans Demo330. Pour l’annulation, lancer --timeout puis Ctrl+C avant deux secondes, ou exécuter les tests de concurrence annulation/panne ci-dessous.

dotnet test tests/Maf.Control.Tests/Maf.Control.Tests.csproj -m:1 -c Release --no-build --no-restore

Évaluer le contrat réduit

La disponibilité d’un résultat typé ne garantit pas sa justesse. Les mots-clés ont des faux positifs et ratent d’autres langues ou formulations. Le client modèle est créé et libéré par demande en mode live ; le fallback reste local. L’annulation et les délais sont coopératifs. Le programme n’ajoute ni fournisseur de secours payant, ni retry, ni contrôle monétaire, ni journal métier durable.

Relier résilience et boucle bornée

Prérequis

Pour poursuivre

lot010-controls-1 · 2026-10-24 · 9a895c94534cd5fc6f3da883e37e5f7856e588c1