Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
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
- Grundlegende Kenntnisse in der YAML-Syntax
- Grundlegendes zu Workflowkonzepten
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önnenRunStreamingAsyncweiterhin verwenden, um andere Workflow-Ereignisse umgehend zu erhalten. - Bei gestreamtem Text
AgentResponseUpdateEventverarbeiten. Fügen Sie den Text einer abgeschlossenen Antwort nicht erneut an, wenn ihre Nachrichten bereits gestreamt wurden. FürSendActivityhaben 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
RequestInfoEventunabhä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.
- Grundlegende Kenntnisse in der YAML-Syntax
- Grundlegendes zu Workflowkonzepten
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
- 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
- Einfaches Starten: Testen grundlegender Abläufe vor dem Hinzufügen von Komplexität
- Verwenden von Standardwerten: Bereitstellen sinnvoller Standardwerte für Eingaben
- Protokollierung hinzufügen: Verwenden von SendActivity zum Debuggen während der Entwicklung
- 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
- Python Deklarative Workflowbeispiele – Vollständige Arbeitsbeispiele erkunden
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 .