<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Multi-Agent Systems on dotnetuniversity</title>
        <link>https://dotnetuniversity.com/tags/multi-agent-systems/</link>
        <description>Recent content in Multi-Agent Systems on dotnetuniversity</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-us</language>
        <lastBuildDate>Thu, 03 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://dotnetuniversity.com/tags/multi-agent-systems/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Microsoft Agent Framework IV: Workflows, Multi-Agent Systems, and Observability</title>
        <link>https://dotnetuniversity.com/microsoft-agent-framework-iv-workflows-multi-agent-systems-and-observability/</link>
        <pubDate>Thu, 03 Sep 2026 00:00:00 +0000</pubDate>
        
        <guid>https://dotnetuniversity.com/microsoft-agent-framework-iv-workflows-multi-agent-systems-and-observability/</guid>
        <description>&lt;img src="https://dotnetuniversity.com/microsoft-agent-framework-iv-workflows-multi-agent-systems-and-observability/romeo-a-9g11WIv0Ias-unsplash.jpg" alt="Featured image of post Microsoft Agent Framework IV: Workflows, Multi-Agent Systems, and Observability" /&gt;&lt;h1 id=&#34;introduction&#34;&gt;Introduction
&lt;/h1&gt;&lt;p&gt;Part III added a harness, planning, and human approval. The next problem is coordination: one agent should not perform every specialist task when a workflow can route work to focused agents, run independent checks in parallel, and combine their findings with traceable execution.&lt;/p&gt;
&lt;p&gt;This article builds a support-ticket workflow with Microsoft Agent Framework concepts and .NET. A classifier identifies the main category, billing and technical specialists investigate independently, and a synthesizer produces the next recommendation. OpenTelemetry sends workflow spans and metrics through an OpenTelemetry Collector to Tempo and Loki, while Grafana provides the operational view.&lt;/p&gt;
&lt;h1 id=&#34;real-world-scenario-a-checkout-ticket&#34;&gt;Real-World Scenario: A Checkout Ticket
&lt;/h1&gt;&lt;p&gt;A support customer writes:&lt;/p&gt;
&lt;div class=&#34;copilot-prompt text-code-block&#34;&gt;&lt;div class=&#34;copilot-prompt__header&#34;&gt;&lt;span class=&#34;text-code-block__language&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;text-code-block__title&#34;&gt;Customer Ticket&lt;/span&gt;&lt;/div&gt;&lt;div class=&#34;copilot-prompt__body&#34;&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I see a duplicate charge and checkout returned error 500 for order ORD-1001.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;One general-purpose agent could answer, but it would mix payment policy, technical diagnostics, and escalation rules. A workflow makes those responsibilities explicit:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Classify the ticket as billing-led.&lt;/li&gt;
&lt;li&gt;Ask billing and technical specialists for independent findings.&lt;/li&gt;
&lt;li&gt;Run those independent checks concurrently.&lt;/li&gt;
&lt;li&gt;Synthesize one support recommendation.&lt;/li&gt;
&lt;li&gt;Emit traces and metrics for each stage.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;mermaid&#34;&gt;flowchart LR
  C((Customer ticket)) --&gt; X[Classifier]
  X --&gt; B[Billing specialist]
  X --&gt; T[Technical specialist]
  B --&gt; S[Synthesizer]
  T --&gt; S
  S --&gt; R([Support recommendation])
  B -. telemetry .-&gt; O[(OTel Collector)]
  T -. telemetry .-&gt; O
  S -. telemetry .-&gt; O
  O --&gt; Te[(Tempo traces)]
  O --&gt; L[(Loki logs)]
  Te --&gt; G[Grafana]
  L --&gt; G

  classDef customer fill:#fef3c7,stroke:#d97706,stroke-width:2px,color:#78350f
  classDef routing fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#1e3a8a
  classDef specialist fill:#cffafe,stroke:#0891b2,stroke-width:2px,color:#164e63
  classDef synthesis fill:#fce7f3,stroke:#db2777,stroke-width:2px,color:#831843
  classDef telemetry fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d
  classDef observability fill:#e0e7ff,stroke:#4f46e5,stroke-width:2px,color:#312e81

  class C customer
  class X routing
  class B,T specialist
  class S synthesis
  class R customer
  class O telemetry
  class Te,L telemetry
  class G observability&lt;/div&gt;
&lt;p&gt;The workflow does not authorize a refund. Part III&amp;rsquo;s approval gate remains the boundary for side effects. Part IV coordinates analysis and makes execution observable.&lt;/p&gt;
&lt;h2 id=&#34;following-the-ticket-through-workflow&#34;&gt;Following the Ticket Through Workflow
&lt;/h2&gt;&lt;p&gt;For &lt;code&gt;ORD-1001&lt;/code&gt;, each executor returns a focused finding instead of a vague conversational reply:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Step&lt;/th&gt;
          &lt;th&gt;Finding&lt;/th&gt;
          &lt;th&gt;Decision&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Classifier&lt;/td&gt;
          &lt;td&gt;Billing-led, with a technical symptom&lt;/td&gt;
          &lt;td&gt;Run both specialists&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Billing specialist&lt;/td&gt;
          &lt;td&gt;Two authorization records share the same order ID; no refund executed&lt;/td&gt;
          &lt;td&gt;Verify settlement status before refund&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Technical specialist&lt;/td&gt;
          &lt;td&gt;Checkout returned HTTP 500 after payment submission&lt;/td&gt;
          &lt;td&gt;Check idempotency and correlation logs&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Synthesizer&lt;/td&gt;
          &lt;td&gt;Payment may be duplicated, but evidence is incomplete&lt;/td&gt;
          &lt;td&gt;Ask an operator to review before refund&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This example shows why fan-out and fan-in are useful. Billing can inspect payment records while technical support checks the failed request. The synthesizer sees both findings, preserves the uncertainty, and recommends approval rather than silently performing a financial action.&lt;/p&gt;
&lt;p&gt;If the technical specialist times out, the workflow should return a partial-result state or retry according to policy. It should not present an unverified “duplicate charge” as fact. A trace makes that distinction visible: the operator can see classifier, specialist, timeout, retry, and synthesis spans under one workflow trace.&lt;/p&gt;
&lt;h1 id=&#34;1-workflows-explicit-execution-graphs&#34;&gt;1. Workflows: Explicit Execution Graphs
&lt;/h1&gt;&lt;h2 id=&#34;what-problem-do-workflows-solve&#34;&gt;What problem do workflows solve?
&lt;/h2&gt;&lt;p&gt;An agent run is useful for a conversation. A workflow is useful when work has multiple steps, branches, parallel tasks, retries, checkpoints, or human input. It represents execution as a graph instead of hiding every decision inside one prompt.&lt;/p&gt;
&lt;p&gt;In Microsoft Agent Framework, executors are the graph&amp;rsquo;s work units. An executor can be ordinary application code, a function, or an AI agent. A workflow connects executors and passes messages between them.&lt;/p&gt;
&lt;div class=&#34;mermaid&#34;&gt;flowchart LR
  A[Classifier executor] --&gt; B[Billing executor]
  A --&gt; C[Technical executor]
  B --&gt; D[Fan-in and synthesis]
  C --&gt; D
  D --&gt; E[Workflow output]

  classDef routing fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#1e3a8a
  classDef specialist fill:#cffafe,stroke:#0891b2,stroke-width:2px,color:#164e63
  classDef synthesis fill:#fce7f3,stroke:#db2777,stroke-width:2px,color:#831843
  classDef result fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d

  class A routing
  class B,C specialist
  class D synthesis
  class E result&lt;/div&gt;
&lt;p&gt;The sample uses an &lt;code&gt;IWorkflowExecutor&lt;/code&gt; interface so the boundary is easy to test. In a larger application, map the same roles to &lt;code&gt;WorkflowBuilder&lt;/code&gt;, &lt;code&gt;MessageHandler&lt;/code&gt;, and framework workflow execution APIs. Keep business rules inside executors and graph wiring in the workflow composition layer.&lt;/p&gt;
&lt;h2 id=&#34;executor-design&#34;&gt;Executor design
&lt;/h2&gt;&lt;p&gt;A good executor has a narrow input and output contract:&lt;/p&gt;
&lt;div class=&#34;copilot-prompt text-code-block&#34;&gt;&lt;div class=&#34;copilot-prompt__header&#34;&gt;&lt;span class=&#34;text-code-block__language&#34;&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&#34;copilot-prompt__body&#34;&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f38ba8&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#cba6f7&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#f9e2af&#34;&gt;IWorkflowExecutor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Task&amp;lt;SpecialistFinding&amp;gt; ExecuteAsync(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        TicketRequest request,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        CancellationToken cancellationToken);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The contract makes it possible to replace a deterministic specialist with an &lt;code&gt;AIAgent&lt;/code&gt;, an HTTP service, or a durable activity without changing the workflow&amp;rsquo;s external API.&lt;/p&gt;
&lt;h1 id=&#34;2-multi-agent-systems-divide-responsibility&#34;&gt;2. Multi-Agent Systems: Divide Responsibility
&lt;/h1&gt;&lt;h2 id=&#34;why-use-multiple-agents&#34;&gt;Why use multiple agents?
&lt;/h2&gt;&lt;p&gt;Multiple agents help when tasks need different instructions, tools, knowledge, or ownership. A billing specialist should not receive technical credentials, and a technical specialist should not decide payment outcomes.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Agent&lt;/th&gt;
          &lt;th&gt;Responsibility&lt;/th&gt;
          &lt;th&gt;Example output&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Classifier&lt;/td&gt;
          &lt;td&gt;Select category and route work&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;billing&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Billing specialist&lt;/td&gt;
          &lt;td&gt;Interpret payment signals&lt;/td&gt;
          &lt;td&gt;Review authorization; avoid retries&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Technical specialist&lt;/td&gt;
          &lt;td&gt;Interpret failures&lt;/td&gt;
          &lt;td&gt;Collect error code and correlation ID&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Synthesizer&lt;/td&gt;
          &lt;td&gt;Combine findings&lt;/td&gt;
          &lt;td&gt;One customer-facing recommendation&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Multi-agent does not mean “give every agent every tool.” Apply least privilege per agent, validate messages at every boundary, and keep final business authority in application services.&lt;/p&gt;
&lt;h2 id=&#34;common-orchestration-patterns&#34;&gt;Common orchestration patterns
&lt;/h2&gt;&lt;p&gt;Microsoft Agent Framework supports several useful topologies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sequential:&lt;/strong&gt; one executor&amp;rsquo;s output becomes the next executor&amp;rsquo;s input. Use for classify, enrich, then summarize.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Concurrent:&lt;/strong&gt; independent specialists run together, then a fan-in executor aggregates findings. The sample uses this pattern.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Group chat:&lt;/strong&gt; several agents take turns under an orchestrator and termination rule. Use when discussion is itself valuable, but bound turns and tokens.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Handoff:&lt;/strong&gt; an agent transfers ownership to a directed specialist. Use for clear routing such as support to billing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manager-driven:&lt;/strong&gt; a manager assigns specialists, monitors progress, and replans. Use for open-ended work with explicit limits.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Choose topology from dependency structure, not from the number of models. Parallelism is useful only when tasks are independent and downstream code can handle partial or conflicting results.&lt;/p&gt;
&lt;h1 id=&#34;3-parallel-execution-and-synthesis&#34;&gt;3. Parallel Execution and Synthesis
&lt;/h1&gt;&lt;p&gt;The sample fans out to both specialists and waits for both results:&lt;/p&gt;
&lt;div class=&#34;copilot-prompt text-code-block&#34;&gt;&lt;div class=&#34;copilot-prompt__header&#34;&gt;&lt;span class=&#34;text-code-block__language&#34;&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&#34;copilot-prompt__body&#34;&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f38ba8&#34;&gt;var&lt;/span&gt; findings = &lt;span style=&#34;color:#cba6f7&#34;&gt;await&lt;/span&gt; Task.WhenAll(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    specialists.Select(specialist =&amp;gt; ExecuteAsync(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        specialist, request, cancellationToken)));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is safe here because specialists are read-only and independent. It would be unsafe to run two payment writes concurrently without idempotency, ordering, and a transaction design.&lt;/p&gt;
&lt;p&gt;The synthesis step should preserve provenance. Return which specialist produced each finding, include confidence or evidence references when available, and never let a generated summary erase a conflicting fact. For long-running work, persist the fan-out results before synthesis so a failed synthesizer can resume without repeating external calls.&lt;/p&gt;
&lt;h1 id=&#34;4-checkpoints-and-human-in-the-loop-workflows&#34;&gt;4. Checkpoints and Human-in-the-Loop Workflows
&lt;/h1&gt;&lt;p&gt;Part III paused a refund before execution. A workflow can pause for the same reason through a request port or approval event, then resume from a checkpoint after the user or operator responds.&lt;/p&gt;
&lt;div class=&#34;mermaid&#34;&gt;sequenceDiagram
  participant W as Workflow
  participant E as Executor
  participant H as Human
  rect rgb(219, 234, 254)
  W-&gt;&gt;E: Process sensitive step
  E--&gt;&gt;W: Approval request
  W-&gt;&gt;W: Save checkpoint
  end
  rect rgb(254, 243, 199)
  W--&gt;&gt;H: Request approval
  H-&gt;&gt;W: Approve or reject
  end
  rect rgb(220, 252, 231)
  W-&gt;&gt;E: Resume from checkpoint
  E--&gt;&gt;W: Result
  end&lt;/div&gt;
&lt;p&gt;File checkpoint storage can suit a local single-process demo. Distributed production workflows need durable storage, optimistic concurrency, expiration, and a strategy for duplicate resume requests. Durable Task integrations are appropriate when execution must survive process failure and scale across workers.&lt;/p&gt;
&lt;p&gt;Checkpointing stores state; it does not automatically provide authorization, distributed locking, idempotency, or a correct retry policy. Add those deliberately.&lt;/p&gt;
&lt;h1 id=&#34;5-observability-see-agent-execution&#34;&gt;5. Observability: See Agent Execution
&lt;/h1&gt;&lt;p&gt;Agent systems have more failure points than ordinary request/response code: model latency, token usage, routing decisions, executor failures, tool calls, fan-in waits, and context growth. Logs alone cannot show their causal relationships.&lt;/p&gt;
&lt;p&gt;Use three signals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Traces:&lt;/strong&gt; one workflow span with child executor spans and correlation IDs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metrics:&lt;/strong&gt; run count, duration, failure count, token usage, and per-agent latency.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logs:&lt;/strong&gt; structured decisions, errors, approval events, and selected business identifiers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The sample creates an &lt;code&gt;ActivitySource&lt;/code&gt; named &lt;code&gt;AgentWorkflows.Workflow&lt;/code&gt; and a &lt;code&gt;Meter&lt;/code&gt; with &lt;code&gt;agent.workflow.runs&lt;/code&gt; and &lt;code&gt;agent.workflow.duration&lt;/code&gt;. It adds executor IDs and workflow categories as span attributes while avoiding ticket text and sensitive payment data.&lt;/p&gt;
&lt;div class=&#34;copilot-prompt text-code-block&#34;&gt;&lt;div class=&#34;copilot-prompt__header&#34;&gt;&lt;span class=&#34;text-code-block__language&#34;&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&#34;copilot-prompt__body&#34;&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#cba6f7&#34;&gt;using&lt;/span&gt; &lt;span style=&#34;color:#fab387&#34;&gt;var&lt;/span&gt; activity = WorkflowTelemetry.Source.StartActivity(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e3a1&#34;&gt;&amp;#34;workflow.session&amp;#34;&lt;/span&gt;, ActivityKind.Internal);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;activity?.SetTag(&lt;span style=&#34;color:#a6e3a1&#34;&gt;&amp;#34;workflow.id&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e3a1&#34;&gt;&amp;#34;support-ticket&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;activity?.SetTag(&lt;span style=&#34;color:#a6e3a1&#34;&gt;&amp;#34;session.id&amp;#34;&lt;/span&gt;, request.TicketId);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Register the source and meter with OpenTelemetry, then export OTLP to the Collector:&lt;/p&gt;
&lt;div class=&#34;copilot-prompt text-code-block&#34;&gt;&lt;div class=&#34;copilot-prompt__header&#34;&gt;&lt;span class=&#34;text-code-block__language&#34;&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&#34;copilot-prompt__body&#34;&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;builder.Services.AddOpenTelemetry()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .WithTracing(tracing =&amp;gt; tracing
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        .AddAspNetCoreInstrumentation()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        .AddSource(&lt;span style=&#34;color:#a6e3a1&#34;&gt;&amp;#34;AgentWorkflows.Workflow&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        .AddOtlpExporter())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .WithMetrics(metrics =&amp;gt; metrics
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        .AddAspNetCoreInstrumentation()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        .AddMeter(&lt;span style=&#34;color:#a6e3a1&#34;&gt;&amp;#34;AgentWorkflows.Workflow&amp;#34;&lt;/span&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The Collector separates application instrumentation from storage backends. It exports traces to Tempo and logs to Loki. Grafana queries both data sources, so an operator can move from a slow workflow trace to related logs without coupling application code to Grafana.&lt;/p&gt;
&lt;p&gt;Do not export prompts, tool arguments, customer payment data, or full model responses by default. Use redaction, sampling, access control, and retention policies. Avoid double-instrumenting the same model call because duplicate spans make latency and cost analysis misleading.&lt;/p&gt;
&lt;h1 id=&#34;the-sample-application&#34;&gt;The Sample Application
&lt;/h1&gt;&lt;p&gt;The complete sample contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SupportWorkflow&lt;/code&gt; with classifier logic, two specialist executors, fan-out, and synthesis.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SupportAgent&lt;/code&gt; using &lt;code&gt;Microsoft.Agents.AI&lt;/code&gt; for optional model-based summarization.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ActivitySource&lt;/code&gt; and &lt;code&gt;Meter&lt;/code&gt; instrumentation.&lt;/li&gt;
&lt;li&gt;Docker Compose infrastructure based on the existing blog observability sample:
&lt;ul&gt;
&lt;li&gt;OpenTelemetry Collector&lt;/li&gt;
&lt;li&gt;Grafana&lt;/li&gt;
&lt;li&gt;Loki&lt;/li&gt;
&lt;li&gt;Tempo&lt;/li&gt;
&lt;li&gt;Prometheus&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Aspire AppHost for starting the same services during local development.&lt;/li&gt;
&lt;li&gt;Unit tests for workflow classification and specialist output.&lt;/li&gt;
&lt;li&gt;API integration tests for validation, health, and workflow results.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Start the observability stack and API:&lt;/p&gt;
&lt;div class=&#34;copilot-prompt text-code-block&#34;&gt;&lt;div class=&#34;copilot-prompt__header&#34;&gt;&lt;span class=&#34;text-code-block__language&#34;&gt;powershell&lt;/span&gt;&lt;/div&gt;&lt;div class=&#34;copilot-prompt__body&#34;&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker compose &lt;span style=&#34;color:#89dceb;font-weight:bold&#34;&gt;-f&lt;/span&gt; deployments/&lt;span style=&#34;color:#89dceb&#34;&gt;docker-compose&lt;/span&gt;/&lt;span style=&#34;color:#89dceb&#34;&gt;docker-compose&lt;/span&gt;.&lt;span style=&#34;color:#fab387&#34;&gt;infrastructure&lt;/span&gt;.&lt;span style=&#34;color:#fab387&#34;&gt;yaml&lt;/span&gt; up -d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f5e0dc&#34;&gt;$env:OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt; = &lt;span style=&#34;color:#a6e3a1&#34;&gt;&amp;#34;http://localhost:14317&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;dotnet run --project src/AgentWorkflows.Api&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Open Grafana at &lt;code&gt;http://localhost:3000&lt;/code&gt; using &lt;code&gt;admin&lt;/code&gt; / &lt;code&gt;admin&lt;/code&gt;. Send a ticket:&lt;/p&gt;
&lt;div class=&#34;copilot-prompt text-code-block&#34;&gt;&lt;div class=&#34;copilot-prompt__header&#34;&gt;&lt;span class=&#34;text-code-block__language&#34;&gt;bash&lt;/span&gt;&lt;/div&gt;&lt;div class=&#34;copilot-prompt__body&#34;&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -X POST http://localhost:5000/api/workflows/support &lt;span style=&#34;color:#89b4fa&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#89b4fa&#34;&gt;&lt;/span&gt;  -H &lt;span style=&#34;color:#a6e3a1&#34;&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#89b4fa&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#89b4fa&#34;&gt;&lt;/span&gt;  -d &lt;span style=&#34;color:#a6e3a1&#34;&gt;&amp;#39;{&amp;#34;ticketId&amp;#34;:&amp;#34;ticket-1&amp;#34;,&amp;#34;text&amp;#34;:&amp;#34;I see a duplicate charge and checkout error 500&amp;#34;}&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The response contains the category, both specialist findings, and the synthesized recommendation. The trace contains the workflow and executor spans.&lt;/p&gt;
&lt;h2 id=&#34;live-grafana-preview&#34;&gt;Live Grafana Preview
&lt;/h2&gt;&lt;p&gt;After running the sample, Grafana shows workflow metrics, structured Loki logs, and trace IDs connected to Tempo. The dashboard refreshes every five seconds. Select a trace ID from the log panel or open the Tempo link to inspect the complete workflow span tree.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://dotnetuniversity.com/microsoft-agent-framework-iv-workflows-multi-agent-systems-and-observability/agent-workflows-grafana-live.png&#34;
	width=&#34;1119&#34;
	height=&#34;1034&#34;
	srcset=&#34;https://dotnetuniversity.com/microsoft-agent-framework-iv-workflows-multi-agent-systems-and-observability/agent-workflows-grafana-live_hu_f3cbe529658f525a.png 480w, https://dotnetuniversity.com/microsoft-agent-framework-iv-workflows-multi-agent-systems-and-observability/agent-workflows-grafana-live_hu_a06e6888111f38d0.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Live Grafana dashboard showing workflow metrics, Loki logs, and Tempo trace inspection&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;108&#34;
		data-flex-basis=&#34;259px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;The official &lt;a class=&#34;link&#34; href=&#34;http://localhost:3000/d/aspnetcore/asp-net-core&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;ASP.NET Core metrics dashboard&lt;/a&gt; and &lt;a class=&#34;link&#34; href=&#34;http://localhost:3000/d/aspnetcore-endpoint/asp-net-core-endpoint&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;ASP.NET Core endpoint dashboard&lt;/a&gt; are linked from the workflow dashboard during local preview.&lt;/p&gt;
&lt;p&gt;Run tests:&lt;/p&gt;
&lt;div class=&#34;copilot-prompt text-code-block&#34;&gt;&lt;div class=&#34;copilot-prompt__header&#34;&gt;&lt;span class=&#34;text-code-block__language&#34;&gt;powershell&lt;/span&gt;&lt;/div&gt;&lt;div class=&#34;copilot-prompt__body&#34;&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f849c&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;dotnet run --project tests/AgentWorkflows.UnitTests/AgentWorkflows.&lt;span style=&#34;color:#fab387&#34;&gt;UnitTests&lt;/span&gt;.&lt;span style=&#34;color:#fab387&#34;&gt;csproj&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;dotnet run --project tests/AgentWorkflows.IntegrationTests/AgentWorkflows.&lt;span style=&#34;color:#fab387&#34;&gt;IntegrationTests&lt;/span&gt;.csproj&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&#34;production-checklist&#34;&gt;Production Checklist
&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;Select sequential, concurrent, group-chat, or handoff topology from actual dependencies.&lt;/li&gt;
&lt;li&gt;Give each agent only tools and context required for its role.&lt;/li&gt;
&lt;li&gt;Bound turns, fan-out size, tokens, latency, and total cost.&lt;/li&gt;
&lt;li&gt;Persist checkpoints and intermediate results for long-running workflows.&lt;/li&gt;
&lt;li&gt;Add idempotency and reconciliation around every side effect.&lt;/li&gt;
&lt;li&gt;Define behavior for partial, conflicting, timed-out, and failed specialist results.&lt;/li&gt;
&lt;li&gt;Propagate correlation IDs through agents, tools, and remote A2A calls.&lt;/li&gt;
&lt;li&gt;Redact sensitive prompts, tool arguments, and customer data from telemetry.&lt;/li&gt;
&lt;li&gt;Monitor token usage, model latency, executor duration, error rate, and queue age.&lt;/li&gt;
&lt;li&gt;Test topology, cancellation, checkpoint resume, duplicate messages, and approval rejection.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h1&gt;&lt;p&gt;Workflows make multi-step agent behavior explicit. Multi-agent systems divide responsibility into focused specialists, and concurrent orchestration reduces latency when work is independent. Checkpoints and approval events make long-running execution resumable, while OpenTelemetry gives operators the evidence needed to debug it.&lt;/p&gt;
&lt;p&gt;The progression across this series is deliberate: Part I introduced the agent boundary, Part II added tools and knowledge, Part III added controlled planning and approval, and Part IV coordinates specialist work while making its behavior observable. The model supplies useful reasoning; workflow code, authorization, persistence, and telemetry keep that reasoning accountable.&lt;/p&gt;
&lt;h2 id=&#34;run-the-complete-example&#34;&gt;Run the Complete Example
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/meysamhadeli/blog-samples/tree/main/src/microsoft-agent-framework-workflows-multi-agent-observability-sample&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/meysamhadeli/blog-samples/tree/main/src/microsoft-agent-framework-workflows-multi-agent-observability-sample&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;reference&#34;&gt;Reference
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/agent-framework/concepts/workflows/executors&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Executors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/agent-framework/workflows/orchestrations/concurrent&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Concurrent orchestration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/agent-framework/workflows/orchestrations/group-chat&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Group chat orchestration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/agent-framework/workflows/as-agents&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Workflows as agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/agent-framework/workflows/human-in-the-loop&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Human-in-the-loop workflows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/agent-framework/workflows/checkpoints&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Workflow checkpoints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/agent-framework/workflows/observability&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Workflow observability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/agent-framework/journey/agent-to-agent&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Agent-to-agent communication&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        
    </channel>
</rss>
