Deklarative Workflows – Übersicht

Mit deklarativen Workflows können Sie Workflowlogik mithilfe von YAML-Konfigurationsdateien definieren, anstatt programmgesteuerten Code zu schreiben. Dieser Ansatz erleichtert es Teams, Workflows zu lesen, zu ändern und zu teilen.

Overview

Mit deklarativen Workflows beschreiben Sie, was Ihr Workflow tun soll, anstatt ihn zu implementieren. Das Framework behandelt die zugrunde liegende Ausführung und konvertiert Ihre YAML-Definitionen in ausführbare Workflowdiagramme.

Wichtige Vorteile:

  • Lesbares Format: YAML-Syntax ist leicht verständlich, auch für Nichtentwickler
  • Portierbar: Workflowdefinitionen können ohne Codeänderungen freigegeben, versionsiert und geändert werden.
  • Schnelle Iteration: Ändern des Workflowverhaltens durch Bearbeiten von Konfigurationsdateien
  • Konsistente Struktur: Vordefinierte Aktionstypen stellen sicher, dass Workflows bewährte Methoden befolgen

Wann man deklarative gegenüber programmatischen Workflows verwenden sollte

Scenario Empfohlener Ansatz
Standard-Orchestrierungsmuster Deklarativ
Workflows, die sich häufig ändern Deklarativ
Nichtentwickler müssen Workflows ändern Deklarativ
Komplexe benutzerdefinierte Logik Programmgesteuert
Maximale Flexibilität und Kontrolle Programmgesteuert
Integration mit vorhandenem Python-Code Programmgesteuert

Grundlegende YAML-Struktur

Die YAML-Struktur unterscheidet sich geringfügig zwischen C#- und Python-Implementierungen. Ausführliche Informationen finden Sie in den unten aufgeführten sprachspezifischen Abschnitten.

Aktionstypen

Deklarative Workflows unterstützen eine Vielzahl von Aktionsarten, die Variablenverwaltung, Steuerungsfluss, Agenten- und Toolaufrufe, HTTP- und MCP-Integration, Human-in-the-Loop und Gesprächssteuerung abdecken. Der vollständige sprachspezifische Bezug wird in den folgenden Zonen angezeigt; eine Verfügbarkeitsmatrix auf einen Blick in beiden Sprachen finden Sie in der Kurzübersicht zu Aktionen am Ende dieses Artikels.

C#-YAML-Struktur

C#-deklarative Workflows verwenden eine triggerbasierte Struktur:

#
# Workflow description as a comment
#
kind: Workflow
trigger:

  kind: OnConversationStart
  id: my_workflow
  actions:

    - kind: ActionType
      id: unique_action_id
      displayName: Human readable name
      # Action-specific properties

Strukturelemente

Element Erforderlich Description
kind Yes Muss Workflow sein
trigger.kind Yes Triggertyp (in der Regel OnConversationStart)
trigger.id Yes Eindeutiger Bezeichner für den Workflow
trigger.actions Yes Liste der auszuführenden Aktionen

Python YAML-Struktur

Python-deklarative Workflows verwenden eine namensbasierte Struktur mit optionalen Eingaben:

name: my-workflow
description: A brief description of what this workflow does

inputs:
  parameterName:
    type: string
    description: Description of the parameter

actions:
  - kind: ActionType
    id: unique_action_id
    displayName: Human readable name
    # Action-specific properties

Strukturelemente

Element Erforderlich Description
name Yes Eindeutiger Bezeichner für den Workflow
description No Menschlich lesbare Beschreibung
inputs No Eingabeparameter, die der Workflow akzeptiert
actions Yes Liste der auszuführenden Aktionen

Voraussetzungen

Bevor Sie beginnen, stellen Sie sicher, dass Sie über Folgendes verfügen:

  • .NET 8.0 oder höher
  • Ein Microsoft Foundry-Projekt mit mindestens einem bereitgestellten Agent
  • Die folgenden NuGet-Pakete sind installiert:
dotnet add package Microsoft.Agents.AI.Workflows.Declarative --prerelease
dotnet add package Microsoft.Agents.AI.Workflows.Declarative.AzureAI --prerelease
  • Wenn Sie die MCP-Toolaufrufaktion zu Ihrem Workflow hinzufügen möchten, installieren Sie auch das folgende NuGet-Paket:
dotnet add package Microsoft.Agents.AI.Workflows.Declarative.Mcp --prerelease

Ihr erster deklarativer Workflow

Erstellen wir einen einfachen Workflow, der einen Benutzer basierend auf seiner Eingabe begrüßt.

Schritt 1: Erstellen der YAML-Datei

Erstellen einer Datei mit dem Namen greeting-workflow.yaml:

#
# This workflow demonstrates a simple greeting based on user input.
# The user's message is captured via System.LastMessage.
#
# Example input: 
# Alice
#
kind: Workflow
trigger:

  kind: OnConversationStart
  id: greeting_workflow
  actions:

    # Capture the user's input from the last message
    - kind: SetVariable
      id: capture_name
      displayName: Capture user name
      variable: Local.userName
      value: =System.LastMessage.Text

    # Set a greeting prefix
    - kind: SetVariable
      id: set_greeting
      displayName: Set greeting prefix
      variable: Local.greeting
      value: Hello

    # Build the full message using an expression
    - kind: SetVariable
      id: build_message
      displayName: Build greeting message
      variable: Local.message
      value: =Concat(Local.greeting, ", ", Local.userName, "!")

    # Send the greeting to the user
    - kind: SendActivity
      id: send_greeting
      displayName: Send greeting to user
      activity: =Local.message

Schritt 2: Konfigurieren des Agentanbieters

Erstellen Sie eine C#-Konsolenanwendung, um den Workflow auszuführen. Konfigurieren Sie zuerst den Agentanbieter, der eine Verbindung mit Foundry herstellt:

using Azure.Identity;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Agents.AI.Workflows.Declarative;
using Microsoft.Extensions.Configuration;

// Load configuration (endpoint should be set in user secrets or environment variables)
IConfiguration configuration = new ConfigurationBuilder()
    .AddUserSecrets<Program>()
    .AddEnvironmentVariables()
    .Build();

string foundryEndpoint = configuration["FOUNDRY_PROJECT_ENDPOINT"] 
    ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT not configured");

// Create the agent provider that connects to Foundry
// WARNING: DefaultAzureCredential is convenient for development but requires 
// careful consideration in production environments.
AzureAgentProvider agentProvider = new(
    new Uri(foundryEndpoint), 
    new DefaultAzureCredential());

Schritt 3: Erstellen und Ausführen des Workflows

// Define workflow options with the agent provider
DeclarativeWorkflowOptions options = new(agentProvider)
{
    Configuration = configuration,
    // LoggerFactory = loggerFactory, // Optional: Enable logging
    // ConversationId = conversationId, // Optional: Continue existing conversation
};

// Build the workflow from the YAML file
string workflowPath = Path.Combine(AppContext.BaseDirectory, "greeting-workflow.yaml");
Workflow workflow = DeclarativeWorkflowBuilder.Build<string>(workflowPath, options);

Console.WriteLine($"Loaded workflow from: {workflowPath}");
Console.WriteLine(new string('-', 40));

// Create a checkpoint manager (in-memory for this example)
CheckpointManager checkpointManager = CheckpointManager.CreateInMemory();

// Execute the workflow with input
string input = "Alice";
StreamingRun run = await InProcessExecution.RunStreamingAsync(
    workflow, 
    input, 
    checkpointManager);

// Process workflow events
await foreach (WorkflowEvent workflowEvent in run.WatchStreamAsync())
{
    switch (workflowEvent)
    {
        case AgentResponseEvent responseEvent:
            Console.WriteLine($"Response: {responseEvent.Response.Text}");
            break;
        case WorkflowErrorEvent errorEvent:
            Console.WriteLine($"Error: {errorEvent.Data}");
            break;
    }
}

Console.WriteLine("Workflow completed!");

Erwartete Ausgabe

Loaded workflow from: C:\path\to\greeting-workflow.yaml
----------------------------------------
Response: Hello, Alice!
Workflow completed!

Kernkonzepte

Variablen-Namensräume

Deklarative Workflows in C# verwenden Namespace-Variablen zum Organisieren des Zustands:

Namespace Description Example
Local.* Lokale Variablen für den Workflow Local.message
System.* Vom System bereitgestellte Werte System.ConversationId, System.LastMessage

Note

C#-deklarative Workflows verwenden keine Workflow.Inputs oder Workflow.Outputs Namespaces. Die Eingabe wird über System.LastMessage empfangen und die Ausgabe wird über SendActivity gesendet.

Systemvariablen

Variable Description
System.ConversationId Aktuelle Gesprächs-ID
System.LastMessage Die letzte Benutzernachricht
System.LastMessage.Text Textinhalt der letzten Nachricht

Ausdruckssprache

Mit der PowerFx-Ausdruckssprache präfixierte = Werte werden als Ausdrücke ausgewertet:

# Literal value (no evaluation)
value: Hello

# Expression (evaluated at runtime)
value: =Concat("Hello, ", Local.userName)

# Access last message text
value: =System.LastMessage.Text

Zu den allgemeinen Funktionen gehören:

  • Concat(str1, str2, ...) - Verketten von Zeichenfolgen
  • If(condition, trueValue, falseValue) - Bedingter Ausdruck
  • IsBlank(value) - Überprüfen, ob der Wert leer ist
  • Upper(text) / Lower(text) - Fallkonvertierung
  • Find(searchText, withinText) - Suchen von Text in einer Zeichenfolge
  • MessageText(message) - Extrahieren von Text aus einem Nachrichtenobjekt
  • UserMessage(text) – Erstellen einer Benutzernachricht aus Text
  • AgentMessage(text) – Erstellen einer Agentnachricht aus Text

Konfigurationsoptionen

Die DeclarativeWorkflowOptions Klasse stellt konfiguration für die Workflowausführung bereit:

DeclarativeWorkflowOptions options = new(agentProvider)
{
    // Application configuration for variable substitution
    Configuration = configuration,

    // Continue an existing conversation (optional)
    ConversationId = "existing-conversation-id",

    // Enable logging (optional)
    LoggerFactory = loggerFactory,

    // MCP tool handler for InvokeMcpTool actions (optional)
    McpToolHandler = mcpToolHandler,

    // HTTP request handler for HttpRequestAction actions (optional)
    HttpRequestHandler = new DefaultHttpRequestHandler(),

    // PowerFx expression limits (optional)
    MaximumCallDepth = 50,
    MaximumExpressionLength = 10000,

    // Telemetry configuration (optional)
    ConfigureTelemetry = opts => { /* configure telemetry */ },
    TelemetryActivitySource = activitySource,
};

Agent-Anbietersetup

Der AzureAgentProvider verbindet Ihren Workflow mit den Foundry-Agents.

using Azure.Identity;
using Microsoft.Agents.AI.Workflows.Declarative;

// Create the agent provider with Azure credentials
AzureAgentProvider agentProvider = new(
    new Uri("https://your-project.api.azureml.ms"), 
    new DefaultAzureCredential())
{
    // Optional: Define functions that agents can automatically invoke
    Functions = [
        AIFunctionFactory.Create(myPlugin.GetData),
        AIFunctionFactory.Create(myPlugin.ProcessItem),
    ],

    // Optional: Allow concurrent function invocation
    AllowConcurrentInvocation = true,

    // Optional: Allow multiple tool calls per response
    AllowMultipleToolCalls = true,
};

Workflowausführung

Verwenden Sie InProcessExecution, um Workflows auszuführen und Ereignisse zu verarbeiten. In diesem Beispiel werden abgeschlossene Antworten angezeigt und Eingabeanforderungen separat behandelt. Außerdem werden keine Streamingupdates oder Aktivitätsbenachrichtigungen für dieselbe Antwort angezeigt:

using Microsoft.Agents.AI.Workflows;
using Microsoft.Agents.AI.Workflows.Checkpointing;

// Create checkpoint manager (choose in-memory or file-based)
CheckpointManager checkpointManager = CheckpointManager.CreateInMemory();
// Or persist to disk:
// var checkpointFolder = Directory.CreateDirectory("./checkpoints");
// var checkpointManager = CheckpointManager.CreateJson(
//     new FileSystemJsonCheckpointStore(checkpointFolder));

// Start workflow execution
StreamingRun run = await InProcessExecution.RunStreamingAsync(
    workflow, 
    input, 
    checkpointManager);

// Process events as they occur
await foreach (WorkflowEvent workflowEvent in run.WatchStreamAsync())
{
    switch (workflowEvent)
    {
        case AgentResponseEvent response:
            Console.WriteLine($"Agent: {response.Response.Text}");
            break;

        case RequestInfoEvent request:
            // Handle external input requests (human-in-the-loop)
            var userInput = await GetUserInputAsync(request);
            await run.SendResponseAsync(request.Request.CreateResponse(userInput));
            break;

        case SuperStepCompletedEvent checkpoint:
            // Checkpoint created - can resume from here if needed
            var checkpointInfo = checkpoint.CompletionInfo?.Checkpoint;
            break;

        case WorkflowErrorEvent error:
            Console.WriteLine($"Error: {error.Data}");
            break;
    }
}

Auswählen der anzuzeigenden Antwortereignisse

Ein Ereignisstrom eines Workflows enthält mehrere Ansichten derselben Aktion. Eine Textaktion SendActivity erzeugt z. B. Folgendes:

Event Purpose
MessageActivityEvent Eine Aktivitätsbenachrichtigung, die den Nachrichtentext enthält.
AgentResponseUpdateEvent Ein AgentResponseUpdate mit Text für Streaming-Clients.
AgentResponseEvent Die abgeschlossene AgentResponse, als Workflowausgabe verfügbar.

Diese Ereignisse stellen keine drei separaten Nachrichten oder drei Ausführungen der Aktion dar. Beim Drucken des Texts aus jedem Ereignis wird dieselbe Meldung wiederholt angezeigt.

Es gibt keine YAML-Einstellung für SendActivity oder Question, die AgentResponseEvent anstelle von AgentResponseUpdateEvent auswählt. Wählen Sie aus, was der C#-Ereignishandler anzeigt:

  • Für abgeschlossene Antworten behandeln Sie AgentResponseEvent, wie im obigen Beispiel. Sie können RunStreamingAsync weiterhin verwenden, um andere Workflow-Ereignisse umgehend zu erhalten.
  • Bei gestreamtem Text AgentResponseUpdateEvent verarbeiten. Fügen Sie den Text einer abgeschlossenen Antwort nicht erneut an, wenn ihre Nachrichten bereits gestreamt wurden. Für SendActivity haben die Aktualisierung und die vollständige Antwort die Antwort-ID und die Nachrichten-ID gemeinsam. Verfolgen Sie die Executor-ID und die Nachrichten-ID beim Korrelieren von gestreamten und abgeschlossenen Nachrichten.
  • Behandeln Sie RequestInfoEvent unabhängig, damit der Workflow externe Eingaben empfangen kann. Das Filtern von Antworttext entfernt nicht die Notwendigkeit, Anforderungen zu beantworten.

Bei Verwendung von workflow.AsAIAgent() steuert das includeWorkflowOutputsInResponse-Argument, wie Workflow-Ausgaben in Agent-Antworten einbezogen werden. Es ändert nicht, welche Ereignisse eine YAML-Aktion ausgibt, und ist keine Option zum Deaktivieren von Streamingupdates.

Fortsetzen ab Prüfpunkten

Workflows können zur Gewährleistung der Fehlertoleranz von Prüfpunkten aus fortgesetzt werden.

// Save checkpoint info when workflow yields
CheckpointInfo? lastCheckpoint = null;

await foreach (WorkflowEvent workflowEvent in run.WatchStreamAsync())
{
    if (workflowEvent is SuperStepCompletedEvent checkpointEvent)
    {
        lastCheckpoint = checkpointEvent.CompletionInfo?.Checkpoint;
    }
}

// Later: Resume from the saved checkpoint
if (lastCheckpoint is not null)
{
    // Recreate the workflow (can be on a different machine)
    Workflow workflow = DeclarativeWorkflowBuilder.Build<string>(workflowPath, options);

    StreamingRun resumedRun = await InProcessExecution.ResumeStreamingAsync(
        workflow, 
        lastCheckpoint, 
        checkpointManager);

    // Continue processing events...
}

AOT und Trim-Aggressive Checkpointing

Wenn Sie mit Native AOT (dotnet publish -p:PublishAot=true) veröffentlichen oder anderweitig den Reflexionsfallback von System.Text.Json (<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>) deaktivieren, schlägt der Standardaufruf CheckpointManager.CreateJson(store) beim Prüfpunkt-Commit oder bei der Rehydrierung fehl.

Das Paket „declarative-workflow“ enthält eine quellgenerierte JsonSerializerOptions-Instanz, DeclarativeWorkflowJsonOptions.Default, die alle Typen des deklarativen Pakets abdeckt, die durch die Checkpoint-Pipeline fließen. Übergeben Sie es als zweites Argument an CheckpointManager.CreateJson:

using Microsoft.Agents.AI.Workflows.Checkpointing;
using Microsoft.Agents.AI.Workflows.Declarative;

// AOT-safe: type info is resolved via the source-generated JsonSerializerContext,
// so no runtime reflection is required.
CheckpointManager checkpointManager = CheckpointManager.CreateJson(
    store,
    DeclarativeWorkflowJsonOptions.Default);

Note

Die Übergabe von DeclarativeWorkflowJsonOptions.Default ist auch in Nicht-AOT-Umgebungen sicher zu verwenden. Es handelt sich um ein Drop-In-Upgrade für CheckpointManager.CreateJson(store) – Spiegelungsfähige Apps sehen keine Verhaltensänderung. Verwenden Sie es konsequent, damit derselbe Code auch dann weiterhin funktioniert, wenn Sie später mit AOT oder Trimming bereitstellen.

DeclarativeWorkflowJsonOptions ist als [Experimental("MAAI001")] markiert. Unterdrücken Sie die Diagnose an der Aufrufstelle oder in Ihrer Projektdatei:

<PropertyGroup>
  <NoWarn>$(NoWarn);MAAI001</NoWarn>
</PropertyGroup>

Registrieren benutzerdefinierter Typen

Wenn die Eingaben Ihres Workflows, benutzerdefinierte ActionExecutorResult.Result Payloads oder nicht-primitive Argumente von Genehmigungsanforderungen benutzerdefinierte Typen sind, klonen Sie Default und fügen Sie Ihren eigenen quellgenerierten Resolver an:

// Compose: declarative-package types + your app's source-gen context.
JsonSerializerOptions options = new(DeclarativeWorkflowJsonOptions.Default);
options.TypeInfoResolverChain.Add(MyAppJsonContext.Default);
options.MakeReadOnly();

CheckpointManager checkpointManager = CheckpointManager.CreateJson(store, options);

Dabei ist MyAppJsonContext ein JsonSerializerContext, den Sie für die Typen Ihrer App definieren:

[JsonSourceGenerationOptions(JsonSerializerDefaults.Web)]
[JsonSerializable(typeof(MyWorkflowInput))]
[JsonSerializable(typeof(MyCustomResult))]
internal sealed partial class MyAppJsonContext : JsonSerializerContext;

Tip

Ein durchgängig ausführbares End-to-End-Beispiel – einschließlich des YAML-Workflows, eines auf AzureCliCredential basierenden Agenten und eines beobachtbaren Modus „Optionen weglassen, um den Fehler zu sehen“ – finden Sie im AotCheckpointing-Beispiel in dotnet/samples/03-workflows/Declarative/AotCheckpointing. Die Probe .csproj setzt JsonSerializerIsReflectionEnabledByDefault=false, um den AOT-Fehlermodus zu reproduzieren, ohne dass eine vollständige AOT-Veröffentlichung erforderlich ist.

Aktionsreferenz

Aktionen sind die Bausteine deklarativer Workflows. Jede Aktion führt einen bestimmten Vorgang aus, und Aktionen werden sequenziell in der Reihenfolge ausgeführt, in der sie in der YAML-Datei angezeigt werden.

Aktionsstruktur

Alle Aktionen teilen gemeinsame Eigenschaften:

- kind: ActionType      # Required: The type of action
  id: unique_id         # Optional: Unique identifier for referencing
  displayName: Name     # Optional: Human-readable name for logging
  # Action-specific properties...

Aktionen zur Variablenverwaltung

VariableSetzen

Legt eine Variable auf einen angegebenen Wert fest.

- kind: SetVariable
  id: set_greeting
  displayName: Set greeting message
  variable: Local.greeting
  value: Hello World

Mit einem Ausdruck:

- kind: SetVariable
  variable: Local.fullName
  value: =Concat(Local.firstName, " ", Local.lastName)

Eigenschaften:

Property Erforderlich Description
variable Yes Variabler Pfad (z. B. Local.name, Workflow.Outputs.result)
value Yes Zu setzender Wert (Literal oder Ausdruck)

SetMultipleVariables

Legt mehrere Variablen in einer einzelnen Aktion fest.

- kind: SetMultipleVariables
  id: initialize_vars
  displayName: Initialize variables
  variables:
    Local.counter: 0
    Local.status: pending
    Local.message: =Concat("Processing order ", Local.orderId)

Eigenschaften:

Property Erforderlich Description
variables Yes Karte von variablen Pfaden zu Werten

SetTextVariable

Legt eine Textvariable auf einen angegebenen Zeichenfolgenwert fest.

- kind: SetTextVariable
  id: set_text
  displayName: Set text content
  variable: Local.description
  value: This is a text description

Eigenschaften:

Property Erforderlich Description
variable Yes Variabler Pfad für den Textwert
value Yes Festzulegenden Textwert

VariableZurücksetzen

Löscht den Wert einer Variablen.

- kind: ResetVariable
  id: clear_counter
  variable: Local.counter

Eigenschaften:

Property Erforderlich Description
variable Yes Variabler Pfad zum Zurücksetzen

ClearAllVariables

Setzt alle Variablen im aktuellen Kontext zurück.

- kind: ClearAllVariables
  id: clear_all
  displayName: Clear all workflow variables

ParseValue

Extrahiert oder konvertiert Daten in ein verwendbares Format.

- kind: ParseValue
  id: parse_json
  displayName: Parse JSON response
  source: =Local.rawResponse
  variable: Local.parsedData

Eigenschaften:

Property Erforderlich Description
source Yes Ausdruck, der den zu analysierenden Wert zurückgibt
variable Yes Variabler Pfad zum Speichern des analysierten Ergebnisses

EditTableV2

Ändert Daten in einem strukturierten Tabellenformat.

- kind: EditTableV2
  id: update_table
  displayName: Update configuration table
  table: Local.configTable
  operation: update
  row:
    key: =Local.settingName
    value: =Local.settingValue

Eigenschaften:

Property Erforderlich Description
table Yes Variabler Pfad zur Tabelle
operation Yes Vorgangstyp (Hinzufügen, Aktualisieren, Löschen)
row Yes Zeilendaten für den Vorgang

Ablaufsteuerungsaktionen

Wenn

Führt Aktionen bedingt basierend auf einer Bedingung aus.

- kind: If
  id: check_age
  displayName: Check user age
  condition: =Local.age >= 18
  then:
    - kind: SendActivity
      activity:
        text: "Welcome, adult user!"
  else:
    - kind: SendActivity
      activity:
        text: "Welcome, young user!"

Eigenschaften:

Property Erforderlich Description
condition Yes Ausdruck, der als wahr/falsch ausgewertet wird
then Yes Auszuführende Aktionen, wenn die Bedingung wahr ist
else No Auszuführende Aktionen, wenn die Bedingung falsch ist

ConditionGroup

Wertet mehrere Bedingungen wie eine Switch/Case-Anweisung aus.

- kind: ConditionGroup
  id: route_by_category
  displayName: Route based on category
  conditions:
    - condition: =Local.category = "electronics"
      id: electronics_branch
      actions:
        - kind: SetVariable
          variable: Local.department
          value: Electronics Team
    - condition: =Local.category = "clothing"
      id: clothing_branch
      actions:
        - kind: SetVariable
          variable: Local.department
          value: Clothing Team
  elseActions:
    - kind: SetVariable
      variable: Local.department
      value: General Support

Eigenschaften:

Property Erforderlich Description
conditions Yes Liste der Bedingungs-/Aktionspaare (erster Treffer gewinnt)
elseActions No Aktionen, wenn keine Bedingung übereinstimmt

Foreach

Durchläuft eine Sammlung.

- kind: Foreach
  id: process_items
  displayName: Process each item
  source: =Local.items
  itemName: item
  indexName: index
  actions:
    - kind: SendActivity
      activity:
        text: '=Concat("Processing item ", index, ": ", item)'

Eigenschaften:

Property Erforderlich Description
source Yes Ausdruck, der eine Sammlung zurückgibt
itemName No Variabler Name für aktuelles Element (Standard: item)
indexName No Variablenname für den aktuellen Index (Standard: index)
actions Yes Auszuführende Aktionen für jedes Element

BreakLoop

Beendet die aktuelle Schleife sofort.

- kind: Foreach
  source: =Local.items
  actions:
    - kind: If
      condition: =item = "stop"
      then:
        - kind: BreakLoop
    - kind: SendActivity
      activity:
        text: =item

ContinueLoop

Springt zur nächsten Iteration der Schleife.

- kind: Foreach
  source: =Local.numbers
  actions:
    - kind: If
      condition: =item < 0
      then:
        - kind: ContinueLoop
    - kind: SendActivity
      activity:
        text: '=Concat("Positive number: ", item)'

GotoAction

Springt zu einer bestimmten Aktion basierend auf der ID.

- kind: SetVariable
  id: start_label
  variable: Local.attempts
  value: =Local.attempts + 1

- kind: SendActivity
  activity:
    text: =Concat("Attempt ", Local.attempts)

- kind: If
  condition: =And(Local.attempts < 3, Not(Local.success))
  then:
    - kind: GotoAction
      actionId: start_label

Eigenschaften:

Property Erforderlich Description
actionId Yes ID der Aktion, zu der gesprungen werden soll

Ausgabeaktionen

SendActivity

Sendet eine Nachricht an den Benutzer.

Eine Textaktivität erzeugt zusätzlich zu ihrer Aktivitätsbenachrichtigung sowohl ein Streaming-Update als auch eine vollständige Antwort. Siehe Auswählen, welche Antwortereignisse angezeigt werden sollen , um zu vermeiden, dass derselbe Text mehrmals angezeigt wird.

- kind: SendActivity
  id: send_welcome
  displayName: Send welcome message
  activity:
    text: "Welcome to our service!"

Mit einem Ausdruck:

- kind: SendActivity
  activity:
    text: =Concat("Hello, ", Local.userName, "! How can I help you today?")

Eigenschaften:

Property Erforderlich Description
activity Yes Die zu sendende Aktivität
activity.text Yes Nachrichtentext (wörtlich oder Ausdruck)

NachrichtSenden

Sendet dem Nutzer eine Textnachricht aus einer Vorlage. Verwenden Sie diese Option SendMessage , wenn Sie nur Text senden müssen und die vollständige Aktivitätsobjektform nicht benötigen.

- kind: SendMessage
  id: show_repo_summary
  message: "Fetched repo: visibility={Local.RepoInfo.visibility}, description={Local.RepoInfo.description}"

Eigenschaften:

Property Erforderlich Description
message Yes Zu sendenden Nachrichtentext. Verwenden Sie {Variable.Path} Platzhalter, um Workflowstatuswerte einzuschließen.

Note

Die C#-deklarative Workflowimplementierung unterstützt SendMessage. Verwenden Sie SendActivity für Python-Workflows.

Agentenaufrufaktionen

InvokeAzureAgent

Ruft einen Foundry-Agent auf.

Grundlegender Aufruf:

- kind: InvokeAzureAgent
  id: call_assistant
  displayName: Call assistant agent
  agent:
    name: AssistantAgent
  conversationId: =System.ConversationId

Mit Eingabe- und Ausgabekonfiguration:

- kind: InvokeAzureAgent
  id: call_analyst
  displayName: Call analyst agent
  agent:
    name: AnalystAgent
  conversationId: =System.ConversationId
  input:
    messages: =Local.userMessage
    arguments:
      topic: =Local.topic
  output:
    responseObject: Local.AnalystResult
    messages: Local.AnalystMessages
    autoSend: true

Mit externer Schleife (wird fortgesetzt, bis die Bedingung erfüllt ist):

- kind: InvokeAzureAgent
  id: support_agent
  agent:
    name: SupportAgent
  input:
    externalLoop:
      when: =Not(Local.IsResolved)
  output:
    responseObject: Local.SupportResult

Eigenschaften:

Property Erforderlich Description
agent.name Yes Name des registrierten Agents
conversationId No Kontextidentifikator für Unterhaltung
input.messages No Nachrichten, die an den Agent gesendet werden sollen
input.arguments No Zusätzliche Argumente für den Agent
input.externalLoop.when No Bedingung zum Fortsetzen der Agentenschleife
output.responseObject No Pfad zum Speichern der Agentenantwort
output.messages No Pfad zum Speichern von Konversationsnachrichten
output.autoSend No Automatisches Senden der Antwort an den Benutzer

Tool- und HTTP-Aktionen

InvokeFunctionTool

Ruft ein Funktionstool direkt aus dem Workflow auf, ohne einen KI-Agent durchzugehen.

- kind: InvokeFunctionTool
  id: invoke_get_data
  displayName: Get data from function
  functionName: GetUserData
  conversationId: =System.ConversationId
  requireApproval: true
  arguments:
    userId: =Local.userId
  output:
    autoSend: true
    result: Local.UserData
    messages: Local.FunctionMessages

Eigenschaften:

Property Erforderlich Description
functionName Yes Name der funktion, die aufgerufen werden soll
conversationId No Kontextidentifikator für Unterhaltung
requireApproval No Gibt an, ob vor der Ausführung eine Benutzergenehmigung erforderlich ist.
arguments No Argumente, die an die Funktion übergeben werden sollen
output.result No Pfad zum Speichern des Funktionsergebnisses
output.messages No Pfad zum Speichern von Funktionsmeldungen
output.autoSend No Automatisches Senden des Ergebnisses an den Benutzer

C#-Setup für InvokeFunctionTool:

Funktionen müssen entweder mit der WorkflowRunner registriert oder über externe Eingaben behandelt werden:

// Define functions that can be invoked
AIFunction[] functions = [
    AIFunctionFactory.Create(myPlugin.GetUserData),
    AIFunctionFactory.Create(myPlugin.ProcessOrder),
];

// Create workflow runner with functions
WorkflowRunner runner = new(functions) { UseJsonCheckpoints = true };
await runner.ExecuteAsync(workflowFactory.CreateWorkflow, input);

InvokeMcpTool

Ruft ein Tool auf einem MCP-Server (Model Context Protocol) auf.

- kind: InvokeMcpTool
  id: invoke_docs_search
  displayName: Search documentation
  serverUrl: https://learn.microsoft.com/api/mcp
  serverLabel: microsoft_docs
  toolName: microsoft_docs_search
  conversationId: =System.ConversationId
  requireApproval: false
  headers:
    X-Custom-Header: custom-value
  arguments:
    query: =Local.SearchQuery
  output:
    autoSend: true
    result: Local.SearchResults

Mit dem Verbindungsnamen für gehostete Szenarien:

- kind: InvokeMcpTool
  id: invoke_hosted_mcp
  serverUrl: https://mcp.ai.azure.com
  toolName: my_tool
  # Connection name is used in hosted scenarios to connect to a ProjectConnectionId in Foundry.
  # Note: This feature is not fully supported yet.
  connection:
    name: my-foundry-connection
  output:
    result: Local.ToolResult

Eigenschaften:

Property Erforderlich Description
serverUrl Yes URL des MCP-Servers
serverLabel No Menschlich lesbare Bezeichnung für den Server
toolName Yes Name des aufzurufenden Tools
conversationId No Kontextidentifikator für Unterhaltung
requireApproval No Gibt an, ob eine Benutzergenehmigung erforderlich ist
arguments No Argumente, die an das Tool übergeben werden sollen
headers No Benutzerdefinierte HTTP-Header für die Anforderung
connection.name No Benannte Verbindung für gehostete Szenarien (stellt eine Verbindung mit ProjectConnectionId in Foundry her; noch nicht vollständig unterstützt)
output.result No Pfad zum Speichern des Toolergebnisses
output.messages No Pfad zum Speichern von Ergebnismeldungen
output.autoSend No Automatisches Senden des Ergebnisses an den Benutzer

C#-Setup für InvokeMcpTool:

Konfigurieren Sie die McpToolHandler in Ihrer Workflow-Fabrik:

using Azure.Core;
using Azure.Identity;
using Microsoft.Agents.AI.Workflows.Declarative;

// Create MCP tool handler with authentication callback
DefaultAzureCredential credential = new();
DefaultMcpToolHandler mcpToolHandler = new(
    httpClientProvider: async (serverUrl, cancellationToken) =>
    {
        if (serverUrl.StartsWith("https://mcp.ai.azure.com", StringComparison.OrdinalIgnoreCase))
        {
            // Acquire token for Azure MCP server
            AccessToken token = await credential.GetTokenAsync(
                new TokenRequestContext(["https://mcp.ai.azure.com/.default"]),
                cancellationToken);

            HttpClient httpClient = new();
            httpClient.DefaultRequestHeaders.Authorization =
                new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token.Token);
            return httpClient;
        }

        // Return null for servers that don't require authentication
        return null;
    });

// Configure workflow factory with MCP handler
WorkflowFactory workflowFactory = new("workflow.yaml", foundryEndpoint)
{
    McpToolHandler = mcpToolHandler
};

HttpRequestAction

Sendet eine HTTP-Anforderung über die konfigurierte IHttpRequestHandler. Erfolgreiche JSON-Antworten werden vor der Zuordnung analysiert; Nicht-2xx-Antworten schlagen die Aktion fehl.

- kind: HttpRequestAction
  id: fetch_repo_info
  method: GET
  url: "https://api.github.com/repos/Microsoft/agent-framework"
  headers:
    Accept: application/vnd.github+json
    User-Agent: agent-framework
  queryParameters:
    per_page: 10
  response: Local.RepoInfo
  responseHeaders: Local.RepoHeaders

Eigenschaften:

Property Erforderlich Description
url Yes Absolute Anforderungs-URL
method No HTTP-Methode; Standardwerte für GET
headers No Anfrage-Header
queryParameters No An die URL angefügte Abfrageparameter
body No Anforderungstext; verwenden Sie kind: json, raw oder none
requestTimeoutInMilliseconds No Timeout pro Anforderung
conversationId No Fügt dem Gespräch einen erfolgreichen Antworttext hinzu
response No Pfad zum Speichern des analysierten Antworttexts
responseHeaders No Pfad zum Speichern von Antwortheadern

C#-Setup für HttpRequestAction:

Legen Sie HttpRequestHandler beim Erstellen des Workflows fest. Verwenden Sie einen benutzerdefinierten Handler, wenn Sie Wiederholungen oder eine URL-Zulassungsliste benötigen.

DeclarativeWorkflowOptions options = new(agentProvider)
{
    HttpRequestHandler = new DefaultHttpRequestHandler(),
};

Workflow workflow = DeclarativeWorkflowBuilder.Build<string>("workflow.yaml", options);

Human-in-the-Loop-Aktionen

Frage

Stellt dem Benutzer eine Frage und speichert die Antwort.

Behandeln Sie bei der direkten Workflow-Ausführung das RequestInfoEvent der Frage. Sein ExternalInputRequest.AgentResponse enthält den Prompt; die AgentResponse-Eigenschaft ist eine Nutzlast, keine eigenständige AgentResponseEvent. Geben Sie über die Anforderung ein ExternalInputResponse zurück, um den Workflow fortzusetzen. Die autoSend folgende Einstellung steuert, wie die akzeptierte Antwort zur Unterhaltung hinzugefügt wird, nicht welche Antwortereignisse ausgegeben werden.

Die C#- und Python-SDKs verwenden unterschiedliche Feldnamen für die Frageaufforderung. In diesem Beispiel wird das kleinste freigegebene Verhalten verwendet: Es fragt nach Text und speichert die Antwort im Workflowstatus.

- kind: Question
  id: ask_name
  displayName: Ask for user name
  property: Local.userName
  prompt:
    kind: Message
    text:
      - "What is your name?"
  entity:
    kind: StringPrebuiltEntity

Eigenschaften:

Property Erforderlich Description
prompt Yes Aktivitätsvorlage, die als Frage gesendet werden soll. Verwenden Sie text mit einer kind: Message-Liste für Eingabeaufforderungen für Nachrichten.
property Yes Pfad zum Speichern der extrahierten Antwort.
entity Yes Entitätstyp, der zum Analysieren und Überprüfen der Antwort verwendet wird. Verwenden Sie StringPrebuiltEntity für Freitextantworten.
alwaysPrompt No Erzwingt, dass die Frage gestellt wird, auch wenn die Zieleigenschaft bereits einen Wert aufweist.
skipQuestionMode No Steuert, ob die Eingabeaufforderung übersprungen werden soll, wenn die Zieleigenschaft bereits einen Wert aufweist. Unterstützte Werte sind AlwaysSkipIfVariableHasValue, SkipOnFirstExecutionIfVariableHasValueund AlwaysAsk.
repeatCount No Maximale Anzahl ungültiger oder nicht erkannter Antworten vor dem Speichern defaultValue.
defaultValue No Wert, der gespeichert werden soll, wenn repeatCount ohne eine gültige Antwort erreicht wird.
defaultValueResponse No Nachrichtenaktivitätsvorlage, die gesendet werden soll, wenn der Standardwert verwendet wird.
invalidPrompt No Nachrichtenaktivitätsvorlage, die gesendet werden soll, wenn der Antworttext nicht mit der konfigurierten Entität übereinstimmt.
unrecognizedPrompt No Nachrichtenaktivitätsvorlage, die gesendet werden soll, wenn die Antwort keine Nachrichten enthält.
autoSend No Fügt der Workflow-Konversation eine gültige Antwort hinzu und aktualisiert System.LastMessage. Wird standardmäßig auf true festgelegt.

RequestExternalInput

Fordert Eingaben von einem externen System oder Prozess an.

- kind: RequestExternalInput
  id: request_approval
  displayName: Request manager approval
  prompt:
    text: "Please provide approval for this request."
  variable: Local.approvalResult
  default: "pending"

Eigenschaften:

Property Erforderlich Description
prompt.text Yes Beschreibung der erforderlichen Eingabe
variable Yes Pfad zum Speichern der Eingabe
default No Standardwert

Workflowsteuerungsaktionen

EndWorkflow

Beendet die Workflowausführung.

- kind: EndWorkflow
  id: finish
  displayName: End workflow

EndConversation

Beendet die aktuelle Unterhaltung.

- kind: EndConversation
  id: end_chat
  displayName: End conversation

CreateConversation

Erstellt einen neuen Unterhaltungskontext.

- kind: CreateConversation
  id: create_new_conv
  displayName: Create new conversation
  conversationId: Local.NewConversationId

Eigenschaften:

Property Erforderlich Description
conversationId Yes Pfad zum Speicherort der neuen Gesprächs-ID

Interaktionsaktionen (nur C#)

GesprächsnachrichtHinzufügen

Fügt einem Unterhaltungsthread eine Nachricht hinzu.

- kind: AddConversationMessage
  id: add_system_message
  displayName: Add system context
  conversationId: =System.ConversationId
  message:
    role: system
    content: =Local.contextInfo

Eigenschaften:

Property Erforderlich Description
conversationId Yes Zielkonversations-Identifikator
message Yes Nachricht, die hinzugefügt werden soll
message.role Yes Nachrichtenrolle (System, Benutzer, Assistent)
message.content Yes Nachrichteninhalt

KonversationsnachrichtenKopieren

Kopiert Nachrichten aus einer Unterhaltung in eine andere.

- kind: CopyConversationMessages
  id: copy_context
  displayName: Copy conversation context
  sourceConversationId: =Local.SourceConversation
  targetConversationId: =System.ConversationId
  limit: 10

Eigenschaften:

Property Erforderlich Description
sourceConversationId Yes Quellkonversations-ID
targetConversationId Yes Zielkonversations-Identifikator
limit No Maximale Anzahl der zu kopierenden Nachrichten

UnterhaltungsnachrichtAbrufen

Ruft eine bestimmte Nachricht aus einer Unterhaltung ab.

- kind: RetrieveConversationMessage
  id: get_message
  displayName: Get specific message
  conversationId: =System.ConversationId
  messageId: =Local.targetMessageId
  variable: Local.retrievedMessage

Eigenschaften:

Property Erforderlich Description
conversationId Yes Konversations-ID
messageId Yes Nachrichtenkennung zum Abrufen
variable Yes Pfad zum Speichern der abgerufenen Nachricht

Konversationsnachrichten abrufen

Ruft mehrere Nachrichten aus einer Unterhaltung ab.

- kind: RetrieveConversationMessages
  id: get_history
  displayName: Get conversation history
  conversationId: =System.ConversationId
  limit: 20
  newestFirst: true
  variable: Local.conversationHistory

Eigenschaften:

Property Erforderlich Description
conversationId Yes Konversations-ID
limit No Maximale Anzahl abzurufender Nachrichten (Standard: 20)
newestFirst No Rückgabe in absteigender Reihenfolge
after No Cursor für Paginierung
before No Cursor für Paginierung
variable Yes Pfad zum Speichern abgerufener Nachrichten

Kurzübersicht zu Aktionen

Action Category C# Python Description
SetVariable Variable ✅ ✅ Festlegen einer einzelnen Variablen
SetMultipleVariables Variable ✅ ✅ Festlegen mehrerer Variablen
SetTextVariable Variable ✅ ✅ Festlegen einer Textvariable
ResetVariable Variable ✅ ✅ Löschen einer Variablen
ClearAllVariables Variable ✅ ✅ Löschen aller Variablen
ParseValue Variable ✅ ✅ Analysieren/Transformieren von Daten
EditTableV2 Variable ✅ ✅ Ändern von Tabellendaten
If Ablaufsteuerung ✅ ✅ Bedingungsverzweigung
ConditionGroup Ablaufsteuerung ✅ ✅ Mehrzweigschalter
Foreach Ablaufsteuerung ✅ ✅ Über die Sammlung iterieren
BreakLoop Ablaufsteuerung ✅ ✅ Beenden der aktuellen Schleife
ContinueLoop Ablaufsteuerung ✅ ✅ Zur nächsten Iteration springen
GotoAction Ablaufsteuerung ✅ ✅ Zur Aktion mit ID springen
SendActivity Output ✅ ✅ Nachricht an Benutzer senden
SendMessage Output ✅ ❌ Senden einer textvorlagenbasierten Nachricht
InvokeAzureAgent Agent ✅ ✅ Azure AI-Agent anrufen
InvokeFunctionTool Tool ✅ ✅ Direktes Aufrufen der Funktion
InvokeMcpTool Tool ✅ ✅ Aufrufen des MCP-Servertools
HttpRequestAction HTTP ✅ ✅ HTTP-Endpunkt aufrufen
Question Human-in-the-Loop ✅ ✅ Benutzer eine Frage stellen
RequestExternalInput Human-in-the-Loop ✅ ✅ Anfordern externer Eingaben
EndWorkflow Workflowsteuerung ✅ ✅ Workflow beenden
EndConversation Workflowsteuerung ✅ ✅ Unterhaltung beenden
CreateConversation Workflowsteuerung ✅ ✅ Neue Unterhaltung erstellen
AddConversationMessage Gespräch ✅ ❌ Hinzufügen einer Nachricht zum Thread
CopyConversationMessages Gespräch ✅ ❌ Kopieren von Nachrichten
RetrieveConversationMessage Gespräch ✅ ❌ Abrufen einer einzelnen Nachricht
RetrieveConversationMessages Gespräch ✅ ❌ Abrufen mehrerer Nachrichten

Erweiterte Muster

Multi-Agent-Orchestrierung

Sequenzielle Agentenpipeline

Übergeben Sie die Arbeit durch mehrere Agenten in einer Reihenfolge.

#
# Sequential agent pipeline for content creation
#
kind: Workflow
trigger:

  kind: OnConversationStart
  id: content_workflow
  actions:

    # First agent: Research
    - kind: InvokeAzureAgent
      id: invoke_researcher
      displayName: Research phase
      conversationId: =System.ConversationId
      agent:
        name: ResearcherAgent

    # Second agent: Write draft
    - kind: InvokeAzureAgent
      id: invoke_writer
      displayName: Writing phase
      conversationId: =System.ConversationId
      agent:
        name: WriterAgent

    # Third agent: Edit
    - kind: InvokeAzureAgent
      id: invoke_editor
      displayName: Editing phase
      conversationId: =System.ConversationId
      agent:
        name: EditorAgent

C#-Setup:

using Azure.AI.Projects;
using Azure.AI.Projects.OpenAI;
using Azure.Identity;

// Ensure agents exist in Foundry
AIProjectClient aiProjectClient = new(foundryEndpoint, new DefaultAzureCredential());

await aiProjectClient.CreateAgentAsync(
    agentName: "ResearcherAgent",
    agentDefinition: new DeclarativeAgentDefinition(modelName)
    {
        Instructions = "You are a research specialist..."
    },
    agentDescription: "Research agent for content pipeline");

// Create and run workflow
WorkflowFactory workflowFactory = new("content-pipeline.yaml", foundryEndpoint);
WorkflowRunner runner = new();
await runner.ExecuteAsync(workflowFactory.CreateWorkflow, "Create content about AI");

Bedingtes Agenten-Routing

Leiten Sie Anfragen basierend auf Bedingungen an verschiedene Agenten weiter.

#
# Route to specialized support agents based on category
#
kind: Workflow
trigger:

  kind: OnConversationStart
  id: support_router
  actions:

    # Capture category from user input or set via another action
    - kind: SetVariable
      id: set_category
      variable: Local.category
      value: =System.LastMessage.Text

    - kind: ConditionGroup
      id: route_request
      displayName: Route to appropriate agent
      conditions:
        - condition: =Local.category = "billing"
          id: billing_route
          actions:
            - kind: InvokeAzureAgent
              id: billing_agent
              agent:
                name: BillingAgent
              conversationId: =System.ConversationId
        - condition: =Local.category = "technical"
          id: technical_route
          actions:
            - kind: InvokeAzureAgent
              id: technical_agent
              agent:
                name: TechnicalAgent
              conversationId: =System.ConversationId
      elseActions:
        - kind: InvokeAzureAgent
          id: general_agent
          agent:
            name: GeneralAgent
          conversationId: =System.ConversationId

Tool-Integrationsmuster

Vorabrufen von Daten mit InvokeFunctionTool

Abrufen von Daten vor dem Aufrufen eines Agents:

#
# Pre-fetch menu data before agent interaction
#
kind: Workflow
trigger:

  kind: OnConversationStart
  id: menu_workflow
  actions:
    # Pre-fetch today's specials
    - kind: InvokeFunctionTool
      id: get_specials
      functionName: GetSpecials
      requireApproval: true
      output:
        autoSend: true
        result: Local.Specials

    # Agent uses pre-fetched data
    - kind: InvokeAzureAgent
      id: menu_agent
      conversationId: =System.ConversationId
      agent:
        name: MenuAgent
      input:
        messages: '=UserMessage("Describe today''s specials: " & Local.Specials)'

MCP-Tool-Integration

Aufrufen des externen Servers mithilfe von MCP:

#
# Search documentation using MCP
#
kind: Workflow
trigger:

  kind: OnConversationStart
  id: docs_search
  actions:

    - kind: SetVariable
      variable: Local.SearchQuery
      value: =System.LastMessage.Text

    # Search Microsoft Learn
    - kind: InvokeMcpTool
      id: search_docs
      serverUrl: https://learn.microsoft.com/api/mcp
      toolName: microsoft_docs_search
      conversationId: =System.ConversationId
      arguments:
        query: =Local.SearchQuery
      output:
        result: Local.SearchResults
        autoSend: true

    # Summarize results with agent
    - kind: InvokeAzureAgent
      id: summarize
      agent:
        name: SummaryAgent
      conversationId: =System.ConversationId
      input:
        messages: =UserMessage("Summarize these search results")

Voraussetzungen

Bevor Sie beginnen, stellen Sie sicher, dass Sie über Folgendes verfügen:

  • Python 3.10 - 3.13 (Python 3.14 wird aufgrund der PowerFx-Kompatibilität noch nicht unterstützt)
  • Das deklarative Agent Framework-Paket wurde installiert:
pip install agent-framework-declarative --pre

Dieses Paket zieht das zugrundeliegende agent-framework-core automatisch hinzu.

Ihr erster deklarativer Workflow

Erstellen wir einen einfachen Workflow, der einen Benutzer anhand des Namens begrüßt.

Schritt 1: Erstellen der YAML-Datei

Erstellen einer Datei mit dem Namen greeting-workflow.yaml:

name: greeting-workflow
description: A simple workflow that greets the user

inputs:
  name:
    type: string
    description: The name of the person to greet

actions:
  # Set a greeting prefix
  - kind: SetVariable
    id: set_greeting
    displayName: Set greeting prefix
    variable: Local.greeting
    value: Hello

  # Build the full message using an expression
  - kind: SetVariable
    id: build_message
    displayName: Build greeting message
    variable: Local.message
    value: =Concat(Local.greeting, ", ", Workflow.Inputs.name, "!")

  # Send the greeting to the user
  - kind: SendActivity
    id: send_greeting
    displayName: Send greeting to user
    activity:
      text: =Local.message

  # Store the result in outputs
  - kind: SetVariable
    id: set_output
    displayName: Store result in outputs
    variable: Workflow.Outputs.greeting
    value: =Local.message

Schritt 2: Laden und Ausführen des Workflows

Erstellen Sie eine Python-Datei zum Ausführen des Workflows:

import asyncio
from pathlib import Path

from agent_framework.declarative import WorkflowFactory


async def main() -> None:
    """Run the greeting workflow."""
    # Create a workflow factory
    factory = WorkflowFactory()

    # Load the workflow from YAML
    workflow_path = Path(__file__).parent / "greeting-workflow.yaml"
    workflow = factory.create_workflow_from_yaml_path(workflow_path)

    print(f"Loaded workflow: {workflow.name}")
    print("-" * 40)

    # Run with a name input
    result = await workflow.run({"name": "Alice"})
    for output in result.get_outputs():
        print(f"Output: {output}")
    for output in result.get_intermediate_outputs():
        print(f"Intermediate: {output}")


if __name__ == "__main__":
    asyncio.run(main())

Erwartete Ausgabe

Loaded workflow: greeting-workflow
----------------------------------------
Output: Hello, Alice!

Kernkonzepte

Variablen-Namensräume

Deklarative Workflows verwenden Namespace-Variablen zum Organisieren des Zustands:

Namespace Description Example
Local.* Lokale Variablen für den Workflow Local.message
Workflow.Inputs.* Eingabeparameter Workflow.Inputs.name
Workflow.Outputs.* Ausgabewerte Workflow.Outputs.result
System.* Vom System bereitgestellte Werte System.ConversationId

Ausdruckssprache

Werte, denen = das Präfix vorangestellt ist, werden als Ausdrücke ausgewertet:

# Literal value (no evaluation)
value: Hello

# Expression (evaluated at runtime)
value: =Concat("Hello, ", Workflow.Inputs.name)

Zu den allgemeinen Funktionen gehören:

  • Concat(str1, str2, ...) - Verketten von Zeichenfolgen
  • If(condition, trueValue, falseValue) - Bedingter Ausdruck
  • IsBlank(value) - Überprüfen, ob der Wert leer ist

Grenzen der Zustandstraversierung in Power Fx

Python überprüft den deklarativen Zustand vor Schreib- und Momentaufnahmen des Zustands und bevor Werte in PowerFx-Symbole konvertiert werden. Zyklische Strukturen werden abgelehnt, statt gekürzt zu werden.

Bei jeder Durchquerung werden die folgenden festen Grenzwerte verwendet:

  • Eine maximale Tiefe von 64, wobei der Wurzelwert die Tiefe 0 hat.
  • Maximal 10.000 besuchte Werte, einschließlich Containern und Zuordnungsschlüsseln.
  • Eine maximale Gesamtgröße von 1.048.576 Zeichen in Zeichenfolgen und Binärbytes.

Wiederholte Verweise und Aliase werden jedes Mal erneut gezählt, wenn sie durchlaufen werden. Ein Zyklus oder ein überschrittenes Limit löst ValueError beim Schreiben eines Zustands oder Erstellen einer Momentaufnahme sowie bei der Power Fx-Konvertierung aus. Diese Traversierungsgrenzen begrenzen weder die Ausführung von Power Fx-Ausdrücken noch anwendungsdefinierte Python-Kopier- oder Konvertierungs-Hooks.

Aktionstypen

Deklarative Workflows unterstützen verschiedene Aktionstypen:

Category Actions
Die Variableverwaltung SetVariable, SetMultipleVariablesResetVariable
Ablaufsteuerung If, ConditionGroup, Foreach, BreakLoop, , ContinueLoop, GotoAction
Output SendActivity
Agent-Aufruf InvokeAzureAgent
Toolaufruf InvokeFunctionTool, InvokeMcpTool
HTTP HttpRequestAction
Human-in-the-Loop Question, RequestExternalInput
Workflowsteuerung EndWorkflow, EndConversationCreateConversation

Aktionsreferenz

Aktionen sind die Bausteine deklarativer Workflows. Jede Aktion führt einen bestimmten Vorgang aus, und Aktionen werden sequenziell in der Reihenfolge ausgeführt, in der sie in der YAML-Datei angezeigt werden.

Aktionsstruktur

Alle Aktionen teilen gemeinsame Eigenschaften:

- kind: ActionType      # Required: The type of action
  id: unique_id         # Optional: Unique identifier for referencing
  displayName: Name     # Optional: Human-readable name for logging
  # Action-specific properties...

Aktionen zur Variablenverwaltung

VariableSetzen

Legt eine Variable auf einen angegebenen Wert fest.

- kind: SetVariable
  id: set_greeting
  displayName: Set greeting message
  variable: Local.greeting
  value: Hello World

Mit einem Ausdruck:

- kind: SetVariable
  variable: Local.fullName
  value: =Concat(Workflow.Inputs.firstName, " ", Workflow.Inputs.lastName)

Eigenschaften:

Property Erforderlich Description
variable Yes Variabler Pfad (z. B. Local.name, Workflow.Outputs.result)
value Yes Zu setzender Wert (Literal oder Ausdruck)

Note

Python unterstützt auch die Aktionsart SetValue, bei der path anstelle von variable für die Zieleigenschaft verwendet wird. Sowohl SetVariable (mit variable) als auch SetValue (mit path) erzielen dasselbe Ergebnis. Beispiel:

- kind: SetValue
  id: set_greeting
  path: Local.greeting
  value: Hello World

SetMultipleVariables

Legt mehrere Variablen in einer einzelnen Aktion fest.

- kind: SetMultipleVariables
  id: initialize_vars
  displayName: Initialize variables
  variables:
    Local.counter: 0
    Local.status: pending
    Local.message: =Concat("Processing order ", Workflow.Inputs.orderId)

Eigenschaften:

Property Erforderlich Description
variables Yes Karte von variablen Pfaden zu Werten

VariableZurücksetzen

Löscht den Wert einer Variablen.

- kind: ResetVariable
  id: clear_counter
  variable: Local.counter

Eigenschaften:

Property Erforderlich Description
variable Yes Variabler Pfad zum Zurücksetzen

Ablaufsteuerungsaktionen

Wenn

Führt Aktionen bedingt basierend auf einer Bedingung aus.

- kind: If
  id: check_age
  displayName: Check user age
  condition: =Workflow.Inputs.age >= 18
  then:
    - kind: SendActivity
      activity:
        text: "Welcome, adult user!"
  else:
    - kind: SendActivity
      activity:
        text: "Welcome, young user!"

Geschachtelte Bedingungen:

- kind: If
  condition: =Workflow.Inputs.role = "admin"
  then:
    - kind: SendActivity
      activity:
        text: "Admin access granted"
  else:
    - kind: If
      condition: =Workflow.Inputs.role = "user"
      then:
        - kind: SendActivity
          activity:
            text: "User access granted"
      else:
        - kind: SendActivity
          activity:
            text: "Access denied"

Eigenschaften:

Property Erforderlich Description
condition Yes Ausdruck, der als wahr/falsch ausgewertet wird
then Yes Auszuführende Aktionen, wenn die Bedingung wahr ist
else No Auszuführende Aktionen, wenn die Bedingung falsch ist

ConditionGroup

Wertet mehrere Bedingungen wie eine Switch/Case-Anweisung aus.

- kind: ConditionGroup
  id: route_by_category
  displayName: Route based on category
  conditions:
    - condition: =Workflow.Inputs.category = "electronics"
      id: electronics_branch
      actions:
        - kind: SetVariable
          variable: Local.department
          value: Electronics Team
    - condition: =Workflow.Inputs.category = "clothing"
      id: clothing_branch
      actions:
        - kind: SetVariable
          variable: Local.department
          value: Clothing Team
    - condition: =Workflow.Inputs.category = "food"
      id: food_branch
      actions:
        - kind: SetVariable
          variable: Local.department
          value: Food Team
  elseActions:
    - kind: SetVariable
      variable: Local.department
      value: General Support

Eigenschaften:

Property Erforderlich Description
conditions Yes Liste der Bedingungs-/Aktionspaare (erster Treffer gewinnt)
elseActions No Aktionen, wenn keine Bedingung übereinstimmt

Foreach

Durchläuft eine Sammlung.

- kind: Foreach
  id: process_items
  displayName: Process each item
  source: =Workflow.Inputs.items
  itemName: item
  indexName: index
  actions:
    - kind: SendActivity
      activity:
        text: '=Concat("Processing item ", index, ": ", item)'

Eigenschaften:

Property Erforderlich Description
source Yes Ausdruck, der eine Sammlung zurückgibt
itemName No Variabler Name für aktuelles Element (Standard: item)
indexName No Variablenname für den aktuellen Index (Standard: index)
actions Yes Auszuführende Aktionen für jedes Element

BreakLoop

Beendet die aktuelle Schleife sofort.

- kind: Foreach
  source: =Workflow.Inputs.items
  actions:
    - kind: If
      condition: =item = "stop"
      then:
        - kind: BreakLoop
    - kind: SendActivity
      activity:
        text: =item

ContinueLoop

Springt zur nächsten Iteration der Schleife.

- kind: Foreach
  source: =Workflow.Inputs.numbers
  actions:
    - kind: If
      condition: =item < 0
      then:
        - kind: ContinueLoop
    - kind: SendActivity
      activity:
        text: '=Concat("Positive number: ", item)'

GotoAction

Springt zu einer bestimmten Aktion basierend auf der ID.

- kind: SetVariable
  id: start_label
  variable: Local.attempts
  value: =Local.attempts + 1

- kind: SendActivity
  activity:
    text: =Concat("Attempt ", Local.attempts)

- kind: If
  condition: =And(Local.attempts < 3, Not(Local.success))
  then:
    - kind: GotoAction
      actionId: start_label

Eigenschaften:

Property Erforderlich Description
actionId Yes ID der Aktion, zu der gesprungen werden soll

Ausgabeaktionen

SendActivity

Sendet eine Nachricht an den Benutzer.

- kind: SendActivity
  id: send_welcome
  displayName: Send welcome message
  activity:
    text: "Welcome to our service!"

Mit einem Ausdruck:

- kind: SendActivity
  activity:
    text: =Concat("Hello, ", Workflow.Inputs.name, "! How can I help you today?")

Eigenschaften:

Property Erforderlich Description
activity Yes Die zu sendende Aktivität
activity.text Yes Nachrichtentext (wörtlich oder Ausdruck)

In Python wird geschriebener Text, der mit = beginnt, als Daten interpretiert und ausgegeben. Das Ausdrucksergebnis wird zur Interpolation mit {Variable.Path} nicht erneut verarbeitet. Erstellen Sie die Vorlage direkt, z. B. Hello, {Local.name}!, oder erstellen Sie die vollständige Zeichenfolge im Ausdruck, z. B. ="Hello, " & Local.name & "!".

Agentenaufrufaktionen

InvokeAzureAgent

Ruft einen Azure AI-Agent auf.

Grundlegender Aufruf:

- kind: InvokeAzureAgent
  id: call_assistant
  displayName: Call assistant agent
  agent:
    name: AssistantAgent
  conversationId: =System.ConversationId

Mit Eingabe- und Ausgabekonfiguration:

- kind: InvokeAzureAgent
  id: call_analyst
  displayName: Call analyst agent
  agent:
    name: AnalystAgent
  conversationId: =System.ConversationId
  input:
    messages: =Local.userMessage
    arguments:
      topic: =Workflow.Inputs.topic
  output:
    responseObject: Local.AnalystResult
    messages: Local.AnalystMessages
    autoSend: true

Mit externer Schleife (wird fortgesetzt, bis die Bedingung erfüllt ist):

- kind: InvokeAzureAgent
  id: support_agent
  agent:
    name: SupportAgent
  input:
    externalLoop:
      when: =Not(Local.IsResolved)
  output:
    responseObject: Local.SupportResult

Eigenschaften:

Property Erforderlich Description
agent.name Yes Name des registrierten Agents
conversationId No Kontextidentifikator für Unterhaltung
input.messages No Nachrichten, die an den Agent gesendet werden sollen
input.arguments No Argumente, die ausgewertet und dem Agenteingabetext hinzugefügt werden sollen
input.externalLoop.when No Bedingung zum Fortsetzen der Agentenschleife
output.responseObject No Pfad zum Speichern der Agentenantwort
output.messages No Pfad zum Speichern von Konversationsnachrichten
output.autoSend No Senden Sie die Antwort automatisch an die Workflow-Ausgabe. Akzeptiert einen booleschen oder einen = Ausdruck und ist standardmäßig auf true.

In Python wertet InvokeAzureAgent jeden konfigurierten input.arguments-Wert aus und formatiert nichtleere Argumente als key: value-Zeilen. Wenn Sie festlegen input.messages, folgt der ausgewertete Nachrichtentext den Argumentzeilen. Wenn input.arguments mindestens ein Argument enthält und Sie input.messages weglassen, verwendet Python nur den Argumenttext und fügt keine Fallbackwerte aus Local.input, Local.userInput, System.LastMessage.Text oder Workflow.Inputs an.

Python wertet output.autoSend vor jeder Ausführung anhand des aktuellen Workflowstatus aus, einschließlich wiederaufgenommener Durchläufe externer Schleifen. Ein False-Ergebnis unterdrückt nur die automatische Workflow-Ausgabe; der Agent wird weiterhin ausgeführt, und konfigurierte Antwort- und Nachrichtenausgaben werden weiterhin gespeichert.

Tool- und HTTP-Aktionen

InvokeFunctionTool

Ruft eine registrierte Python-Funktion direkt aus dem Workflow auf, ohne einen KI-Agent durchzugehen.

- kind: InvokeFunctionTool
  id: invoke_weather
  displayName: Get weather data
  functionName: get_weather
  arguments:
    location: =Local.location
    unit: =Local.unit
  output:
    result: Local.weatherInfo
    messages: Local.weatherToolCallItems
    autoSend: true

Eigenschaften:

Property Erforderlich Description
functionName Yes Name der registrierten Funktion, die aufgerufen werden soll
arguments No Argumente, die an die Funktion übergeben werden sollen
output.result No Pfad zum Speichern des Funktionsergebnisses
output.messages No Pfad zum Speichern von Funktionsmeldungen
output.autoSend No Senden Sie das Ergebnis automatisch an die Workflowausgabe. Akzeptiert einen booleschen oder einen = Ausdruck und ist standardmäßig auf true.

Python wertet output.autoSend unmittelbar vor der Ausführung des Tools anhand des aktuellen Workflowstatus aus. Ein falsches Ergebnis unterdrückt nur die automatische Workflowausgabe; Das Tool wird weiterhin ausgeführt, und konfigurierte Ergebnisse und Nachrichtenausgabe werden weiterhin gespeichert. Eine abgelehnte Genehmigung wird abgeschlossen, ohne den Ausdruck auszuwerten.

Python-Setup für InvokeFunctionTool:

Funktionen müssen mit WorkflowFactory unter Verwendung von register_tool registriert werden.

from agent_framework.declarative import WorkflowFactory

# Define your functions
def get_weather(location: str, unit: str = "F") -> dict:
    """Get weather information for a location."""
    # Your implementation here
    return {"location": location, "temp": 72, "unit": unit}

def format_message(template: str, data: dict) -> str:
    """Format a message template with data."""
    return template.format(**data)

# Register functions with the factory
factory = (
    WorkflowFactory()
    .register_tool("get_weather", get_weather)
    .register_tool("format_message", format_message)
)

# Load and run the workflow
workflow = factory.create_workflow_from_yaml_path("workflow.yaml")
result = await workflow.run({"location": "Seattle", "unit": "F"})

InvokeMcpTool

Ruft ein Tool auf einem MCP-Server über die konfigurierte auf.

- kind: InvokeMcpTool
  id: search_docs
  serverUrl: https://learn.microsoft.com/api/mcp
  serverLabel: microsoft_docs
  toolName: microsoft_docs_search
  arguments:
    query: =Local.searchQuery
  output:
    result: Local.searchResults
    messages: Local.toolMessage
    autoSend: true

Eigenschaften:

Property Erforderlich Description
serverUrl Yes MCP-Server-URL
toolName Yes Toolname auf dem MCP-Server
serverLabel No Menschlich lesbares Serveretikett
arguments No An das Tool übergebene Argumente
headers No Anforderungsheader; Leere Werte werden übersprungen
connection.name No Benannte Verbindung für benutzerdefinierte Handler
conversationId No Fügt dem Gespräch eine erfolgreiche Ausgabe eines Tools hinzu
requireApproval No Fordert die Genehmigung vor dem Aufrufen des Tools an.
output.result No Pfad zum Speichern analysierter Toolausgabe
output.messages No Pfad zum Speichern der Toolnachricht
output.autoSend No Gibt die Ausgabe des Tools an das Workflowergebnis aus; standardmäßig auf true

Wenn requireApprovaltrue ist, bindet die Python-Laufzeit die ausgewerteten Aktions-Header an die Genehmigungsanfrage. Die Anforderung enthält Kopfzeilennamen, aber keine Headerwerte. Wenn sich die Header ändern, die Anmeldeinformationen wechseln oder eine wiederhergestellte Legacy-Anforderung über keinen verifizierbaren Bindungsstatus verfügt, erstellt der Workflow eine Ersatz-Genehmigungsanfrage mit einer neuen Anforderungs-ID und ruft das Tool nicht auf. Schützen Sie den Speicher des Workflowprüfpunkts, da er den separaten Überprüfungsschlüssel enthält. Benutzerdefinierte Handler bleiben für Anmeldedaten verantwortlich, die sie außerhalb des headers der Aktion ermitteln.

Python-Setup für InvokeMcpTool:

Übergeben Sie einen MCP-Toolhandler an WorkflowFactory. Verwenden Sie einen benutzerdefinierten Handler, wenn Sie Authentifizierung, verwaltete Verbindungen oder URL-Zulassungslisten benötigen.

from agent_framework.declarative import DefaultMCPToolHandler, WorkflowFactory

factory = WorkflowFactory(mcp_tool_handler=DefaultMCPToolHandler())
workflow = factory.create_workflow_from_yaml_path("workflow.yaml")

Ohne eine client_provider, DefaultMCPToolHandler wiederverwendet MCP-Sitzungen über einen gebundenen Cache. Mit einem client_provider erstellt und schließt es für jeden Aufruf, einschließlich tools/list, ein separates MCP-Tool und eine separate Sitzung, auch wenn der Anbieter denselben Client oder None zurückgibt. Der Handler schließt intern erstellte Fallback-HTTP-Clients, aber vom Aufrufer bereitgestellte httpx.AsyncClient Instanzen bleiben im Besitz des Aufrufers.

Anbietergestützte Aufrufe behalten den Sitzungszustand des Servers nicht bei. Wenn Ihr Workflow MCP-Sitzungskontinuität erfordert, implementieren Sie einen benutzerdefinierten MCPToolHandler Vertrag mit expliziter Authentifizierung und Lebensdauer.

HttpRequestAction

Sendet eine HTTP-Anforderung über die konfigurierte HttpRequestHandler. Erfolgreiche JSON-Antworten werden vor der Zuordnung analysiert. Nicht-2xx-Antworten lassen die Aktion fehlschlagen. In Python enthält der Fehler die Anforderungs-URL und den Statuscode, lässt jedoch den Antworttext aus.

- kind: HttpRequestAction
  id: fetch_repo_info
  method: GET
  url: =Concat("https://api.github.com/repos/", Local.repoName)
  headers:
    Accept: application/vnd.github+json
    User-Agent: agent-framework
  queryParameters:
    per_page: 10
  response: Local.repoInfo
  responseHeaders: Local.repoHeaders

Eigenschaften:

Property Erforderlich Description
url Yes Absolute Anforderungs-URL
method No HTTP-Methode; Standardwerte für GET
headers No Anfrage-Header
queryParameters No An die URL angefügte Abfrageparameter
body No Anforderungstext; verwenden Sie kind: json, raw oder none
requestTimeoutInMilliseconds No Timeout pro Anforderung
connection.name No Benannte Verbindung für benutzerdefinierte Handler
conversationId No Fügt dem Gespräch einen erfolgreichen Antworttext hinzu
response No Pfad zum Speichern des analysierten Antworttexts
responseHeaders No Pfad zum Speichern von Antwortheadern

Python Setup für HttpRequestAction:

Übergeben Sie einen HTTP-Anforderungshandler an WorkflowFactory. Verwenden Sie einen benutzerdefinierten Handler, wenn Sie Authentifizierung, Wiederholungen oder URL-Zulassungslisten benötigen.

from agent_framework.declarative import DefaultHttpRequestHandler, WorkflowFactory

factory = WorkflowFactory(http_request_handler=DefaultHttpRequestHandler())
workflow = factory.create_workflow_from_yaml_path("workflow.yaml")

Der Standardhandler lehnt URLs ab, die keine absoluten HTTP- oder HTTPS-URLs sind. Die URL wird normalisiert, und queryParameters wird an eine bereits vorhandene Abfragezeichenfolge angehängt, wobei deren Reihenfolge und Bytefolge erhalten bleiben.

A client_provider empfängt einen normalisierten HttpRequestInfo Snapshot. Die Momentaufnahme enthält die zusammengesetzte URL info.url und eine leere info.query_parametersURL. Die ursprünglichen Anforderungsinformationen werden nicht geändert. Die Standardwerte des ausgewählten Clients gelten weiterhin. Das Umleitungsverhalten bleibt durch die Einstellung dieses follow_redirects Clients gesteuert, und der Anbieter wird nicht erneut für Umleitungshüpfungen aufgerufen.

Der Standardhandler verwendet einen internen HTTP-Client, speichert jedoch keine Antwortcookies. Wenn ein Workflow Cookies für die Authentifizierung, Sitzungskontinuität oder Load-Balancer-Affinität erfordert, übergeben Sie ein konfiguriertes Cookie über httpx.AsyncClient und client= oder geben Sie eines von client_provider= zurück. Beschränken Sie Clients, die Cookies verwenden, auf einen einzigen authentifizierten Principal und schließen Sie vom Aufrufer bereitgestellte Clients innerhalb Ihrer Anwendung. Explizite Cookie Anforderungsheader und Antwortheader Set-Cookie bleiben verfügbar.

Human-in-the-Loop-Aktionen

Frage

Stellt dem Benutzer eine Frage und speichert die Antwort.

Die C#- und Python-SDKs verwenden unterschiedliche Feldnamen für die Frageaufforderung. In diesem Abschnitt wird das kleinste gemeinsame Verhalten verwendet: Text anfordern und die Antwort im Workflow-Status speichern.

- kind: Question
  id: ask_name
  displayName: Ask for user name
  question:
    text: "What is your name?"
  property: Local.userName

Eigenschaften:

Property Erforderlich Description
question Yes Die Frage, die gestellt werden soll. Verwenden Sie question.text für die verschachtelte Form oder einen String-Wert für die kompakte Form.
variable Yes Pfad zum Speichern der Antwort. Verwenden Sie property als alternativen Feldnamen der obersten Ebene, wenn Sie mit dem Namen des C#-Zielfelds übereinstimmen möchten.
default No Standardwert, der in den Metadaten der Anfrage enthalten ist und vom Aufrufer angewendet werden soll.
choices No Liste der Optionen, die zum Anfordern von Metadaten hinzugefügt wurden. Jede Auswahl kann eine Zeichenfolge oder ein Objekt mit value und optionalen label Feldern sein.
allowFreeText No Gibt an, ob der Aufrufer Text außerhalb der konfigurierten choicesAkzeptieren kann. Wird standardmäßig auf true festgelegt.

Python akzeptiert auch alternative Feldnamen zur Kompatibilität. Verwenden Sie die oberste Ebene text anstelle von question und defaultValue anstelle von default.

- kind: Question
  id: ask_color
  text: "Choose a color:"
  property: Local.color
  choices:
    - value: red
      label: Red
    - blue
  allowFreeText: false

RequestExternalInput

Fordert Eingaben von einem externen System oder Prozess an.

- kind: RequestExternalInput
  id: request_approval
  displayName: Request manager approval
  prompt:
    text: "Please provide approval for this request."
  variable: Local.approvalResult
  default: "pending"

Eigenschaften:

Property Erforderlich Description
prompt.text Yes Beschreibung der erforderlichen Eingabe
variable Yes Pfad zum Speichern der Eingabe
default No Standardwert

Workflowsteuerungsaktionen

EndWorkflow

Beendet die Workflowausführung.

- kind: EndWorkflow
  id: finish
  displayName: End workflow

EndConversation

Beendet die aktuelle Unterhaltung.

- kind: EndConversation
  id: end_chat
  displayName: End conversation

CreateConversation

Erstellt einen neuen Unterhaltungskontext.

- kind: CreateConversation
  id: create_new_conv
  displayName: Create new conversation
  conversationId: Local.NewConversationId

Eigenschaften:

Property Erforderlich Description
conversationId Yes Pfad zum Speicherort der neuen Gesprächs-ID

Kurzübersicht zu Aktionen

Action Category Description
SetVariable Variable Festlegen einer einzelnen Variablen
SetMultipleVariables Variable Festlegen mehrerer Variablen
ResetVariable Variable Löschen einer Variablen
If Ablaufsteuerung Bedingungsverzweigung
ConditionGroup Ablaufsteuerung Mehrzweigschalter
Foreach Ablaufsteuerung Über die Sammlung iterieren
BreakLoop Ablaufsteuerung Beenden der aktuellen Schleife
ContinueLoop Ablaufsteuerung Zur nächsten Iteration springen
GotoAction Ablaufsteuerung Zur Aktion mit ID springen
SendActivity Output Nachricht an Benutzer senden
InvokeAzureAgent Agent Azure AI-Agent anrufen
InvokeFunctionTool Tool Aufrufen der registrierten Funktion
InvokeMcpTool Tool Aufrufen des MCP-Servertools
HttpRequestAction HTTP HTTP-Endpunkt aufrufen
Question Human-in-the-Loop Benutzer eine Frage stellen
RequestExternalInput Human-in-the-Loop Anfordern externer Eingaben
EndWorkflow Workflowsteuerung Workflow beenden
EndConversation Workflowsteuerung Unterhaltung beenden
CreateConversation Workflowsteuerung Neue Unterhaltung erstellen

Ausdruckssyntax

Deklarative Workflows verwenden eine PowerFx-ähnliche Ausdruckssprache, um Zustands- und Berechnung dynamischer Werte zu verwalten. Werte, denen = das Präfix vorangestellt ist, werden zur Laufzeit als Ausdrücke ausgewertet.

Details des variablen Namespace

Namespace Description Access
Local.* Workflow-lokale Variablen Lesen/Schreiben
Workflow.Inputs.* An den Workflow übergebene Eingabeparameter Schreibgeschützt
Workflow.Outputs.* Vom Workflow zurückgegebene Werte Lesen/Schreiben
System.* Vom System bereitgestellte Werte Schreibgeschützt
Agent.* Ergebnisse von Agentaufrufen Schreibgeschützt

Systemvariablen

Variable Description
System.ConversationId Aktuelle Gesprächs-ID
System.LastMessage Die letzte Nachricht
System.Timestamp Aktueller Zeitstempel

Agentvariablen

Greifen Sie nach dem Aufrufen eines Agents über die Ausgabevariable auf Antwortdaten zu:

actions:
  - kind: InvokeAzureAgent
    id: call_assistant
    agent:
      name: MyAgent
    output:
      responseObject: Local.AgentResult

  # Access agent response
  - kind: SendActivity
    activity:
      text: =Local.AgentResult.text

Literal- und Ausdruckswerte

# Literal string (stored as-is)
value: Hello World

# Expression (evaluated at runtime)
value: =Concat("Hello ", Workflow.Inputs.name)

# Literal number
value: 42

# Expression returning a number
value: =Workflow.Inputs.quantity * 2

Zeichenfolgenvorgänge

Concat

Verketten mehrerer Zeichenfolgen:

value: =Concat("Hello, ", Workflow.Inputs.name, "!")
# Result: "Hello, Alice!" (if Workflow.Inputs.name is "Alice")

value: =Concat(Local.firstName, " ", Local.lastName)
# Result: "John Doe" (if firstName is "John" and lastName is "Doe")

IsBlank

Überprüfen Sie, ob ein Wert leer oder nicht definiert ist:

condition: =IsBlank(Workflow.Inputs.optionalParam)
# Returns true if the parameter is not provided

value: =If(IsBlank(Workflow.Inputs.name), "Guest", Workflow.Inputs.name)
# Returns "Guest" if name is blank, otherwise returns the name

Bedingte Ausdrücke

If-Funktion

Zurückgeben unterschiedlicher Werte basierend auf einer Bedingung:

value: =If(Workflow.Inputs.age < 18, "minor", "adult")

value: =If(Local.count > 0, "Items found", "No items")

# Nested conditions
value: =If(Workflow.Inputs.role = "admin", "Full access", If(Workflow.Inputs.role = "user", "Limited access", "No access"))

Vergleichsoperatoren

Bediener Description Example
= Ist gleich =Workflow.Inputs.status = "active"
<> Ungleich =Workflow.Inputs.status <> "deleted"
< Kleiner als =Workflow.Inputs.age < 18
> Größer als =Workflow.Inputs.count > 0
<= Kleiner oder gleich =Workflow.Inputs.score <= 100
>= Größer als oder gleich =Workflow.Inputs.quantity >= 1

Boolesche Funktionen

# Or - returns true if any condition is true
condition: =Or(Workflow.Inputs.role = "admin", Workflow.Inputs.role = "moderator")

# And - returns true if all conditions are true
condition: =And(Workflow.Inputs.age >= 18, Workflow.Inputs.hasConsent)

# Not - negates a condition
condition: =Not(IsBlank(Workflow.Inputs.email))

Mathematische Vorgänge

# Addition
value: =Workflow.Inputs.price + Workflow.Inputs.tax

# Subtraction
value: =Workflow.Inputs.total - Workflow.Inputs.discount

# Multiplication
value: =Workflow.Inputs.quantity * Workflow.Inputs.unitPrice

# Division
value: =Workflow.Inputs.total / Workflow.Inputs.count

Beispiele für praktische Ausdrücke

Benutzerkategorisierung

name: categorize-user
inputs:
  age:
    type: integer
    description: User's age

actions:
  - kind: SetVariable
    variable: Local.age
    value: =Workflow.Inputs.age

  - kind: SetVariable
    variable: Local.category
    value: =If(Local.age < 13, "child", If(Local.age < 20, "teenager", If(Local.age < 65, "adult", "senior")))

  - kind: SendActivity
    activity:
      text: '=Concat("You are categorized as: ", Local.category)'

  - kind: SetVariable
    variable: Workflow.Outputs.category
    value: =Local.category

Bedingte Begrüßung

name: smart-greeting
inputs:
  name:
    type: string
    description: User's name (optional)
  timeOfDay:
    type: string
    description: morning, afternoon, or evening

actions:
  # Set the greeting based on time of day
  - kind: SetVariable
    variable: Local.timeGreeting
    value: =If(Workflow.Inputs.timeOfDay = "morning", "Good morning", If(Workflow.Inputs.timeOfDay = "afternoon", "Good afternoon", "Good evening"))

  # Handle optional name
  - kind: SetVariable
    variable: Local.userName
    value: =If(IsBlank(Workflow.Inputs.name), "friend", Workflow.Inputs.name)

  # Build the full greeting
  - kind: SetVariable
    variable: Local.fullGreeting
    value: =Concat(Local.timeGreeting, ", ", Local.userName, "!")

  - kind: SendActivity
    activity:
      text: =Local.fullGreeting

Eingabeüberprüfung

name: validate-order
inputs:
  quantity:
    type: integer
    description: Number of items to order
  email:
    type: string
    description: Customer email

actions:
  # Check if inputs are valid
  - kind: SetVariable
    variable: Local.isValidQuantity
    value: =And(Workflow.Inputs.quantity > 0, Workflow.Inputs.quantity <= 100)

  - kind: SetVariable
    variable: Local.hasEmail
    value: =Not(IsBlank(Workflow.Inputs.email))

  - kind: SetVariable
    variable: Local.isValid
    value: =And(Local.isValidQuantity, Local.hasEmail)

  - kind: If
    condition: =Local.isValid
    then:
      - kind: SendActivity
        activity:
          text: "Order validated successfully!"
    else:
      - kind: SendActivity
        activity:
          text: =If(Not(Local.isValidQuantity), "Invalid quantity (must be 1-100)", "Email is required")

Erweiterte Muster

Da Ihre Workflows komplexer werden, benötigen Sie Muster, die mehrstufige Prozesse, Agentenkoordination und interaktive Szenarien behandeln.

Multi-Agent-Orchestrierung

Sequenzielle Agentenpipeline

Leiten Sie die Arbeit durch mehrere Agenten in Reihenfolge weiter, wobei jeder Agent auf der Ausgabe des vorherigen Agenten aufbaut.

Anwendungsfall: Inhaltserstellungspipelinen, bei denen verschiedene Spezialisten Recherchen, Schreiben und Bearbeiten behandeln.

name: content-pipeline
description: Sequential agent pipeline for content creation

kind: Workflow
trigger:
  kind: OnConversationStart
  id: content_workflow
  actions:
    # First agent: Research and analyze
    - kind: InvokeAzureAgent
      id: invoke_researcher
      displayName: Research phase
      conversationId: =System.ConversationId
      agent:
        name: ResearcherAgent

    # Second agent: Write draft based on research
    - kind: InvokeAzureAgent
      id: invoke_writer
      displayName: Writing phase
      conversationId: =System.ConversationId
      agent:
        name: WriterAgent

    # Third agent: Edit and polish
    - kind: InvokeAzureAgent
      id: invoke_editor
      displayName: Editing phase
      conversationId: =System.ConversationId
      agent:
        name: EditorAgent

Python-Setup:

from agent_framework.declarative import WorkflowFactory

# Create factory and register agents
factory = WorkflowFactory()
factory.register_agent("ResearcherAgent", researcher_agent)
factory.register_agent("WriterAgent", writer_agent)
factory.register_agent("EditorAgent", editor_agent)

# Load and run
workflow = factory.create_workflow_from_yaml_path("content-pipeline.yaml")
result = await workflow.run({"topic": "AI in healthcare"})

Bedingtes Agenten-Routing

Leiten Sie Anforderungen an verschiedene Agenten basierend auf den Eingabe- oder Zwischenergebnissen weiter.

Anwendungsfall: Supportsysteme, die basierend auf dem Problemtyp an spezialisierte Agents weitergeleitet werden.

name: support-router
description: Route to specialized support agents

inputs:
  category:
    type: string
    description: Support category (billing, technical, general)

actions:
  - kind: ConditionGroup
    id: route_request
    displayName: Route to appropriate agent
    conditions:
      - condition: =Workflow.Inputs.category = "billing"
        id: billing_route
        actions:
          - kind: InvokeAzureAgent
            id: billing_agent
            agent:
              name: BillingAgent
            conversationId: =System.ConversationId
      - condition: =Workflow.Inputs.category = "technical"
        id: technical_route
        actions:
          - kind: InvokeAzureAgent
            id: technical_agent
            agent:
              name: TechnicalAgent
            conversationId: =System.ConversationId
    elseActions:
      - kind: InvokeAzureAgent
        id: general_agent
        agent:
          name: GeneralAgent
        conversationId: =System.ConversationId

Agent mit externer Schleife

Setzen Sie die Interaktion mit dem Agenten fort, bis eine Bedingung erfüllt ist, wie beispielsweise die Lösung des Problems.

Anwendungsfall: Unterstützungsgespräche, die fortgesetzt werden, bis zur Lösung des Benutzerproblems.

name: support-conversation
description: Continue support until resolved

actions:
  - kind: SetVariable
    variable: Local.IsResolved
    value: false

  - kind: InvokeAzureAgent
    id: support_agent
    displayName: Support agent with external loop
    agent:
      name: SupportAgent
    conversationId: =System.ConversationId
    input:
      externalLoop:
        when: =Not(Local.IsResolved)
    output:
      responseObject: Local.SupportResult

  - kind: SendActivity
    activity:
      text: "Thank you for contacting support. Your issue has been resolved."

Schleifensteuerungsmuster

Iterative Agentenkonversation

Erstellen Sie wechselseitige Gespräche zwischen Agenten mit kontrollierter Iteration.

Anwendungsfall: Szenarien für Kursleiter, Diskussionssimulationen oder iterative Verfeinerung.

name: student-teacher
description: Iterative learning conversation between student and teacher

kind: Workflow
trigger:
  kind: OnConversationStart
  id: learning_session
  actions:
    # Initialize turn counter
    - kind: SetVariable
      id: init_counter
      variable: Local.TurnCount
      value: 0

    - kind: SendActivity
      id: start_message
      activity:
        text: '=Concat("Starting session for: ", Workflow.Inputs.problem)'

    # Student attempts solution (loop entry point)
    - kind: SendActivity
      id: student_label
      activity:
        text: "\n[Student]:"

    - kind: InvokeAzureAgent
      id: student_attempt
      conversationId: =System.ConversationId
      agent:
        name: StudentAgent

    # Teacher reviews
    - kind: SendActivity
      id: teacher_label
      activity:
        text: "\n[Teacher]:"

    - kind: InvokeAzureAgent
      id: teacher_review
      conversationId: =System.ConversationId
      agent:
        name: TeacherAgent
      output:
        messages: Local.TeacherResponse

    # Increment counter
    - kind: SetVariable
      id: increment
      variable: Local.TurnCount
      value: =Local.TurnCount + 1

    # Check completion conditions
    - kind: ConditionGroup
      id: check_completion
      conditions:
        # Success: Teacher congratulated student
        - condition: =Not(IsBlank(Find("congratulations", Local.TeacherResponse)))
          id: success_check
          actions:
            - kind: SendActivity
              activity:
                text: "Session complete - student succeeded!"
            - kind: SetVariable
              variable: Workflow.Outputs.result
              value: success
        # Continue: Under turn limit
        - condition: =Local.TurnCount < 4
          id: continue_check
          actions:
            - kind: GotoAction
              actionId: student_label
      elseActions:
        # Timeout: Reached turn limit
        - kind: SendActivity
          activity:
            text: "Session ended - turn limit reached."
        - kind: SetVariable
          variable: Workflow.Outputs.result
          value: timeout

Zählerbasierte Schleifen

Implementieren Sie herkömmliche Zählschleifen mithilfe von Variablen und GotoAction.

name: counter-loop
description: Process items with a counter

actions:
  - kind: SetVariable
    variable: Local.counter
    value: 0

  - kind: SetVariable
    variable: Local.maxIterations
    value: 5

  # Loop start
  - kind: SetVariable
    id: loop_start
    variable: Local.counter
    value: =Local.counter + 1

  - kind: SendActivity
    activity:
      text: =Concat("Processing iteration ", Local.counter)

  # Your processing logic here
  - kind: SetVariable
    variable: Local.result
    value: =Concat("Result from iteration ", Local.counter)

  # Check if should continue
  - kind: If
    condition: =Local.counter < Local.maxIterations
    then:
      - kind: GotoAction
        actionId: loop_start
    else:
      - kind: SendActivity
        activity:
          text: "Loop complete!"

Früher Ausstieg mit BreakLoop

Verwenden Sie BreakLoop, um Iterationen frühzeitig zu beenden, wenn eine Bedingung erfüllt ist.

name: search-workflow
description: Search through items and stop when found

actions:
  - kind: SetVariable
    variable: Local.found
    value: false

  - kind: Foreach
    source: =Workflow.Inputs.items
    itemName: currentItem
    actions:
      # Check if this is the item we're looking for
      - kind: If
        condition: =currentItem.id = Workflow.Inputs.targetId
        then:
          - kind: SetVariable
            variable: Local.found
            value: true
          - kind: SetVariable
            variable: Local.result
            value: =currentItem
          - kind: BreakLoop

      - kind: SendActivity
        activity:
          text: '=Concat("Checked item: ", currentItem.name)'

  - kind: If
    condition: =Local.found
    then:
      - kind: SendActivity
        activity:
          text: '=Concat("Found: ", Local.result.name)'
    else:
      - kind: SendActivity
        activity:
          text: "Item not found"

Muster mit menschlicher Interaktion

Interaktive Umfrage

Sammeln Sie mehrere Informationen vom Benutzer.

name: customer-survey
description: Interactive customer feedback survey

actions:
  - kind: SendActivity
    activity:
      text: "Welcome to our customer feedback survey!"

  # Collect name
  - kind: Question
    id: ask_name
    question:
      text: "What is your name?"
    variable: Local.userName
    default: "Anonymous"

  - kind: SendActivity
    activity:
      text: =Concat("Nice to meet you, ", Local.userName, "!")

  # Collect rating
  - kind: Question
    id: ask_rating
    question:
      text: "How would you rate our service? (1-5)"
    variable: Local.rating
    default: "3"

  # Respond based on rating
  - kind: If
    condition: =Local.rating >= 4
    then:
      - kind: SendActivity
        activity:
          text: "Thank you for the positive feedback!"
    else:
      - kind: Question
        id: ask_improvement
        question:
          text: "What could we improve?"
        variable: Local.feedback

  # Collect additional feedback
  - kind: RequestExternalInput
    id: additional_comments
    prompt:
      text: "Any additional comments? (optional)"
    variable: Local.comments
    default: ""

  # Summary
  - kind: SendActivity
    activity:
      text: =Concat("Thank you, ", Local.userName, "! Your feedback has been recorded.")

  - kind: SetVariable
    variable: Workflow.Outputs.survey
    value:
      name: =Local.userName
      rating: =Local.rating
      feedback: =Local.feedback
      comments: =Local.comments

Genehmigungsworkflow

Fordern Sie die Genehmigung an, bevor Sie mit einer Aktion fortfahren.

name: approval-workflow
description: Request approval before processing

inputs:
  requestType:
    type: string
    description: Type of request
  amount:
    type: number
    description: Request amount

actions:
  - kind: SendActivity
    activity:
      text: =Concat("Processing ", Workflow.Inputs.requestType, " request for $", Workflow.Inputs.amount)

  # Check if approval is needed
  - kind: If
    condition: =Workflow.Inputs.amount > 1000
    then:
      - kind: SendActivity
        activity:
          text: "This request requires manager approval."

      - kind: Question
        id: get_approval
        question:
          text: =Concat("Do you approve this ", Workflow.Inputs.requestType, " request for $", Workflow.Inputs.amount, "? (yes/no)")
        variable: Local.approved

      - kind: If
        condition: =Local.approved = "yes"
        then:
          - kind: SendActivity
            activity:
              text: "Request approved. Processing..."
          - kind: SetVariable
            variable: Workflow.Outputs.status
            value: approved
        else:
          - kind: SendActivity
            activity:
              text: "Request denied."
          - kind: SetVariable
            variable: Workflow.Outputs.status
            value: denied
    else:
      - kind: SendActivity
        activity:
          text: "Request auto-approved (under threshold)."
      - kind: SetVariable
        variable: Workflow.Outputs.status
        value: auto_approved

Komplexe Orchestrierung

Supportticketworkflow

Ein umfassendes Beispiel für die Kombination mehrerer Muster: Agentrouting, bedingte Logik und Gesprächsmanagement.

name: support-ticket-workflow
description: Complete support ticket handling with escalation

kind: Workflow
trigger:
  kind: OnConversationStart
  id: support_workflow
  actions:
    # Initial self-service agent
    - kind: InvokeAzureAgent
      id: self_service
      displayName: Self-service agent
      agent:
        name: SelfServiceAgent
      conversationId: =System.ConversationId
      input:
        externalLoop:
          when: =Not(Local.ServiceResult.IsResolved)
      output:
        responseObject: Local.ServiceResult

    # Check if resolved by self-service
    - kind: If
      condition: =Local.ServiceResult.IsResolved
      then:
        - kind: SendActivity
          activity:
            text: "Issue resolved through self-service."
        - kind: SetVariable
          variable: Workflow.Outputs.resolution
          value: self_service
        - kind: EndWorkflow
          id: end_resolved

    # Create support ticket
    - kind: SendActivity
      activity:
        text: "Creating support ticket..."

    - kind: SetVariable
      variable: Local.TicketId
      value: =Concat("TKT-", System.ConversationId)

    # Route to appropriate team
    - kind: ConditionGroup
      id: route_ticket
      conditions:
        - condition: =Local.ServiceResult.Category = "technical"
          id: technical_route
          actions:
            - kind: InvokeAzureAgent
              id: technical_support
              agent:
                name: TechnicalSupportAgent
              conversationId: =System.ConversationId
              output:
                responseObject: Local.TechResult
        - condition: =Local.ServiceResult.Category = "billing"
          id: billing_route
          actions:
            - kind: InvokeAzureAgent
              id: billing_support
              agent:
                name: BillingSupportAgent
              conversationId: =System.ConversationId
              output:
                responseObject: Local.BillingResult
      elseActions:
        # Escalate to human
        - kind: SendActivity
          activity:
            text: "Escalating to human support..."
        - kind: SetVariable
          variable: Workflow.Outputs.resolution
          value: escalated

    - kind: SendActivity
      activity:
        text: =Concat("Ticket ", Local.TicketId, " has been processed.")

Bewährte Methoden

Namenskonventionen

Verwenden Sie klare, beschreibende Namen für Aktionen und Variablen:

# Good
- kind: SetVariable
  id: calculate_total_price
  variable: Local.orderTotal

# Avoid
- kind: SetVariable
  id: sv1
  variable: Local.x

Organisieren großer Workflows

Unterteilen Sie komplexe Workflows in logische Abschnitte mit Kommentaren:

actions:
  # === INITIALIZATION ===
  - kind: SetVariable
    id: init_status
    variable: Local.status
    value: started

  # === DATA COLLECTION ===
  - kind: Question
    id: collect_name
    # ...

  # === PROCESSING ===
  - kind: InvokeAzureAgent
    id: process_request
    # ...

  # === OUTPUT ===
  - kind: SendActivity
    id: send_result
    # ...

Fehlerbehandlung

Verwenden Sie bedingte Prüfungen, um potenzielle Probleme zu behandeln:

actions:
  - kind: SetVariable
    variable: Local.hasError
    value: false

  - kind: InvokeAzureAgent
    id: call_agent
    agent:
      name: ProcessingAgent
    output:
      responseObject: Local.AgentResult

  - kind: If
    condition: =IsBlank(Local.AgentResult)
    then:
      - kind: SetVariable
        variable: Local.hasError
        value: true
      - kind: SendActivity
        activity:
          text: "An error occurred during processing."
    else:
      - kind: SendActivity
        activity:
          text: =Local.AgentResult.message

Teststrategien

  1. Einfaches Starten: Testen grundlegender Abläufe vor dem Hinzufügen von Komplexität
  2. Verwenden von Standardwerten: Bereitstellen sinnvoller Standardwerte für Eingaben
  3. Protokollierung hinzufügen: Verwenden von SendActivity zum Debuggen während der Entwicklung
  4. Edge Cases testen: Verhalten mit fehlenden oder ungültigen Eingaben überprüfen
# Debug logging example
- kind: SendActivity
  id: debug_log
  activity:
    text: '=Concat("[DEBUG] Current state: counter=", Local.counter, ", status=", Local.status)'

Nächste Schritte

  • C#-Deklarative Workflowbeispiele – Erkunden Sie vollständige Arbeitsbeispiele, einschließlich:
    • StudentTeacher - Multi-Agenten-Gespräch mit iterativem Lernen
    • InvokeMcpTool – MCP-Server-Tool-Integration
    • InvokeFunctionTool – Direkte Funktionsaufrufe aus Workflows
    • FunctionTools – Agent mit Funktionstools
    • ToolApproval - Menschliche Genehmigung für die Toolausführung
    • CustomerSupport – Komplexer Support-Ticketworkflow
    • DeepResearch – Forschungsworkflow mit mehreren Agents

Note

Go-Unterstützung für dieses Feature wird in Kürze verfügbar sein. Den neuesten Status finden Sie im Agent Framework Go-Repository .