mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
[BREAKING] Python: Move single-config fluent methods to constructor parameters (#3693)
* Move single-config fluent methods to constructor parameters * Updates * Adjust magentic and group chat
This commit is contained in:
committed by
GitHub
Unverified
parent
5d355ac507
commit
74ac470a56
@@ -17,7 +17,7 @@ The default aggregator fans in their results and yields output containing
|
||||
a list[ChatMessage] representing the concatenated conversations from all agents.
|
||||
|
||||
Demonstrates:
|
||||
- Minimal wiring with ConcurrentBuilder().participants([...]).build()
|
||||
- Minimal wiring with ConcurrentBuilder(participants=[...]).build()
|
||||
- Fan-out to multiple agents, fan-in aggregation of final ChatMessages
|
||||
- Workflow completion when idle with no pending work
|
||||
|
||||
@@ -57,7 +57,7 @@ async def main() -> None:
|
||||
|
||||
# 2) Build a concurrent workflow
|
||||
# Participants are either Agents (type of SupportsAgentRun) or Executors
|
||||
workflow = ConcurrentBuilder().participants([researcher, marketer, legal]).build()
|
||||
workflow = ConcurrentBuilder(participants=[researcher, marketer, legal]).build()
|
||||
|
||||
# 3) Run with a single prompt and pretty-print the final combined messages
|
||||
events = await workflow.run("We are launching a new budget-friendly electric bike for urban commuters.")
|
||||
|
||||
@@ -27,7 +27,7 @@ ConcurrentBuilder API and the default aggregator.
|
||||
Demonstrates:
|
||||
- Executors that create their ChatAgent in __init__ (via AzureOpenAIChatClient)
|
||||
- A @handler that converts AgentExecutorRequest -> AgentExecutorResponse
|
||||
- ConcurrentBuilder().participants([...]) to build fan-out/fan-in
|
||||
- ConcurrentBuilder(participants=[...]) to build fan-out/fan-in
|
||||
- Default aggregator returning list[ChatMessage] (one user + one assistant per agent)
|
||||
- Workflow completion when all participants become idle
|
||||
|
||||
@@ -103,7 +103,7 @@ async def main() -> None:
|
||||
marketer = MarketerExec(chat_client)
|
||||
legal = LegalExec(chat_client)
|
||||
|
||||
workflow = ConcurrentBuilder().participants([researcher, marketer, legal]).build()
|
||||
workflow = ConcurrentBuilder(participants=[researcher, marketer, legal]).build()
|
||||
|
||||
events = await workflow.run("We are launching a new budget-friendly electric bike for urban commuters.")
|
||||
outputs = events.get_outputs()
|
||||
|
||||
@@ -18,7 +18,7 @@ to synthesize a concise, consolidated summary from the experts' outputs.
|
||||
The workflow completes when all participants become idle.
|
||||
|
||||
Demonstrates:
|
||||
- ConcurrentBuilder().participants([...]).with_aggregator(callback)
|
||||
- ConcurrentBuilder(participants=[...]).with_aggregator(callback)
|
||||
- Fan-out to agents and fan-in at an aggregator
|
||||
- Aggregation implemented via an LLM call (chat_client.get_response)
|
||||
- Workflow output yielded with the synthesized summary string
|
||||
@@ -87,7 +87,7 @@ async def main() -> None:
|
||||
# • Custom callback -> return value becomes workflow output (string here)
|
||||
# The callback can be sync or async; it receives list[AgentExecutorResponse].
|
||||
workflow = (
|
||||
ConcurrentBuilder().participants([researcher, marketer, legal]).with_aggregator(summarize_results).build()
|
||||
ConcurrentBuilder(participants=[researcher, marketer, legal]).with_aggregator(summarize_results).build()
|
||||
)
|
||||
|
||||
events = await workflow.run("We are launching a new budget-friendly electric bike for urban commuters.")
|
||||
|
||||
@@ -33,7 +33,7 @@ instances created by the same builder. This is particularly useful when you need
|
||||
requests or tasks in parallel with stateful participants.
|
||||
|
||||
Demonstrates:
|
||||
- ConcurrentBuilder().register_participants([...]).with_aggregator(callback)
|
||||
- ConcurrentBuilder(participant_factories=[...]).with_aggregator(callback)
|
||||
- Fan-out to agents and fan-in at an aggregator
|
||||
- Aggregation implemented via an LLM call (chat_client.get_response)
|
||||
- Workflow output yielded with the synthesized summary string
|
||||
@@ -125,8 +125,7 @@ async def main() -> None:
|
||||
# SupportsAgentRun (agents) or Executor instances.
|
||||
# - register_aggregator(...) takes a factory function that returns an Executor instance.
|
||||
concurrent_builder = (
|
||||
ConcurrentBuilder()
|
||||
.register_participants([create_researcher, create_marketer, create_legal])
|
||||
ConcurrentBuilder(participant_factories=[create_researcher, create_marketer, create_legal])
|
||||
.register_aggregator(SummarizationExecutor)
|
||||
)
|
||||
|
||||
|
||||
@@ -65,16 +65,20 @@ async def main() -> None:
|
||||
)
|
||||
|
||||
# Build the group chat workflow
|
||||
# termination_condition: stop after 4 assistant messages
|
||||
# (The agent orchestrator will intelligently decide when to end before this limit but just in case)
|
||||
# intermediate_outputs=True: Enable intermediate outputs to observe the conversation as it unfolds
|
||||
# (Intermediate outputs will be emitted as WorkflowOutputEvent events)
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.with_orchestrator(agent=orchestrator_agent)
|
||||
.participants([researcher, writer])
|
||||
GroupChatBuilder(
|
||||
participants=[researcher, writer],
|
||||
termination_condition=lambda messages: sum(1 for msg in messages if msg.role == "assistant") >= 4,
|
||||
intermediate_outputs=True,
|
||||
orchestrator_agent=orchestrator_agent,
|
||||
)
|
||||
# Set a hard termination condition: stop after 4 assistant messages
|
||||
# The agent orchestrator will intelligently decide when to end before this limit but just in case
|
||||
.with_termination_condition(lambda messages: sum(1 for msg in messages if msg.role == "assistant") >= 4)
|
||||
# Enable intermediate outputs to observe the conversation as it unfolds
|
||||
# Intermediate outputs will be emitted as WorkflowEvent with type "output" events
|
||||
.with_intermediate_outputs()
|
||||
.build()
|
||||
)
|
||||
|
||||
|
||||
@@ -207,14 +207,17 @@ Share your perspective authentically. Feel free to:
|
||||
chat_client=_get_chat_client(),
|
||||
)
|
||||
|
||||
# termination_condition: stop after 10 assistant messages
|
||||
# intermediate_outputs=True: Enable intermediate outputs to observe the conversation as it unfolds
|
||||
# (Intermediate outputs will be emitted as WorkflowOutputEvent events)
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.with_orchestrator(agent=moderator)
|
||||
.participants([farmer, developer, teacher, activist, spiritual_leader, artist, immigrant, doctor])
|
||||
GroupChatBuilder(
|
||||
participants=[farmer, developer, teacher, activist, spiritual_leader, artist, immigrant, doctor],
|
||||
termination_condition=lambda messages: sum(1 for msg in messages if msg.role == "assistant") >= 10,
|
||||
intermediate_outputs=True,
|
||||
orchestrator_agent=moderator,
|
||||
)
|
||||
.with_termination_condition(lambda messages: sum(1 for msg in messages if msg.role == "assistant") >= 10)
|
||||
# Enable intermediate outputs to observe the conversation as it unfolds
|
||||
# Intermediate outputs will be emitted as WorkflowEvent with type "output" events
|
||||
.with_intermediate_outputs()
|
||||
.build()
|
||||
)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ from azure.identity import AzureCliCredential
|
||||
Sample: Group Chat with a round-robin speaker selector
|
||||
|
||||
What it does:
|
||||
- Demonstrates the with_orchestrator() API for GroupChat orchestration
|
||||
- Demonstrates the selection_func parameter for GroupChat orchestration
|
||||
- Uses a pure Python function to control speaker selection based on conversation state
|
||||
|
||||
Prerequisites:
|
||||
@@ -80,19 +80,26 @@ async def main() -> None:
|
||||
)
|
||||
|
||||
# Build the group chat workflow
|
||||
# termination_condition: stop after 6 messages (user task + one full rounds + 1)
|
||||
# One round is expert -> verifier -> clarifier -> skeptic, after which the expert gets to respond again.
|
||||
# This will end the conversation after the expert has spoken 2 times (one iteration loop)
|
||||
# Note: it's possible that the expert gets it right the first time and the other participants
|
||||
# have nothing to add, but for demo purposes we want to see at least one full round of interaction.
|
||||
# intermediate_outputs=True: Enable intermediate outputs to observe the conversation as it unfolds
|
||||
# (Intermediate outputs will be emitted as WorkflowOutputEvent events)
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.participants([expert, verifier, clarifier, skeptic])
|
||||
.with_orchestrator(selection_func=round_robin_selector)
|
||||
GroupChatBuilder(
|
||||
participants=[expert, verifier, clarifier, skeptic],
|
||||
termination_condition=lambda conversation: len(conversation) >= 6,
|
||||
intermediate_outputs=True,
|
||||
selection_func=round_robin_selector,
|
||||
)
|
||||
# Set a hard termination condition: stop after 6 messages (user task + one full rounds + 1)
|
||||
# One round is expert -> verifier -> clarifier -> skeptic, after which the expert gets to respond again.
|
||||
# This will end the conversation after the expert has spoken 2 times (one iteration loop)
|
||||
# Note: it's possible that the expert gets it right the first time and the other participants
|
||||
# have nothing to add, but for demo purposes we want to see at least one full round of interaction.
|
||||
.with_termination_condition(lambda conversation: len(conversation) >= 6)
|
||||
# Enable intermediate outputs to observe the conversation as it unfolds
|
||||
# Intermediate outputs will be emitted as WorkflowEvent with type "output" events
|
||||
.with_intermediate_outputs()
|
||||
.build()
|
||||
)
|
||||
|
||||
|
||||
@@ -78,10 +78,15 @@ async def main() -> None:
|
||||
|
||||
# Build the workflow with autonomous mode
|
||||
# In autonomous mode, agents continue iterating until they invoke a handoff tool
|
||||
# termination_condition: Terminate after coordinator provides 5 assistant responses
|
||||
workflow = (
|
||||
HandoffBuilder(
|
||||
name="autonomous_iteration_handoff",
|
||||
participants=[coordinator, research_agent, summary_agent],
|
||||
termination_condition=lambda conv: sum(
|
||||
1 for msg in conv if msg.author_name == "coordinator" and msg.role == "assistant"
|
||||
)
|
||||
>= 5,
|
||||
)
|
||||
.with_start_agent(coordinator)
|
||||
.add_handoff(coordinator, [research_agent, summary_agent])
|
||||
@@ -98,10 +103,6 @@ async def main() -> None:
|
||||
resolve_agent_id(summary_agent): 5,
|
||||
}
|
||||
)
|
||||
.with_termination_condition(
|
||||
# Terminate after coordinator provides 5 assistant responses
|
||||
lambda conv: sum(1 for msg in conv if msg.author_name == "coordinator" and msg.role == "assistant") >= 5
|
||||
)
|
||||
.build()
|
||||
)
|
||||
|
||||
|
||||
@@ -217,6 +217,9 @@ async def _run_workflow(workflow: Workflow, user_inputs: list[str]) -> None:
|
||||
async def main() -> None:
|
||||
"""Run the autonomous handoff workflow with participant factories."""
|
||||
# Build the handoff workflow using participant factories
|
||||
# termination_condition: Custom termination that checks if the triage agent has provided a closing message.
|
||||
# This looks for the last message being from triage_agent and containing "welcome",
|
||||
# which indicates the conversation has concluded naturally.
|
||||
workflow_builder = (
|
||||
HandoffBuilder(
|
||||
name="Autonomous Handoff with Participant Factories",
|
||||
@@ -226,18 +229,13 @@ async def main() -> None:
|
||||
"order_status": create_order_status_agent,
|
||||
"return": create_return_agent,
|
||||
},
|
||||
)
|
||||
.with_start_agent("triage")
|
||||
.with_termination_condition(
|
||||
# Custom termination: Check if the triage agent has provided a closing message.
|
||||
# This looks for the last message being from triage_agent and containing "welcome",
|
||||
# which indicates the conversation has concluded naturally.
|
||||
lambda conversation: (
|
||||
termination_condition=lambda conversation: (
|
||||
len(conversation) > 0
|
||||
and conversation[-1].author_name == "triage_agent"
|
||||
and "welcome" in conversation[-1].text.lower()
|
||||
)
|
||||
),
|
||||
)
|
||||
.with_start_agent("triage")
|
||||
)
|
||||
|
||||
# Scripted user responses for reproducible demo
|
||||
|
||||
@@ -198,7 +198,7 @@ async def main() -> None:
|
||||
# - participants: All agents that can participate in the workflow
|
||||
# - with_start_agent: The triage agent is designated as the start agent, which means
|
||||
# it receives all user input first and orchestrates handoffs to specialists
|
||||
# - with_termination_condition: Custom logic to stop the request/response loop.
|
||||
# - termination_condition: Custom logic to stop the request/response loop.
|
||||
# Without this, the default behavior continues requesting user input until max_turns
|
||||
# is reached. Here we use a custom condition that checks if the conversation has ended
|
||||
# naturally (when one of the agents says something like "you're welcome").
|
||||
@@ -206,14 +206,14 @@ async def main() -> None:
|
||||
HandoffBuilder(
|
||||
name="customer_support_handoff",
|
||||
participants=[triage, refund, order, support],
|
||||
)
|
||||
.with_start_agent(triage)
|
||||
.with_termination_condition(
|
||||
# Custom termination: Check if one of the agents has provided a closing message.
|
||||
# This looks for the last message containing "welcome", which indicates the
|
||||
# conversation has concluded naturally.
|
||||
lambda conversation: len(conversation) > 0 and "welcome" in conversation[-1].text.lower()
|
||||
termination_condition=lambda conversation: (
|
||||
len(conversation) > 0 and "welcome" in conversation[-1].text.lower()
|
||||
),
|
||||
)
|
||||
.with_start_agent(triage)
|
||||
.build()
|
||||
)
|
||||
|
||||
|
||||
@@ -163,10 +163,11 @@ async def main() -> None:
|
||||
|
||||
async with create_agents(credential) as (triage, code_specialist):
|
||||
workflow = (
|
||||
HandoffBuilder()
|
||||
HandoffBuilder(
|
||||
termination_condition=lambda conv: sum(1 for msg in conv if msg.role == "user") >= 2,
|
||||
)
|
||||
.participants([triage, code_specialist])
|
||||
.with_start_agent(triage)
|
||||
.with_termination_condition(lambda conv: sum(1 for msg in conv if msg.role == "user") >= 2)
|
||||
.build()
|
||||
)
|
||||
|
||||
|
||||
@@ -72,20 +72,16 @@ async def main() -> None:
|
||||
|
||||
print("\nBuilding Magentic Workflow...")
|
||||
|
||||
workflow = (
|
||||
MagenticBuilder()
|
||||
.participants([researcher_agent, coder_agent])
|
||||
.with_manager(
|
||||
agent=manager_agent,
|
||||
max_round_count=10,
|
||||
max_stall_count=3,
|
||||
max_reset_count=2,
|
||||
)
|
||||
# Enable intermediate outputs to observe the conversation as it unfolds
|
||||
# Intermediate outputs will be emitted as WorkflowEvent events
|
||||
.with_intermediate_outputs()
|
||||
.build()
|
||||
)
|
||||
# intermediate_outputs=True: Enable intermediate outputs to observe the conversation as it unfolds
|
||||
# (Intermediate outputs will be emitted as WorkflowOutputEvent events)
|
||||
workflow = MagenticBuilder(
|
||||
participants=[researcher_agent, coder_agent],
|
||||
intermediate_outputs=True,
|
||||
manager_agent=manager_agent,
|
||||
max_round_count=10,
|
||||
max_stall_count=3,
|
||||
max_reset_count=2,
|
||||
).build()
|
||||
|
||||
task = (
|
||||
"I am preparing a report on the energy efficiency of different machine learning model architectures. "
|
||||
|
||||
@@ -76,18 +76,14 @@ def build_workflow(checkpoint_storage: FileCheckpointStorage):
|
||||
|
||||
# The builder wires in the Magentic orchestrator, sets the plan review path, and
|
||||
# stores the checkpoint backend so the runtime knows where to persist snapshots.
|
||||
return (
|
||||
MagenticBuilder()
|
||||
.participants([researcher, writer])
|
||||
.with_plan_review()
|
||||
.with_manager(
|
||||
agent=manager_agent,
|
||||
max_round_count=10,
|
||||
max_stall_count=3,
|
||||
)
|
||||
.with_checkpointing(checkpoint_storage)
|
||||
.build()
|
||||
)
|
||||
return MagenticBuilder(
|
||||
participants=[researcher, writer],
|
||||
enable_plan_review=True,
|
||||
checkpoint_storage=checkpoint_storage,
|
||||
manager_agent=manager_agent,
|
||||
max_round_count=10,
|
||||
max_stall_count=3,
|
||||
).build()
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
|
||||
@@ -115,22 +115,18 @@ async def main() -> None:
|
||||
|
||||
print("\nBuilding Magentic Workflow with Human Plan Review...")
|
||||
|
||||
workflow = (
|
||||
MagenticBuilder()
|
||||
.participants([researcher_agent, analyst_agent])
|
||||
.with_manager(
|
||||
agent=manager_agent,
|
||||
max_round_count=10,
|
||||
max_stall_count=1,
|
||||
max_reset_count=2,
|
||||
)
|
||||
# Request human input for plan review
|
||||
.with_plan_review()
|
||||
# Enable intermediate outputs to observe the conversation as it unfolds
|
||||
# Intermediate outputs will be emitted as WorkflowEvent with type "output"
|
||||
.with_intermediate_outputs()
|
||||
.build()
|
||||
)
|
||||
# enable_plan_review=True: Request human input for plan review
|
||||
# intermediate_outputs=True: Enable intermediate outputs to observe the conversation as it unfolds
|
||||
# (Intermediate outputs will be emitted as WorkflowOutputEvent events)
|
||||
workflow = MagenticBuilder(
|
||||
participants=[researcher_agent, analyst_agent],
|
||||
enable_plan_review=True,
|
||||
intermediate_outputs=True,
|
||||
manager_agent=manager_agent,
|
||||
max_round_count=10,
|
||||
max_stall_count=1,
|
||||
max_reset_count=2,
|
||||
).build()
|
||||
|
||||
task = "Research sustainable aviation fuel technology and summarize the findings."
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ async def main() -> None:
|
||||
)
|
||||
|
||||
# 2) Build sequential workflow: writer -> reviewer
|
||||
workflow = SequentialBuilder().participants([writer, reviewer]).build()
|
||||
workflow = SequentialBuilder(participants=[writer, reviewer]).build()
|
||||
|
||||
# 3) Run and collect outputs
|
||||
outputs: list[list[ChatMessage]] = []
|
||||
|
||||
@@ -66,7 +66,7 @@ async def main() -> None:
|
||||
|
||||
# 2) Build sequential workflow: content -> summarizer
|
||||
summarizer = Summarizer(id="summarizer")
|
||||
workflow = SequentialBuilder().participants([content, summarizer]).build()
|
||||
workflow = SequentialBuilder(participants=[content, summarizer]).build()
|
||||
|
||||
# 3) Run workflow and extract final conversation
|
||||
events = await workflow.run("Explain the benefits of budget eBikes for commuters.")
|
||||
|
||||
@@ -70,7 +70,7 @@ async def run_workflow(workflow: Workflow, query: str) -> None:
|
||||
|
||||
async def main() -> None:
|
||||
# 1) Create a builder with participant factories
|
||||
builder = SequentialBuilder().register_participants([
|
||||
builder = SequentialBuilder(participant_factories=[
|
||||
lambda: Accumulate("accumulator"),
|
||||
create_agent,
|
||||
])
|
||||
|
||||
Reference in New Issue
Block a user