<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Orchestration | The .NET Blog</title><link>https://thedotnetblog.com/tags/orchestration/</link><description>Articles, tutorials and insights from the .NET community.</description><generator>Hugo</generator><language>en</language><managingEditor>@thedotnetblog (The .NET Blog)</managingEditor><webMaster>@thedotnetblog</webMaster><lastBuildDate>Mon, 01 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://thedotnetblog.com/tags/orchestration/index.xml" rel="self" type="application/rss+xml"/><item><title>The Handoff Pattern: When One Agent Isn't Enough</title><link>https://thedotnetblog.com/news/emiliano-montesdeoca/maf-handoff-orchestration-pattern-tour/</link><pubDate>Mon, 01 Jun 2026 00:00:00 +0000</pubDate><author>Emiliano Montesdeoca</author><guid>https://thedotnetblog.com/news/emiliano-montesdeoca/maf-handoff-orchestration-pattern-tour/</guid><description>Microsoft Agent Framework's Handoff orchestration pattern lets agents decide who handles the next turn — without losing conversation context or breaking topology rules.</description><content:encoded>&lt;p&gt;At some point every multi-agent system outgrows a simple router. The first sign is usually when a specialist agent needs to ask a follow-up question, or realizes mid-turn that another agent should continue. A fixed pipeline breaks there. A one-shot router breaks there.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s exactly the problem the Handoff orchestration pattern in Microsoft Agent Framework is designed for.&lt;/p&gt;
&lt;h2 id="how-handoff-works"&gt;How Handoff Works&lt;/h2&gt;
&lt;p&gt;The developer declares a graph: here are the agents, here are the edges between them. The framework does the rest — it synthesizes a handoff tool per outbound edge and injects it into each agent. When an agent decides to pass control, it calls the tool. The framework enforces the topology.&lt;/p&gt;
&lt;p&gt;Three things make this different from just having agents call each other:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;One shared transcript&lt;/strong&gt; — the receiving agent sees the full conversation history. No starting from scratch.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Topology enforcement&lt;/strong&gt; — an agent can only hand off to declared targets. You catch routing bugs at authoring time, not in production.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Natural termination&lt;/strong&gt; — when the active agent finishes its turn without calling a handoff tool, the workflow yields to the user. No polling, no explicit exit conditions.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="a-minimal-example"&gt;A Minimal Example&lt;/h2&gt;
&lt;p&gt;In .NET, building a handoff workflow looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Agents.AI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Agents.AI.Workflows&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;AIAgent&lt;/span&gt; &lt;span class="n"&gt;triage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chatClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsAIAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Route to the right specialist.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Triage&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;AIAgent&lt;/span&gt; &lt;span class="n"&gt;billing&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chatClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsAIAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Handle billing questions.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Billing&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;AIAgent&lt;/span&gt; &lt;span class="n"&gt;tech&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chatClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsAIAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Handle technical support.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Tech&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;workflow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HandoffWorkflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;triage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;billing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tech&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;billing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;triage&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tech&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;triage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;billing&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Triage can send to either specialist. Both specialists can send back to triage. The graph is acyclic-friendly but supports back-edges when you need them (&amp;ldquo;I need more info&amp;rdquo; → back to research).&lt;/p&gt;
&lt;h2 id="when-to-use-handoff-and-when-not-to"&gt;When to Use Handoff (and When Not To)&lt;/h2&gt;
&lt;p&gt;Handoff is a good fit when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ownership can change mid-conversation&lt;/strong&gt; — an agent may realize it&amp;rsquo;s the wrong specialist&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Back-edges matter&lt;/strong&gt; — you might need to revisit an earlier step without restarting&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Routing decisions are fuzzy&lt;/strong&gt; — the choice to hand off is contextual and better made by the model than typed predicates&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&amp;rsquo;s &lt;em&gt;not&lt;/em&gt; the right choice when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your pipeline is fixed and sequential — use the &lt;code&gt;Sequential&lt;/code&gt; workflow for that&lt;/li&gt;
&lt;li&gt;Each step is independent — agents sharing a transcript where only one of them needed it is just noise&lt;/li&gt;
&lt;li&gt;You need strict processing guarantees — the non-determinism of model-driven routing isn&amp;rsquo;t what you want&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="back-edges-and-human-in-the-loop"&gt;Back-Edges and Human-in-the-Loop&lt;/h2&gt;
&lt;p&gt;One of the more interesting shapes Handoff enables is genuine back-edges. An agent can decide &amp;ldquo;I don&amp;rsquo;t have enough information&amp;rdquo; and route back to a research step, not with a hardcoded loop, but because the model decides it&amp;rsquo;s the right call.&lt;/p&gt;
&lt;p&gt;Human-in-the-loop interactions also compose naturally. When a specialist needs user input, the workflow yields back to the user via the default turn loop, collects the response, and resumes with full context. The agent never lost the conversation.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Handoff is one of those patterns that sounds simple but enables a lot once you internalize it: decentralized routing, shared context, enforced topology, natural termination. It&amp;rsquo;s the right next step when your agents start saying &amp;ldquo;actually, someone else should handle this.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Read the full walkthrough in the original post: &lt;a href="https://devblogs.microsoft.com/agent-framework/a-tour-of-handoff-orchestration-pattern/"&gt;A Tour of the Handoff Orchestration Pattern&lt;/a&gt;&lt;/p&gt;</content:encoded></item></channel></rss>