声明性工作流 - 概述

声明性工作流允许使用 YAML 配置文件定义工作流逻辑,而不是编写编程代码。 此方法使工作流更易于跨团队读取、修改和共享。

概述

使用声明性工作流,描述工作流需要做什么,而不是如何实现它。 框架处理基础执行,将 YAML 定义转换为可执行的工作流图。

主要优势:

  • 可读格式:YAML 语法易于理解,即使对于非开发人员也是如此
  • 可移植:无需更改代码即可共享、版本控制和修改工作流定义
  • 快速迭代:通过编辑配置文件更改工作流行为
  • 一致的结构:预定义的作类型确保工作流遵循最佳做法

何时使用声明性工作流与编程工作流

Scenario 建议的方法
标准编排模式 声明性
经常更改的工作流 声明性
非开发人员需要修改工作流 声明性
复杂的自定义逻辑 Programmatic
最大灵活性和控制 Programmatic
与现有 Python 代码集成 Programmatic

基本 YAML 结构

YAML 结构在 C# 和 Python 实现之间略有不同。 有关详细信息,请参阅下面的特定于语言的部分。

操作类型

声明性工作流支持各种操作类型,包括变量管理、控制流、代理和工具调用、HTTP 和 MCP 集成、人工循环和聊天控制。 完整的特定于语言的引用将显示在下面的每个区域中;有关这两种语言的概览可用性矩阵,请参阅本文底部的 “快速参考表 ”。

C# YAML 结构

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

结构元素

元素 必选 Description
kind 是的 必须是 Workflow
trigger.kind 是的 触发器类型(通常 OnConversationStart
trigger.id 是的 工作流的唯一标识符
trigger.actions 是的 要执行的操作列表

Python YAML 结构

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

结构元素

元素 必选 Description
name 是的 工作流的唯一标识符
description 人工可读说明
inputs 工作流接受的输入参数
actions 是的 要执行的操作列表

先决条件

在开始之前,请确保具备:

  • .NET 8.0 或更高版本
  • 具有至少一个已部署代理 的 Microsoft Foundry 项目
  • 已安装以下 NuGet 包:
dotnet add package Microsoft.Agents.AI.Workflows.Declarative --prerelease
dotnet add package Microsoft.Agents.AI.Workflows.Declarative.AzureAI --prerelease
  • 如果您打算将 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 MessageActivityEvent activityEvent:
            Console.WriteLine($"Activity: {activityEvent.Message}");
            break;
        case AgentResponseEvent responseEvent:
            Console.WriteLine($"Response: {responseEvent.Response.Text}");
            break;
        case WorkflowErrorEvent errorEvent:
            Console.WriteLine($"Error: {errorEvent.Data}");
            break;
    }
}

Console.WriteLine("Workflow completed!");

预期输出

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

核心概念

变量命名空间

C# 中的声明性工作流使用命名空间变量来组织状态:

Namespace Description Example
Local.* 工作流本地变量 Local.message
System.* 系统提供的值 System.ConversationIdSystem.LastMessage

注释

C# 声明性工作流不使用 Workflow.InputsWorkflow.Outputs 命名空间。 通过 System.LastMessage 接收输入,并通过SendActivity发送输出。

系统变量

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

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

        case AgentResponseEvent response:
            Console.WriteLine($"Agent: {response.Response.Text}");
            break;

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

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

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

从检查点恢复

可以从检查点恢复工作流,以实现容错。

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

操作参考

动作是声明性工作流的基础模块。 每个动作执行特定操作,并且动作按照 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)

性能:

资产 必选 Description
variable 是的 变量路径 (例如, Local.nameWorkflow.Outputs.result
value 是的 要设置的值(文本或表达式)

设置多个变量

在单个操作中设置多个变量。

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

性能:

资产 必选 Description
variables 是的 将变量路径映射到值

SetTextVariable (仅限 C#)

将文本变量设置为指定的字符串值。

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

性能:

资产 必选 Description
variable 是的 文本值的变量路径
value 是的 要设置的文本值

重置变量

清除变量的值。

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

性能:

资产 必选 Description
variable 是的 要重置的变量路径

ClearAllVariables (仅限 C#)

重置当前上下文中的所有变量。

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

ParseValue (仅限 C#)

将数据提取或转换为可用格式。

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

性能:

资产 必选 Description
source 是的 返回要分析的值的表达式
variable 是的 用于存储已分析结果的变量路径

EditTableV2 (仅限 C#)

修改结构化表格式的数据。

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

性能:

资产 必选 Description
table 是的 表格的可变路径
operation 是的 作类型(添加、更新、删除)
row 是的 操作的行数据

控制流操作

If

根据条件执行操作。

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

性能:

资产 必选 Description
condition 是的 计算结果为 true/false 的表达式
then 是的 如果条件为真,则执行的动作
else 如果条件为假,则执行的操作

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

性能:

资产 必选 Description
conditions 是的 条件/作对列表(第一场比赛获胜)
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)

性能:

资产 必选 Description
source 是的 返回集合的表达式
itemName 当前项的变量名称(默认值: item
indexName 当前索引的变量名称(默认值: index
actions 是的 要为每个项目执行的操作

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

按 ID 跳转到特定操作。

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

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

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

性能:

资产 必选 Description
actionId 是的 要跳转到的动作 ID

输出动作

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

性能:

资产 必选 Description
activity 是的 要发送的活动
activity.text 是的 消息文本(文本或表达式)

代理调用操作

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

性能:

资产 必选 Description
agent.name 是的 已注册代理的名称
conversationId 对话上下文标识符
input.messages 发送给代理的消息
input.arguments 代理的其他参数
input.externalLoop.when 继续代理循环的条件
output.responseObject 存储代理响应的路径
output.messages 存储对话消息的路径
output.autoSend 自动向用户发送响应

工具和 HTTP 操作

InvokeFunctionTool

直接从工作流调用函数工具,而无需通过 AI 代理。

- 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

性能:

资产 必选 Description
functionName 是的 要调用的函数的名称
conversationId 对话上下文标识符
requireApproval 是否在执行前要求用户批准
arguments 要传递给函数的参数
output.result 存储函数结果的路径
output.messages 存储函数消息的路径
output.autoSend 自动向用户发送结果

InvokeFunctionTool 的 C# 设置:

函数必须注册到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

性能:

资产 必选 Description
serverUrl 是的 MCP 服务器的 URL
serverLabel 服务器的人类可读标签
toolName 是的 要调用的工具的名称
conversationId 对话上下文标识符
requireApproval 是否需要用户批准
arguments 要传递给工具的参数
headers 请求的自定义 HTTP 标头
connection.name 托管场景的命名连接(连接到 Foundry 中的 ProjectConnectionId;尚未完全支持)
output.result 存储工具结果的路径
output.messages 存储结果消息的路径
output.autoSend 自动向用户发送结果

InvokeMcpTool 的 C# 设置:

在您的工作流工厂中配置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

通过配置的 IHttpRequestHandler 发送 HTTP 请求。 在分配之前,成功的 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

性能:

资产 必选 Description
url 是的 绝对请求 URL
method HTTP 方法;默认值为 GET
headers 请求标头
queryParameters 附加到 URL 的查询参数
body 请求正文:使用kind: jsonrawnone
requestTimeoutInMilliseconds 每个请求的超时设置
conversationId 将响应正文成功地添加到对话中
response 存储已分析响应正文的路径
responseHeaders 存储响应标头的路径

HttpRequestAction 的 C# 设置:

在生成工作流时设置 HttpRequestHandler 。 需要重试或加入 URL 白名单时,请使用自定义处理程序。

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

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

人机互动操作

问题

向用户提问并存储响应。

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

性能:

资产 必选 Description
question.text 是的 要提问的问题
variable 是的 存储响应的路径
default 如果未响应,则默认值

RequestExternalInput

从外部系统或进程请求输入。

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

性能:

资产 必选 Description
prompt.text 是的 所需输入的说明
variable 是的 存储输入的路径
default 默认值

工作流控制动作

EndWorkflow

终止工作流执行。

- kind: EndWorkflow
  id: finish
  displayName: End workflow

EndConversation

结束当前对话。

- kind: EndConversation
  id: end_chat
  displayName: End conversation

CreateConversation

创建新的对话上下文。

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

性能:

资产 必选 Description
conversationId 是的 存储新会话 ID 的路径

对话操作(仅限 C#)

添加对话消息

将消息添加到会话线程。

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

性能:

资产 必选 Description
conversationId 是的 目标会话标识符
message 是的 待添加的消息
message.role 是的 消息角色(系统、用户、助理)
message.content 是的 消息内容

复制对话消息

将消息从一个对话复制到另一个对话。

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

性能:

资产 必选 Description
sourceConversationId 是的 源对话标识符
targetConversationId 是的 目标会话标识符
limit 要复制的最大消息数

获取会话消息

从对话中检索特定消息。

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

性能:

资产 必选 Description
conversationId 是的 对话标识符
messageId 是的 待检索的消息标识符
variable 是的 存储检索到的消息的路径

检索会话消息

从会话中检索多个消息。

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

性能:

资产 必选 Description
conversationId 是的 对话标识符
limit 要检索的最大消息数(默认值:20)
newestFirst 按降序返回
after 分页的游标
before 分页的游标
variable 是的 存储检索到的消息的路径

操作快速参考

Action 类别 C# Python Description
SetVariable Variable 设置单个变量
SetMultipleVariables Variable 设置多个变量
SetTextVariable Variable 设置文本变量
AppendValue Variable 追加到列表/字符串
ResetVariable Variable 清除变量
ClearAllVariables Variable 清除所有变量
ParseValue Variable 分析/转换数据
EditTableV2 Variable 修改表数据
If 控制流 条件分支
ConditionGroup 控制流 多分支交换机
Foreach 控制流 循环访问集合
RepeatUntil 控制流 循环直到满足条件
BreakLoop 控制流 退出当前循环
ContinueLoop 控制流 跳至下一次迭代
GotoAction 控制流 跳转到动作按 ID
SendActivity 输出 向用户发送消息
EmitEvent 输出 发出自定义事件
InvokeAzureAgent 代理人 调用 Azure AI 代理
InvokeFunctionTool 工具 直接调用函数
InvokeMcpTool 工具 调用 MCP 服务器工具
HttpRequestAction HTTP 调用 HTTP 终结点
Question 人在环 向用户提问
Confirmation 人在环 是/否确认
RequestExternalInput 人在环 请求外部输入
WaitForInput 人在环 等待输入
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")

先决条件

在开始之前,请确保具备:

  • Python 3.10 - 3.13(由于 PowerFx 兼容性,尚不支持 Python 3.14)
  • 已安装的 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 Description Example
Local.* 工作流本地变量 Local.message
Workflow.Inputs.* 输入参数 Workflow.Inputs.name
Workflow.Outputs.* 输出值 Workflow.Outputs.result
System.* 系统提供的值 System.ConversationId

表达式语言

值以 = 为前缀时,将被计算为表达式:

# 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) - 检查值是否为空

操作类型

声明性工作流支持各种动作类型:

类别 行动
变量管理 SetVariableSetMultipleVariablesAppendValueResetVariable
控制流 IfConditionGroupForeachRepeatUntilBreakLoopContinueLoopGotoAction
输出 SendActivityEmitEvent
代理调用 InvokeAzureAgent
工具调用 InvokeFunctionToolInvokeMcpTool
HTTP HttpRequestAction
人在环 QuestionConfirmationRequestExternalInputWaitForInput
工作流控件 EndWorkflowEndConversationCreateConversation

操作参考

动作是声明性工作流的基础模块。 每个动作执行特定操作,并且动作按照 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)

性能:

资产 必选 Description
variable 是的 变量路径 (例如, Local.nameWorkflow.Outputs.result
value 是的 要设置的值(文本或表达式)

注释

Python 还支持SetValue操作类型,该类型使用path而不是variable来作为目标属性。 SetVariable (与variable) 和 SetValue (与path) 都取得了相同的结果。 例如:

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

设置多个变量

在单个操作中设置多个变量。

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

性能:

资产 必选 Description
variables 是的 将变量路径映射到值

AppendValue

将值追加到列表或连接到字符串。

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

性能:

资产 必选 Description
variable 是的 要追加到的变量路径
value 是的 要追加的值

重置变量

清除变量的值。

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

性能:

资产 必选 Description
variable 是的 要重置的变量路径

控制流操作

If

根据条件执行操作。

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

性能:

资产 必选 Description
condition 是的 计算结果为 true/false 的表达式
then 是的 如果条件为真,则执行的动作
else 如果条件为假,则执行的操作

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

性能:

资产 必选 Description
conditions 是的 条件/作对列表(第一场比赛获胜)
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)

性能:

资产 必选 Description
source 是的 返回集合的表达式
itemName 当前项的变量名称(默认值: item
indexName 当前索引的变量名称(默认值: index
actions 是的 要为每个项目执行的操作

重复直到

重复动作,直到条件为真。

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

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

性能:

资产 必选 Description
condition 是的 循环一直持续到此为真
actions 是的 要重复的动作

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

按 ID 跳转到特定操作。

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

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

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

性能:

资产 必选 Description
actionId 是的 要跳转到的动作 ID

输出动作

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

性能:

资产 必选 Description
activity 是的 要发送的活动
activity.text 是的 消息文本(文本或表达式)

EmitEvent

发出自定义事件。

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

性能:

资产 必选 Description
eventType 是的 事件的类型标识符
data 事件有效负载数据

代理调用操作

InvokeAzureAgent

调用 Azure AI 代理。

基本调用:

- 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

性能:

资产 必选 Description
agent.name 是的 已注册代理的名称
conversationId 对话上下文标识符
input.messages 发送给代理的消息
input.arguments 代理的其他参数
input.externalLoop.when 继续代理循环的条件
output.responseObject 存储代理响应的路径
output.messages 存储对话消息的路径
output.autoSend 自动向用户发送响应

工具和 HTTP 操作

InvokeFunctionTool

直接从工作流调用已注册的 Python 函数,而无需通过 AI 代理。

- 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

性能:

资产 必选 Description
functionName 是的 要调用的已注册函数的名称
arguments 要传递给函数的参数
output.result 存储函数结果的路径
output.messages 存储函数消息的路径
output.autoSend 自动向用户发送结果

InvokeFunctionTool 的 Python 设置:

函数必须注册到 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

通过配置的 MCPToolHandler 在 MCP 服务器上调用工具。

- 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

性能:

资产 必选 Description
serverUrl 是的 MCP 服务器 URL
toolName 是的 MCP 服务器上的工具名称
serverLabel 人工可读服务器标签
arguments 传递给工具的参数
headers 请求标头;跳过空值
connection.name 自定义处理程序的命名连接
conversationId 将成功生成的工具输出添加到对话中
requireApproval 在调用该工具之前请求审批
output.result 存储已分析工具输出的路径
output.messages 存储工具消息的路径
output.autoSend 向工作流结果发出工具输出;默认值为 true

InvokeMcpTool 的 Python 环境设置:

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

HttpRequestAction

通过配置的 HttpRequestHandler 发送 HTTP 请求。 在分配之前,成功的 JSON 响应会被解析;非 2xx 响应会导致操作失败。

- 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

性能:

资产 必选 Description
url 是的 绝对请求 URL
method HTTP 方法;默认值为 GET
headers 请求标头
queryParameters 附加到 URL 的查询参数
body 请求正文;使用kind: jsonrawnone
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")

人机互动操作

问题

向用户提问并存储响应。

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

性能:

资产 必选 Description
question.text 是的 要提问的问题
variable 是的 存储响应的路径
default 如果未响应,则默认值

确认

要求用户进行“是/否”确认。

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

性能:

资产 必选 Description
question.text 是的 确认问题
variable 是的 用于存储布尔结果的路径

RequestExternalInput

从外部系统或进程请求输入。

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

性能:

资产 必选 Description
prompt.text 是的 所需输入的说明
variable 是的 存储输入的路径
default 默认值

等待输入

暂停工作流并等待外部输入。

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

性能:

资产 必选 Description
variable 是的 接收到输入时的存储路径

工作流控制动作

EndWorkflow

终止工作流执行。

- kind: EndWorkflow
  id: finish
  displayName: End workflow

EndConversation

结束当前对话。

- kind: EndConversation
  id: end_chat
  displayName: End conversation

CreateConversation

创建新的对话上下文。

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

性能:

资产 必选 Description
conversationId 是的 存储新会话 ID 的路径

操作快速参考

Action 类别 Description
SetVariable Variable 设置单个变量
SetMultipleVariables Variable 设置多个变量
AppendValue Variable 追加到列表/字符串
ResetVariable Variable 清除变量
If 控制流 条件分支
ConditionGroup 控制流 多分支交换机
Foreach 控制流 循环访问集合
RepeatUntil 控制流 循环直到满足条件
BreakLoop 控制流 退出当前循环
ContinueLoop 控制流 跳至下一次迭代
GotoAction 控制流 跳转到动作按 ID
SendActivity 输出 向用户发送消息
EmitEvent 输出 发出自定义事件
InvokeAzureAgent 代理人 调用 Azure AI 代理
InvokeFunctionTool 工具 调用已注册的函数
InvokeMcpTool 工具 调用 MCP 服务器工具
HttpRequestAction HTTP 调用 HTTP 终结点
Question 人在环 向用户提问
Confirmation 人在环 是/否确认
RequestExternalInput 人在环 请求外部输入
WaitForInput 人在环 等待输入
EndWorkflow 工作流控件 终止工作流
EndConversation 工作流控件 结束对话
CreateConversation 工作流控件 创建新对话

表达式语法

声明性工作流使用类似于 PowerFx 的表达式语言来管理状态和计算动态值。 带有前缀 = 的值在运行时作为表达式计算。

变量命名空间详细信息

Namespace Description Access
Local.* 工作流局部变量 读/写
Workflow.Inputs.* 传递给工作流的输入参数 只读
Workflow.Outputs.* 从工作流返回的值 读/写
System.* 系统提供的值 只读
Agent.* 代理调用的结果 只读

系统变量

Variable Description
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

字符串操作

Concat

连接多个字符串:

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

条件表达式

If 函数

根据条件返回不同的值:

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

比较运算符

Operator Description 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

基于计数器的循环

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

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

复杂编排

支持票证工作流

组合多个模式的综合示例:代理路由、条件逻辑和聊天管理。

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 - 具有多个代理的研究工作流