Demonstrates two complementary patterns for dispatching to multiple agents: sequential routing (classify then dispatch to one specialist) and parallel fan-out (send the same query to all agents simultaneously). Both patterns include OpenTelemetry tracing wired through all four Temporal + agent span sources.
This sample demonstrates:
RoutingWorkflow: keyword-based classification via aRoutingActivities.ClassifyRequestactivity, then dispatch to one of three specialistsParallelAgentWorkflow: fan-out to all three specialists at once viaExecuteAgentsInParallelAsync- OTel tracing with
TracingInterceptorandTemporalAgentTelemetryspan sources - Routing decision as a durable, auditable activity result
User Question
│
▼
RoutingWorkflow ParallelAgentWorkflow
│ │
├─ Activity: ClassifyRequest() ├─ GetAgent("WeatherAgent") ─┐
│ └─ returns: "WeatherAgent" ├─ GetAgent("BillingAgent") ─┼─ ExecuteAgentsInParallelAsync
│ └─ GetAgent("TechSupportAgent") ┘
└─ GetAgent(agentName).RunAsync() │
└─ specialist response IReadOnlyList<AgentResponse>
agent.client.send (TemporalAgentTelemetry — agent name, session ID)
UpdateWorkflow:RunAgent (TracingInterceptor — workflow update span)
RunActivity:ExecuteAgent (TracingInterceptor — activity span)
agent.turn (TemporalAgentTelemetry — token counts, correlation ID)
All four sources must be registered for the full trace to appear:
Sdk.CreateTracerProviderBuilder()
.AddSource(TracingInterceptor.ClientSource.Name)
.AddSource(TracingInterceptor.WorkflowsSource.Name)
.AddSource(TracingInterceptor.ActivitiesSource.Name)
.AddSource(TemporalAgentTelemetry.ActivitySourceName)
.AddConsoleExporter()
.Build();- Routing decision as activity.
ClassifyRequestruns in aRoutingActivitiesactivity, not inline in the workflow. The result is cached in event history — a crash after classification won't re-invoke the classifier, and the decision is visible in the Temporal Web UI. - Consistent fallback agent.
ClassifyRequestreturnsTechSupportAgentfor both empty input and for input where no keywords match any specialist. This makesTechSupportAgentthe single general-purpose fallback in all unresolved cases. ExecuteAgentsInParallelAsyncfor fan-out. The workflow-safe equivalent ofTask.WhenAll— dispatches multiple agent activities concurrently and returns results in input order. Each agent runs in its own session; results come back asIReadOnlyList<AgentResponse>in the same order the agents were passed in..ConfigureAwait(true)on all workflow awaits. Allawaitcalls inside[Workflow]-attributed classes use.ConfigureAwait(true). This is required to keep the Temporal workflow task scheduler active — omitting it can cause the workflow context to be lost during replay.TracingInterceptorpropagates context. Registered onITemporalClientviaopts.Interceptors, it propagates OTel trace context across Temporal's RPC boundary so spans from client to workflow to activity form a single trace.- Three registered specialists.
WeatherAgent,BillingAgent, andTechSupportAgentare all added viaAddTemporalAgents()on the same worker, demonstrating multi-agent registration.
- .NET 10 SDK or later
- A local Temporal server:
temporal server start-dev - An OpenAI-compatible API key
OPENAI_API_KEY is required and validated first on startup. OPENAI_API_BASE_URL is also required.
dotnet user-secrets set "OPENAI_API_KEY" "sk-..." --project samples/MAF/MultiAgentRouting
dotnet user-secrets set "OPENAI_API_BASE_URL" "https://api.openai.com/v1" --project samples/MAF/MultiAgentRoutingdotnet run --project samples/MAF/MultiAgentRouting/MultiAgentRouting.csprojWorker started.
── Demonstrating workflow-based routing ────────────────────
User: Will it rain in Seattle tomorrow?
Agent: <WeatherAgent response>
User: Why was I charged twice on my last invoice?
Agent: <BillingAgent response>
User: My application keeps crashing with a null reference exception.
Agent: <TechSupportAgent response>
── Demonstrating parallel agent execution ──────────────────
Fan-out query (sent to all 3 agents simultaneously): "Briefly introduce yourself..."
Parallel responses:
[WeatherAgent]: I'm a weather specialist...
[BillingAgent]: I handle billing and payments...
[TechSupportAgent]: I provide technical support...
Done.