MICROSOFT AGENT FRAMEWORK · 0.16.0-rc.1
Plan proposé et exécution contrôlée
Lire un plan comme une proposition bornée, puis vérifier ce que l’hôte exécute réellement.
Objectifs
- Proposition de plan
- Validation et formatage
- Un plan ne s’exécute pas seul
Le plan est une donnée
La demande « Ajouter un export CSV Reporting » peut suggérer create_backlog puis respond. J2 séparait déjà proposition et exécution ; cette séparation reste essentielle. Un tableau steps décrit des intentions. Le programme doit encore décider si chaque étape est permise, si ses arguments sont recevables et quel effet il autorise. Les trois programmes de cette progression affichent une exécution simulée : retrieve ne cherche pas dans SQLite et create_backlog ne crée aucune ligne.
Du type C# au contrat de plan
Demo270 appelle RunAsync<Plan> puis parcourt Steps. Le switch reconnaît retrieve, create_backlog, ask_user et respond, et arrête au premier type inconnu. Le prompt demande deux à quatre étapes ; ce souhait ne constitue pas une validation hôte de la cardinalité ou de la cohérence. Une étape inconnue tardive peut être découverte après des étapes précédentes déjà affichées. Les sessions du planner restent en processus.
AgentResponse<Plan> planResp = await planner.RunAsync<Plan>(input, session: session, serializerOptions: jsonOpts);
var plan = planResp.Result;
Console.WriteLine("\n[PLAN]");
int i = 1;
foreach (var s in plan.Steps)
Console.WriteLine($"{i++}. {s.Type} ({s.Argument})");
Console.WriteLine("\n[EXECUTION SIMULÉE]");
Code source · Demo270 · Dossier
- Demande → modèle → proposition JSON
- Contrôle de format → validation hôte → désérialisation
- Interpréteur à liste fermée → simulation des étapes
- Un effet réel demanderait autorisation, validation métier et preuve de stockage supplémentaires
Valider tout le plan avant de parcourir les étapes
Demo280 récupère le texte brut, extrait un objet JSON en retirant éventuellement les fences et en sélectionnant de la première accolade à la dernière, puis évalue le schéma. Cet extracteur est un nettoyage limité, pas un parseur général de réponses mixtes. Le schéma interdit les propriétés supplémentaires, borne steps de un à quatre et ferme les types. argument, lorsqu’il existe, doit compter de un à 400 caractères ; il est obligatoire pour ask_user, respond et create_backlog, mais facultatif pour retrieve.
// 3) Validation JSON Schema (+ retry unique si invalide)
if (!ValidatePlanJson(jsonText, out var err))
{
Console.WriteLine("⚠️ Plan JSON invalide. Retry unique...");
Console.WriteLine(err);
var repairPrompt = $"""
Tu as renvoyé un JSON invalide ou non conforme.
Corrige-le et renvoie UNIQUEMENT un JSON valide, sans texte autour, conforme à ce schéma :
{PlanJsonSchema}
Demande utilisateur :
{input}
""";
var rawResp2 = await planner.RunAsync(message: repairPrompt, session: session);
var rawText2 = GetResponseText(rawResp2);
jsonText = ExtractJsonObject(rawText2);
if (!ValidatePlanJson(jsonText, out err))
{
Console.WriteLine("⛔ JSON toujours invalide après retry. Arrêt.");
Console.WriteLine(err);
Console.WriteLine();
continue;
}
}
// 4) Désérialisation une fois validé
Code source · Demo280 · Dossier
Une première non-conformité déclenche un seul appel de réparation. Une seconde non-conformité affiche le rejet et passe à la demande suivante sans afficher [EXECUTION SIMULÉE] pour ce plan. La désérialisation vient après le contrôle global. Cela ne prouve pas qu’un enchaînement autorisé soit pertinent : deux créations identiques peuvent respecter le schéma. Les règles de sens et les permissions restent à définir avant de brancher de vrais services.
Demander le format et revalider
Demo290 compile le schéma une fois et le réutilise. AgentRunOptions.ResponseFormat porte ForJsonSchema au moment de l’appel, y compris à la réparation. La réponse est seulement Trim, sans ExtractJsonObject. La validation hôte reste présente : le format demandé ne prouve ni le support effectif du fournisseur ni une autorisation métier. Les anciens supports qualifiaient cette variante d’industrialisable ; ici, cela désigne le renforcement de format, sans transformer la simulation en service de production.
var compiledSchema = JsonSchema.FromText(PlanJsonSchema);
using var schemaDoc = JsonDocument.Parse(PlanJsonSchema);
JsonElement schemaElement = schemaDoc.RootElement.Clone();
// ResponseFormat explicite au moment de l'appel.
var runOptions = new AgentRunOptions
{
ResponseFormat = ChatResponseFormat.ForJsonSchema(
schemaElement,
schemaName: "Plan",
schemaDescription: "Short execution plan with 1 to 4 steps")
Un plan rejeté, de manière reproductible
Dans une copie de travail de Demo280, remplacer temporairement les deux affectations jsonText issues du modèle par le même texte JSON {"steps":[{"type":"delete_all","argument":"Reporting"}]}. Ce point d’injection pédagogique est après la réponse, avant ValidatePlanJson ; il ne change pas le schéma. Compiler cette copie et fournir une demande. Attendre deux évaluations invalides, un retry unique et aucune exécution simulée pour cette demande. Répéter avec steps vide, cinq étapes, puis respond sans argument. Ne jamais modifier la distribution canonique pour cet exercice.
Pour isoler le validateur sans modèle, le contrôle local de ce lot extrait le schéma et la véritable fonction ValidatePlanJson du programme dans un exécutable de vérification privé. Il vérifie les contre-exemples et un plan valide. Ce contrôle ne teste pas la génération live ni la boucle de réparation complète ; conserver cette distinction dans le compte rendu.
Ce que la conformité ne garantit pas
Aucun des trois programmes n’ouvre un index RAG, n’appelle un outil d’écriture ou ne suspend un workflow sur ask_user. Chaque branche affiche du texte. Un futur interpréteur réel doit ajouter validation des arguments métier, contrôle d’accès, délais, gestion des échecs et idempotence des effets. Le résultat simulé ne doit pas être présenté comme un ticket créé ou une approbation obtenue.
