In my previous post, I showed you how to build a single C# AI agent with the Microsoft Agent Framework (MAF) — streaming, function tools, and multi-turn memory — using a free model through OpenRouter.
A single agent is a great start. But the real reason frameworks like MAF exist is the next step: getting multiple agents to work together. In this post, I’ll show you two ways to do that, building on the same project:
- A sequential workflow — two agents in a pipeline, where you decide the order.
- An orchestrator agent — a top-level agent that decides for itself which specialists to call.
As before, everything runs on the same free NVIDIA model via OpenRouter — no paid keys required.
The full source is on GitHub: MicrosoftAgentFrameworkDemo (see samples/02-multi-agent-sequential and samples/03-orchestrator-agent).
Why multi-agent?
Before any code, let’s talk about why you’d want more than one agent — because this is where the real value is, and it’s easy to miss if you jump straight to the API.
Imagine you want an AI to write a marketing tagline and critique it. You could cram everything into one giant agent:
“You are a copywriter AND an editor. Write a tagline, then put on your editor hat, review your own work, fix it, and explain what you changed…”
That works for a demo, but it has real problems:
- One bloated prompt doing several jobs — hard to read, hard to tune.
- Mixed concerns — the “writer” voice and the “editor” voice fight inside one set of instructions.
- No real second opinion — an agent grading its own homework rarely pushes back on itself.
The multi-agent approach flips this. You create small, focused specialist agents — each with one job and its own tight instructions:
- A Writer that only knows how to draft a punchy tagline.
- A Reviewer that only knows how to sharpen a draft and say what it changed.
The benefits are the kind you feel as a developer:
- Separation of concerns — each agent’s instructions stay short and clear.
- A genuine review step — a separate Reviewer actually critiques the Writer’s output.
- Easier to reason about, test, and evolve — you can tweak one agent without touching the other, or even point each agent at a different model.
So the question becomes: once you have specialist agents, how do you coordinate them? MAF gives you two answers.
Two ways to coordinate agents
| Pattern | Who decides the order? | When to reach for it |
|---|---|---|
| Sequential workflow (Sample 02) | You, in C# code | The steps are known and fixed (draft → review) |
| Orchestrator agent (Sample 03) | The LLM | The path can vary — let the model plan the work |
Let’s build both.
Prerequisites
- .NET 10 SDK
- A free OpenRouter API key — see the setup walkthrough in my Sample 01 post (sign up, generate a key, drop it into
appsettings.Development.json).
Both samples reuse the exact OpenRouter client setup from Sample 01 — OpenRouter is OpenAI-compatible, so we point the OpenAI SDK at OpenRouter’s endpoint and wrap it as an IChatClient. One small note on packages:
- Sample 02 adds one NuGet package:
Microsoft.Agents.AI.Workflows(for the workflow builder). - Sample 03 needs no new package — agents-as-tools lives in the core
Microsoft.Agents.AI.
Part 1 — Sample 02: a sequential multi-agent workflow
Our scenario is a tiny content pipeline: you type a product or topic, the Writer drafts a tagline, and the Reviewer sharpens it. The Writer’s output flows into the Reviewer automatically.
It starts with two ordinary agents — exactly like Sample 01, nothing special:
AIAgent writer = chatClient.AsAIAgent( instructions: """ You are a concise copywriter. Given a product or topic, write ONE punchy marketing tagline (max ~12 words). Output only the tagline — no preamble. """, name: "Writer");AIAgent reviewer = chatClient.AsAIAgent( instructions: """ You are a sharp marketing editor. You will be given a draft tagline. Rewrite it to be punchier and clearer, then on a new line add exactly: Changed: <one short sentence on what you changed and why> """, name: "Reviewer");
Now the one line that turns those two agents into a pipeline:
Key MAF Concept —
AgentWorkflowBuilder.BuildSequentialComposes agents into a pipeline. The Writer’s output automatically becomes the Reviewer’s input, in the order you list them. You don’t write any “now call the reviewer” routing code.
Workflow workflow = AgentWorkflowBuilder.BuildSequential([writer, reviewer]);
To run it, MAF executes the whole workflow in-process and streams events back as each agent works:
var messages = new List<ChatMessage> { new(ChatRole.User, userInput) };await using StreamingRun run = await InProcessExecution.RunStreamingAsync(workflow, messages);await run.TrySendMessageAsync(new TurnToken(emitEvents: true));string? lastExecutorId = null;await foreach (WorkflowEvent evt in run.WatchStreamAsync()){ if (evt is AgentResponseUpdateEvent update) // a streamed text chunk from an agent { if (update.ExecutorId != lastExecutorId) // a new agent started talking { lastExecutorId = update.ExecutorId; Console.Write($"\n[{update.ExecutorId}]: "); } Console.Write(update.Update.Text); } else if (evt is WorkflowOutputEvent) // the pipeline finished { break; }}
The key thing to notice is the event stream: each AgentResponseUpdateEvent tells you which agent (ExecutorId) is producing text. (One small detail: the workflow gives each agent an ExecutorId like Writer_a1b2c3…, so in the repo I trim the suffix to print a clean [Writer] label.) When you run it:

The real value here: the Reviewer never sees “you are also a writer.” It only ever receives “improve this draft”.
Part 2 — Sample 03: an orchestrator agent
The sequential workflow is clean, but notice its limitation: the order is hardcoded. BuildSequential([writer, reviewer]) will always run the Writer once and the Reviewer once, in that order — because I wrote it that way.
But what if I want the agent to decide? Draft once, maybe review twice, maybe skip the review if the draft is already great? For that, I don’t want a fixed pipeline — I want an orchestrator agent that’s smart enough to plan the work itself.
The trick is in MAF, an agent can be turned into a tool, and (from Sample 01) we know agents can call tools.
Key MAF Concept —
AsAIFunction():agent.AsAIFunction()turns an entire agent into a callable tool. So you can hand the Writer and Reviewer to another agent as its tools — and let that agent decide when to use each one.
// Turn each specialist agent into a tool:AITool writerTool = writer.AsAIFunction();AITool reviewerTool = reviewer.AsAIFunction();// The Orchestrator is itself an agent — and its "tools" are agents.// The LLM decides when to draft, when to review, and when it's done.AIAgent orchestrator = chatClient.AsAIAgent( instructions: """ You produce a polished marketing tagline by delegating — never write one yourself. Steps you must follow: 1. Call the Writer tool to create a first-draft tagline. 2. Call the Reviewer tool to improve that draft. 3. Reply with one line: Final: <the improved tagline> """, name: "Orchestrator", description: "Coordinates the Writer and Reviewer agents.", tools: [writerTool, reviewerTool]);
That’s the whole idea — but there’s one problem: when the orchestrator delegates, it happens inside the model, so you can’t see it. MAF lets us peek in with middleware:
Key MAF Concept — function-invocation middleware :
agent.AsBuilder().Use(...)wraps the agent so you can observe (or modify) every tool call. We use it to log each delegation — which also reassures you the model is working.
AIAgent orchestrator = chatClient.AsAIAgent( /* ...as above... */ ) .AsBuilder() .Use(async (innerAgent, context, next, cancellationToken) => { Console.WriteLine($"\n [🔧 Orchestrator → {context.Function.Name}]"); return await next(context, cancellationToken); }) .Build();
Now run it and you can watch the orchestrator think:

Sequential vs Orchestrator — when to use which
Both patterns coordinate the same two agents. The difference is who’s in charge of the flow.
| Sequential workflow (02) | Orchestrator agent (03) | |
|---|---|---|
| Who decides the order? | Your code (BuildSequential) | The orchestrator’s LLM |
| Writer & Reviewer are… | pipeline stages | tools the orchestrator can call |
| Can it skip or repeat a step? | No — fixed | Yes — the model decides |
| Predictability | Deterministic, easy to test | Flexible, but depends on the model |
| Best for | Known, fixed steps | Variable paths, “let the model plan” |
Rule of thumb: if the steps are known and always the same, use a sequential workflow. If the path can vary and you want the agent to figure it out, reach for an orchestrator agent.
- Full source code on GitHub: MicrosoftAgentFrameworkDemo (
samples/02-multi-agent-sequential,samples/03-orchestrator-agent) - Official docs: Microsoft Agent Framework — MS Learn and Workflow Orchestrations
- Start here if you missed it: Getting Started with Microsoft Agent Framework – Build a C# AI Agent
🙂



Leave a comment