Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Декларативные рабочие процессы позволяют определять логику рабочего процесса с помощью файлов конфигурации 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
- Базовое знакомство с синтаксисом YAML
- Понимание концепций рабочих процессов
Первый декларативный рабочий процесс
Давайте создадим простой рабочий процесс, который приветствует пользователя на основе их входных данных.
Шаг 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 пакет.
- Базовое знакомство с синтаксисом YAML
- Понимание концепций рабочих процессов
Первый декларативный рабочий процесс
Создадим простой рабочий процесс, который приветствует пользователя по имени.
Шаг 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
Стратегии тестирования
- Начните с простого: проверка базовых процессов перед добавлением сложности
- Использование значений по умолчанию: предоставление разумных значений по умолчанию для входных данных
- Добавление ведения журнала. Использование SendActivity для отладки во время разработки
- Тестовые варианты пограничных вычислений: проверка поведения с отсутствующими или недопустимыми входными данными
# 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 — рабочий процесс исследования с несколькими агентами
- Примеры декларативного рабочего процесса Python . Изучение полных рабочих примеров
Замечание
Поддержка Go для этой функции скоро появится. Сведения о последнем состоянии см. в репозитории Agent Framework Go .