Декларативные рабочие процессы — обзор

Декларативные рабочие процессы позволяют определять логику рабочего процесса с помощью файлов конфигурации YAML вместо написания программного кода. Такой подход упрощает чтение, изменение и совместное использование рабочих процессов между командами.

Overview

С декларативными рабочими процессами вы описываете , что должен делать рабочий процесс, а не как его реализовать. Платформа обрабатывает базовое выполнение, преобразуя определения YAML в исполняемые графики рабочих процессов.

Ключевые преимущества:

  • Доступный для чтения формат: синтаксис YAML легко понять, даже для разработчиков, не являющихся разработчиками
  • Переносимая: определения рабочих процессов могут быть разделяемыми, версионируемыми и изменены без изменения кода
  • Быстрая итерация: изменение поведения рабочего процесса путем редактирования файлов конфигурации
  • Согласованная структура: предопределенные типы действий гарантируют, что рабочие процессы соответствуют рекомендациям

Когда использовать декларативные или программируемые рабочие процессы

Scenario Рекомендуемый подход
Стандартные шаблоны оркестрации Декларативный
Рабочие процессы, которые часто изменяются Декларативный
Людям, не являющимся разработчиками, нужно изменять рабочие процессы Декларативный
Сложная настраиваемая логика Programmatic
Максимальная гибкость и контроль Programmatic
Интеграция с существующим кодом Python Programmatic

Базовая структура YAML

Структура YAML немного отличается от реализаций C# и Python. Дополнительные сведения см. в разделах, посвященных языку.

Типы действий

Декларативные рабочие процессы поддерживают широкий спектр действий, охватывающих управление переменными, контроль потоков, вызов агента и инструмента, интеграцию HTTP и MCP, управление взаимодействием с человеком в процессе и управление беседами. Полный справочник по языку отображается в каждой зоне ниже; Сведения о матрице доступности на обоих языках см. в разделе "Краткий справочник по действиям " в нижней части этой статьи.

Структура YAML C#

Декларативные рабочие процессы C# используют структуру на основе триггеров:

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

Элементы структуры

Элемент Обязательный Описание
kind Yes Должен содержать значение Workflow.
trigger.kind Yes Тип триггера (обычно OnConversationStart)
trigger.id Yes Уникальный идентификатор рабочего процесса
trigger.actions Yes Список действий для выполнения

Структура YAML Python

Декларативные рабочие процессы Python используют структуру на основе имен с необязательными входными данными:

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

Элементы структуры

Элемент Обязательный Описание
name Yes Уникальный идентификатор рабочего процесса
description Нет Описание, доступное для чтения человеком
inputs Нет Входные параметры, которые рабочий процесс принимает
actions Yes Список действий для выполнения

Prerequisites

Прежде чем начать, убедитесь, что у вас есть следующее:

  • .NET 8.0 или более поздней версии
  • Проект Microsoft Foundry с по крайней мере одним развернутым агентом
  • Установлены следующие пакеты NuGet:
dotnet add package Microsoft.Agents.AI.Workflows.Declarative.Foundry --prerelease
  • Примеры в этой статье загружают конфигурацию из секретов пользователей и переменных среды, для которых требуются следующие пакеты NuGet:
dotnet add package Microsoft.Extensions.Configuration.EnvironmentVariables
dotnet add package Microsoft.Extensions.Configuration.UserSecrets
  • Если вы планируете добавить действие вызова средства MCP в рабочий процесс, установите следующий пакет NuGet:
dotnet add package Microsoft.Agents.AI.Workflows.Declarative.Mcp --prerelease

Первый декларативный рабочий процесс

Давайте создадим простой рабочий процесс, который приветствует пользователя на основе их входных данных.

Шаг 1. Создание ФАЙЛА YAML

Создайте файл с именем 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

Шаг 2. Настройка поставщика агента

Создайте консольное приложение C# для выполнения рабочего процесса. Сначала настройте поставщика агента, который подключается к 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());

Шаг 3. Сборка и запуск рабочего процесса

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

Ожидаемые выходные данные

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

Основные понятия

Пространства имен переменных

Декларативные рабочие процессы в C# используют переменные пространства имен для упорядочивания состояния:

Namespace Описание Example
Local.* Переменные, локальные для рабочего процесса Local.message
System.* Системные значения System.ConversationId, System.LastMessage

Замечание

Декларативные рабочие процессы C# не используют Workflow.Inputs или Workflow.Outputs пространства имен. Входные данные получаются с помощью System.LastMessage и выходные данные отправляются с помощью SendActivity действий.

Системные переменные

Variable Описание
System.ConversationId Текущий идентификатор беседы
System.LastMessage Последнее сообщение пользователя
System.LastMessage.Text Текстовое содержимое последнего сообщения

Язык выражений

Значения, префиксированные с = помощью языка выражений PowerFx, оцениваются как выражения:

# Literal value (no evaluation)
value: Hello

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

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

К общим функциям относятся:

  • Concat(str1, str2, ...) — сцепляемые строки
  • If(condition, trueValue, falseValue) — условное выражение
  • IsBlank(value) — проверьте, является ли значение пустым.
  • Upper(text) / Lower(text) — преобразование регистра
  • Find(searchText, withinText) — Поиск текста в строке
  • MessageText(message) — извлечение текста из объекта сообщения
  • UserMessage(text) — создание сообщения пользователя из текста
  • AgentMessage(text) — создание сообщения агента из текста

Параметры конфигурации

Класс DeclarativeWorkflowOptions предоставляет конфигурацию для выполнения рабочего процесса:

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

Настройка поставщика агента

Процесс рабочего AzureAgentProvider связывает вас с агентами 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,
};

Выполнение рабочего процесса

Используется InProcessExecution для выполнения рабочих процессов и обработки событий. В этом примере отображаются завершенные ответы и обрабатываются входные запросы отдельно. Он также не отображает потоковые обновления или уведомления о действиях для того же ответа:

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

Выбор отображаемых событий отклика

Поток событий рабочего процесса содержит несколько представлений одного действия. Например, текстовое SendActivity действие создает:

Event Purpose
MessageActivityEvent Уведомление о действии, содержащее текст сообщения.
AgentResponseUpdateEvent Содержащий AgentResponseUpdate текст для потребителей потоковой передачи.
AgentResponseEvent Завершенный AgentResponse, доступный как выходные данные рабочего процесса.

Эти события не представляют собой три отдельных сообщения или три выполнения действия. При печати текста из каждого события отображается одно и то же сообщение.

Не существует параметра YAML SendActivity или Question, который позволяет выбрать AgentResponseEvent вместо AgentResponseUpdateEvent. Выберите, что отображает обработчик событий C#:

  • Для завершённых ответов обрабатывайте AgentResponseEvent, как в примере выше. Вы по-прежнему можете использовать RunStreamingAsync, чтобы оперативно получать другие события рабочего процесса.
  • Для потокового текста обрабатывайте AgentResponseUpdateEvent. Не добавляйте текст завершённого ответа повторно, если его сообщения уже были переданы потоково. Для SendActivity обновление и завершенный ответ используют один и тот же идентификатор ответа и идентификатор сообщения. Отслеживайте идентификатор исполнителя и идентификатор сообщения при сопоставлении потоковых и завершенных сообщений.
  • Обрабатывать RequestInfoEvent независимо, чтобы рабочий процесс смог получить внешние входные данные. Фильтрация текста ответа не удаляет необходимость ответа на запросы.

При использовании workflow.AsAIAgent()includeWorkflowOutputsInResponse аргумент определяет, как выходные данные рабочего процесса включаются в ответы агента. Он не изменяет события, которые выдает действие YAML и не является переключателем для отключения потоковых обновлений.

Возобновление с контрольных точек

Рабочие процессы можно возобновить из чекпоинтов для обеспечения отказоустойчивости.

// 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 и Trim-Aggressive

При публикации с Native AOT (dotnet publish -p:PublishAot=true) или при ином отключении резервного механизма на основе рефлексии у System.Text.Json (<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>) стандартный вызов CheckpointManager.CreateJson(store) завершается сбоем при фиксации контрольной точки или восстановлении состояния.

Пакет declarative-workflow поставляется с экземпляром JsonSerializerOptions, сгенерированным из исходного кода, DeclarativeWorkflowJsonOptions.Default, который охватывает все типы declarative-package, проходящие через конвейер контрольных точек. Передайте его как второй аргумент в 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);

Замечание

Передача DeclarativeWorkflowJsonOptions.Defaultтакже безопасна для использования в средах, отличных от AOT . Это обновление с прямой заменой для CheckpointManager.CreateJson(store) — приложения, использующие рефлексию, не заметят никаких изменений в поведении. Используйте это безоговорочно, чтобы тот же код продолжал работать, если позже вы выполните публикацию с AOT или триммингом.

DeclarativeWorkflowJsonOptions отмечен как [Experimental("MAAI001")]. Отключите диагностику на сайте вызова или в файле проекта:

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

Регистрация определяемых пользователем типов

Если входные данные рабочего процесса, настраиваемые полезные нагрузки ActionExecutorResult.Result или аргументы запроса на утверждение непримитивных типов являются типами, определяемыми пользователем, клонируйте Default и добавьте собственный резолвер, сгенерированный исходным кодом:

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

Где MyAppJsonContext — это JsonSerializerContext, который вы определяете для типов вашего приложения:

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

Tip

Полный рабочий пример, который можно запустить, включая YAML-процесс, агент на базе AzureCliCredential и наблюдаемый режим «удалите параметры, чтобы увидеть сбой», см. AotCheckpointing пример в dotnet/samples/03-workflows/Declarative/AotCheckpointing. В примере .csproj задаётся JsonSerializerIsReflectionEnabledByDefault=false, чтобы воспроизвести режим сбоя AOT без необходимости выполнять полную AOT-публикацию.

Справочник по действиям

Действия — это стандартные блоки декларативных рабочих процессов. Каждое действие выполняет определенную операцию, и действия выполняются последовательно в том порядке, в который они отображаются в YAML-файле.

Структура действий

Все действия используют общие свойства:

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

Действия по управлению переменными

УстановитьПеременную

Задает переменную заданному значению.

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

С выражением:

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

Свойства:

Недвижимость Обязательный Описание
variable Yes Путь переменной (например, Local.name, Workflow.Outputs.result)
value Yes Значение для установки (литеральное значение или выражение)

SetMultipleVariables

Задает несколько переменных в одном действии.

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

Свойства:

Недвижимость Обязательный Описание
variables Yes Сопоставление путей переменных со значениями

SetTextVariable

Задает текстовую переменную указанным строковым значением.

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

Свойства:

Недвижимость Обязательный Описание
variable Yes Путь переменной для текстового значения
value Yes Текстовое значение для установки

ResetVariable

Очищает значение переменной.

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

Свойства:

Недвижимость Обязательный Описание
variable Yes Переменный путь для сброса

Очистить все переменные

Сбрасывает все переменные в текущем контексте.

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

ParseValue

Извлекает или преобразует данные в доступный формат.

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

Свойства:

Недвижимость Обязательный Описание
source Yes Выражение, возвращающее значение для синтаксического анализа
variable Yes Переменный путь для хранения выходных данных парсинга

EditTableV2

Изменяет данные в структурированном формате таблицы.

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

Свойства:

Недвижимость Обязательный Описание
table Yes Переменный путь к таблице
operation Yes Тип операции (добавление, обновление, удаление)
row Yes Данные строк для операции

Действия потока управления

Если

Выполняет действия условно на основе условия.

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

Свойства:

Недвижимость Обязательный Описание
condition Yes Выражение, которое оценивается как истина/ложь
then Yes Действия, выполняемые, если условие истинно
else Нет Действия для выполнения, если условие имеет значение false

ConditionGroup

Оценивает несколько условий, как это делается в операторе 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

Свойства:

Недвижимость Обязательный Описание
conditions Yes Список пар условий и действий (первые победы матча)
elseActions Нет Действия, если ни одно условие не выполняется

Foreach

Выполняет итерацию по коллекции.

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

Свойства:

Недвижимость Обязательный Описание
source Yes Выражение, возвращающее коллекцию
itemName Нет Имя переменной для текущего элемента (по умолчанию: item)
indexName Нет Имя переменной для текущего индекса (по умолчанию: index)
actions Yes Действия для каждого элемента

BreakLoop

Немедленно завершает текущий цикл.

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

ContinueLoop

Пропускает следующую итерацию цикла.

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

GotoAction

Переход к определенному действию по идентификатору.

- 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

Свойства:

Недвижимость Обязательный Описание
actionId Yes Идентификатор действия для перехода к определенному месту

Выходные действия

SendActivity

Отправляет пользователю сообщение.

Текстовая активность формирует как потоковое обновление, так и окончательный ответ, помимо уведомления об активности. См. раздел «Выберите, какие события ответа отображать», чтобы избежать повторного отображения одного и того же текста.

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

С выражением:

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

Свойства:

Недвижимость Обязательный Описание
activity Yes Задача отправки
activity.text Yes Текст сообщения (литерал или выражение)

SendMessage

Отправляет пользователю шаблонное текстовое сообщение. Используйте SendMessage , только если нужно отправить текст и не требуется полная фигура объекта действия.

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

Свойства:

Недвижимость Обязательный Описание
message Yes Текст сообщения для отправки. Используйте местозаполнители {Variable.Path}, чтобы вставить значения состояния рабочего процесса.

Замечание

Декларативная реализация рабочего процесса на C# поддерживает SendMessage. Используется SendActivity для рабочих процессов Python.

Действия вызова агента

InvokeAzureAgent

Вызывает агент Foundry.

Базовый вызов:

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

С конфигурацией ввода и вывода:

- 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

С внешним циклом (продолжается до тех пор, пока не будет выполнено условие):

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

Свойства:

Недвижимость Обязательный Описание
agent.name Yes Имя зарегистрированного агента
conversationId Нет Идентификатор контекста беседы
input.messages Нет Сообщения для отправки агенту
input.arguments Нет Дополнительные аргументы агента
input.externalLoop.when Нет Условие продолжения цикла агента
output.responseObject Нет Путь для сохранения ответа агента
output.messages Нет Путь для сохранения сообщений переписки
output.autoSend Нет Автоматическая отправка ответа пользователю

Действия инструмента и HTTP

InvokeFunctionTool

Вызывает средство-функцию непосредственно из рабочего процесса без прохождения агента ИИ.

- 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

Свойства:

Недвижимость Обязательный Описание
functionName Yes Имя вызываемой функции
conversationId Нет Идентификатор контекста беседы
requireApproval Нет Требуется ли утверждение пользователя перед выполнением
arguments Нет Аргументы для передачи в функцию
output.result Нет Путь к хранилищу результатов функции
output.messages Нет Путь для хранения сообщений функций
output.autoSend Нет Автоматическая отправка результата пользователю

Настройка C# для InvokeFunctionTool:

Функции должны быть зарегистрированы с помощью WorkflowRunner или обработаны через внешний ввод.

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

Вызывает средство на сервере протокола контекста модели (MCP).

- 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

Имя подключения для размещаемых сценариев:

- 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

Свойства:

Недвижимость Обязательный Описание
serverUrl Yes URL-адрес сервера MCP
serverLabel Нет Читаемая человеком метка для сервера
toolName Yes Имя вызываемого средства
conversationId Нет Идентификатор контекста беседы
requireApproval Нет Требуется ли утверждение пользователя
arguments Нет Аргументы для передачи средству
headers Нет Пользовательские заголовки HTTP для запроса
connection.name Нет Именованное подключение для размещенных сценариев (подключается к ProjectConnectionId в Foundry; пока полностью не поддерживается)
output.result Нет Путь к хранилищу результатов инструмента
output.messages Нет Путь к хранилищу сообщений результатов
output.autoSend Нет Автоматическая отправка результата пользователю

Настройка C# для InvokeMcpTool:

Настройте McpToolHandler в вашей фабрике конфигурации рабочих процессов:

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

Отправляет HTTP-запрос через настроенный IHttpRequestHandler. Успешные ответы JSON анализируются перед назначением; ответы, отличные от 2xx, приводят к сбою действия.

- 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

Свойства:

Недвижимость Обязательный Описание
url Yes Абсолютный URL-адрес запроса
method Нет Метод HTTP; значение по умолчанию GET
headers Нет Заголовки запроса
queryParameters Нет Параметры запроса, добавленные к URL-адресу
body Нет Текст запроса; используйте kind: json, raw или none
requestTimeoutInMilliseconds Нет Время ожидания на каждый запрос
conversationId Нет Добавляет текст успешного ответа в беседу
response Нет Путь для хранения разобранного содержимого ответа
responseHeaders Нет Путь к хранилищу заголовков ответов

Настройка C# для HttpRequestAction:

Задает HttpRequestHandler при создании рабочего процесса. Используйте пользовательский обработчик, если вам нужны повторные попытки или внесение URL-адресов в белый список.

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

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

Действия человека в цикле

Question

Задает пользователю вопрос и сохраняет ответ.

Для прямого выполнения рабочего процесса обработайте вопрос RequestInfoEvent. Его ExternalInputRequest.AgentResponse содержит запрос; свойство AgentResponse представляет собой полезную нагрузку, а не отдельный AgentResponseEvent. Верните ExternalInputResponse через запрос, чтобы возобновить процесс. Параметр autoSend, указанный ниже, управляет тем, как принятый ответ добавляется в беседу, а не тем, какие события ответа генерируются.

В SDK для C# и Python используются разные названия полей для текста вопроса. В этом примере используется наименьшее общее поведение: он запрашивает текст и сохраняет ответ в состоянии рабочего процесса.

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

Свойства:

Недвижимость Обязательный Описание
prompt Yes Шаблон действия, который отправляется как вопрос. Используйте kind: Message со списком text для подсказок сообщений.
property Yes Путь к хранилищу извлеченного ответа.
entity Yes Тип сущности, используемый для синтаксического анализа и проверки ответа. Используйте StringPrebuiltEntity для ответов в свободной форме.
alwaysPrompt Нет Принудительно запрашивает значение, даже если для целевого свойства оно уже задано.
skipQuestionMode Нет Определяет, следует ли пропускать запрос, если целевое свойство уже имеет значение. Поддерживаются значения AlwaysSkipIfVariableHasValue, SkipOnFirstExecutionIfVariableHasValue и AlwaysAsk.
repeatCount Нет Максимальное количество недопустимых или нераспознанных ответов перед сохранением defaultValue.
defaultValue Нет Значение, которое следует сохранить при достижении repeatCount при отсутствии допустимого ответа.
defaultValueResponse Нет Шаблон активности сообщения, используемый при отправке, если используется значение по умолчанию.
invalidPrompt Нет Шаблон активности сообщения, используемый для отправки, если текст ответа не соответствует настроенной сущности.
unrecognizedPrompt Нет Шаблон активности сообщения для отправки, если ответ не содержит сообщений.
autoSend Нет Добавляет корректный ответ в беседу рабочего процесса и обновляет System.LastMessage. По умолчанию — true.

Запрос внешнего ввода

Запрашивает входные данные из внешней системы или процесса.

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

Свойства:

Недвижимость Обязательный Описание
prompt.text Yes Описание обязательных входных данных
variable Yes Путь к хранилищу входных данных
default Нет Значение по умолчанию

Действия управления рабочим процессом

EndWorkflow

Завершает выполнение рабочего процесса.

- kind: EndWorkflow
  id: finish
  displayName: End workflow

ЗавершитьРазговор

Заканчивает текущую беседу.

- kind: EndConversation
  id: end_chat
  displayName: End conversation

СоздатьРазговор

Создает новый контекст беседы.

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

Свойства:

Недвижимость Обязательный Описание
conversationId Yes Путь для хранения нового идентификатора беседы

Действия беседы (только для C#)

ДобавитьСообщениеВБеседу

Добавляет сообщение в поток беседы.

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

Свойства:

Недвижимость Обязательный Описание
conversationId Yes Идентификатор целевой беседы
message Yes Сообщение для добавления
message.role Yes Роль сообщения (система, пользователь, помощник)
message.content Yes Содержимое сообщения

КопироватьСообщенияРазговора

Копирует сообщения из одной беседы в другую.

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

Свойства:

Недвижимость Обязательный Описание
sourceConversationId Yes Идентификатор исходной беседы
targetConversationId Yes Идентификатор целевой беседы
limit Нет Максимальное количество сообщений для копирования

ПолучитьСообщениеРазговора

Извлекает определенное сообщение из беседы.

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

Свойства:

Недвижимость Обязательный Описание
conversationId Yes Идентификатор беседы
messageId Yes Идентификатор сообщения для получения
variable Yes Путь к хранилищу полученного сообщения

ПолучитьСообщенияБеседы

Извлекает несколько сообщений из беседы.

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

Свойства:

Недвижимость Обязательный Описание
conversationId Yes Идентификатор беседы
limit Нет Максимальное количество полученных сообщений (по умолчанию: 20)
newestFirst Нет Возврат в порядке убывания
after Нет Курсор для разбиения на страницы
before Нет Курсор для разбиения на страницы
variable Yes Путь к хранилищу полученных сообщений

Краткий справочник по действиям

Действие Category C# Python Описание
SetVariable Variable ✅ ✅ Установка одной переменной
SetMultipleVariables Variable ✅ ✅ Задание нескольких переменных
SetTextVariable Variable ✅ ✅ Установка текстовой переменной
ResetVariable Variable ✅ ✅ Очистка переменной
ClearAllVariables Variable ✅ ✅ Очистка всех переменных
ParseValue Variable ✅ ✅ Анализ и преобразование данных
EditTableV2 Variable ✅ ✅ Изменение данных таблицы
If Управление потоком ✅ ✅ Условное ветвление
ConditionGroup Управление потоком ✅ ✅ Коммутатор с несколькими ответвлениями
Foreach Управление потоком ✅ ✅ Выполнить итерацию по коллекции
BreakLoop Управление потоком ✅ ✅ Выход из текущего цикла
ContinueLoop Управление потоком ✅ ✅ Переход к следующей итерации
GotoAction Управление потоком ✅ ✅ Переход к действию по идентификатору
SendActivity Выходные данные ✅ ✅ Отправка сообщения пользователю
SendMessage Выходные данные ✅ ❌ Отправка текстового сообщения с шаблоном
InvokeAzureAgent Агент ✅ ✅ Вызовите агента ИИ Azure
InvokeFunctionTool инструмент ✅ ✅ Вызов функции напрямую
InvokeMcpTool инструмент ✅ ✅ Вызов средства сервера MCP
HttpRequestAction HTTP ✅ ✅ Вызов конечной точки HTTP
Question Человек в контуре ✅ ✅ Задать пользователю вопрос
RequestExternalInput Человек в контуре ✅ ✅ Запрос внешних входных данных
EndWorkflow Управление рабочим процессом ✅ ✅ Завершение рабочего процесса
EndConversation Управление рабочим процессом ✅ ✅ Завершение беседы
CreateConversation Управление рабочим процессом ✅ ✅ Создать новую беседу
AddConversationMessage Разговор ✅ ❌ Добавление сообщения в тему
CopyConversationMessages Разговор ✅ ❌ Копирование сообщений
RetrieveConversationMessage Разговор ✅ ❌ Получение одного сообщения
RetrieveConversationMessages Разговор ✅ ❌ Получение нескольких сообщений

Расширенные шаблоны

Оркестрация многоагентных систем

Последовательный конвейер агента

Передавайте работу через нескольких агентов последовательно.

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

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

Условная маршрутизация агентов

Маршрутизация запросов к различным агентам на основе условий.

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

Шаблоны интеграции инструментов

Предварительное извлечение данных с помощью InvokeFunctionTool

Получение данных перед вызовом агента:

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

Вызов внешнего сервера с помощью 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")

Prerequisites

Прежде чем начать, убедитесь, что у вас есть следующее:

  • Python 3.10 — 3.13 (Python 3.14 еще не поддерживается из-за совместимости PowerFx)
  • Установленный декларативный пакет Agent Framework:
pip install agent-framework-declarative --pre

Этот пакет автоматически извлекает базовый agent-framework-core пакет.

Первый декларативный рабочий процесс

Создадим простой рабочий процесс, который приветствует пользователя по имени.

Шаг 1. Создание ФАЙЛА YAML

Создайте файл с именем 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

Шаг 2. Загрузка и запуск рабочего процесса

Создайте файл Python для выполнения рабочего процесса:

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

Ожидаемые выходные данные

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

Основные понятия

Пространства имен переменных

Декларативные рабочие процессы используют переменные пространства имен для упорядочивания состояния:

Namespace Описание Example
Local.* Переменные, локальные для рабочего процесса Local.message
Workflow.Inputs.* Входные параметры Workflow.Inputs.name
Workflow.Outputs.* Выходные значения Workflow.Outputs.result
System.* Системные значения System.ConversationId

Пути атрибута объекта

Python отличает ключи словаря от атрибутов объекта, когда путь состояния проходит по вложенным значениям. Ключи словаря сохраняют точное строковое значение, но сегмент атрибута объекта должен соответствовать [A-Za-z][A-Za-z0-9_]*.

Например, Local.profile.display_name может считывать общедоступный атрибут объекта. Local.profile._private и Local.profile.display-name не обращаются к атрибутам объекта и возвращают значение по умолчанию при поиске. Если Local.bag — словарь, Local.bag._private по-прежнему считывает значение по ключу _private. Идентификаторы UUID, дефисированные имена и другие нерегулярные строки также остаются допустимыми ключами словаря.

При переносе состояния, поддерживаемого объектом, предоставляйте общедоступные атрибуты, соответствующие поддерживаемой схеме, или сохраняйте нерегулярные имена в виде ключей словаря.

Язык выражений

Значения с префиксом = интерпретируются как выражения.

# Literal value (no evaluation)
value: Hello

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

К общим функциям относятся:

  • Concat(str1, str2, ...) — сцепляемые строки
  • If(condition, trueValue, falseValue) — условное выражение
  • IsBlank(value) — проверьте, является ли значение пустым.

Ограничения для обхода состояния PowerFx

Python проверяет декларативно заданное состояние перед записью состояния и созданием его снимков, а также перед преобразованием значений в символы PowerFx. Циклические структуры отклоняются, а не усекаются.

Каждый обход использует эти фиксированные ограничения:

  • Максимальная глубина — 64, при этом корневое значение находится на глубине 0.
  • Максимум 10 000 обрабатываемых значений, включая контейнеры и ключи сопоставлений.
  • Максимальный совокупный размер 1 048 576 строковых символов и двоичных байтов.

Повторяющиеся ссылки и псевдонимы учитываются повторно при каждом обходе. Цикл или превышение лимита вызывает ValueError при записи состояния, создании моментального снимка или при преобразовании PowerFx. Эти ограничения обхода не ограничивают выполнение выражений PowerFx или определённые приложением обработчики Python для копирования или преобразования.

Типы действий

Декларативные рабочие процессы поддерживают различные типы действий:

Category Действия
Управление переменными SetVariable, SetMultipleVariables, ResetVariable
Управление потоком If, ConditionGroup, BreakLoop, Foreach, ContinueLoop, GotoAction
Выходные данные SendActivity
Вызов агента InvokeAzureAgent
Вызов инструмента InvokeFunctionTool, InvokeMcpTool
HTTP HttpRequestAction
Человек в контуре Question, RequestExternalInput
Управление рабочим процессом EndWorkflow, EndConversation, CreateConversation

Справочник по действиям

Действия — это стандартные блоки декларативных рабочих процессов. Каждое действие выполняет определенную операцию, и действия выполняются последовательно в том порядке, в который они отображаются в YAML-файле.

Структура действий

Все действия используют общие свойства:

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

Действия по управлению переменными

УстановитьПеременную

Задает переменную заданному значению.

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

С выражением:

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

Свойства:

Недвижимость Обязательный Описание
variable Yes Путь переменной (например, Local.name, Workflow.Outputs.result)
value Yes Значение для установки (литеральное значение или выражение)

Замечание

Python также поддерживает SetValue тип действия, который использует path вместо целевого variable свойства. Оба SetVariable (с variable) и SetValue (с path) достигают одного результата. Рассмотрим пример.

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

SetMultipleVariables

Задает несколько переменных в одном действии.

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

Свойства:

Недвижимость Обязательный Описание
variables Yes Сопоставление путей переменных со значениями

ResetVariable

Очищает значение переменной.

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

Свойства:

Недвижимость Обязательный Описание
variable Yes Переменный путь для сброса

Действия потока управления

Если

Выполняет действия условно на основе условия.

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

Вложенные условия:

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

Свойства:

Недвижимость Обязательный Описание
condition Yes Выражение, которое оценивается как истина/ложь
then Yes Действия, выполняемые, если условие истинно
else Нет Действия для выполнения, если условие имеет значение false

ConditionGroup

Оценивает несколько условий, как это делается в операторе 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

Свойства:

Недвижимость Обязательный Описание
conditions Yes Список пар условий и действий (первые победы матча)
elseActions Нет Действия, если ни одно условие не выполняется

Foreach

Выполняет итерацию по коллекции.

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

Свойства:

Недвижимость Обязательный Описание
source Yes Выражение, возвращающее коллекцию
itemName Нет Имя переменной для текущего элемента (по умолчанию: item)
indexName Нет Имя переменной для текущего индекса (по умолчанию: index)
actions Yes Действия для каждого элемента

BreakLoop

Немедленно завершает текущий цикл.

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

ContinueLoop

Пропускает следующую итерацию цикла.

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

GotoAction

Переход к определенному действию по идентификатору.

- 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

Свойства:

Недвижимость Обязательный Описание
actionId Yes Идентификатор действия для перехода к определенному месту

Выходные действия

SendActivity

Отправляет пользователю сообщение.

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

С выражением:

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

Свойства:

Недвижимость Обязательный Описание
activity Yes Задача отправки
activity.text Yes Текст сообщения (литерал или выражение)

В Python текст, начинающийся с=, вычисляется и генерируется как данные. Результат выражения не обрабатывается повторно для {Variable.Path} интерполяции. Создайте шаблон напрямую, например Hello, {Local.name}!или создайте полную строку в выражении, например ="Hello, " & Local.name & "!".

Действия вызова агента

InvokeAzureAgent

Вызывает агента ИИ Azure.

Базовый вызов:

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

С конфигурацией ввода и вывода:

- 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

С внешним циклом (продолжается до тех пор, пока не будет выполнено условие):

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

Свойства:

Недвижимость Обязательный Описание
agent.name Yes Имя зарегистрированного агента
conversationId Нет Идентификатор контекста беседы
input.messages Нет Сообщения для отправки агенту
input.arguments Нет Аргументы для вычисления и добавления в входной текст агента
input.externalLoop.when Нет Условие продолжения цикла агента
output.responseObject Нет Путь для сохранения ответа агента
output.messages Нет Путь для сохранения сообщений переписки
output.autoSend Нет Автоматически отправляет ответ в выходные данные рабочего процесса. Принимает логическое значение или выражение = и имеет значение true по умолчанию.

В Python InvokeAzureAgent вычисляет каждое настроенное input.arguments значение и форматирует аргументы nonempty в виде key: value строк. Если задано input.messages, вычисляемое сообщение следует строкам аргументов. Если input.arguments содержит хотя бы один аргумент, а input.messages опущен, Python использует только текст аргумента и не добавляет запасные значения из Local.input, Local.userInput, System.LastMessage.Text или Workflow.Inputs.

Python оценивает output.autoSend по текущему состоянию рабочего процесса перед каждым вызовом, включая возобновлённые итерации внешнего цикла. Значение false подавляет только вывод автоматизированного рабочего процесса; агент всё равно запускается, а настроенные выходные данные ответа и сообщения всё равно сохраняются.

Действия инструмента и HTTP

InvokeFunctionTool

Вызывает зарегистрированную функцию Python непосредственно из рабочего процесса без прохождения агента ИИ.

- 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

Свойства:

Недвижимость Обязательный Описание
functionName Yes Имя зарегистрированной функции для вызова
arguments Нет Аргументы для передачи в функцию
output.result Нет Путь к хранилищу результата функции
output.messages Нет Путь для хранения сообщений функций
output.autoSend Нет Автоматически отправляет результат в выходные данные рабочего процесса. Принимает логическое значение или выражение = и имеет значение true по умолчанию.

Python проверяет output.autoSend по текущему состоянию рабочего процесса непосредственно перед запуском инструмента. Результат со значением False подавляет только вывод автоматизированного рабочего процесса; сам инструмент по-прежнему выполняется, а настроенные выходные данные результатов и сообщений по-прежнему сохраняются. Отклоненное утверждение завершается без оценки выражения.

Настройка Python для InvokeFunctionTool:

Функции должны быть зарегистрированы с WorkflowFactory, используя 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"})

InvokeMcpTool

Вызывает средство на сервере MCP с помощью настроенного MCPToolHandler.

- 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

Свойства:

Недвижимость Обязательный Описание
serverUrl Yes URL-адрес сервера MCP
toolName Yes Название инструмента на сервере MCP
serverLabel Нет Метка сервера, понятная человеку
arguments Нет Аргументы, переданные средству
headers Нет Заголовки запросов; Пустые значения пропускаются
connection.name Нет Именованное соединение для пользовательских обработчиков
conversationId Нет Добавляет результаты выполнения средства в беседу
requireApproval Нет Запрашивает утверждение перед вызовом инструмента
output.result Нет Путь к хранилищу выходных данных средства синтаксического анализа
output.messages Нет Путь для сохранения сообщения инструмента
output.autoSend Нет Выводит результаты инструмента в результат рабочего процесса; значение по умолчанию true

Когда requireApproval имеет значение true, среда выполнения Python привязывает вычисленные заголовки действия к запросу на утверждение. Запрос содержит имена заголовков, но не значения заголовков. Если заголовки изменяются, учетные данные поворачиваются или восстановленный устаревший запрос не имеет проверяемого состояния привязки, рабочий процесс выдает запрос на утверждение замены с новым идентификатором запроса и не вызывает средство. Защитите хранилище контрольных точек рабочего процесса, так как оно содержит отдельный ключ проверки. Пользовательские обработчики остаются ответственными за учетные данные, которые они разрешают за пределами действия headers.

Python настройка для InvokeMcpTool:

Передайте обработчик инструмента MCP для WorkflowFactory. Используйте пользовательский обработчик, если требуется проверка подлинности, управляемые соединения или список разрешенных URL-адресов.

from agent_framework.declarative import DefaultMCPToolHandler, WorkflowFactory

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

Без client_provider, DefaultMCPToolHandler повторно использует сеансы MCP через ограниченный кэш. С client_provider при каждом вызове создаются и закрываются отдельные инструмент MCP и сеанс, включая tools/list, даже если провайдер возвращает тот же клиент или None. Обработчик закрывает внутренне созданные резервные HTTP-клиенты, но экземпляры httpx.AsyncClient, переданные вызывающей стороной, остаются в её владении.

Вызовы, поддерживаемые поставщиком, не сохраняют состояние сеанса сервера. Если в вашем рабочем процессе требуется сохранение сеанса MCP, реализуйте пользовательский MCPToolHandler с явной аутентификацией и явно определённым сроком действия.

HttpRequestAction

Отправляет HTTP-запрос через настроенный HttpRequestHandler. Успешные ответы JSON анализируются перед назначением. Ответы, отличные от 2xx, приводят к ошибке действия. В Python ошибка включает URL-адрес запроса и код состояния, но не содержит текст ответа.

- 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

Свойства:

Недвижимость Обязательный Описание
url Yes Абсолютный URL-адрес запроса
method Нет Метод HTTP; значение по умолчанию GET
headers Нет Заголовки запроса
queryParameters Нет Параметры запроса, добавленные к URL-адресу
body Нет Текст запроса; используйте kind: json, raw или none
requestTimeoutInMilliseconds Нет Время ожидания на каждый запрос
connection.name Нет Именованное соединение для пользовательских обработчиков
conversationId Нет Добавляет текст успешного ответа в беседу
response Нет Путь для хранения разобранного содержимого ответа
responseHeaders Нет Путь к хранилищу заголовков ответов

** Настройка Python для HttpRequestAction:

Передайте обработчик HTTP-запроса в WorkflowFactory. Используйте пользовательский обработчик, если требуется проверка подлинности, повторные попытки или список разрешений URL-адресов.

from agent_framework.declarative import DefaultHttpRequestHandler, WorkflowFactory

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

Обработчик по умолчанию отклоняет URL-адреса, которые не являются абсолютными URL-адресами HTTP или HTTPS. Он нормализует URL-адрес и добавляет queryParameters после любой существующей строки запроса, сохраняя существующий порядок параметров запроса и исходное байтовое представление.

client_provider получает нормализованный снимок HttpRequestInfo. Моментальный снимок содержит вложенный URL-адрес info.url и пустой info.query_parameters; исходные сведения о запросе не изменяются. Значения по умолчанию выбранного клиента по-прежнему применяются. Поведение перенаправления по-прежнему определяется параметром клиента follow_redirects, и провайдер не вызывается снова при переходах перенаправления.

Обработчик по умолчанию повторно использует внутренний HTTP-клиент, но не сохраняет файлы cookie ответа. Если для процесса требуются файлы cookie для аутентификации, непрерывности сеанса или привязки к балансировщику нагрузки, передайте настроенный httpx.AsyncClient через client= или верните его из client_provider=. Ограничьте клиентов, использующих cookie, одним аутентифицированным субъектом и закрывайте клиентов, переданных вызывающей стороной, в своем приложении. Явные Cookie заголовки запросов и заголовки ответа Set-Cookie остаются доступными.

Действия человека в цикле

Question

Задает пользователю вопрос и сохраняет ответ.

В SDK для C# и Python используются разные названия полей для текста вопроса. В этом разделе используется минимальный общий шаблон поведения: запрашивается текст, а ответ сохраняется в состоянии рабочего процесса.

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

Свойства:

Недвижимость Обязательный Описание
question Yes Вопрос, который нужно задать. Используется question.text для вложенной формы или строкового значения для компактной формы.
variable Yes Путь к хранилищу ответа. Используйте property в качестве альтернативного имени поля верхнего уровня, если вы хотите выровняться с именем целевого поля C#.
default Нет Значение по умолчанию, включённое в метаданные запроса, которое должна применять вызывающая сторона.
choices Нет Список вариантов, добавленных для запроса метаданных. Каждый вариант может быть строкой или объектом с полем label и необязательным полем value.
allowFreeText Нет Указывает, может ли вызывающий объект принимать текст вне настроенного choices. По умолчанию — true.

Python также принимает альтернативные имена полей для совместимости. Используйте default верхнего уровня вместо defaultValue и question вместо text.

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

Запрос внешнего ввода

Запрашивает входные данные из внешней системы или процесса.

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

Свойства:

Недвижимость Обязательный Описание
prompt.text Yes Описание обязательных входных данных
variable Yes Путь к хранилищу входных данных
default Нет Значение по умолчанию

Действия управления рабочим процессом

EndWorkflow

Завершает выполнение рабочего процесса.

- kind: EndWorkflow
  id: finish
  displayName: End workflow

ЗавершитьРазговор

Заканчивает текущую беседу.

- kind: EndConversation
  id: end_chat
  displayName: End conversation

СоздатьРазговор

Создает новый контекст беседы.

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

Свойства:

Недвижимость Обязательный Описание
conversationId Yes Путь для хранения нового идентификатора беседы

Краткий справочник по действиям

Действие Category Описание
SetVariable Variable Установка одной переменной
SetMultipleVariables Variable Задание нескольких переменных
ResetVariable Variable Очистка переменной
If Управление потоком Условное ветвление
ConditionGroup Управление потоком Коммутатор с несколькими ответвлениями
Foreach Управление потоком Выполнить итерацию по коллекции
BreakLoop Управление потоком Выход из текущего цикла
ContinueLoop Управление потоком Переход к следующей итерации
GotoAction Управление потоком Переход к действию по идентификатору
SendActivity Выходные данные Отправка сообщения пользователю
InvokeAzureAgent Агент Вызовите агента ИИ Azure
InvokeFunctionTool инструмент Вызов зарегистрированной функции
InvokeMcpTool инструмент Вызов средства сервера MCP
HttpRequestAction HTTP Вызов конечной точки HTTP
Question Человек в контуре Задать пользователю вопрос
RequestExternalInput Человек в контуре Запрос внешних входных данных
EndWorkflow Управление рабочим процессом Завершение рабочего процесса
EndConversation Управление рабочим процессом Завершение беседы
CreateConversation Управление рабочим процессом Создать новую беседу

Синтаксис выражений

Декларативные рабочие процессы используют язык выражений PowerFx для управления состоянием и динамическими значениями вычислений. Значения, префиксированные с помощью, = оцениваются как выражения во время выполнения.

Сведения о пространстве имен переменной

Namespace Описание Access
Local.* Локальные переменные рабочего процесса Чтение и запись
Workflow.Inputs.* Входные параметры, переданные рабочему процессу Только для чтения
Workflow.Outputs.* Значения, возвращаемые из рабочего процесса Чтение и запись
System.* Системные значения Только для чтения
Agent.* Результаты вызовов агента Только для чтения

Системные переменные

Variable Описание
System.ConversationId Текущий идентификатор беседы
System.LastMessage Последнее сообщение
System.Timestamp Текущая метка времени

Переменные агента

После вызова агента доступ к данным ответа через выходную переменную:

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

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

Литеральные значения и значения выражений

# 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

Строковые операции

Конкатенировать

Объединение нескольких строк:

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

Проверьте, является ли значение пустым или неопределенным:

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

Условные выражения

Если функция

Возвращает разные значения на основе условия:

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

Операторы сравнения

Оператор Описание Example
= Равняется =Workflow.Inputs.status = "active"
<> Не равно =Workflow.Inputs.status <> "deleted"
< Меньше =Workflow.Inputs.age < 18
> Больше =Workflow.Inputs.count > 0
<= Меньше или равно =Workflow.Inputs.score <= 100
>= Больше или равно =Workflow.Inputs.quantity >= 1

Логические функции

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

Математические операции

# 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

Примеры практических выражений

Классификация пользователей

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

Условное приветствие

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

Проверка входных данных

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

Расширенные шаблоны

По мере увеличения сложности рабочих процессов вам потребуются шаблоны, которые обрабатывают многофакторные процессы, координацию агентов и интерактивные сценарии.

Оркестрация многоагентных систем

Последовательный конвейер агента

Передавайте работу через несколько агентов поочередно, где каждый агент строится на базе выходных данных предыдущего агента.

Вариант использования: конвейеры создания контента, в которых различные специалисты обрабатывают исследования, написание и редактирование.

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:

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

Условная маршрутизация агентов

Перенаправляйте запросы к разным агентам на основе входных или промежуточных результатов.

Вариант использования: системы поддержки, которые направляют к специализированным агентам на основе типа проблемы.

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

Агент с внешним циклом

Продолжайте процесс взаимодействия до тех пор, пока не будет выполнено условие, например, решение проблемы.

Вариант использования: поддержка бесед, которые продолжаются до устранения проблемы пользователя.

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

Шаблоны управления циклом

Беседа с итеративным агентом

Создавайте диалоги между агентами с контролируемым количеством итераций.

Вариант использования: сценарии учащихся-преподавателей, имитации дебатов или итеративное уточнение.

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

циклы Counter-Based

Реализуйте традиционные циклы подсчета с помощью переменных и 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!"

Ранний выход с помощью BreakLoop

Используйте BreakLoop для выхода из цикла рано, когда условие выполняется.

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"

Шаблоны "Человек в цикле"

Интерактивный опрос

Необходимо собрать несколько фрагментов информации от пользователя.

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

Рабочий процесс утверждения

Запросить утверждение перед продолжением действия.

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

Сложная оркестрация

Рабочий процесс запроса в службу поддержки

Полный пример объединения нескольких шаблонов: маршрутизация агентов, условная логика и управление беседами.

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

Лучшие практики

Соглашения об именах

Используйте четкие и описательные имена для действий и переменных:

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

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

Организация крупных рабочих процессов

Разорвать сложные рабочие процессы в логические разделы с комментариями:

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

Обработка ошибок

Используйте условные проверки для обработки потенциальных проблем:

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

Стратегии тестирования

  1. Начните с простого: проверка базовых процессов перед добавлением сложности
  2. Использование значений по умолчанию: предоставление разумных значений по умолчанию для входных данных
  3. Добавление ведения журнала. Использование SendActivity для отладки во время разработки
  4. Тестовые варианты пограничных вычислений: проверка поведения с отсутствующими или недопустимыми входными данными
# Debug logging example
- kind: SendActivity
  id: debug_log
  activity:
    text: '=Concat("[DEBUG] Current state: counter=", Local.counter, ", status=", Local.status)'

Дальнейшие шаги

  • Примеры декларативного рабочего процесса C# — изучение полных рабочих примеров, включая:
    • StudentTeacher — беседа с несколькими агентами с итеративным обучением
    • InvokeMcpTool — интеграция средств сервера MCP
    • InvokeFunctionTool — вызов прямой функции из рабочих процессов
    • FunctionTools — агент с инструментами функций
    • ToolApproval — одобрение человеком для исполнения инструмента
    • CustomerSupport — сложный рабочий процесс запроса в службу поддержки
    • DeepResearch — рабочий процесс исследования с несколькими агентами

Замечание

Поддержка Go для этой функции скоро появится. Сведения о последнем состоянии см. в репозитории Agent Framework Go .