Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This tutorial demonstrates how to create a simple sequential workflow using Agent Framework Workflows.
Sequential workflows are the foundation of building complex AI agent systems. This tutorial shows how to create a simple two-step workflow where each step processes data and passes it to the next step.
Overview
In this tutorial, you'll create a workflow with two executors:
- Uppercase Executor - Converts input text to uppercase
- Reverse Text Executor - Reverses the text and outputs the final result
The workflow demonstrates core concepts like:
- Creating custom executors that implement
IMessageHandler<TInput, TOutput> - Using
WorkflowBuilderto connect executors with edges - Processing data through sequential steps
- Observing workflow execution through events
Prerequisites
- .NET 8.0 SDK or later
- No external AI services required for this basic example
- A new console application
Step-by-Step Implementation
The following sections show how to build the sequential workflow step by step.
Step 1: Install NuGet packages
First, install the required packages for your .NET project:
dotnet add package Microsoft.Agents.AI.Workflows --prerelease
Step 2: Define the Uppercase Executor
Define an executor that converts text to uppercase:
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Agents.AI.Workflows;
/// <summary>
/// First executor: converts input text to uppercase.
/// </summary>
internal sealed class UppercaseExecutor() : ReflectingExecutor<UppercaseExecutor>("UppercaseExecutor"),
IMessageHandler<string, string>
{
public ValueTask<string> HandleAsync(string input, IWorkflowContext context, CancellationToken cancellationToken = default)
{
// Convert input to uppercase and pass to next executor
return ValueTask.FromResult(input.ToUpper());
}
}
Key Points:
- Inherits from
ReflectingExecutor<T>for basic executor functionality - Implements
IMessageHandler<string, string>- takes string input, produces string output - The
HandleAsyncmethod processes the input and returns the result
Step 3: Define the Reverse Text Executor
Define an executor that reverses the text:
/// <summary>
/// Second executor: reverses the input text and completes the workflow.
/// </summary>
internal sealed class ReverseTextExecutor() : ReflectingExecutor<ReverseTextExecutor>("ReverseTextExecutor"),
IMessageHandler<string, string>
{
public ValueTask<string> HandleAsync(string input, IWorkflowContext context, CancellationToken cancellationToken = default)
{
// Reverse the input text
return ValueTask.FromResult(new string(input.Reverse().ToArray()));
}
}
Key Points:
- Same pattern as the first executor.
- Reverses the string using LINQ's
Reverse()method. - This will be the final executor in the workflow.
Step 4: Build and Connect the Workflow
Connect the executors using WorkflowBuilder:
// Create the executors
UppercaseExecutor uppercase = new();
ReverseTextExecutor reverse = new();
// Build the workflow by connecting executors sequentially
WorkflowBuilder builder = new(uppercase);
builder.AddEdge(uppercase, reverse).WithOutputFrom(reverse);
var workflow = builder.Build();
Key Points:
WorkflowBuilderconstructor takes the starting executorAddEdge()creates a directed connection from uppercase to reverseWithOutputFrom()specifies which executors produce workflow outputsBuild()creates the immutable workflow
Step 5: Execute the Workflow
Run the workflow and observe the results:
// Execute the workflow with input data
await using Run run = await InProcessExecution.RunAsync(workflow, "Hello, World!");
foreach (WorkflowEvent evt in run.NewEvents)
{
switch (evt)
{
case ExecutorCompletedEvent executorComplete:
Console.WriteLine($"{executorComplete.ExecutorId}: {executorComplete.Data}");
break;
case WorkflowOutputEvent workflowOutput:
Console.WriteLine($"Workflow '{workflowOutput.SourceId}' outputs: {workflowOutput.Data}");
break;
}
}
Step 6: Understanding the Workflow Output
When you run the workflow, you'll see output like:
UppercaseExecutor: HELLO, WORLD!
ReverseTextExecutor: !DLROW ,OLLEH
The input "Hello, World!" is first converted to uppercase ("HELLO, WORLD!"), then reversed ("!DLROW ,OLLEH").
Key Concepts Explained
Executor Interface
Executors implement IMessageHandler<TInput, TOutput>:
- TInput: The type of data this executor accepts
- TOutput: The type of data this executor produces
- HandleAsync: The method that processes the input and returns the output
.NET Workflow Builder Pattern
The WorkflowBuilder provides a fluent API for constructing workflows:
- Constructor: Takes the starting executor
- AddEdge(): Creates directed connections between executors
- WithOutputFrom(): Specifies which executors produce workflow outputs
- Build(): Creates the final immutable workflow
.NET Event Types
During execution, you can observe these event types:
ExecutorCompletedEvent- When an executor finishes processingWorkflowOutputEvent- Contains the final workflow result (for streaming execution)
Running the .NET Example
- Create a new console application
- Install the
Microsoft.Agents.AI.WorkflowsNuGet package - Combine all the code snippets from the steps above into your
Program.cs - Run the application
The workflow will process your input through both executors and display the results.
Complete .NET Example
For the complete, ready-to-run implementation, see the 01_ExecutorsAndEdges sample in the Agent Framework repository.
This sample includes:
- Full implementation with all using statements and class structure
- Additional comments explaining the workflow concepts
- Complete project setup and configuration
Overview
In this tutorial, you'll create a workflow with two executors:
- Upper Case Executor - Converts input text to uppercase
- Reverse Text Executor - Reverses the text and outputs the final result
The workflow demonstrates core concepts like:
- Using the
@executordecorator to create workflow nodes - Connecting executors with
WorkflowBuilder - Passing data between steps with
ctx.send_message() - Yielding final output with
ctx.yield_output() - Streaming events for real-time observability
Prerequisites
- Python 3.10 or later
- Agent Framework Core Python package installed:
pip install agent-framework-core - No external AI services required for this basic example
Step-by-Step Implementation
The following sections show how to build the sequential workflow step by step.
Step 1: Import Required Modules
First, import the necessary modules from Agent Framework:
import asyncio
from typing_extensions import Never
from agent_framework import WorkflowBuilder, WorkflowContext, WorkflowOutputEvent, executor
Step 2: Create the First Executor
Create an executor that converts text to uppercase using the @executor decorator:
@executor(id="upper_case_executor")
async def to_upper_case(text: str, ctx: WorkflowContext[str]) -> None:
"""Transform the input to uppercase and forward it to the next step."""
result = text.upper()
# Send the intermediate result to the next executor
await ctx.send_message(result)
Key Points:
- The
@executordecorator registers this function as a workflow node WorkflowContext[str]indicates this executor sends a string downstreamctx.send_message()passes data to the next step
Step 3: Create the Second Executor
Create an executor that reverses the text and yields the final output:
@executor(id="reverse_text_executor")
async def reverse_text(text: str, ctx: WorkflowContext[Never, str]) -> None:
"""Reverse the input and yield the workflow output."""
result = text[::-1]
# Yield the final output for this workflow run
await ctx.yield_output(result)
Key Points:
WorkflowContext[Never, str]indicates this is a terminal executorctx.yield_output()provides the final workflow result- The workflow completes when it becomes idle
Step 4: Build the Workflow
Connect the executors using WorkflowBuilder:
workflow = (
WorkflowBuilder()
.add_edge(to_upper_case, reverse_text)
.set_start_executor(to_upper_case)
.build()
)
Key Points:
add_edge()creates directed connections between executorsset_start_executor()defines the entry pointbuild()finalizes the workflow
Step 5: Run the Workflow with Streaming
Execute the workflow and observe events in real-time:
async def main():
# Run the workflow and stream events
async for event in workflow.run_stream("hello world"):
print(f"Event: {event}")
if isinstance(event, WorkflowOutputEvent):
print(f"Workflow completed with result: {event.data}")
if __name__ == "__main__":
asyncio.run(main())
Step 6: Understanding the Output
When you run the workflow, you'll see events like:
Event: ExecutorInvokedEvent(executor_id=upper_case_executor)
Event: ExecutorCompletedEvent(executor_id=upper_case_executor)
Event: ExecutorInvokedEvent(executor_id=reverse_text_executor)
Event: ExecutorCompletedEvent(executor_id=reverse_text_executor)
Event: WorkflowOutputEvent(data='DLROW OLLEH', source_executor_id=reverse_text_executor)
Workflow completed with result: DLROW OLLEH
Key Concepts Explained
Workflow Context Types
The WorkflowContext generic type defines what data flows between executors:
WorkflowContext[str]- Sends a string to the next executorWorkflowContext[Never, str]- Terminal executor that yields workflow output of type string
Event Types
During streaming execution, you'll observe these event types:
ExecutorInvokedEvent- When an executor starts processingExecutorCompletedEvent- When an executor finishes processingWorkflowOutputEvent- Contains the final workflow result
Python Workflow Builder Pattern
The WorkflowBuilder provides a fluent API for constructing workflows:
- add_edge(): Creates directed connections between executors
- set_start_executor(): Defines the workflow entry point
- build(): Finalizes and returns an immutable workflow object
Running the Example
- Combine all the code snippets from the steps above into a single Python file
- Save it as
sequential_workflow.py - Run with:
python sequential_workflow.py
The workflow will process the input "hello world" through both executors and display the streaming events.
Complete Example
For the complete, ready-to-run implementation, see the sequential_streaming.py sample in the Agent Framework repository.
This sample includes:
- Full implementation with all imports and documentation
- Additional comments explaining the workflow concepts
- Sample output showing the expected results