Principle:Microsoft Agent framework Handler Declaration
Template:Microsoft Agent framework Sidebar
Overview
Handler Declaration is a pattern in the Microsoft Agent Framework for registering methods on executor nodes as message handlers using the @handler decorator. The decorator marks an async method so that the workflow engine can route specific typed input messages to it at runtime. It supports two complementary modes of type discovery: introspection-based (types are inferred automatically from the function's signature) and explicit (types are provided as decorator parameters).
| Property | Value |
|---|---|
| Category | Workflow Engine |
| Source | python/packages/core/agent_framework/_workflows/_executor.py (lines L546-685)
|
| API | handler(func, *, input, output, workflow_output)
|
| Import | from agent_framework import handler
|
| Domains | Workflow_Engine |
| Related Implementation | Handler Decorator |
Description
Handler Declaration establishes the contract between executor node methods and the workflow routing layer. When a developer decorates an async method with @handler, the framework records that method as capable of receiving a particular message type and, optionally, producing a particular output type. The workflow engine consults these registrations at runtime to determine which method should handle an incoming message on a given executor node.
The pattern addresses a core design requirement of the workflow engine: executor nodes may define multiple handler methods, each specializing in a different message type. The @handler decorator provides the metadata the router needs to dispatch messages to the correct method without requiring the developer to write explicit routing logic.
Type Discovery Modes
The decorator supports two complementary strategies for determining which types a handler processes:
Introspection Mode (Default)
When @handler is applied without explicit type parameters, the framework inspects the decorated function's signature to determine:
- Input type: Derived from the first non-
selfparameter's type annotation (e.g.,text: strimplies input typestr). - Output type: Derived from the
WorkflowContextgeneric parameter (e.g.,ctx: WorkflowContext[str]implies output typestr).
This mode minimizes boilerplate by leveraging Python's type annotation system directly. The developer writes a normally-annotated method and the framework extracts all routing metadata automatically.
from agent_framework import handler
from agent_framework._workflows._executor import WorkflowContext
@handler
async def process(self, text: str, ctx: WorkflowContext[str]) -> None:
await ctx.send_message(text.upper())
Here the framework introspects that:
- The input type is
str(from thetextparameter). - The output type is
str(fromWorkflowContext[str]).
Explicit Mode
When precise control is needed, or when the signature does not carry sufficient type information, the developer can pass input, output, and workflow_output parameters directly to the decorator. These explicit values override any types that would have been introspected from the signature.
from agent_framework import handler
@handler(input=str, output=int)
async def count_words(self, message, ctx) -> None:
await ctx.send_message(len(message.split()))
Explicit mode is useful when:
- The handler accepts a union of types (e.g.,
input=str | bytes). - The parameter annotations are intentionally broad or missing.
- The developer wants the routing type to differ from the runtime parameter type.
Registration Semantics
Applying @handler does not immediately register the method with the workflow engine. Instead, it attaches metadata to the method object. When the executor class is instantiated and added to a workflow, the engine scans for methods carrying this metadata and builds the routing table. This deferred registration allows handler methods to be defined declaratively on the class without coupling them to a specific workflow instance at definition time.
Theoretical Basis
The Handler Declaration pattern implements a variant of the Observer Pattern specialized for typed message dispatch. Each handler declares interest in a specific message type, and the workflow engine acts as the dispatcher that routes messages to the appropriate observers. By encoding the message type in the decorator metadata rather than requiring explicit subscription calls, the pattern achieves a declarative style that integrates naturally with Python class definitions.
The dual-mode type discovery (introspection vs. explicit) follows the Convention over Configuration principle: the common case requires zero configuration (types are inferred), while the explicit path provides an escape hatch for advanced scenarios.
Usage
from agent_framework import handler
from agent_framework._workflows._executor import WorkflowContext
class TextProcessor:
# Introspection mode: types inferred from signature
@handler
async def process(self, text: str, ctx: WorkflowContext[str]) -> None:
await ctx.send_message(text.upper())
# Explicit mode: types specified as decorator parameters
@handler(input=str, output=int)
async def count_words(self, message, ctx) -> None:
await ctx.send_message(len(message.split()))
# Explicit mode with workflow_output
@handler(input=bytes, output=str, workflow_output=str)
async def decode(self, data, ctx) -> None:
await ctx.send_message(data.decode("utf-8"))
Related Pages
- Implementation:Microsoft_Agent_framework_Handler_Decorator
- Implementation: Handler Decorator -- the concrete implementation of the
@handlerdecorator