Compartir a través de


Flujos de trabajo declarativos: información general

Los flujos de trabajo declarativos permiten definir la lógica de flujo de trabajo mediante archivos de configuración de YAML en lugar de escribir código mediante programación. Este enfoque facilita la lectura, modificación y uso compartido de flujos de trabajo entre equipos.

Información general

Con los flujos de trabajo declarativos, se describe lo que debe hacer el flujo de trabajo en lugar de cómo implementarlo. El marco controla la ejecución subyacente, convirtiendo las definiciones de YAML en gráficos de flujo de trabajo ejecutables.

Ventajas clave:

  • Formato legible: la sintaxis de YAML es fácil de entender, incluso para los no desarrolladores
  • Portable: las definiciones de flujo de trabajo se pueden compartir, modificar y modificar sin cambios de código.
  • Iteración rápida: modificar el comportamiento del flujo de trabajo mediante la edición de archivos de configuración
  • Estructura coherente: los tipos de acción predefinidos garantizan que los flujos de trabajo siguen los procedimientos recomendados

Cuándo usar flujos de trabajo declarativos frente a flujos de trabajo mediante programación

Escenario Enfoque recomendado
Patrones de orquestación estándar Declarativo
Flujos de trabajo que cambian con frecuencia Declarativo
Los no desarrolladores necesitan modificar flujos de trabajo Declarativo
Lógica personalizada compleja Mediante programación
Máxima flexibilidad y control Mediante programación
Integración con código de Python existente Mediante programación

Estructura básica de YAML

La estructura YAML difiere ligeramente entre las implementaciones de C# y Python. Consulte las secciones específicas del idioma a continuación para obtener más información.

Tipos de acción

Los flujos de trabajo declarativos admiten varios tipos de acción. En la tabla siguiente se muestra la disponibilidad por idioma:

Categoría Acciones C# Pitón
Administración de variables SetVariable, , SetMultipleVariables, ResetVariable
Administración de variables AppendValue
Administración de variables SetTextVariable, ClearAllVariables, , ParseValue, EditTableV2
Flujo de control If, ConditionGroup, Foreach, BreakLoop, , ContinueLoop, GotoAction
Flujo de control RepeatUntil
Salida SendActivity
Salida EmitEvent
Invocación de agente InvokeAzureAgent
Invocación de herramientas InvokeFunctionTool
Invocación de herramientas InvokeMcpTool
Humano en el Bucle Question, RequestExternalInput
Humano en el Bucle Confirmation, WaitForInput
Control de flujo de trabajo EndWorkflow, , EndConversation, CreateConversation
Conversación AddConversationMessage, CopyConversationMessages, , RetrieveConversationMessage, RetrieveConversationMessages

Estructura YAML de C#

Los flujos de trabajo declarativos de C# usan una estructura basada en desencadenadores:

#
# 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

Elementos de estructura

Elemento Obligatorio Description
kind Debe ser Workflow
trigger.kind Tipo de desencadenador (normalmente OnConversationStart)
trigger.id Identificador único del flujo de trabajo
trigger.actions Lista de acciones que se van a ejecutar

Estructura YAML de Python

Los flujos de trabajo declarativos de Python usan una estructura basada en nombres con entradas opcionales:

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

Elementos de estructura

Elemento Obligatorio Description
name Identificador único del flujo de trabajo
description No Descripción legible para humanos
inputs No Parámetros de entrada que acepta el flujo de trabajo
actions Lista de acciones que se van a ejecutar

Prerrequisitos

Antes de comenzar, asegúrese de que tiene:

  • .NET 8.0 o posterior
  • Un proyecto de Microsoft Foundry con al menos un agente implementado
  • Los siguientes paquetes NuGet instalados:
dotnet add package Microsoft.Agents.AI.Workflows.Declarative --prerelease
dotnet add package Microsoft.Agents.AI.Workflows.Declarative.AzureAI --prerelease
  • Si tiene previsto agregar la acción de invocación de la herramienta MCP al flujo de trabajo, instale también el siguiente paquete NuGet:
dotnet add package Microsoft.Agents.AI.Workflows.Declarative.Mcp --prerelease

Tu Primer Flujo de Trabajo Declarativo

Vamos a crear un flujo de trabajo sencillo que salude a un usuario en función de su entrada.

Paso 1: Crear el archivo YAML

Cree un archivo denominado 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

Paso 2: Configurar el proveedor del agente

Cree una aplicación de consola de C# para ejecutar el flujo de trabajo. En primer lugar, configure el proveedor del agente que se conecta a Foundry:

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());

Paso 3: Compilar y ejecutar el flujo de trabajo

// 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 MessageActivityEvent activityEvent:
            Console.WriteLine($"Activity: {activityEvent.Message}");
            break;
        case AgentResponseEvent responseEvent:
            Console.WriteLine($"Response: {responseEvent.Response.Text}");
            break;
        case WorkflowErrorEvent errorEvent:
            Console.WriteLine($"Error: {errorEvent.Data}");
            break;
    }
}

Console.WriteLine("Workflow completed!");

Salida esperada

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

Conceptos básicos

Espacios de nombres para variables

Los flujos de trabajo declarativos en C# usan variables con espacio de nombres para organizar el estado:

Namespace Description Example
Local.* Variables locales para el flujo de trabajo Local.message
System.* Valores proporcionados por el sistema System.ConversationId, System.LastMessage

Nota:

Los flujos de trabajo declarativos de C# no usan ni Workflow.Inputs ni Workflow.Outputs espacios de nombres. La entrada se recibe a través de System.LastMessage y la salida se envía mediante SendActivity acciones.

Variables del sistema

Variable Description
System.ConversationId Identificador de conversación actual
System.LastMessage Mensaje de usuario más reciente
System.LastMessage.Text Contenido de texto del último mensaje

Lenguaje de expresiones

Los valores con = prefijo se evalúan como expresiones mediante el lenguaje de expresiones de PowerFx:

# Literal value (no evaluation)
value: Hello

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

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

Entre las funciones comunes se incluyen las siguientes:

  • Concat(str1, str2, ...) - Concatenar cadenas
  • If(condition, trueValue, falseValue) - Expresión condicional
  • IsBlank(value) - Comprobar si el valor está vacío
  • Upper(text) / Lower(text) - Conversión de mayúsculas y minúsculas
  • Find(searchText, withinText) - Buscar texto dentro de la cadena
  • MessageText(message) - Extracción de texto de un objeto de mensaje
  • UserMessage(text) - Creación de un mensaje de usuario a partir de texto
  • AgentMessage(text) - Creación de un mensaje de agente a partir de texto

Opciones de configuración

La DeclarativeWorkflowOptions clase proporciona configuración para la ejecución del flujo de trabajo:

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,

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

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

Configuración del agente proveedor

AzureAgentProvider conecta el flujo de trabajo a los agentes de Foundry:

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,
};

Ejecución del flujo de trabajo

Use InProcessExecution para ejecutar flujos de trabajo y controlar eventos:

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 MessageActivityEvent activity:
            Console.WriteLine($"Message: {activity.Message}");
            break;

        case AgentResponseUpdateEvent streamEvent:
            Console.Write(streamEvent.Update.Text); // Streaming text
            break;

        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;
    }
}

Reanudación desde puntos de control

Los flujos de trabajo se pueden reanudar desde puntos de control para la tolerancia a errores:

// 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...
}

Referencia de Acciones

Las acciones son los bloques de construcción de flujos de trabajo declarativos. Cada acción realiza una operación específica y las acciones se ejecutan secuencialmente en el orden en que aparecen en el archivo YAML.

Estructura de acciones

Todas las acciones comparten propiedades comunes:

- 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...

Acciones de administración de variables

EstablecerVariable

Establece una variable en un valor especificado.

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

Con una expresión:

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

Propiedades:

Propiedad Obligatorio Description
variable Ruta variable de acceso (por ejemplo, Local.name, Workflow.Outputs.result)
value Valor que se va a establecer (literal o expresión)

SetMultipleVariables

Establece varias variables en una sola acción.

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

Propiedades:

Propiedad Obligatorio Description
variables Mapa de caminos de variables a valores

SetTextVariable (solo C#)

Establece una variable de texto en un valor de cadena especificado.

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

Propiedades:

Propiedad Obligatorio Description
variable Ruta variable para el valor de texto
value Valor de texto a establecer

ResetVariable

Borra el valor de una variable.

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

Propiedades:

Propiedad Obligatorio Description
variable Ruta de acceso de la variable para reiniciar

ClearAllVariables (solo C#)

Restablece todas las variables en el contexto actual.

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

ParseValue (solo C#)

Extrae o convierte datos en un formato utilizable.

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

Propiedades:

Propiedad Obligatorio Description
source Expresión que devuelve el valor para analizar
variable Ruta variable para almacenar el resultado parseado

EditTableV2 (solo C#)

Modifica los datos en un formato de tabla estructurado.

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

Propiedades:

Propiedad Obligatorio Description
table Ruta variable a la tabla
operation Tipo de operación (agregar, actualizar, eliminar)
row Datos de fila para la operación

Acciones de flujo de control

Si

Ejecuta acciones condicionalmente basadas en una condición.

- 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!"

Propiedades:

Propiedad Obligatorio Description
condition Expresión que se evalúa como verdadero/falso
then Acciones que se van a ejecutar si la condición es true
else No Acciones para ejecutar si la condición es false

ConditionGroup

Evalúa varias condiciones como una sentencia switch/case.

- 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

Propiedades:

Propiedad Obligatorio Description
conditions Lista de pares de condición/acciones (gana la primera coincidencia)
elseActions No Acciones si no coincide ninguna condición

Foreach

Recorre en iteración una colección.

- 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)

Propiedades:

Propiedad Obligatorio Description
source Expresión que devuelve una colección
itemName No Nombre de variable para el elemento actual (valor predeterminado: item)
indexName No Nombre de variable para el índice actual (valor predeterminado: index)
actions Acciones que se van a ejecutar para cada elemento

BreakLoop

Sale del bucle actual inmediatamente.

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

ContinueLoop

Omite la siguiente iteración del bucle.

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

GotoAction

Salta a una acción específica por identificador.

- 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

Propiedades:

Propiedad Obligatorio Description
actionId Identificador de la acción a la que se desea saltar

Acciones de salida

SendActivity

Envía un mensaje al usuario.

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

Con una expresión:

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

Propiedades:

Propiedad Obligatorio Description
activity La actividad que se va a enviar
activity.text Texto del mensaje (literal o expresión)

Acciones de invocación del agente

InvokeAzureAgent

Invoca un agente de Foundry.

Invocación básica:

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

Con la configuración de entrada y salida:

- 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

Usando el bucle externo (continúa hasta que se cumpla la condición):

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

Propiedades:

Propiedad Obligatorio Description
agent.name Nombre del agente registrado
conversationId No Identificador de contexto de conversación
input.messages No Mensajes que se van a enviar al agente
input.arguments No Argumentos adicionales para el agente
input.externalLoop.when No Condición para continuar con el bucle del agente
output.responseObject No Ruta de acceso para almacenar la respuesta del agente
output.messages No Ruta de acceso para almacenar mensajes de conversación
output.autoSend No Enviar automáticamente respuesta al usuario

Acciones de invocación de herramientas (solo C#)

InvokeFunctionTool

Invoca una herramienta de función directamente desde el flujo de trabajo sin pasar por un agente de IA.

- 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

Propiedades:

Propiedad Obligatorio Description
functionName Nombre de la función que se va a invocar
conversationId No Identificador de contexto de conversación
requireApproval No Si se debe requerir la aprobación del usuario antes de la ejecución
arguments No Argumentos que se van a pasar a la función
output.result No Ruta de acceso para almacenar el resultado de la función
output.messages No Ruta de acceso para almacenar mensajes de función
output.autoSend No Enviar automáticamente el resultado al usuario

Programa de instalación de C# para InvokeFunctionTool:

Las funciones deben registrarse con WorkflowRunner o controlarse a través de una entrada externa.

// 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

Invoca una herramienta en un servidor MCP (Modelo de Protocolo de Contexto).

- 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

Con el nombre de conexión para escenarios hospedados:

- 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

Propiedades:

Propiedad Obligatorio Description
serverUrl Dirección URL del servidor MCP
serverLabel No Etiqueta legible por humanos para el servidor
toolName Nombre de la herramienta que se va a invocar
conversationId No Identificador de contexto de conversación
requireApproval No Si se necesita la aprobación del usuario
arguments No Argumentos que se van a pasar a la herramienta
headers No Encabezados HTTP personalizados para la solicitud
connection.name No Conexión con nombre para escenarios hospedados (se conecta a ProjectConnectionId en Foundry; aún no se admite completamente)
output.result No Ruta de almacenamiento del resultado de la herramienta
output.messages No Ruta de acceso para almacenar mensajes de resultado
output.autoSend No Enviar automáticamente el resultado al usuario

Programa de instalación de C# para InvokeMcpTool:

Configura el McpToolHandler en tu fábrica de flujos de trabajo.

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
};

Acciones humanas en el bucle

Pregunta

Formula una pregunta al usuario y almacena la respuesta.

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

Propiedades:

Propiedad Obligatorio Description
question.text La pregunta que se va a formular
variable Ruta de acceso para almacenar la respuesta
default No Valor predeterminado si no hay respuesta

RequestExternalInput

Solicita la entrada de un sistema externo o un proceso.

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

Propiedades:

Propiedad Obligatorio Description
prompt.text Descripción de la entrada necesaria
variable Ruta de acceso para almacenar la entrada
default No Valor predeterminado

Acciones de control de flujo de trabajo

EndWorkflow

Finaliza la ejecución del flujo de trabajo.

- kind: EndWorkflow
  id: finish
  displayName: End workflow

EndConversation

Finaliza la conversación actual.

- kind: EndConversation
  id: end_chat
  displayName: End conversation

CrearConversación

Crea un nuevo contexto de conversación.

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

Propiedades:

Propiedad Obligatorio Description
conversationId Ruta de almacenamiento del nuevo ID de conversación

Acciones de conversación (solo C#)

AddConversationMessage

Agrega un mensaje a un hilo de conversación.

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

Propiedades:

Propiedad Obligatorio Description
conversationId Identificador de conversación de destino
message Mensaje que se va a agregar
message.role Rol de mensaje (sistema, usuario, asistente)
message.content Contenido del mensaje

CopiarMensajesDeConversación

Copia mensajes de una conversación a otra.

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

Propiedades:

Propiedad Obligatorio Description
sourceConversationId Identificador de conversación de origen
targetConversationId Identificador de conversación de destino
limit No Número máximo de mensajes que se van a copiar

ObtenerMensajeDeConversación

Recupera un mensaje específico de una conversación.

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

Propiedades:

Propiedad Obligatorio Description
conversationId Identificador de conversación
messageId Identificador de mensaje que se va a recuperar
variable Ruta de acceso para almacenar el mensaje recuperado

RecuperarMensajesDeConversación

Recupera varios mensajes de una conversación.

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

Propiedades:

Propiedad Obligatorio Description
conversationId Identificador de conversación
limit No Número máximo de mensajes que se van a recuperar (valor predeterminado: 20)
newestFirst No Retornar en orden descendente
after No Cursor para paginación
before No Cursor para paginación
variable Ruta de acceso para almacenar mensajes recuperados

Referencia rápida de acciones

Acción Categoría C# Pitón Description
SetVariable Variable Establecimiento de una sola variable
SetMultipleVariables Variable Establecimiento de varias variables
SetTextVariable Variable Establecer una variable de texto
AppendValue Variable Anexar a lista/cadena
ResetVariable Variable Borrar una variable
ClearAllVariables Variable Borrar todas las variables
ParseValue Variable Análisis y transformación de datos
EditTableV2 Variable Modificación de datos de tabla
If Flujo de control Ramificación condicional.
ConditionGroup Flujo de control Conmutador de varias ramas
Foreach Flujo de control Iteración de la colección
RepeatUntil Flujo de control Repetir el bucle hasta que se cumpla la condición
BreakLoop Flujo de control Salir del bucle actual
ContinueLoop Flujo de control Saltar a la siguiente iteración
GotoAction Flujo de control Saltar a la acción por identificador
SendActivity Salida Enviar mensaje al usuario
EmitEvent Salida Emisión de eventos personalizados
InvokeAzureAgent Agente Llamada al agente de Azure AI
InvokeFunctionTool Herramienta Invocar la función directamente
InvokeMcpTool Herramienta Invocar herramienta de servidor MCP
Question Humano en el Bucle Formular una pregunta al usuario
Confirmation Humano en el Bucle Sí/no confirmación
RequestExternalInput Humano en el Bucle Solicitud de entrada externa
WaitForInput Humano en el Bucle Esperar entrada
EndWorkflow Control de flujo de trabajo Finalizar flujo de trabajo
EndConversation Control de flujo de trabajo Finalizar conversación
CreateConversation Control de flujo de trabajo Creación de una nueva conversación
AddConversationMessage Conversación Añadir mensaje al hilo
CopyConversationMessages Conversación Copiar mensajes
RetrieveConversationMessage Conversación Obtener mensaje único
RetrieveConversationMessages Conversación Recibir varios mensajes

Patrones avanzados

Orquestación multiagente

Canalización del agente secuencial

Pase el trabajo a través de varios agentes en secuencia.

#
# 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

Configuración de C#:

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");

Enrutamiento condicional de agentes

Enrutar solicitudes a diferentes agentes en función de las condiciones.

#
# 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

Patrones de integración de herramientas

Captura previa de datos con InvokeFunctionTool

Capturar datos antes de llamar a un agente:

#
# 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)

Integración de herramientas de MCP

Llame al servidor externo mediante 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")

Prerrequisitos

Antes de comenzar, asegúrese de que tiene:

  • Python 3.10: 3.13 (Python 3.14 aún no se admite debido a la compatibilidad de PowerFx)
  • El paquete declarativo de Agent Framework instalado:
pip install agent-framework-declarative --pre

Este paquete extrae automáticamente el subyacente agent-framework-core .

Tu Primer Flujo de Trabajo Declarativo

Vamos a crear un flujo de trabajo sencillo que salude a un usuario por nombre.

Paso 1: Crear el archivo YAML

Cree un archivo denominado 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

Paso 2: Cargar y ejecutar el flujo de trabajo

Cree un archivo de Python para ejecutar el flujo de trabajo:

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}")


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

Salida esperada

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

Conceptos básicos

Espacios de nombres para variables

Los flujos de trabajo declarativos usan variables con espacio de nombres para organizar el estado:

Namespace Description Example
Local.* Variables locales para el flujo de trabajo Local.message
Workflow.Inputs.* Parámetros de entrada Workflow.Inputs.name
Workflow.Outputs.* Valores de salida Workflow.Outputs.result
System.* Valores proporcionados por el sistema System.ConversationId

Lenguaje de expresiones

Los valores con = prefijo se evalúan como expresiones:

# Literal value (no evaluation)
value: Hello

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

Entre las funciones comunes se incluyen las siguientes:

  • Concat(str1, str2, ...) - Concatenar cadenas
  • If(condition, trueValue, falseValue) - Expresión condicional
  • IsBlank(value) - Comprobar si el valor está vacío

Tipos de acción

Los flujos de trabajo declarativos admiten varios tipos de acciones:

Categoría Acciones
Administración de variables SetVariable, SetMultipleVariables, , AppendValue, ResetVariable
Flujo de control If, ConditionGroup, Foreach, RepeatUntil, BreakLoop, , ContinueLoopGotoAction
Salida SendActivity, EmitEvent
Invocación de agente InvokeAzureAgent
Invocación de herramientas InvokeFunctionTool
Humano en el Bucle Question, Confirmation, , RequestExternalInput, WaitForInput
Control de flujo de trabajo EndWorkflow, , EndConversation, CreateConversation

Referencia de Acciones

Las acciones son los bloques de construcción de flujos de trabajo declarativos. Cada acción realiza una operación específica y las acciones se ejecutan secuencialmente en el orden en que aparecen en el archivo YAML.

Estructura de acciones

Todas las acciones comparten propiedades comunes:

- 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...

Acciones de administración de variables

EstablecerVariable

Establece una variable en un valor especificado.

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

Con una expresión:

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

Propiedades:

Propiedad Obligatorio Description
variable Ruta variable de acceso (por ejemplo, Local.name, Workflow.Outputs.result)
value Valor que se va a establecer (literal o expresión)

Nota:

Python también admite el SetValue tipo de acción, que usa path en lugar de variable para la propiedad de destino. Tanto SetVariable (con variable) como SetValue (con path) logran el mismo resultado. Por ejemplo:

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

SetMultipleVariables

Establece varias variables en una sola acción.

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

Propiedades:

Propiedad Obligatorio Description
variables Mapa de caminos de variables a valores

AppendValue

Anexa un valor a una lista o concatena a una cadena.

- kind: AppendValue
  id: add_item
  variable: Local.items
  value: =Workflow.Inputs.newItem

Propiedades:

Propiedad Obligatorio Description
variable Ruta de acceso de la variable para anexar
value Valor para adjuntar

ResetVariable

Borra el valor de una variable.

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

Propiedades:

Propiedad Obligatorio Description
variable Ruta de acceso de la variable para reiniciar

Acciones de flujo de control

Si

Ejecuta acciones condicionalmente basadas en una condición.

- 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!"

Condiciones anidadas:

- 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"

Propiedades:

Propiedad Obligatorio Description
condition Expresión que se evalúa como verdadero/falso
then Acciones que se van a ejecutar si la condición es true
else No Acciones para ejecutar si la condición es false

ConditionGroup

Evalúa varias condiciones como una sentencia switch/case.

- 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

Propiedades:

Propiedad Obligatorio Description
conditions Lista de pares de condición/acciones (gana la primera coincidencia)
elseActions No Acciones si no coincide ninguna condición

Foreach

Recorre en iteración una colección.

- 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)

Propiedades:

Propiedad Obligatorio Description
source Expresión que devuelve una colección
itemName No Nombre de variable para el elemento actual (valor predeterminado: item)
indexName No Nombre de variable para el índice actual (valor predeterminado: index)
actions Acciones que se van a ejecutar para cada elemento

RepetirHasta

Repite las acciones hasta que una condición se vuelve verdadera.

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

- kind: RepeatUntil
  id: count_loop
  displayName: Count to 5
  condition: =Local.counter >= 5
  actions:
    - kind: SetVariable
      variable: Local.counter
      value: =Local.counter + 1
    - kind: SendActivity
      activity:
        text: =Concat("Counter: ", Local.counter)

Propiedades:

Propiedad Obligatorio Description
condition El bucle continúa hasta que sea verdadero
actions Acciones que se van a repetir

BreakLoop

Sale del bucle actual inmediatamente.

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

ContinueLoop

Omite la siguiente iteración del bucle.

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

GotoAction

Salta a una acción específica por identificador.

- 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

Propiedades:

Propiedad Obligatorio Description
actionId Identificador de la acción a la que se desea saltar

Acciones de salida

SendActivity

Envía un mensaje al usuario.

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

Con una expresión:

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

Propiedades:

Propiedad Obligatorio Description
activity La actividad que se va a enviar
activity.text Texto del mensaje (literal o expresión)

EmitirEvento

Emite un evento personalizado.

- kind: EmitEvent
  id: emit_status
  displayName: Emit status event
  eventType: order_status_changed
  data:
    orderId: =Workflow.Inputs.orderId
    status: =Local.newStatus

Propiedades:

Propiedad Obligatorio Description
eventType Identificador de tipo para el evento
data No Datos de carga de eventos

Acciones de invocación del agente

InvokeAzureAgent

Invoca un agente de Azure AI.

Invocación básica:

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

Con la configuración de entrada y salida:

- 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

Usando el bucle externo (continúa hasta que se cumpla la condición):

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

Propiedades:

Propiedad Obligatorio Description
agent.name Nombre del agente registrado
conversationId No Identificador de contexto de conversación
input.messages No Mensajes que se van a enviar al agente
input.arguments No Argumentos adicionales para el agente
input.externalLoop.when No Condición para continuar con el bucle del agente
output.responseObject No Ruta de acceso para almacenar la respuesta del agente
output.messages No Ruta de acceso para almacenar mensajes de conversación
output.autoSend No Enviar automáticamente respuesta al usuario

Acciones de invocación de herramientas

InvokeFunctionTool

Invoca una función de Python registrada directamente desde el flujo de trabajo sin pasar por un agente de IA.

- 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

Propiedades:

Propiedad Obligatorio Description
functionName Nombre de la función registrada que se va a invocar
arguments No Argumentos que se van a pasar a la función
output.result No Ruta de acceso para almacenar el resultado de la función
output.messages No Ruta de acceso para almacenar mensajes de función
output.autoSend No Enviar automáticamente el resultado al usuario

Configuración de Python para InvokeFunctionTool:

Las funciones deben registrarse en WorkflowFactory usando register_tool:

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"})

Acciones humanas en el bucle

Pregunta

Formula una pregunta al usuario y almacena la respuesta.

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

Propiedades:

Propiedad Obligatorio Description
question.text La pregunta que se va a formular
variable Ruta de acceso para almacenar la respuesta
default No Valor predeterminado si no hay respuesta

Confirmación

Pide al usuario una confirmación sí/no.

- kind: Confirmation
  id: confirm_delete
  displayName: Confirm deletion
  question:
    text: "Are you sure you want to delete this item?"
  variable: Local.confirmed

Propiedades:

Propiedad Obligatorio Description
question.text Pregunta de confirmación
variable Ruta para almacenar el resultado booleano

RequestExternalInput

Solicita la entrada de un sistema externo o un proceso.

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

Propiedades:

Propiedad Obligatorio Description
prompt.text Descripción de la entrada necesaria
variable Ruta de acceso para almacenar la entrada
default No Valor predeterminado

WaitForInput

Pausa el flujo de trabajo y espera la entrada externa.

- kind: WaitForInput
  id: wait_for_response
  variable: Local.externalResponse

Propiedades:

Propiedad Obligatorio Description
variable Ruta de acceso para almacenar la entrada cuando se recibe

Acciones de control de flujo de trabajo

EndWorkflow

Finaliza la ejecución del flujo de trabajo.

- kind: EndWorkflow
  id: finish
  displayName: End workflow

EndConversation

Finaliza la conversación actual.

- kind: EndConversation
  id: end_chat
  displayName: End conversation

CrearConversación

Crea un nuevo contexto de conversación.

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

Propiedades:

Propiedad Obligatorio Description
conversationId Ruta de almacenamiento del nuevo ID de conversación

Referencia rápida de acciones

Acción Categoría Description
SetVariable Variable Establecimiento de una sola variable
SetMultipleVariables Variable Establecimiento de varias variables
AppendValue Variable Anexar a lista/cadena
ResetVariable Variable Borrar una variable
If Flujo de control Ramificación condicional.
ConditionGroup Flujo de control Conmutador de varias ramas
Foreach Flujo de control Iteración de la colección
RepeatUntil Flujo de control Repetir el bucle hasta que se cumpla la condición
BreakLoop Flujo de control Salir del bucle actual
ContinueLoop Flujo de control Saltar a la siguiente iteración
GotoAction Flujo de control Saltar a la acción por identificador
SendActivity Salida Enviar mensaje al usuario
EmitEvent Salida Emisión de eventos personalizados
InvokeAzureAgent Agente Llamada al agente de Azure AI
InvokeFunctionTool Herramienta Invocar función registrada
Question Humano en el Bucle Formular una pregunta al usuario
Confirmation Humano en el Bucle Sí/no confirmación
RequestExternalInput Humano en el Bucle Solicitud de entrada externa
WaitForInput Humano en el Bucle Esperar entrada
EndWorkflow Control de flujo de trabajo Finalizar flujo de trabajo
EndConversation Control de flujo de trabajo Finalizar conversación
CreateConversation Control de flujo de trabajo Creación de una nueva conversación

Sintaxis de expresión

Los flujos de trabajo declarativos usan un lenguaje de expresiones similar a PowerFx para administrar valores dinámicos de estado y proceso. Los valores con = prefijo se evalúan como expresiones en tiempo de ejecución.

Detalles del espacio de nombres de variables

Namespace Description Acceso
Local.* Variables locales de flujo de trabajo Lectura y escritura
Workflow.Inputs.* Parámetros de entrada pasados al flujo de trabajo Solo lectura
Workflow.Outputs.* Valores devueltos desde el flujo de trabajo Lectura y escritura
System.* Valores proporcionados por el sistema Solo lectura
Agent.* Resultados de invocaciones del agente Solo lectura

Variables del sistema

Variable Description
System.ConversationId Identificador de conversación actual
System.LastMessage El mensaje más reciente
System.Timestamp Marca de tiempo actual

Variables del agente

Después de invocar un agente, acceda a los datos de respuesta a través de la variable de salida:

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

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

Valores literales frente a valores de expresión

# 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

Operaciones con cadenas

Concat

Concatenar varias cadenas:

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

Compruebe si un valor está vacío o no definido:

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

Expresiones condicionales

If (función)

Devuelve valores diferentes en función de una condición:

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"))

Operadores de comparación

Operador Description Example
= Igual a =Workflow.Inputs.status = "active"
<> No es igual a =Workflow.Inputs.status <> "deleted"
< Menor que =Workflow.Inputs.age < 18
> Mayor que =Workflow.Inputs.count > 0
<= Menor o igual que =Workflow.Inputs.score <= 100
>= Mayor o igual que =Workflow.Inputs.quantity >= 1

Funciones booleanas

# 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))

Operaciones matemáticas

# 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

Ejemplos prácticos de expresiones

Categorización de usuarios

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

Saludo condicional

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

Validación de entrada

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")

Patrones avanzados

A medida que tus flujos de trabajo crecen en complejidad, necesitarás patrones que manejen los procesos de varios pasos, la coordinación de agentes y los escenarios interactivos.

Orquestación multiagente

Canalización del agente secuencial

Pase el trabajo por múltiples agentes en secuencia, donde cada agente se basa en el resultado del agente anterior.

Caso de uso: canalizaciones de creación de contenido en las que diferentes especialistas controlan la investigación, la escritura y la edición.

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

Configuración de Python:

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"})

Enrutamiento condicional de agentes

Enrutar solicitudes a diferentes agentes en función de los resultados de entrada o intermedios.

Caso de uso: Soporte a sistemas que enrutan a agentes especializados según el tipo de problema.

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

Agente con bucle externo

Continúe la interacción del agente hasta que se cumpla una condición, como el problema que se va a resolver.

Caso de uso: admita conversaciones que continúen hasta que se resuelva el problema del usuario.

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."

Patrones de control de bucle

Conversación del agente iterador

Cree conversaciones de ida y vuelta entre agentes con iteración controlada.

Caso de uso: escenarios de alumno-profesor, simulaciones de debate o refinamiento iterativo.

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

Bucles Basados en Contador

Implemente bucles de recuento tradicionales mediante variables y 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!"

Salida anticipada con BreakLoop

Usar BreakLoop para salir de las iteraciones antes cuando se cumple una condición.

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"

Patrones de bucle humano en el bucle

Encuesta interactiva

Recopile varios fragmentos de información del usuario.

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

Flujo de trabajo de aprobación

Solicitar aprobación antes de continuar con una acción.

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: Confirmation
        id: get_approval
        question:
          text: =Concat("Do you approve this ", Workflow.Inputs.requestType, " request for $", Workflow.Inputs.amount, "?")
        variable: Local.approved

      - kind: If
        condition: =Local.approved
        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

Orquestación compleja

Flujo de trabajo de tickets de soporte

Un ejemplo completo que combina varios patrones: enrutamiento de agentes, lógica condicional y administración de conversaciones.

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.")

Procedimientos recomendados

Convenciones de nomenclatura

Use nombres claros y descriptivos para acciones y variables:

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

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

Organización de flujos de trabajo grandes

Dividir flujos de trabajo complejos en secciones lógicas con comentarios:

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
    # ...

Tratamiento de errores

Use comprobaciones condicionales para controlar posibles problemas:

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

Estrategias de prueba

  1. Iniciar sencillo: Probar los flujos básicos antes de agregar complejidad
  2. Usar valores predeterminados: proporcione valores predeterminados razonables para las entradas.
  3. Agregar registro: Usar SendActivity para la depuración durante el desarrollo
  4. Casos perimetrales de prueba: comprobación del comportamiento con entradas que faltan o no son válidas
# Debug logging example
- kind: SendActivity
  id: debug_log
  activity:
    text: =Concat("[DEBUG] Current state: counter=", Local.counter, ", status=", Local.status)

Pasos siguientes

  • Ejemplos de flujo de trabajo declarativos de C# : explore ejemplos de trabajo completos, entre los que se incluyen:
    • StudentTeacher : conversación multiagente con aprendizaje iterativo
    • InvokeMcpTool : integración de herramientas de servidor MCP
    • InvokeFunctionTool : invocación directa de funciones desde flujos de trabajo
    • FunctionTools : agente con herramientas de función
    • ToolApproval : aprobación humana para la ejecución de herramientas
    • CustomerSupport : flujo de trabajo de incidencias de soporte técnico complejo
    • DeepResearch : flujo de trabajo de investigación con varios agentes