Principle:Microsoft Agent framework Conditional Routing
| Knowledge Sources | |
|---|---|
| Domains | Workflow_Engine, Control_Flow |
| Last Updated | 2026-02-11 15:00 GMT |
Overview
The Conditional Routing principle describes the mechanism for attaching boolean predicates to workflow edges so that the runtime can dynamically decide which downstream executors receive a message. An EdgeCondition is a callable that receives the source executor's output data and returns a boolean value determining whether the edge should be traversed. By attaching multiple conditional edges from the same source executor, workflow authors create routing branches that implement if/else, switch-case, and guard-clause patterns without modifying the executors themselves.
Description
In a workflow graph, edges connect a source executor to one or more target executors. By default, every edge is unconditional -- when the source executor produces output, the message is forwarded to all connected targets. Conditional routing introduces a predicate function on each edge that acts as a gate: the message only flows along that edge if the predicate evaluates to True.
The EdgeCondition Type
The EdgeCondition type alias defines the contract for all edge predicates:
EdgeCondition: TypeAlias = Callable[[Any], bool | Awaitable[bool]]
Key characteristics:
- Input: The condition receives exactly one argument -- the output data produced by the source executor. This is the same payload that would be forwarded along the edge.
- Output: The condition returns a
bool(synchronous) or anAwaitable[bool](asynchronous). The runtime transparently handles both. - Purity: Conditions should be side-effect-free predicates. They inspect the data and render a verdict; they do not mutate the payload or trigger external effects.
Branching Patterns
When multiple conditional edges originate from the same source executor, the runtime evaluates each edge's condition independently. This creates several natural routing patterns:
| Pattern | Description | Edge Configuration |
|---|---|---|
| If/Else | Route to one of two targets based on a single boolean property of the data. | Two edges from the same source: one with condition=is_true, the other with condition=is_false.
|
| Switch/Case | Route to one of N targets based on a categorical property. | N edges from the same source, each with a mutually exclusive condition checking a different value. |
| Guard Clause | Conditionally skip a processing step. | One conditional edge that gates entry to an optional executor; an unconditional edge continues the main path. |
| Fan-Out with Filter | Broadcast to a subset of targets. | Multiple edges from the same source, each with an independent condition. Zero or more edges may fire simultaneously. |
Evaluation Semantics
The runtime evaluates edge conditions with the following guarantees:
1. Independent evaluation: Each edge's condition is evaluated independently. The result of one condition does not influence the evaluation of another. This means that from a single source, zero, one, or many conditional edges may fire simultaneously.
2. Unconditional fallback: An edge defined without a condition always fires. This provides a deterministic default path that is unaffected by the outcome of sibling conditional edges.
3. Sync/async transparency: The runtime inspects the return value of the condition callable. If the result is awaitable, it is awaited; otherwise the boolean value is used directly. This allows conditions to perform lightweight async checks (e.g., a cache lookup) without requiring all conditions to be async.
4. No short-circuit ordering: Conditions are not evaluated in a defined order and the runtime does not short-circuit after the first true condition (unless using the SwitchCaseEdgeGroup variant, which does implement first-match semantics). For mutually exclusive routing, the author must ensure the conditions themselves are mutually exclusive.
Theoretical Basis
Conditional routing applies the concept of guarded transitions from automata theory and Petri nets to agent workflow graphs:
1. Guarded transitions: In formal models of concurrent systems, a transition may fire only when its guard predicate is satisfied. Edge conditions serve exactly this role, gating data flow between executors based on runtime state.
2. Data-driven control flow: Rather than embedding control flow logic inside executors, the routing decision is externalized to the graph topology. This preserves the single-responsibility of each executor and makes the workflow's branching logic visible at the structural level.
3. Composability: Because conditions are ordinary Python callables, they compose naturally. Complex routing predicates can be built from simple ones using standard boolean operations, higher-order functions, or partial application.
Usage
Use Conditional Routing when:
- Data-dependent branching is needed -- different downstream processing paths should be selected based on the content of the source executor's output.
- Multiple exclusive paths must be modeled (e.g., positive vs. negative sentiment, approved vs. rejected, success vs. failure).
- Optional processing stages should be skipped when their preconditions are not met.
- Dynamic fan-out is required where only a subset of available targets should receive the message based on runtime data.
Example
from agent_framework import WorkflowBuilder
def is_positive(data) -> bool:
return data.agent_response.value.sentiment == "positive"
def is_negative(data) -> bool:
return data.agent_response.value.sentiment == "negative"
builder = WorkflowBuilder(start_executor=classifier)
builder.add_edge(classifier, positive_handler, condition=is_positive)
builder.add_edge(classifier, negative_handler, condition=is_negative)
workflow = builder.build()
result = await workflow.run("Analyze customer feedback")
In this example, the classifier executor produces output with a sentiment field. The two conditional edges route the message to the appropriate handler based on the sentiment value. If neither condition matches, no downstream executor receives the message.