e-naxosMAF COURSE / COURS
English

MICROSOFT AGENT FRAMEWORK · 0.16.0-rc.1

Demo430 — Business Audit

Exécuter le workflow et relire l’effet local, puis comparer une panne injectée.

Objectifs

Préparer un essai isolé

Depuis la racine BaseTechnique ou du dépôt extrait, compiler la solution Release. Le projet App appelle ObservabilityDemo dans Maf.Observability ; Demo420 et Demo430 partagent cette implémentation. Le mode documenté --deterministic ne charge aucun modèle, embedding ou collecteur. Une base neuve permet de distinguer les actions de cet essai.

dotnet build BaseTechnique.slnx -m:1 -c Release

Lancer sans modèle

$journal = Join-Path $env:TEMP ("maf-lot012-" + [guid]::NewGuid().ToString("N") + ".db")
dotnet run --project demos/Demo430_BusinessAudit/Demo430.App.csproj -c Release --no-build -- --deterministic --db $journal

Noter ExecutionId, TraceId et Database. Attendre le workflow accepté puis ActionCommitted et ExecutionSucceeded. Les Trace: sont des JSON SafeSpan ; les Workflow event: sont des événements de progression. Dans ce mode, aucune consommation modèle n’est mesurée. Les identifiants et durées changent à chaque exécution.

public long CommitReview(Guid executionId)
{
    using var connection = Open();
    using var transaction = connection.BeginTransaction();
    using var command = connection.CreateCommand();
    command.Transaction = transaction;
    command.CommandText = "INSERT INTO RecordedDecisions(ExecutionId,Decision) VALUES($id,'ReviewRequested') ON CONFLICT DO NOTHING";
    command.Parameters.AddWithValue("$id", executionId.ToString("N"));
    bool created = command.ExecuteNonQuery() == 1;
    command.CommandText = "SELECT Id FROM RecordedDecisions WHERE ExecutionId=$id";
    long id = (long)command.ExecuteScalar()!;
    if (created) Insert(connection, transaction, executionId, JournalKind.ActionCommitted, id);
    transaction.Commit();
    return id;

shared/Maf.Observability/BusinessJournal.cs · L58–L71 · 9a895c94534c

Code source · Maf.Observability · Dossier

Injecter une panne et relire

dotnet run --project demos/Demo430_BusinessAudit/Demo430.App.csproj -c Release --no-build -- --deterministic --fail --db $journal

Cette nouvelle exécution a un autre GUID. --fail interrompt immédiatement avant CommitReview : sortie 1, Intention puis ExecutionFailed, aucun ActionCommitted pour ce GUID. Les actions antérieures restent présentes. --read attend un GUID existant ; remplacer la valeur de saisie par celle de l’exécution à examiner.

$executionId = Read-Host 'ExecutionId to inspect'
dotnet run --project demos/Demo430_BusinessAudit/Demo430.App.csproj -c Release --no-build -- --db $journal --read $executionId

Sans --deterministic, le programme charge la connexion commune et appelle un vrai agent avec get_review_policy puis un reviewer natif. L’appel outil obligatoire est vérifié ; une panne ne déclenche aucune décision générée de secours. Exécuter uniquement avec le fournisseur configuré disponible ; les compteurs par requête peuvent rester null.

dotnet run --project demos/Demo430_BusinessAudit/Demo430.App.csproj -c Release --no-build -- --db $journal

Vérifier la preuve, pas seulement la sortie

Dans un nouveau processus, relire le succès et vérifier ActionId avec HasAction. Relire aussi l’échec : son code de lecture peut être 0 malgré l’absence d’action, car toutes les actions présentes ont été vérifiées et il n’y en a aucune. Lire Kind et ActionId. Les tests contrôlent reprise, rejeu du même GUID, corrélation native, secrets exclus et usage manquant.

dotnet test tests/Maf.Observability.Tests/Maf.Observability.Tests.csproj -m:1 -c Release --no-build --no-restore
public bool HasAction(Guid executionId, long actionId)
{
    using var connection = Open();
    using var command = connection.CreateCommand();
    command.CommandText = "SELECT COUNT(*) FROM RecordedDecisions WHERE ExecutionId=$id AND Id=$action";
    command.Parameters.AddWithValue("$id", executionId.ToString("N"));
    command.Parameters.AddWithValue("$action", actionId);
    return (long)command.ExecuteScalar()! == 1;

shared/Maf.Observability/BusinessJournal.cs · L103–L110 · 9a895c94534c

Code source · Maf.Observability · Dossier

Sortie observée · 2026-10-24 · lot012-journal
[
  {
    "ActionId": null,
    "ExecutionId": "50779844-cf13-4867-bce1-c4aa87657161",
    "Id": 1,
    "Kind": 0,
    "TraceId": "cde643d10fdee1c7910ad01766baba4d",
    "Utc": "2026-10-24T19:12:56.6041955+00:00"
  },
  {
    "ActionId": null,
    "ExecutionId": "50779844-cf13-4867-bce1-c4aa87657161",
    "Id": 2,
    "Kind": 1,
    "TraceId": "cde643d10fdee1c7910ad01766baba4d",
    "Utc": "2026-10-24T19:12:56.7815492+00:00"
  },
  {
    "ActionId": 1,
    "ExecutionId": "50779844-cf13-4867-bce1-c4aa87657161",
    "Id": 3,
    "Kind": 3,
    "TraceId": "cde643d10fdee1c7910ad01766baba4d",
    "Utc": "2026-10-24T19:12:56.8936463+00:00"
  },
  {
    "ActionId": null,
    "ExecutionId": "50779844-cf13-4867-bce1-c4aa87657161",
    "Id": 4,
    "Kind": 4,
    "TraceId": "cde643d10fdee1c7910ad01766baba4d",
    "Utc": "2026-10-24T19:12:56.9878252+00:00"
  }
]

Sortie réellement relue le 24 octobre 2026, sans modèle, sources de l’extraction isolée identiques au code canonique. Le sérialiseur conserve Kind sous forme numérique : 0=RequestReceived, 1=Intention, 3=ActionCommitted, 4=ExecutionSucceeded. Le programme de contrôle a aussi relu RecordedDecisions dans SQLite et vérifié le même ActionId. Les GUID, timestamps UTC et TraceId appartiennent à cet essai ; ils ne sont pas à recopier dans une nouvelle commande.

Préserver la portée locale

Le commit local porte ReviewRequested ; il ne supprime, rembourse ou crée aucun effet distant. --erase supprime seulement les métadonnées de l’exécution choisie et n’est pas une annulation métier. Le journal n’est pas immuable. Une redirection de console est sous la responsabilité de son propriétaire ; aucun fichier brut n’est publié sur le site.

Suivre le code partagé

public long CommitReview(Guid executionId)
{
    using var connection = Open();
    using var transaction = connection.BeginTransaction();
    using var command = connection.CreateCommand();
    command.Transaction = transaction;
    command.CommandText = "INSERT INTO RecordedDecisions(ExecutionId,Decision) VALUES($id,'ReviewRequested') ON CONFLICT DO NOTHING";
    command.Parameters.AddWithValue("$id", executionId.ToString("N"));
    bool created = command.ExecuteNonQuery() == 1;
    command.CommandText = "SELECT Id FROM RecordedDecisions WHERE ExecutionId=$id";
    long id = (long)command.ExecuteScalar()!;
    if (created) Insert(connection, transaction, executionId, JournalKind.ActionCommitted, id);
    transaction.Commit();
    return id;

shared/Maf.Observability/BusinessJournal.cs · L58–L71 · 9a895c94534c

Code source · Maf.Observability · Dossier

Prérequis

Pour poursuivre

Dossier complet de la démo · README

lot012-observability-1 · 2026-10-24 · 9a895c94534cd5fc6f3da883e37e5f7856e588c1