Python: WorkflowBuilder registry (#2486)

* Add workflow builder factory pattern

* Add internal edge groups to registered executors; next samples

* Update samples: Part 1

* register -> register_executor

* update hil samples

* Update other samples

* Update agent  samples

* Update doc string

* Add new sample

* Fix mypy

* Address comments

* Fix mypy
This commit is contained in:
Tao Chen
2025-12-04 21:26:10 -08:00
committed by GitHub
Unverified
parent 6809510413
commit f2ed5b55f6
33 changed files with 1609 additions and 696 deletions
@@ -7,6 +7,7 @@ from typing import Annotated, Never
from agent_framework import (
AgentExecutorResponse,
ChatAgent,
ChatMessage,
Executor,
FunctionApprovalRequestContent,
@@ -210,10 +211,9 @@ async def conclude_workflow(
await ctx.yield_output(email_response.agent_run_response.text)
async def main() -> None:
# Create the agent and executors
chat_client = OpenAIChatClient()
email_writer = chat_client.create_agent(
def create_email_writer_agent() -> ChatAgent:
"""Create the Email Writer agent with tools that require approval."""
return OpenAIChatClient().create_agent(
name="Email Writer",
instructions=("You are an excellent email assistant. You respond to incoming emails."),
# tools with `approval_mode="always_require"` will trigger approval requests
@@ -225,14 +225,21 @@ async def main() -> None:
get_my_information,
],
)
email_preprocessor = EmailPreprocessor(special_email_addresses={"mike@contoso.com"})
async def main() -> None:
# Build the workflow
workflow = (
WorkflowBuilder()
.set_start_executor(email_preprocessor)
.add_edge(email_preprocessor, email_writer)
.add_edge(email_writer, conclude_workflow)
.register_agent(create_email_writer_agent, name="email_writer")
.register_executor(
lambda: EmailPreprocessor(special_email_addresses={"mike@contoso.com"}),
name="email_preprocessor",
)
.register_executor(lambda: conclude_workflow, name="conclude_workflow")
.set_start_executor("email_preprocessor")
.add_edge("email_preprocessor", "email_writer")
.add_edge("email_writer", "conclude_workflow")
.build()
)
@@ -5,7 +5,8 @@ from dataclasses import dataclass
from agent_framework import (
AgentExecutorRequest, # Message bundle sent to an AgentExecutor
AgentExecutorResponse, # Result returned by an AgentExecutor
AgentExecutorResponse,
ChatAgent, # Result returned by an AgentExecutor
ChatMessage, # Chat message structure
Executor, # Base class for workflow executors
RequestInfoEvent, # Event emitted when human input is requested
@@ -142,11 +143,9 @@ class TurnManager(Executor):
await ctx.send_message(AgentExecutorRequest(messages=[user_msg], should_respond=True))
async def main() -> None:
# Create the chat agent and wrap it in an AgentExecutor.
# response_format enforces that the model produces JSON compatible with GuessOutput.
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
agent = chat_client.create_agent(
def create_guessing_agent() -> ChatAgent:
"""Create the guessing agent with instructions to guess a number between 1 and 10."""
return AzureOpenAIChatClient(credential=AzureCliCredential()).create_agent(
name="GuessingAgent",
instructions=(
"You guess a number between 1 and 10. "
@@ -154,19 +153,22 @@ async def main() -> None:
'You MUST return ONLY a JSON object exactly matching this schema: {"guess": <integer 1..10>}. '
"No explanations or additional text."
),
# Structured output enforced via Pydantic model.
# response_format enforces that the model produces JSON compatible with GuessOutput.
response_format=GuessOutput,
)
# TurnManager coordinates and gathers human replies while AgentExecutor runs the model.
turn_manager = TurnManager(id="turn_manager")
async def main() -> None:
"""Run the human-in-the-loop guessing game workflow."""
# Build a simple loop: TurnManager <-> AgentExecutor.
workflow = (
WorkflowBuilder()
.set_start_executor(turn_manager)
.add_edge(turn_manager, agent) # Ask agent to make/adjust a guess
.add_edge(agent, turn_manager) # Agent's response comes back to coordinator
.register_agent(create_guessing_agent, name="guessing_agent")
.register_executor(lambda: TurnManager(id="turn_manager"), name="turn_manager")
.set_start_executor("turn_manager")
.add_edge("turn_manager", "guessing_agent") # Ask agent to make/adjust a guess
.add_edge("guessing_agent", "turn_manager") # Agent's response comes back to coordinator
).build()
# Human in the loop run: alternate between invoking the workflow and supplying collected responses.