e-naxosMAF COURSE / COURS
Français

MICROSOFT AGENT FRAMEWORK · 0.16.0-rc.1

Degradation and cancellation

Provide an explicit degraded outcome without absorbing cancellation or inventing measured confidence.

Objectives

The dependency can fail

Triage must remain understandable when the model is unavailable, slow or returns an unusable contract. EP011 proposed a resilient facade: business code calls a service that manages the model call. The current program retains this separation, replaces the filming menu with arguments and explicitly reports Origine, CauseBascule and Confiance. Default mode is a deliberate local heuristic without a model attempt.

One bounded attempt, then a defined outcome

ResilientAgentTriageService uses Polly to bound one model attempt to 60 seconds; --timeout deliberately waits then expires after two seconds. --unavailable raises a simulated transport outage, --invalid simulates invalid output and --live calls the configured model. There is no retry or automatic provider change. A model result must have category Access, Billing, Incident or General and a nonblank decision.

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

Source code · Demo350 · Folder

The filter catches TimeoutRejectedException, OperationCanceledException, HttpRequestException, InvalidOperationException and JsonException. It first rechecks the caller token. Other exception categories propagate. This list is the current policy; it cannot distinguish every programming failure that might surface as InvalidOperationException. No repeated-failure circuit breaker is configured, despite its presence in the earlier list of possibilities.

Do less and expose provenance

Triage service paths
  1. Input + token → explicit heuristic mode or model attempt
  2. Valid model output → Origine=Model, Confiance=null
  3. Handled failure → keywords → Origine=Heuristic, visible cause, Confiance=null
  4. Caller cancellation → propagated exception, no replacement heuristic result

Fallback checks invoice/payment before password/login/account, then urgent/production; otherwise General. A request containing both “production” and “login” therefore becomes Access by rule ordering. The decision asks for human review without actually creating a request in an external system. This is limited keyword triage. Earlier capped-score wording is corrected: Confiance is null even when the model claims 0.99. Neither a deterministic heuristic nor a model-generated number is a calibrated probability.

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

Source code · Demo350 · Folder

Cancel without falling back

The service checks cancellation before explicit fallback, after a model result and in the failure filter. If network failure and cancellation occur together, cancellation must win. The console then returns 130 and states that no heuristic result was produced. Tests inject a model that cancels then fails, and an already-cancelled call. These are actual service calls with controlled transport; they do not establish that every provider immediately stops computing.

Make failure 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

Compare the four JSON outputs. All must show Heuristic and null Confiance, but causes differ: explicit mode, HttpRequestException, InvalidOperationException, TimeoutRejectedException. Exit code 0 here means a degraded triage result was returned; it does not have the same business meaning as Completed in Demo330. To cancel, run --timeout then Ctrl+C before two seconds, or run the cancellation/failure race tests below.

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

Evaluate the reduced contract

Availability of typed output does not guarantee correctness. Keywords produce false positives and miss other languages or phrasing. Live mode creates and disposes the model client per request; fallback remains local. Cancellation and timeouts are cooperative. The program adds no paid backup provider, retry, monetary control or durable business journal.

Connect resilience and bounded execution

Prerequisites

Continue reading

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