|
| 1 | +--- |
| 2 | +title: Register Factories to Workflow Builder |
| 3 | +description: Learn how to register factories to the workflow builder. |
| 4 | +zone_pivot_groups: programming-languages |
| 5 | +author: TaoChenOSU |
| 6 | +ms.topic: tutorial |
| 7 | +ms.author: taochen |
| 8 | +ms.date: 09/29/2025 |
| 9 | +ms.service: agent-framework |
| 10 | +--- |
| 11 | + |
| 12 | +# Register Factories to Workflow Builder |
| 13 | + |
| 14 | +Up to this point, we've been creating executor instances and passing them directly to the `WorkflowBuilder`. This approach works well for simple scenarios where you only need a single workflow instance. However, in more complex cases you may want to create multiple, isolated instances of the same workflow. To support this, each workflow instance must receive its own set of executor instances. Reusing the same executors would cause their internal state to be shared across workflows, resulting in unintended side effects. To avoid this, you can register executor factories with the `WorkflowBuilder`, ensuring that new executor instances are created for each workflow instance. |
| 15 | + |
| 16 | +## Registering Factories to Workflow Builder |
| 17 | + |
| 18 | +::: zone pivot="programming-language-csharp" |
| 19 | + |
| 20 | +Coming soon... |
| 21 | + |
| 22 | +::: zone-end |
| 23 | + |
| 24 | +::: zone pivot="programming-language-python" |
| 25 | + |
| 26 | +To register an executor factory to the `WorkflowBuilder`, you can use the `register_executor` method. This method takes two parameters: the factory function that creates instances of the executor (of type `Executor` or derivation of `Executor`) and the name of the factory to be used in the workflow configuration. |
| 27 | + |
| 28 | +```python |
| 29 | +class UpperCase(Executor): |
| 30 | + def __init__(self, id: str): |
| 31 | + super().__init__(id=id) |
| 32 | + |
| 33 | + @handler |
| 34 | + async def to_upper_case(self, text: str, ctx: WorkflowContext[str]) -> None: |
| 35 | + """Convert the input to uppercase and forward it to the next node.""" |
| 36 | + result = text.upper() |
| 37 | + |
| 38 | + # Send the result to the next executor in the workflow. |
| 39 | + await ctx.send_message(result) |
| 40 | + |
| 41 | +class Accumulate(Executor): |
| 42 | + def __init__(self, id: str): |
| 43 | + super().__init__(id=id) |
| 44 | + # Executor internal state that should not be shared among different workflow instances. |
| 45 | + self._text_length = 0 |
| 46 | + |
| 47 | + @handler |
| 48 | + async def accumulate(self, text: str, ctx: WorkflowContext) -> None: |
| 49 | + """Accumulate the length of the input text and log it.""" |
| 50 | + self._text_length += len(text) |
| 51 | + print(f"Accumulated text length: {self._text_length}") |
| 52 | + |
| 53 | +@executor(id="reverse_text_executor") |
| 54 | +async def reverse_text(text: str, ctx: WorkflowContext[str]) -> None: |
| 55 | + """Reverse the input string and send it downstream.""" |
| 56 | + result = text[::-1] |
| 57 | + |
| 58 | + # Send the result to the next executor in the workflow. |
| 59 | + await ctx.yield_output(result) |
| 60 | + |
| 61 | +workflow_builder = ( |
| 62 | + WorkflowBuilder() |
| 63 | + .register_executor( |
| 64 | + factory_func=lambda: UpperCase(id="UpperCaseExecutor"), |
| 65 | + name="UpperCase", |
| 66 | + ) |
| 67 | + .register_executor( |
| 68 | + factory_func=lambda: Accumulate(id="AccumulateExecutor"), |
| 69 | + name="Accumulate", |
| 70 | + ) |
| 71 | + .register_executor( |
| 72 | + factory_func=lambda: reverse_text, |
| 73 | + name="ReverseText", |
| 74 | + ) |
| 75 | + # Use the factory name to configure the workflow |
| 76 | + .add_fan_out_edges("UpperCase", ["Accumulate", "ReverseText"]) |
| 77 | + .set_start_executor("UpperCase") |
| 78 | +) |
| 79 | +``` |
| 80 | + |
| 81 | +Build a workflow using the builder |
| 82 | + |
| 83 | +```python |
| 84 | +# Build the workflow using the builder |
| 85 | +workflow_a = workflow_builder.build() |
| 86 | +await workflow_a.run("hello world") |
| 87 | +await workflow_a.run("hello world") |
| 88 | +``` |
| 89 | + |
| 90 | +Expected output: |
| 91 | + |
| 92 | +```plaintext |
| 93 | +Accumulated text length: 22 |
| 94 | +``` |
| 95 | + |
| 96 | +Now let's create another workflow instance and run it. The `Accumulate` executor should have its own internal state and not share the state with the first workflow instance. |
| 97 | + |
| 98 | +```python |
| 99 | +# Build another workflow using the builder |
| 100 | +# This workflow will have its own set of executors, including a new instance of the Accumulate executor. |
| 101 | +workflow_b = workflow_builder.build() |
| 102 | +await workflow_b.run("hello world") |
| 103 | +``` |
| 104 | + |
| 105 | +Expected output: |
| 106 | + |
| 107 | +```plaintext |
| 108 | +Accumulated text length: 11 |
| 109 | +``` |
| 110 | + |
| 111 | +To register an agent factory to the `WorkflowBuilder`, you can use the `register_agent` method. This method takes two parameters: the factory function that creates instances of the agent (of types that implement `AgentProtocol`) and the name of the factory to be used in the workflow configuration. |
| 112 | + |
| 113 | +```python |
| 114 | +def create_agent() -> ChatAgent: |
| 115 | + """Factory function to create a Writer agent.""" |
| 116 | + return AzureOpenAIChatClient(credential=AzureCliCredential()).create_agent( |
| 117 | + instructions=("You are a helpful assistant.",), |
| 118 | + name="assistant", |
| 119 | + ) |
| 120 | + |
| 121 | +workflow_builder = ( |
| 122 | + WorkflowBuilder() |
| 123 | + .register_agent( |
| 124 | + factory_func=create_agent, |
| 125 | + name="Assistant", |
| 126 | + ) |
| 127 | + # Register other executors or agents as needed and configure the workflow |
| 128 | + ... |
| 129 | +) |
| 130 | + |
| 131 | +# Build the workflow using the builder |
| 132 | +workflow = workflow_builder.build() |
| 133 | +``` |
| 134 | + |
| 135 | +Each time a new workflow instance is created, the agent in the workflow will be a new instance created by the factory function, and will get a new thread instance. |
| 136 | + |
| 137 | +::: zone-end |
| 138 | + |
| 139 | +## Workflow State Isolation |
| 140 | + |
| 141 | +To learn more about workflow state isolation, refer to the [Workflow State Isolation](../../user-guide/workflows/state-isolation.md) documentation. |
0 commit comments