Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion dotnet/agent-framework-dotnet.slnx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Solution>
<Solution>
Comment thread
lokitoth marked this conversation as resolved.
Outdated
<Configurations>
<BuildType Name="Debug" />
<BuildType Name="Publish" />
Expand Down Expand Up @@ -278,6 +278,7 @@
</Folder>
<Folder Name="/Samples/03-workflows/Orchestration/">
<Project Path="samples/03-workflows/Orchestration/Handoff/Handoff.csproj" />
<Project Path="samples/03-workflows/Orchestration/Magentic/Magentic.csproj" />
</Folder>
<Folder Name="/Samples/03-workflows/Observability/">
<Project Path="samples/03-workflows/Observability/ApplicationInsights/ApplicationInsights.csproj" />
Expand Down
23 changes: 23 additions & 0 deletions dotnet/samples/03-workflows/Orchestration/Magentic/Magentic.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>

<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<NoWarn>$(NoWarn);MAAIW001;OPENAI001</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.AI.Projects" />
<PackageReference Include="Azure.Identity" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Workflows\Microsoft.Agents.AI.Workflows.csproj" />
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Foundry\Microsoft.Agents.AI.Foundry.csproj" />
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI\Microsoft.Agents.AI.csproj" />
</ItemGroup>

</Project>
195 changes: 195 additions & 0 deletions dotnet/samples/03-workflows/Orchestration/Magentic/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
// Copyright (c) Microsoft. All rights reserved.

// This sample ports the Python Magentic orchestration sample to .NET.
// A Magentic workflow coordinates a researcher and a coder, streams orchestration
// events as the plan evolves, and prints the final conversation transcript.

#pragma warning disable OPENAI001
Comment thread
lokitoth marked this conversation as resolved.
Outdated

using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Agents.AI.Workflows.Specialized.Magentic;
using Microsoft.Extensions.AI;

namespace WorkflowMagenticOrchestrationSample;

/// <summary>
/// Demonstrates Magentic orchestration with a researcher, a coder, and an LLM manager.
/// </summary>
/// <remarks>
/// Pre-requisites:
/// - An Azure AI Foundry project endpoint and model deployment must be configured.
/// - Run <c>az login</c> before executing the sample.
/// </remarks>
public static class Program
{
private const string TaskPrompt =
"I am preparing a report on the energy efficiency of different machine learning model architectures. " +
"Compare the estimated training and inference energy consumption of ResNet-50, BERT-base, and GPT-2 " +
"on standard datasets (e.g., ImageNet for ResNet, GLUE for BERT, WebText for GPT-2). " +
"Then, estimate the CO2 emissions associated with each, assuming training on an Azure Standard_NC6s_v3 " +
"VM for 24 hours. Provide tables for clarity, and recommend the most energy-efficient model " +
"per task type (image classification, text classification, and text generation).";

private static async Task Main()
{
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT")
?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
Comment thread
lokitoth marked this conversation as resolved.

// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIProjectClient projectClient = new(new Uri(endpoint), new DefaultAzureCredential());

AIAgent researcherAgent = projectClient.AsAIAgent(
deploymentName,
name: "ResearcherAgent",
description: "Specialist in research and information gathering.",
instructions: "You are a researcher. Find relevant information without doing additional computation or quantitative analysis.");

AIAgent coderAgent = projectClient.AsAIAgent(
deploymentName,
name: "CoderAgent",
description: "A helpful assistant that writes and executes code to analyze data.",
instructions: "You solve quantitative questions by writing and running code. Show the analysis and the computation process clearly.",
tools: [new HostedCodeInterpreterTool()]);

AIAgent managerAgent = projectClient.AsAIAgent(
deploymentName,
name: "MagenticManager",
description: "Orchestrator that coordinates the research and coding workflow.",
instructions: "You coordinate the team to complete complex tasks efficiently.");

Workflow workflow = new MagenticWorkflowBuilder(managerAgent)
.AddParticipants([researcherAgent, coderAgent])
.WithName("Magentic Orchestration Workflow")
.WithDescription("Coordinates a researcher and coder to solve a complex analytical task.")
.RequirePlanSignoff(false)
.WithMaxRounds(10)
.WithMaxStalls(3)
.WithMaxResets(2)
.Build();

Console.WriteLine("Building Magentic workflow...");
Console.WriteLine();
Console.WriteLine($"Task: {TaskPrompt}");
Console.WriteLine();
Console.WriteLine("Starting workflow execution...");

await using StreamingRun run = await InProcessExecution.RunStreamingAsync(
workflow,
new List<ChatMessage> { new(ChatRole.User, TaskPrompt) });

await run.TrySendMessageAsync(new TurnToken(emitEvents: true));

string? lastResponseId = null;
WorkflowOutputEvent? finalOutput = null;

await foreach (WorkflowEvent workflowEvent in run.WatchStreamAsync())
{
switch (workflowEvent)
{
case AgentResponseUpdateEvent updateEvent:
WriteStreamingUpdate(updateEvent, ref lastResponseId);
break;

case MagenticPlanCreatedEvent planCreated:
WriteMagenticMessage("Initial Plan", planCreated.FullTaskLedger.Text);
PauseIfInteractive();
break;

case MagenticReplannedEvent replanned:
WriteMagenticMessage("Replanned", replanned.FullTaskLedger.Text);
PauseIfInteractive();
break;

case MagenticProgressLedgerUpdatedEvent progressUpdated:
WriteMagenticMessage("Progress Ledger", FormatProgressLedger(progressUpdated.ProgressLedger));
PauseIfInteractive();
break;

case WorkflowOutputEvent outputEvent when outputEvent.Is<List<ChatMessage>>():
finalOutput = outputEvent;
break;

case WorkflowErrorEvent workflowError:
Console.ForegroundColor = ConsoleColor.Red;
Console.Error.WriteLine(workflowError.Exception?.ToString() ?? "Unknown workflow error occurred.");
Console.ResetColor();
break;

case ExecutorFailedEvent executorFailed:
Console.ForegroundColor = ConsoleColor.Red;
Console.Error.WriteLine($"Executor '{executorFailed.ExecutorId}' failed with {(executorFailed.Data is null ? "unknown error" : $"exception {executorFailed.Data}")}.");
Comment thread
lokitoth marked this conversation as resolved.
Console.ResetColor();
break;
}
}

if (finalOutput?.As<List<ChatMessage>>() is { } transcript)
{
Console.WriteLine();
Console.WriteLine(new string('=', 80));
Console.WriteLine();
Console.WriteLine("Final Conversation Transcript:");
Console.WriteLine();

foreach (ChatMessage message in transcript)
{
Console.WriteLine($"{message.AuthorName ?? message.Role.ToString()}: {message.Text}");
Console.WriteLine();
}
}
}

private static void WriteStreamingUpdate(AgentResponseUpdateEvent updateEvent, ref string? lastResponseId)
{
string responseId = updateEvent.Update.ResponseId ?? updateEvent.Update.MessageId ?? updateEvent.ExecutorId;
if (!string.Equals(responseId, lastResponseId, StringComparison.Ordinal))
{
if (lastResponseId is not null)
{
Console.WriteLine();
Console.WriteLine();
}

Console.Write($"- {updateEvent.ExecutorId}: ");
lastResponseId = responseId;
}

if (!string.IsNullOrEmpty(updateEvent.Update.Text))
{
Console.Write(updateEvent.Update.Text);
}
}

private static void WriteMagenticMessage(string title, string? content)
{
Console.WriteLine();
Console.WriteLine($"[Magentic {title}]");
Console.WriteLine(content);
}

private static string FormatProgressLedger(MagenticProgressLedger ledger) =>
string.Join(Environment.NewLine,
$"Request satisfied: {ledger.IsRequestSatisfied}",
$"In loop: {ledger.IsInLoop}",
$"Making progress: {ledger.IsProgressBeingMade}",
$"Next speaker: {ledger.NextSpeaker}",
$"Instruction: {ledger.InstructionOrQuestion}");

private static void PauseIfInteractive()
{
if (Console.IsInputRedirected)
{
return;
}

Console.Write("Press Enter to continue...");
Console.ReadLine();
Console.WriteLine();
}
Comment thread
lokitoth marked this conversation as resolved.
}
40 changes: 40 additions & 0 deletions dotnet/samples/03-workflows/Orchestration/Magentic/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Magentic Orchestration Sample

This sample ports the Python `magentic.py` workflow sample to .NET. It builds a `MagenticWorkflowBuilder` orchestration that coordinates three roles:
Comment thread
lokitoth marked this conversation as resolved.
Outdated

- **ResearcherAgent** gathers factual background information.
- **CoderAgent** uses `HostedCodeInterpreterTool` for quantitative analysis.
- **MagenticManager** plans the work, tracks progress, and decides who should act next.

## What This Sample Demonstrates

- Building a Magentic workflow with `MagenticWorkflowBuilder`
- Combining standard responses-based agents with a code interpreter-enabled participant
- Streaming orchestration events such as the initial plan, replans, and progress-ledger updates
- Printing the final multi-agent conversation transcript

## Prerequisites

- `AZURE_AI_PROJECT_ENDPOINT` set to your Azure AI Foundry project endpoint
- `AZURE_AI_MODEL_DEPLOYMENT_NAME` set to your model deployment name (defaults to `gpt-5.4-mini`)
Comment thread
lokitoth marked this conversation as resolved.
- `az login` completed before running the sample

## Running the Sample

```bash
dotnet run
```

## Expected Output

The sample prints:

1. The original task prompt
2. Streamed updates from the participating agents
3. Magentic plan and progress-ledger events as the workflow coordinates the team
4. The final conversation transcript returned by the workflow

## Related Samples

- [Handoff Orchestration](../Handoff) - another multi-agent orchestration pattern in .NET workflows
- [Python Magentic workflow sample](../../../../../python/samples/03-workflows/orchestrations/magentic.py) - the source scenario that this sample ports
1 change: 1 addition & 0 deletions dotnet/samples/03-workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,4 @@ Once completed, please proceed to the other samples listed below.
| Sample | Concepts |
|--------|----------|
| [Handoff Orchestration](./Orchestration/Handoff) | Introduces the Handoff Orchestration pattern |
| [Magentic Orchestration](./Orchestration/Magentic) | Coordinates multiple agents with a Magentic manager, streamed plan events, and a final transcript |
Loading