Skip to content

Conversation

@TaoChenOSU
Copy link
Contributor

@TaoChenOSU TaoChenOSU commented Dec 23, 2025

Motivation and Context

Required by #429

Description

This PR extensively refactors and simplifies the orchestrations in Agent Framework Workflows. Details are as follows.

Note: Since this PR refactors group chat, and handoff and magentic depend on group chat, this PR results in large changes.

Group Chat

  1. Split the group chat orchestrator executor into dedicated agent-based and function-based, allowing for better maintainability and extensibility. See BaseGroupChatOrchestrator, GroupChatOrchestrator and AgentBasedGroupChatOrchestrator.
  2. Consolidate group chat contract while maintaining support for agents and custom executors in the same group. See GroupChatRequestMessage and GroupChatParticipantMessage.
  3. Simplify group chat workflow to a star topology, making it easier to maintain and moving responsibilities to executors.
  4. Simplify group chat request info mechanism to rely to sub workflows. See AgentApprovalExecutor and AgentRequestInfoExecutor. HIL happens after an agent responds and the agent-HIL loop stops until instructed.
  5. Move group chat to a broadcasting model where the orchestrator will broadcast participant responds to other participants to make sure all of them stay synchronized.

Handoff

  1. Remove single tier. We should recommend users to explore replacing single tier handoff with group chat.
  2. Remove coordinator. Handoff is a decentralized model thus it doesn't require a central node to coordinate communication.
  3. Remove support for custom executor. We are not sure how well we support custom executors now. We may add support back in the future.
  4. Move handoff handling to executors.
  5. Introduce HandoffAgentExecutor which derives from AgentExecutor. This executor handles handoff.
  6. Move handoff to a broadcasting model where each agent will broadcast its responds to other agents when they finish.

Magentic

  1. Refactors are driven by changes in group chat.

Sequential & Concurrent

  1. Simplify request info mechanism to rely to sub workflows. See AgentApprovalExecutor and AgentRequestInfoExecutor. HIL happens after an agent responds and the agent-HIL loop stops until instructed.

Sub-workflow (WorkflowExecutor)

  1. Requests can now also propagate to parent as the original event without the need to wrap it in a specialized message. This is needed for GroupChat HIL.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

@TaoChenOSU TaoChenOSU self-assigned this Dec 23, 2025
@TaoChenOSU TaoChenOSU added python agent orchestration Issues related to agent orchestration workflows Related to Workflows in agent-framework labels Dec 23, 2025
@github-actions github-actions bot changed the title Refactor orchestrations Python: Refactor orchestrations Dec 23, 2025
@TaoChenOSU TaoChenOSU changed the title Python: Refactor orchestrations [BREAKING] Python: Refactor orchestrations Dec 23, 2025
@TaoChenOSU TaoChenOSU marked this pull request as ready for review January 7, 2026 18:58
Copilot AI review requested due to automatic review settings January 7, 2026 18:58
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors Python orchestrations in the Agent Framework, introducing breaking changes to group chat, handoff, magentic, and other orchestration patterns. The refactoring aims to improve maintainability, simplify workflows, and move to a broadcasting model for better participant synchronization.

Key Changes:

  • Refactored group chat to split agent-based and function-based orchestrators with a star topology
  • Simplified handoff by removing single-tier and coordinator concepts, moving to a decentralized broadcasting model
  • Updated magentic orchestration with new event types and progress tracking
  • Simplified request info mechanisms across sequential and concurrent workflows
  • Enhanced sub-workflow request propagation capabilities

Reviewed changes

Copilot reviewed 46 out of 50 changed files in this pull request and generated no comments.

Show a summary per file
File Description
group_chat_builder_tool_approval.py Updated to use new group chat API with with_select_speaker_func, GroupChatState, and new event types
concurrent_builder_tool_approval.py Updated tool approval sample with modified request info handling and agent setup
sequential_custom_executors.py Updated custom executor to handle AgentExecutorResponse instead of raw conversation
magentic_human_replan.py Deleted - functionality moved to other samples
magentic_human_plan_update.py Deleted - replaced by magentic_human_plan_review.py
magentic_human_plan_review.py New sample demonstrating plan review with simplified API
magentic_checkpoint.py Updated for new magentic API with MagenticPlanReviewRequest
magentic_agent_clarification.py Deleted - functionality consolidated
magentic.py Significantly updated with new event types and simplified orchestrator handling
handoff_with_code_interpreter_file.py Updated handoff API from set_coordinator to with_start_agent
handoff_specialist_to_specialist.py Deleted - functionality replaced by simpler handoff model
handoff_simple.py Major refactor with new API (with_start_agent, HandoffAgentUserRequest)
handoff_return_to_previous.py Deleted - return-to-previous pattern removed
handoff_participant_factory.py Updated to use new handoff API
handoff_autonomous.py Updated autonomous mode API with per-agent turn limits
group_chat_simple_selector.py Completely rewritten with new selector function signature
group_chat_philosophical_debate.py Updated to use with_agent_orchestrator instead of set_manager
group_chat_agent_manager.py Updated orchestrator agent setup and API
sequential_request_info.py Updated request info to work after agent execution instead of before
group_chat_request_info.py Updated for new request info API with AgentExecutorResponse
concurrent_request_info.py Updated request info handling for concurrent workflows
sub_workflow_basics.py Minor whitespace fix
magentic_workflow_as_agent.py Simplified event handling, removed specific event type processing
handoff_workflow_as_agent.py Major refactor with new handoff API and request handling
group_chat_workflow_as_agent.py Updated to use with_agent_orchestrator
test_workflow_kwargs.py Updated tests for new APIs with breaking changes marked as xfail where needed
test_sequential.py Updated test to handle AgentExecutorResponse in custom executors
test_orchestration_request_info.py Extensive test updates for new request info architecture
test_magentic.py Major test refactoring for new magentic implementation
test_handoff.py Extensive test refactoring for simplified handoff model
test_executor.py Added comprehensive tests for executor output type introspection
test_agent_run_event_typing.py Removed tests for nullable event data
_workflow_context.py Added request_id parameter support for request info tracking
_workflow.py Updated response type validation with utility function
_runner_context.py Changed request tracking to use RequestInfoEvent instead of raw data

@markwallace-microsoft
Copy link
Member

markwallace-microsoft commented Jan 7, 2026

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/core/agent_framework/_workflows
   _agent_executor.py1712485%26, 93, 115, 149, 165–166, 217–218, 220–221, 253–255, 263–265, 275–277, 279, 283, 287, 291–292
   _base_group_chat_orchestrator.py1721392%25, 135, 301, 316, 350–352, 356, 375, 436, 480–482
   _concurrent.py1892984%52, 61–62, 70–71, 90–91, 96, 101, 126, 131, 136–137, 143, 165, 175, 182, 353, 356, 384, 440, 452, 491, 493–494, 496, 519, 523, 552
   _events.py1301489%59–60, 78, 86, 90, 180–181, 232, 257, 294, 312, 337, 378, 392
   _executor.py151994%209, 338, 353, 355, 368, 371, 479, 484, 494
   _group_chat.py2605678%54, 171, 332, 339, 365–366, 368–369, 373, 377–378, 384, 389, 405, 432–437, 439, 460, 463–465, 472–475, 477, 482–486, 562–565, 569–570, 575–576, 594, 598, 603, 658, 663, 701, 710, 716, 761, 849, 852, 884, 894
   _handoff.py3795984%57, 109–110, 112, 141–142, 162–172, 174, 176, 178, 183, 282, 330, 355, 381, 389–390, 404, 453–454, 484, 531–533, 726, 733, 738, 825, 828, 837–840, 850, 855, 862, 868–871, 905, 910, 1100, 1113, 1118, 1126, 1144, 1151, 1225
   _magentic.py57111180%43, 48, 70–79, 84, 88–99, 264, 275, 279, 299, 360, 369, 371, 413, 430, 439–440, 442–444, 446, 457, 597–601, 603, 641, 689, 725–727, 729, 737–740, 744–747, 790, 817–820, 911, 917, 923, 962, 1000, 1029, 1046, 1057, 1072–1075, 1111–1112, 1116–1118, 1142, 1163–1164, 1177, 1193, 1215, 1263–1264, 1302–1303, 1342–1343, 1345–1346, 1348, 1416, 1419, 1428, 1431, 1436, 1671–1672, 1674, 1688, 1697, 1714, 1723, 1726
   _orchestration_request_info.py530100% 
   _orchestration_state.py28582%20, 28, 68, 85–86
   _orchestrator_helpers.py22290%92–93
   _runner_context.py166994%77–78, 80–81, 83, 377, 397, 494, 498
   _sequential.py1091388%73, 167, 187, 198, 204, 241, 243–244, 246, 269, 273, 290, 297
   _workflow.py2491892%88, 258–260, 262–263, 281, 307, 309, 410, 690, 724, 729, 732, 751–753, 818
   _workflow_context.py1772585%61–62, 70, 74, 88, 164, 189, 307, 426, 440, 469–471, 473, 475–476, 478–479, 488–490, 492–494, 496
   _workflow_executor.py1744474%29, 95, 444, 455, 467–470, 473–475, 478–479, 481, 484–486, 489–493, 497–498, 507, 512, 546, 572–577, 580, 583, 591, 596, 607, 617, 621, 627, 631, 641, 645
TOTAL15904232585% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
2543 151 💤 0 ❌ 0 🔥 56.753s ⏱️

@markwallace-microsoft markwallace-microsoft added the documentation Improvements or additions to documentation label Jan 7, 2026
Copy link
Contributor

@moonbox3 moonbox3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After another look, some questions for you.

.add_handoff(summary_agent, [coordinator])
.with_autonomous_mode(
turn_limits={
coordinator.display_name: 5,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me what an agent's display_name has to do with the turn limit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Users can set different turn limits for different agents. I will add a comment. Or we can accept only a single limit.

participants=[coordinator, research_agent, summary_agent],
)
.set_coordinator(coordinator)
.with_start_agent(coordinator)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't we planning to allow the use of executors in the future with these patterns (not only agents as we do today)? .with_start_agent kind of locks us into agent-only use, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don' think we should support executors because that makes implicitly requirements on the executor. This is true for other orchestrations too but those are more manageable. Handoff has features like autonomous mode that is difficult to enforce on custom executors.

# Please refer to `with_plan_review` for proper human interaction during planning phases.
await asyncio.get_event_loop().run_in_executor(None, input, "Press Enter to continue...")

elif isinstance(event, GroupChatRequestSentEvent):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this elif isinstance(event, GroupChatRequestSentEvent) in this sample? As a dev, I need to know that the GroupChatRequestSentEvent type is applicable to the magentic pattern?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can get rid of it in the sample. But this event is generic to group chat.

last_message_id = message_id
print(event.data, end="", flush=True)

elif isinstance(event, MagenticOrchestratorEvent):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to break DevUI - we previously moved away from custom magentic events (we had these in the past) to purely raising AgentRunUpdateEvent which was used throughout workflows.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may not be accurate to create an AgentRunUpdateEvent because the manager is not necessarily an agent.

What is the reason that DevUI can't support custom events?

elif isinstance(event, WorkflowOutputEvent):
output_event = event

if not output_event:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like we should look at somehow always producing a "WorkflowOutputEvent" to avoid the need to have to check if the event is present or not (idle, error, complete, etc).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we will always produce a workflow output event (unless there are bugs, which we should fix). The check here is for type checking purposes because the type of output_event is WorkflowOutputEvent | None.

name="Coordinator",
description="Coordinates multi-agent collaboration by selecting speakers",
instructions="""
ORCHESTRATOR_AGENT_INSTRUCTIONS = """
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These instructions aren't super verbose. Why can't we have them inline below?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am open to it, but this makes the sample look cleaner.

automatically replanning.
Key concepts:
- with_human_input_on_stall(): Enables human intervention when workflow detects stalls
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see us configuring the builder with this with_human_input_on_stall(). It would be good to have this in a sample (which looks to now be deleted).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will update the comment.

with_human_input_on_stall can be achieved via with_plan_review. They serve the same purpose. When a plan review is requested, it contains a flag indicating if the manager is stalled.

self._full_conversation.extend(self._cache)

# Check termination condition before running the agent
if await self._check_terminate_and_yield(cast(WorkflowContext[Never, list[ChatMessage]], ctx)):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it intended that termination conditions can short-circuit immediately on initial input? This could cause surprising behavior where a workflow terminates before any agent has spoken if the initial message triggers the condition.

new_tools: list[AIFunction[Any, Any]] = []
for target in targets:
tool = self._create_handoff_tool(target.target_id, target.description)
if tool.name in existing_names:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To make sure I understand: if tools are added dynamically at runtime, could there still be conflicts?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent orchestration Issues related to agent orchestration documentation Improvements or additions to documentation python workflows Related to Workflows in agent-framework

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

4 participants